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 images-advance

This commit is contained in:
Claus Vium 2020-11-11 13:03:14 +01:00 committed by GitHub
commit 23c7509094
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
586 changed files with 53884 additions and 50003 deletions

View file

@ -0,0 +1,63 @@
jobs:
- job: Build
displayName: 'Build'
strategy:
matrix:
Development:
BuildConfiguration: development
Production:
BuildConfiguration: production
Standalone:
BuildConfiguration: standalone
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Install Node'
inputs:
versionSpec: '12.x'
- task: Cache@2
displayName: 'Check Cache'
inputs:
key: 'yarn | yarn.lock'
path: 'node_modules'
cacheHitVar: CACHE_RESTORED
- script: 'yarn install --frozen-lockfile'
displayName: 'Install Dependencies'
condition: ne(variables.CACHE_RESTORED, 'true')
- script: 'yarn build:development'
displayName: 'Build Development'
condition: eq(variables['BuildConfiguration'], 'development')
- script: 'yarn build:production'
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'
- script: 'mv dist jellyfin-web'
displayName: 'Rename Directory'
- task: ArchiveFiles@2
displayName: 'Archive Directory'
inputs:
rootFolderOrFile: 'jellyfin-web'
includeRootFolder: true
archiveFile: 'jellyfin-web-$(BuildConfiguration)'
- task: PublishPipelineArtifact@1
displayName: 'Publish Release'
inputs:
targetPath: '$(Build.SourcesDirectory)/jellyfin-web-$(BuildConfiguration).zip'
artifactName: 'jellyfin-web-$(BuildConfiguration)'

View file

@ -0,0 +1,29 @@
jobs:
- job: Lint
displayName: 'Lint'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Install Node'
inputs:
versionSpec: '12.x'
- task: Cache@2
displayName: 'Check Cache'
inputs:
key: 'yarn | yarn.lock'
path: 'node_modules'
cacheHitVar: CACHE_RESTORED
- script: 'yarn install --frozen-lockfile'
displayName: 'Install Dependencies'
condition: ne(variables.CACHE_RESTORED, 'true')
- script: 'yarn run lint --quiet'
displayName: 'Run ESLint'
- script: 'yarn run stylelint'
displayName: 'Run Stylelint'

View file

@ -0,0 +1,122 @@
jobs:
- job: BuildPackage
displayName: 'Build Packages'
strategy:
matrix:
CentOS:
BuildConfiguration: centos
Debian:
BuildConfiguration: debian
Fedora:
BuildConfiguration: fedora
Portable:
BuildConfiguration: portable
pool:
vmImage: 'ubuntu-latest'
steps:
- script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-web-$(BuildConfiguration) deployment'
displayName: 'Build Dockerfile'
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="yes" -e BUILD_ID=$(Build.BuildNumber) jellyfin-web-$(BuildConfiguration)'
displayName: 'Run Dockerfile (unstable)'
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="no" -e BUILD_ID=$(Build.BuildNumber) jellyfin-web-$(BuildConfiguration)'
displayName: 'Run Dockerfile (stable)'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
- task: PublishPipelineArtifact@1
displayName: 'Publish Release'
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
targetPath: '$(Build.SourcesDirectory)/deployment/dist'
artifactName: 'jellyfin-web-$(BuildConfiguration)'
- task: SSH@0
displayName: 'Create target directory on repository server'
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
sshEndpoint: repository
runOptions: 'inline'
inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
- task: CopyFilesOverSSH@0
displayName: 'Upload artifacts to repository server'
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
inputs:
sshEndpoint: repository
sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
contents: '**'
targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
- job: BuildDocker
displayName: 'Build Docker'
pool:
vmImage: 'ubuntu-latest'
variables:
- name: JellyfinVersion
value: 0.0.0
steps:
- script: echo "##vso[task.setvariable variable=JellyfinVersion]$( awk -F '/' '{ print $NF }' <<<'$(Build.SourceBranch)' | sed 's/^v//' )"
displayName: Set release version (stable)
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
- task: Docker@2
displayName: 'Push Unstable Image'
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
inputs:
repository: 'jellyfin/jellyfin-web'
command: buildAndPush
buildContext: '.'
Dockerfile: 'deployment/Dockerfile.docker'
containerRegistry: Docker Hub
tags: |
unstable-$(Build.BuildNumber)
unstable
- task: Docker@2
displayName: 'Push Stable Image'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
inputs:
repository: 'jellyfin/jellyfin-web'
command: buildAndPush
buildContext: '.'
Dockerfile: 'deployment/Dockerfile.docker'
containerRegistry: Docker Hub
tags: |
stable-$(Build.BuildNumber)
$(JellyfinVersion)
- job: CollectArtifacts
displayName: 'Collect Artifacts'
dependsOn:
- BuildPackage
- BuildDocker
condition: and(succeeded('BuildPackage'), succeeded('BuildDocker'))
pool:
vmImage: 'ubuntu-latest'
steps:
- task: SSH@0
displayName: 'Update Unstable Repository'
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
inputs:
sshEndpoint: repository
runOptions: 'inline'
inline: 'sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber) unstable'
- task: SSH@0
displayName: 'Update Stable Repository'
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
inputs:
sshEndpoint: repository
runOptions: 'inline'
inline: 'sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber)'

View file

@ -12,94 +12,6 @@ pr:
- '*' - '*'
jobs: jobs:
- job: Build - template: azure-pipelines-build.yml
displayName: 'Build' - template: azure-pipelines-lint.yml
- template: azure-pipelines-package.yml
strategy:
matrix:
Development:
BuildConfiguration: development
Production:
BuildConfiguration: production
Standalone:
BuildConfiguration: standalone
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Install Node'
inputs:
versionSpec: '12.x'
- task: Cache@2
displayName: 'Check Cache'
inputs:
key: 'yarn | yarn.lock'
path: 'node_modules'
cacheHitVar: CACHE_RESTORED
- script: 'yarn install --frozen-lockfile'
displayName: 'Install Dependencies'
condition: ne(variables.CACHE_RESTORED, 'true')
- script: 'yarn build:development'
displayName: 'Build Development'
condition: eq(variables['BuildConfiguration'], 'development')
- script: 'yarn build:production'
displayName: 'Build Bundle'
condition: eq(variables['BuildConfiguration'], 'production')
- script: 'yarn build:standalone'
displayName: 'Build Standalone'
condition: eq(variables['BuildConfiguration'], 'standalone')
- script: 'test -d dist'
displayName: 'Check Build'
- script: 'mv dist jellyfin-web'
displayName: 'Rename Directory'
- task: ArchiveFiles@2
displayName: 'Archive Directory'
inputs:
rootFolderOrFile: 'jellyfin-web'
includeRootFolder: true
archiveFile: 'jellyfin-web-$(BuildConfiguration)'
- task: PublishPipelineArtifact@1
displayName: 'Publish Release'
inputs:
targetPath: '$(Build.SourcesDirectory)/jellyfin-web-$(BuildConfiguration).zip'
artifactName: 'jellyfin-web-$(BuildConfiguration)'
- job: Lint
displayName: 'Lint'
pool:
vmImage: 'ubuntu-latest'
steps:
- task: NodeTool@0
displayName: 'Install Node'
inputs:
versionSpec: '12.x'
- task: Cache@2
displayName: 'Check Cache'
inputs:
key: 'yarn | yarn.lock'
path: 'node_modules'
cacheHitVar: CACHE_RESTORED
- script: 'yarn install --frozen-lockfile'
displayName: 'Install Dependencies'
condition: ne(variables.CACHE_RESTORED, 'true')
- script: 'yarn run lint --quiet'
displayName: 'Run ESLint'
- script: 'yarn run stylelint'
displayName: 'Run Stylelint'

View file

@ -8,5 +8,5 @@ trim_trailing_whitespace = true
insert_final_newline = true insert_final_newline = true
end_of_line = lf end_of_line = lf
[json] [*.json]
indent_size = 2 indent_size = 2

View file

@ -2,4 +2,3 @@ node_modules
dist dist
.idea .idea
.vscode .vscode
src/libraries

View file

@ -1,6 +1,9 @@
const restrictedGlobals = require('confusing-browser-globals');
module.exports = { module.exports = {
root: true, root: true,
plugins: [ plugins: [
'@babel',
'promise', 'promise',
'import', 'import',
'eslint-comments' 'eslint-comments'
@ -27,29 +30,36 @@ module.exports = {
'plugin:compat/recommended' 'plugin:compat/recommended'
], ],
rules: { rules: {
'block-spacing': ["error"], 'block-spacing': ['error'],
'brace-style': ["error"], 'brace-style': ['error', '1tbs', { 'allowSingleLine': true }],
'comma-dangle': ["error", "never"], 'comma-dangle': ['error', 'never'],
'comma-spacing': ["error"], 'comma-spacing': ['error'],
'eol-last': ["error"], 'eol-last': ['error'],
'indent': ["error", 4, { "SwitchCase": 1 }], 'indent': ['error', 4, { 'SwitchCase': 1 }],
'keyword-spacing': ["error"], 'keyword-spacing': ['error'],
'max-statements-per-line': ["error"], 'max-statements-per-line': ['error'],
'no-floating-decimal': ["error"], 'no-floating-decimal': ['error'],
'no-multi-spaces': ["error"], 'no-multi-spaces': ['error'],
'no-multiple-empty-lines': ["error", { "max": 1 }], 'no-multiple-empty-lines': ['error', { 'max': 1 }],
'no-trailing-spaces': ["error"], 'no-restricted-globals': ['error'].concat(restrictedGlobals),
'one-var': ["error", "never"], 'no-trailing-spaces': ['error'],
'quotes': ["error", "single", { "avoidEscape": true, "allowTemplateLiterals": false }], '@babel/no-unused-expressions': ['error', { 'allowShortCircuit': true, 'allowTernary': true, 'allowTaggedTemplates': true }],
'semi': ["error"], 'one-var': ['error', 'never'],
'space-before-blocks': ["error"], 'padded-blocks': ['error', 'never'],
"space-infix-ops": "error" 'prefer-const': ['error', {'destructuring': 'all'}],
'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
'@babel/semi': ['error'],
'no-var': ['error'],
'space-before-blocks': ['error'],
'space-infix-ops': 'error',
'yoda': 'error'
}, },
overrides: [ overrides: [
{ {
files: [ files: [
'./src/**/*.js' './src/**/*.js'
], ],
parser: '@babel/eslint-parser',
env: { env: {
node: false, node: false,
amd: true, amd: true,
@ -74,7 +84,6 @@ module.exports = {
'ApiClient': 'writable', 'ApiClient': 'writable',
'AppInfo': 'writable', 'AppInfo': 'writable',
'chrome': 'writable', 'chrome': 'writable',
'ConnectionManager': 'writable',
'DlnaProfilePage': 'writable', 'DlnaProfilePage': 'writable',
'Dashboard': 'writable', 'Dashboard': 'writable',
'DashboardPage': 'writable', 'DashboardPage': 'writable',
@ -97,11 +106,11 @@ module.exports = {
}, },
rules: { rules: {
// TODO: Fix warnings and remove these rules // TODO: Fix warnings and remove these rules
'no-redeclare': ["warn"], 'no-redeclare': ['off'],
'no-unused-vars': ["warn"], 'no-useless-escape': ['off'],
'no-useless-escape': ["warn"], 'no-unused-vars': ['off'],
// TODO: Remove after ES6 migration is complete // TODO: Remove after ES6 migration is complete
'import/no-unresolved': ["off"] 'import/no-unresolved': ['off']
}, },
settings: { settings: {
polyfills: [ polyfills: [
@ -131,6 +140,7 @@ module.exports = {
'Object.getOwnPropertyDescriptor', 'Object.getOwnPropertyDescriptor',
'Object.getPrototypeOf', 'Object.getPrototypeOf',
'Object.keys', 'Object.keys',
'Object.entries',
'Object.getOwnPropertyNames', 'Object.getOwnPropertyNames',
'Function.name', 'Function.name',
'Function.hasInstance', 'Function.hasInstance',
@ -191,4 +201,4 @@ module.exports = {
} }
} }
] ]
} };

4
.github/CODEOWNERS vendored
View file

@ -1,4 +1,6 @@
.ci @dkanada @EraYaN .ci @dkanada @EraYaN
.github @jellyfin/core .github @jellyfin/core
build.sh @joshuaboniface fedora @joshuaboniface
debian @joshuaboniface
.copr @joshuaboniface
deployment @joshuaboniface deployment @joshuaboniface

View file

@ -1,23 +1,20 @@
--- ---
name: Bug report name: Bug Report
about: Create a bug report about: You have noticed a general issue or regression, and would like to report it
title: ''
labels: bug labels: bug
assignees: ''
--- ---
**Describe the bug** **Describe The Bug**
<!-- A clear and concise description of what the bug is. --> <!-- A clear and concise description of what the bug is. -->
**To Reproduce** **Steps To Reproduce**
<!-- Steps to reproduce the behavior: --> <!-- Steps to reproduce the behavior: -->
1. Go to '...' 1. Go to '...'
2. Click on '....' 2. Click on '....'
3. Scroll down to '....' 3. Scroll down to '....'
4. See error 4. See error
**Expected behavior** **Expected Behavior**
<!-- A clear and concise description of what you expected to happen. --> <!-- A clear and concise description of what you expected to happen. -->
**Logs** **Logs**
@ -27,9 +24,9 @@ assignees: ''
<!-- If applicable, add screenshots to help explain your problem. --> <!-- If applicable, add screenshots to help explain your problem. -->
**System (please complete the following information):** **System (please complete the following information):**
- OS: [e.g. Docker, Debian, Windows] - Platform: [e.g. Linux, Windows, iPhone, Tizen]
- Browser: [e.g. Firefox, Chrome, Safari] - Browser: [e.g. Firefox, Chrome, Safari]
- Jellyfin Version: [e.g. 10.0.1] - Jellyfin Version: [e.g. 10.6.0]
**Additional context** **Additional Context**
<!-- Add any other context about the problem here. --> <!-- Add any other context about the problem here. -->

View file

@ -0,0 +1,22 @@
---
name: Playback Issue
about: You have playback issues with some files
labels: playback
---
**Describe The Bug**
<!-- A clear and concise description of what the bug is. -->
**Media Information**
<!-- Please paste any ffprobe or MediaInfo logs. -->
**Screenshots**
<!-- Add screenshots from the Playback Data and Media Info. -->
**System (please complete the following information):**
- Platform: [e.g. Linux, Windows, iPhone, Tizen]
- Browser: [e.g. Firefox, Chrome, Safari]
- Jellyfin Version: [e.g. 10.6.0]
**Additional Context**
<!-- Add any other context about the problem here. -->

View file

@ -0,0 +1,13 @@
---
name: Technical Discussion
about: You want to discuss technical aspects of changes you intend to make
labels: enhancement
---
<!-- Explain the change and the motivations behind it.
For example, if you plan to rely on a new dependency, explain why and what
it brings to the project.
If you plan to make significant changes, go roughly over the steps you intend
to take and how you would divide the change in PRs of a manageable size. -->

View file

@ -0,0 +1,9 @@
---
name: Meta Issue
about: You want to track a number of other issues as part of a larger project
labels: meta
---
* [ ] Issue 1 [#123]
* [ ] Issue 2 [#456]
* [ ] ...

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View file

@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Feature Request
url: https://features.jellyfin.org/
about: Please head over to our feature request hub to vote on or submit a feature.
- name: Help Or Question
url: https://matrix.to/#/#jellyfin-troubleshooting:matrix.org
about: Please join the troubleshooting Matrix channel to get some help.

24
.github/SUPPORT.md vendored Normal file
View file

@ -0,0 +1,24 @@
# Support
Jellyfin contributors have limited availability to address general support
questions. Please make sure you are using the latest version of Jellyfin.
When looking for support or information, please first search for your
question in these venues:
* [Jellyfin Forum](https://forum.jellyfin.org)
* [Jellyfin Documentation](https://docs.jellyfin.org)
* [Open or **closed** issues in the organization](https://github.com/issues?q=sort%3Aupdated-desc+org%3Ajellyfin+is%3Aissue+)
If you didn't find an answer in the resources above, contributors and other
users are reachable through the following channels:
* #jellyfin on [Matrix](https://matrix.to/#/#jellyfin:matrix.org%22) or [IRC](https://webchat.freenode.net/#jellyfin)
* #jellyfin-troubleshooting on [Matrix](https://matrix.to/#/#jellyfin-troubleshooting:matrix.org) or [IRC](https://webchat.freenode.net/#jellyfin-troubleshooting)
* [/r/jellyfin on Reddit](https://www.reddit.com/r/jellyfin)
GitHub issues are for tracking enhancements and bugs, not general support.
The open source license grants you the freedom to use Jellyfin.
It does not guarantee commitments of other people's time.
Please be respectful and manage your expectations.

6
.gitignore vendored
View file

@ -1,11 +1,11 @@
# config
config.json
# npm # npm
dist dist
web web
node_modules node_modules
# config
config.json
# ide # ide
.idea .idea
.vscode .vscode

View file

@ -34,8 +34,13 @@
- [Ryan Hartzell](https://github.com/ryan-hartzell) - [Ryan Hartzell](https://github.com/ryan-hartzell)
- [Thibault Nocchi](https://github.com/ThibaultNocchi) - [Thibault Nocchi](https://github.com/ThibaultNocchi)
- [MrTimscampi](https://github.com/MrTimscampi) - [MrTimscampi](https://github.com/MrTimscampi)
- [ConfusedPolarBear](https://github.com/ConfusedPolarBear)
- [Sarab Singh](https://github.com/sarab97) - [Sarab Singh](https://github.com/sarab97)
- [GuilhermeHideki](https://github.com/GuilhermeHideki)
- [Andrei Oanca](https://github.com/OancaAndrei) - [Andrei Oanca](https://github.com/OancaAndrei)
- [Cromefire_](https://github.com/cromefire)
- [Orry Verducci](https://github.com/orryverducci)
- [Camc314](https://github.com/camc314)
# Emby Contributors # Emby Contributors

View file

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

View file

@ -1,7 +1,7 @@
--- ---
# We just wrap `build` so this is really it # We just wrap `build` so this is really it
name: "jellyfin-web" name: "jellyfin-web"
version: "10.6.0" version: "10.7.0"
packages: packages:
- debian.all - debian.all
- fedora.all - fedora.all

View file

@ -4,6 +4,7 @@
set -o errexit set -o errexit
set -o pipefail set -o pipefail
set -o xtrace
usage() { usage() {
echo -e "bump_version - increase the shared version and generate changelogs" echo -e "bump_version - increase the shared version and generate changelogs"
@ -23,10 +24,7 @@ build_file="./build.yaml"
new_version="$1" new_version="$1"
# Parse the version from shared version file # Parse the version from shared version file
old_version="$( old_version="$( grep "appVersion" ${shared_version_file} | head -1 | sed -E "s/var appVersion = '([0-9\.]+)';/\1/" | tr -d '[:space:]' )"
grep "appVersion" ${shared_version_file} | head -1 \
| sed -E 's/var appVersion = "([0-9\.]+)";/\1/'
)"
echo "Old version in appHost is: $old_version" echo "Old version in appHost is: $old_version"
# Set the shared version to the specified new_version # Set the shared version to the specified new_version
@ -34,11 +32,8 @@ old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' cha
new_version_sed="$( cut -f1 -d'-' <<<"${new_version}" )" new_version_sed="$( cut -f1 -d'-' <<<"${new_version}" )"
sed -i "s/${old_version_sed}/${new_version_sed}/g" ${shared_version_file} sed -i "s/${old_version_sed}/${new_version_sed}/g" ${shared_version_file}
old_version="$( old_version="$( grep "version:" ${build_file} | sed -E 's/version: "([0-9\.]+[-a-z0-9]*)"/\1/' )"
grep "version:" ${build_file} \ echo "Old version in ${build_file}: ${old_version}"
| sed -E 's/version: "([0-9\.]+[-a-z0-9]*)"/\1/'
)"
echo "Old version in ${build_file}: $old_version`"
# Set the build.yaml version to the specified new_version # Set the build.yaml version to the specified new_version
old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
@ -54,7 +49,7 @@ fi
debian_changelog_file="debian/changelog" debian_changelog_file="debian/changelog"
debian_changelog_temp="$( mktemp )" debian_changelog_temp="$( mktemp )"
# Create new temp file with our changelog # Create new temp file with our changelog
echo -e "jellyfin (${new_version_deb}) unstable; urgency=medium echo -e "jellyfin-web (${new_version_deb}) unstable; urgency=medium
* New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v${new_version} * New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v${new_version}
@ -65,15 +60,15 @@ cat ${debian_changelog_file} >> ${debian_changelog_temp}
mv ${debian_changelog_temp} ${debian_changelog_file} mv ${debian_changelog_temp} ${debian_changelog_file}
# Write out a temporary Yum changelog with our new stuff prepended and some templated formatting # Write out a temporary Yum changelog with our new stuff prepended and some templated formatting
fedora_spec_file="fedora/jellyfin.spec" fedora_spec_file="fedora/jellyfin-web.spec"
fedora_changelog_temp="$( mktemp )" fedora_changelog_temp="$( mktemp )"
fedora_spec_temp_dir="$( mktemp -d )" fedora_spec_temp_dir="$( mktemp -d )"
fedora_spec_temp="${fedora_spec_temp_dir}/jellyfin.spec.tmp" fedora_spec_temp="${fedora_spec_temp_dir}/jellyfin-web.spec.tmp"
# Make a copy of our spec file for hacking # Make a copy of our spec file for hacking
cp ${fedora_spec_file} ${fedora_spec_temp_dir}/ cp ${fedora_spec_file} ${fedora_spec_temp_dir}/
pushd ${fedora_spec_temp_dir} pushd ${fedora_spec_temp_dir}
# Split out the stuff before and after changelog # Split out the stuff before and after changelog
csplit jellyfin.spec "/^%changelog/" # produces xx00 xx01 csplit jellyfin-web.spec "/^%changelog/" # produces xx00 xx01
# Update the version in xx00 # Update the version in xx00
sed -i "s/${old_version_sed}/${new_version_sed}/g" xx00 sed -i "s/${old_version_sed}/${new_version_sed}/g" xx00
# Remove the header from xx01 # Remove the header from xx01
@ -92,5 +87,5 @@ mv ${fedora_spec_temp} ${fedora_spec_file}
rm -rf ${fedora_changelog_temp} ${fedora_spec_temp_dir} rm -rf ${fedora_changelog_temp} ${fedora_spec_temp_dir}
# Stage the changed files for commit # Stage the changed files for commit
git add ${shared_version_file} ${build_file} ${debian_changelog_file} ${fedora_spec_file} Dockerfile* git add ${shared_version_file} ${build_file} ${debian_changelog_file} ${fedora_spec_file}
git status git status

6
debian/changelog vendored
View file

@ -1,3 +1,9 @@
jellyfin-web (10.7.0-1) unstable; urgency=medium
* Forthcoming stable release
-- Jellyfin Packaging Team <packaging@jellyfin.org> Mon, 27 Jul 2020 19:13:31 -0400
jellyfin-web (10.6.0-1) unstable; urgency=medium jellyfin-web (10.6.0-1) unstable; urgency=medium
* New upstream version 10.6.0; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.0 * New upstream version 10.6.0; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.0

View file

@ -1,7 +1,9 @@
FROM centos:7 FROM centos:7
# Docker build arguments # Docker build arguments
ARG SOURCE_DIR=/jellyfin ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist ARG ARTIFACT_DIR=/dist
# Docker run environment # Docker run environment
ENV SOURCE_DIR=/jellyfin ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist ENV ARTIFACT_DIR=/dist
@ -18,10 +20,10 @@ RUN curl -fSsLo /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo
&& yum install -y yarn && yum install -y yarn
# Link to build script # Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.centos.all /build.sh RUN ln -sf ${SOURCE_DIR}/deployment/build.centos /build.sh
VOLUME ${SOURCE_DIR}/ VOLUME ${SOURCE_DIR}
VOLUME ${ARTIFACT_DIR}/ VOLUME ${ARTIFACT_DIR}
ENTRYPOINT ["/build.sh"] ENTRYPOINT ["/build.sh"]

View file

@ -1,7 +1,9 @@
FROM debian:10 FROM debian:10
# Docker build arguments # Docker build arguments
ARG SOURCE_DIR=/jellyfin ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist ARG ARTIFACT_DIR=/dist
# Docker run environment # Docker run environment
ENV SOURCE_DIR=/jellyfin ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist ENV ARTIFACT_DIR=/dist
@ -16,10 +18,10 @@ RUN apt-get update \
RUN npm install -g yarn RUN npm install -g yarn
# Link to build script # Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian.all /build.sh RUN ln -sf ${SOURCE_DIR}/deployment/build.debian /build.sh
VOLUME ${SOURCE_DIR}/ VOLUME ${SOURCE_DIR}
VOLUME ${ARTIFACT_DIR}/ VOLUME ${ARTIFACT_DIR}
ENTRYPOINT ["/build.sh"] ENTRYPOINT ["/build.sh"]

View file

@ -0,0 +1,11 @@
FROM node:alpine
ARG SOURCE_DIR=/src
ARG ARTIFACT_DIR=/jellyfin-web
RUN apk add autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python
WORKDIR ${SOURCE_DIR}
COPY . .
RUN yarn install && mv dist ${ARTIFACT_DIR}

View file

@ -1,7 +1,9 @@
FROM fedora:31 FROM fedora:31
# Docker build arguments # Docker build arguments
ARG SOURCE_DIR=/jellyfin ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist ARG ARTIFACT_DIR=/dist
# Docker run environment # Docker run environment
ENV SOURCE_DIR=/jellyfin ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist ENV ARTIFACT_DIR=/dist
@ -12,10 +14,10 @@ RUN dnf update -y \
&& dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core nodejs-yarn autoconf automake glibc-devel && dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core nodejs-yarn autoconf automake glibc-devel
# Link to build script # Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora.all /build.sh RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora /build.sh
VOLUME ${SOURCE_DIR}/ VOLUME ${SOURCE_DIR}
VOLUME ${ARTIFACT_DIR}/ VOLUME ${ARTIFACT_DIR}
ENTRYPOINT ["/build.sh"] ENTRYPOINT ["/build.sh"]

View file

@ -1,11 +1,12 @@
FROM debian:10 FROM debian:10
# Docker build arguments # Docker build arguments
ARG SOURCE_DIR=/jellyfin ARG SOURCE_DIR=/jellyfin
ARG ARTIFACT_DIR=/dist ARG ARTIFACT_DIR=/dist
# Docker run environment # Docker run environment
ENV SOURCE_DIR=/jellyfin ENV SOURCE_DIR=/jellyfin
ENV ARTIFACT_DIR=/dist ENV ARTIFACT_DIR=/dist
ENV DEB_BUILD_OPTIONS=noddebs
ENV IS_DOCKER=YES ENV IS_DOCKER=YES
# Prepare Debian build environment # Prepare Debian build environment
@ -18,8 +19,8 @@ RUN npm install -g yarn
# Link to build script # Link to build script
RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh
VOLUME ${SOURCE_DIR}/ VOLUME ${SOURCE_DIR}
VOLUME ${ARTIFACT_DIR}/ VOLUME ${ARTIFACT_DIR}
ENTRYPOINT ["/build.sh"] ENTRYPOINT ["/build.sh"]

41
deployment/build.centos Executable file
View file

@ -0,0 +1,41 @@
#!/bin/bash
set -o errexit
set -o xtrace
# move to source directory
pushd ${SOURCE_DIR}
cp -a yarn.lock /tmp/yarn.lock
# modify changelog to unstable configuration if IS_UNSTABLE
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
pushd fedora
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
sed -i "s/Version:.*/Version: ${BUILD_ID}/" jellyfin-web.spec
sed -i "/%changelog/q" jellyfin-web.spec
cat <<EOF >>jellyfin-web.spec
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
- Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
EOF
popd
fi
# build rpm
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
# move the artifacts
mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
rm -f fedora/jellyfin*.tar.gz
cp -a /tmp/yarn.lock yarn.lock
popd

View file

@ -1,27 +0,0 @@
#!/bin/bash
#= CentOS 7 all .rpm
set -o errexit
set -o xtrace
# Move to source directory
pushd ${SOURCE_DIR}
cp -a yarn.lock /tmp/yarn.lock
# Build RPM
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
# Move the artifacts out
mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
rm -f fedora/jellyfin*.tar.gz
cp -a /tmp/yarn.lock yarn.lock
popd

39
deployment/build.debian Executable file
View file

@ -0,0 +1,39 @@
#!/bin/bash
set -o errexit
set -o xtrace
# move to source directory
pushd ${SOURCE_DIR}
cp -a yarn.lock /tmp/yarn.lock
# modify changelog to unstable configuration if IS_UNSTABLE
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
pushd debian
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
cat <<EOF >changelog
jellyfin-web (${BUILD_ID}-unstable) unstable; urgency=medium
* Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
EOF
popd
fi
# build deb
dpkg-buildpackage -us -uc --pre-clean --post-clean
mkdir -p ${ARTIFACT_DIR}
mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}
cp -a /tmp/yarn.lock yarn.lock
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
popd

View file

@ -1,25 +0,0 @@
#!/bin/bash
#= Debian/Ubuntu all .deb
set -o errexit
set -o xtrace
# Move to source directory
pushd ${SOURCE_DIR}
cp -a yarn.lock /tmp/yarn.lock
# Build DEB
dpkg-buildpackage -us -uc --pre-clean --post-clean
mkdir -p ${ARTIFACT_DIR}/
mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}/
cp -a /tmp/yarn.lock yarn.lock
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
popd

41
deployment/build.fedora Executable file
View file

@ -0,0 +1,41 @@
#!/bin/bash
set -o errexit
set -o xtrace
# move to source directory
pushd ${SOURCE_DIR}
cp -a yarn.lock /tmp/yarn.lock
# modify changelog to unstable configuration if IS_UNSTABLE
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
pushd fedora
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
sed -i "s/Version:.*/Version: ${BUILD_ID}/" jellyfin-web.spec
sed -i "/%changelog/q" jellyfin-web.spec
cat <<EOF >>jellyfin-web.spec
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
- Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
EOF
popd
fi
# build rpm
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
# move the artifacts
mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
rm -f fedora/jellyfin*.tar.gz
cp -a /tmp/yarn.lock yarn.lock
popd

View file

@ -1,27 +0,0 @@
#!/bin/bash
#= Fedora 29+ all .rpm
set -o errexit
set -o xtrace
# Move to source directory
pushd ${SOURCE_DIR}
cp -a yarn.lock /tmp/yarn.lock
# Build RPM
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
# Move the artifacts out
mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/
if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
fi
rm -f fedora/jellyfin*.tar.gz
cp -a /tmp/yarn.lock yarn.lock
popd

View file

@ -1,25 +1,27 @@
#!/bin/bash #!/bin/bash
#= Portable .NET DLL .tar.gz
set -o errexit set -o errexit
set -o xtrace set -o xtrace
# Move to source directory # move to source directory
pushd ${SOURCE_DIR} pushd ${SOURCE_DIR}
# Get version # get version
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
version="${BUILD_ID}"
else
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )" version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
fi
# Build archives # build archives
npx yarn install npx yarn install
mv dist/ jellyfin-web_${version} mv dist jellyfin-web_${version}
tar -czf jellyfin-web_${version}_portable.tar.gz jellyfin-web_${version} tar -czf jellyfin-web_${version}_portable.tar.gz jellyfin-web_${version}
rm -rf dist/ rm -rf dist
# Move the artifacts out # move the artifacts
mkdir -p ${ARTIFACT_DIR}/ mkdir -p ${ARTIFACT_DIR}
mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}/ mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}
if [[ ${IS_DOCKER} == YES ]]; then if [[ ${IS_DOCKER} == YES ]]; then
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR} chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}

View file

@ -1,7 +1,7 @@
%global debug_package %{nil} %global debug_package %{nil}
Name: jellyfin-web Name: jellyfin-web
Version: 10.6.0 Version: 10.7.0
Release: 1%{?dist} Release: 1%{?dist}
Summary: The Free Software Media System web client Summary: The Free Software Media System web client
License: GPLv3 License: GPLv3
@ -12,8 +12,11 @@ Source0: jellyfin-web-%{version}.tar.gz
%if 0%{?centos} %if 0%{?centos}
BuildRequires: yarn BuildRequires: yarn
%else %else
BuildRequires nodejs-yarn BuildRequires: nodejs-yarn
%endif %endif
# sadly the yarn RPM at https://dl.yarnpkg.com/rpm/ uses git but doesn't Requires: it
# ditto for Fedora's yarn RPM
BuildRequires: git
BuildArch: noarch BuildArch: noarch
# Disable Automatic Dependency Processing # Disable Automatic Dependency Processing
@ -39,5 +42,7 @@ mv dist %{buildroot}%{_datadir}/jellyfin-web
%{_datadir}/licenses/jellyfin/LICENSE %{_datadir}/licenses/jellyfin/LICENSE
%changelog %changelog
* Mon Jul 27 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
- Forthcoming stable release
* Mon Mar 23 2020 Jellyfin Packaging Team <packaging@jellyfin.org> * Mon Mar 23 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
- Forthcoming stable release - Forthcoming stable release

View file

@ -181,16 +181,15 @@ function copy(query) {
.pipe(browserSync.stream()); .pipe(browserSync.stream());
} }
function copyIndex() {
return src(options.injectBundle.query, { base: './src/' })
.pipe(dest('dist/'))
.pipe(browserSync.stream());
}
function injectBundle() { function injectBundle() {
return src(options.injectBundle.query, { base: './src/' }) return src(options.injectBundle.query, { base: './src/' })
.pipe(inject( .pipe(inject(
src(['src/scripts/apploader.js'], { read: false }, { base: './src/' }), { relative: true } src(['src/scripts/apploader.js'], { read: false }, { base: './src/' }), {
relative: true,
transform: function (filepath) {
return `<script src="${filepath}" defer></script>`;
}
}
)) ))
.pipe(dest('dist/')) .pipe(dest('dist/'))
.pipe(browserSync.stream()); .pipe(browserSync.stream());
@ -200,6 +199,6 @@ function build(standalone) {
return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy)); return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy));
} }
exports.default = series(build(false), copyIndex); exports.default = series(build(false), injectBundle);
exports.standalone = series(build(true), injectBundle); exports.standalone = series(build(true), injectBundle);
exports.serve = series(exports.standalone, serve); exports.serve = series(exports.standalone, serve);

View file

@ -5,27 +5,31 @@
"repository": "https://github.com/jellyfin/jellyfin-web", "repository": "https://github.com/jellyfin/jellyfin-web",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",
"devDependencies": { "devDependencies": {
"@babel/core": "^7.9.6", "@babel/core": "^7.12.3",
"@babel/plugin-transform-modules-amd": "^7.9.6", "@babel/eslint-parser": "^7.12.1",
"@babel/polyfill": "^7.8.7", "@babel/eslint-plugin": "^7.12.1",
"@babel/preset-env": "^7.8.6", "@babel/plugin-proposal-class-properties": "^7.10.1",
"autoprefixer": "^9.8.0", "@babel/plugin-proposal-private-methods": "^7.12.1",
"@babel/plugin-transform-modules-amd": "^7.12.1",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.12.1",
"autoprefixer": "^9.8.6",
"babel-loader": "^8.0.6", "babel-loader": "^8.0.6",
"browser-sync": "^2.26.7", "browser-sync": "^2.26.13",
"clean-webpack-plugin": "^3.0.0", "confusing-browser-globals": "^1.0.10",
"copy-webpack-plugin": "^5.1.1", "copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.4.2", "css-loader": "^5.0.1",
"cssnano": "^4.1.10", "cssnano": "^4.1.10",
"del": "^5.1.0", "del": "^6.0.0",
"eslint": "^6.8.0", "eslint": "^7.13.0",
"eslint-plugin-compat": "^3.5.1", "eslint-plugin-compat": "^3.5.1",
"eslint-plugin-eslint-comments": "^3.1.2", "eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-import": "^2.20.2", "eslint-plugin-import": "^2.22.1",
"eslint-plugin-promise": "^4.2.1", "eslint-plugin-promise": "^4.2.1",
"file-loader": "^6.0.0", "file-loader": "^6.2.0",
"gulp": "^4.0.2", "gulp": "^4.0.2",
"gulp-babel": "^8.0.0", "gulp-babel": "^8.0.0",
"gulp-cli": "^2.2.1", "gulp-cli": "^2.3.0",
"gulp-concat": "^2.6.1", "gulp-concat": "^2.6.1",
"gulp-htmlmin": "^5.0.1", "gulp-htmlmin": "^5.0.1",
"gulp-if": "^3.0.0", "gulp-if": "^3.0.0",
@ -35,52 +39,51 @@
"gulp-postcss": "^8.0.0", "gulp-postcss": "^8.0.0",
"gulp-sass": "^4.0.2", "gulp-sass": "^4.0.2",
"gulp-sourcemaps": "^2.6.5", "gulp-sourcemaps": "^2.6.5",
"gulp-terser": "^1.2.0", "gulp-terser": "^1.4.1",
"html-webpack-plugin": "^4.3.0", "html-webpack-plugin": "^4.5.0",
"lazypipe": "^1.0.2", "lazypipe": "^1.0.2",
"node-sass": "^4.13.1", "node-sass": "^5.0.0",
"postcss-loader": "^3.0.0", "postcss-loader": "^3.0.0",
"postcss-preset-env": "^6.7.0", "postcss-preset-env": "^6.7.0",
"style-loader": "^1.1.3", "style-loader": "^2.0.0",
"stylelint": "^13.5.0", "stylelint": "^13.7.2",
"stylelint-config-rational-order": "^0.1.2", "stylelint-config-rational-order": "^0.1.2",
"stylelint-no-browser-hacks": "^1.2.1", "stylelint-no-browser-hacks": "^1.2.1",
"stylelint-order": "^4.0.0", "stylelint-order": "^4.1.0",
"webpack": "^4.41.5", "webpack": "^5.4.0",
"webpack-cli": "^3.3.10",
"webpack-concat-plugin": "^3.0.0",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^4.2.2", "webpack-merge": "^4.2.2",
"webpack-stream": "^5.2.1" "webpack-stream": "^6.1.0",
"worker-plugin": "^5.0.0"
}, },
"dependencies": { "dependencies": {
"alameda": "^1.4.0", "alameda": "^1.4.0",
"blurhash": "^1.1.3",
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
"core-js": "^3.6.5", "core-js": "^3.7.0",
"date-fns": "^2.14.0", "date-fns": "^2.16.1",
"document-register-element": "^1.14.3", "epubjs": "^0.3.85",
"fast-text-encoding": "^1.0.1", "pdfjs-dist": "2.5.207",
"fast-text-encoding": "^1.0.3",
"flv.js": "^1.5.0", "flv.js": "^1.5.0",
"headroom.js": "^0.11.0", "headroom.js": "^0.12.0",
"hls.js": "^0.13.1", "hls.js": "^0.14.16",
"howler": "^2.2.0", "howler": "^2.2.1",
"intersection-observer": "^0.10.0", "intersection-observer": "^0.11.0",
"jellyfin-apiclient": "^1.1.2", "jellyfin-apiclient": "^1.4.2",
"jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto",
"jquery": "^3.5.1", "jquery": "^3.5.1",
"jstree": "^3.3.7", "jstree": "^3.3.10",
"libarchive.js": "^1.3.0",
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv", "libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv",
"material-design-icons-iconfont": "^5.0.1", "material-design-icons-iconfont": "^6.1.0",
"native-promise-only": "^0.8.0-a", "native-promise-only": "^0.8.0-a",
"page": "^1.11.6", "page": "^1.11.6",
"query-string": "^6.11.1", "query-string": "^6.13.7",
"resize-observer-polyfill": "^1.5.1", "resize-observer-polyfill": "^1.5.1",
"screenfull": "^5.0.2", "screenfull": "^5.0.2",
"shaka-player": "^2.5.11", "sortablejs": "^1.12.0",
"sortablejs": "^1.10.2", "swiper": "^6.3.5",
"swiper": "^5.4.1",
"webcomponents.js": "^0.7.24", "webcomponents.js": "^0.7.24",
"whatwg-fetch": "^3.0.0" "whatwg-fetch": "^3.5.0"
}, },
"babel": { "babel": {
"presets": [ "presets": [
@ -89,34 +92,285 @@
"overrides": [ "overrides": [
{ {
"test": [ "test": [
"src/components/accessSchedule/accessSchedule.js",
"src/components/actionSheet/actionSheet.js",
"src/components/activitylog.js",
"src/components/alert.js",
"src/components/alphaPicker/alphaPicker.js",
"src/components/appFooter/appFooter.js",
"src/components/apphost.js",
"src/components/appRouter.js",
"src/components/autoFocuser.js", "src/components/autoFocuser.js",
"src/components/backdrop/backdrop.js",
"src/components/cardbuilder/cardBuilder.js", "src/components/cardbuilder/cardBuilder.js",
"src/scripts/fileDownloader.js", "src/components/cardbuilder/chaptercardbuilder.js",
"src/components/cardbuilder/peoplecardbuilder.js",
"src/components/channelMapper/channelMapper.js",
"src/components/collectionEditor/collectionEditor.js",
"src/components/confirm/confirm.js",
"src/components/dialog/dialog.js",
"src/components/dialogHelper/dialogHelper.js",
"src/components/directorybrowser/directorybrowser.js",
"src/components/displaySettings/displaySettings.js",
"src/components/favoriteitems.js",
"src/components/fetchhelper.js",
"src/components/filterdialog/filterdialog.js",
"src/components/filtermenu/filtermenu.js",
"src/components/focusManager.js",
"src/components/groupedcards.js",
"src/components/guide/guide.js",
"src/components/guide/guide-settings.js",
"src/components/homeScreenSettings/homeScreenSettings.js",
"src/components/homesections/homesections.js",
"src/components/htmlMediaHelper.js",
"src/components/imageOptionsEditor/imageOptionsEditor.js",
"src/components/images/imageLoader.js", "src/components/images/imageLoader.js",
"src/components/imageDownloader/imageDownloader.js",
"src/components/imageeditor/imageeditor.js",
"src/components/imageUploader/imageUploader.js",
"src/components/indicators/indicators.js",
"src/components/itemContextMenu.js",
"src/components/itemHelper.js",
"src/components/itemidentifier/itemidentifier.js",
"src/components/itemMediaInfo/itemMediaInfo.js",
"src/components/itemsrefresher.js",
"src/components/layoutManager.js",
"src/components/lazyLoader/lazyLoaderIntersectionObserver.js", "src/components/lazyLoader/lazyLoaderIntersectionObserver.js",
"src/components/libraryoptionseditor/libraryoptionseditor.js",
"src/components/listview/listview.js",
"src/components/loading/loading.js",
"src/components/maintabsmanager.js",
"src/components/mediainfo/mediainfo.js",
"src/components/mediaLibraryCreator/mediaLibraryCreator.js",
"src/components/mediaLibraryEditor/mediaLibraryEditor.js",
"src/components/metadataEditor/metadataEditor.js",
"src/components/metadataEditor/personEditor.js",
"src/components/multiSelect/multiSelect.js",
"src/components/notifications/notifications.js",
"src/components/nowPlayingBar/nowPlayingBar.js",
"src/components/packageManager.js",
"src/components/playback/brightnessosd.js",
"src/components/playback/mediasession.js", "src/components/playback/mediasession.js",
"src/components/playback/nowplayinghelper.js",
"src/components/playback/playbackorientation.js",
"src/components/playback/playbackmanager.js",
"src/components/playback/playerSelectionMenu.js",
"src/components/playback/playersettingsmenu.js",
"src/components/playback/playmethodhelper.js",
"src/components/playback/playqueuemanager.js",
"src/components/playback/remotecontrolautoplay.js",
"src/components/playback/volumeosd.js",
"src/components/playbackSettings/playbackSettings.js",
"src/components/playerstats/playerstats.js",
"src/components/playlisteditor/playlisteditor.js",
"src/components/playmenu.js",
"src/components/pluginManager.js",
"src/components/prompt/prompt.js",
"src/components/qualityOptions.js",
"src/components/quickConnectSettings/quickConnectSettings.js",
"src/components/recordingcreator/recordingbutton.js",
"src/components/recordingcreator/recordingcreator.js",
"src/components/recordingcreator/seriesrecordingeditor.js",
"src/components/recordingcreator/recordinghelper.js",
"src/components/refreshdialog/refreshdialog.js",
"src/components/recordingcreator/recordingeditor.js",
"src/components/recordingcreator/recordingfields.js",
"src/components/remotecontrol/remotecontrol.js",
"src/components/sanatizefilename.js", "src/components/sanatizefilename.js",
"src/components/scrollManager.js", "src/components/scrollManager.js",
"src/components/syncplay/playbackPermissionManager.js", "src/plugins/experimentalWarnings/plugin.js",
"src/components/syncplay/groupSelectionMenu.js", "src/plugins/sessionPlayer/plugin.js",
"src/components/syncplay/timeSyncManager.js", "src/plugins/htmlAudioPlayer/plugin.js",
"src/components/syncplay/syncPlayManager.js", "src/plugins/comicsPlayer/plugin.js",
"src/plugins/chromecastPlayer/plugin.js",
"src/components/slideshow/slideshow.js",
"src/components/sortmenu/sortmenu.js",
"src/plugins/htmlVideoPlayer/plugin.js",
"src/plugins/logoScreensaver/plugin.js",
"src/plugins/playAccessValidation/plugin.js",
"src/components/search/searchfields.js",
"src/components/search/searchresults.js",
"src/components/settingshelper.js",
"src/components/shortcuts.js",
"src/components/subtitleeditor/subtitleeditor.js",
"src/components/subtitlesync/subtitlesync.js",
"src/components/subtitlesettings/subtitleappearancehelper.js",
"src/components/subtitlesettings/subtitlesettings.js",
"src/components/syncPlay/groupSelectionMenu.js",
"src/components/syncPlay/playbackPermissionManager.js",
"src/components/syncPlay/syncPlayManager.js",
"src/components/syncPlay/timeSyncManager.js",
"src/components/themeMediaPlayer.js",
"src/components/tabbedview/tabbedview.js",
"src/components/viewManager/viewManager.js",
"src/components/tvproviders/schedulesdirect.js",
"src/components/tvproviders/xmltv.js",
"src/components/toast/toast.js",
"src/components/tunerPicker.js",
"src/components/upnextdialog/upnextdialog.js",
"src/components/userdatabuttons/userdatabuttons.js",
"src/components/viewContainer.js",
"src/components/viewSettings/viewSettings.js",
"src/components/castSenderApi.js",
"src/controllers/session/addServer/index.js",
"src/controllers/session/forgotPassword/index.js",
"src/controllers/session/resetPassword/index.js",
"src/controllers/session/login/index.js",
"src/controllers/session/selectServer/index.js",
"src/controllers/dashboard/apikeys.js",
"src/controllers/dashboard/dashboard.js",
"src/controllers/dashboard/devices/device.js",
"src/controllers/dashboard/devices/devices.js",
"src/controllers/dashboard/dlna/profile.js",
"src/controllers/dashboard/dlna/profiles.js",
"src/controllers/dashboard/dlna/settings.js",
"src/controllers/dashboard/encodingsettings.js",
"src/controllers/dashboard/general.js",
"src/controllers/dashboard/librarydisplay.js",
"src/controllers/dashboard/logs.js",
"src/controllers/music/musicalbums.js",
"src/controllers/music/musicartists.js",
"src/controllers/music/musicgenres.js",
"src/controllers/music/musicplaylists.js",
"src/controllers/music/musicrecommended.js",
"src/controllers/music/songs.js",
"src/controllers/dashboard/library.js",
"src/controllers/dashboard/metadataImages.js",
"src/controllers/dashboard/metadatanfo.js",
"src/controllers/dashboard/networking.js",
"src/controllers/dashboard/notifications/notification/index.js",
"src/controllers/dashboard/notifications/notifications/index.js",
"src/controllers/dashboard/playback.js",
"src/controllers/dashboard/plugins/add/index.js",
"src/controllers/dashboard/plugins/installed/index.js",
"src/controllers/dashboard/plugins/available/index.js",
"src/controllers/dashboard/plugins/repositories/index.js",
"src/controllers/dashboard/quickConnect.js",
"src/controllers/dashboard/scheduledtasks/scheduledtask.js",
"src/controllers/dashboard/scheduledtasks/scheduledtasks.js",
"src/controllers/dashboard/serveractivity.js",
"src/controllers/dashboard/streaming.js",
"src/controllers/dashboard/users/useredit.js",
"src/controllers/dashboard/users/userlibraryaccess.js",
"src/controllers/dashboard/users/usernew.js",
"src/controllers/dashboard/users/userparentalcontrol.js",
"src/controllers/dashboard/users/userpasswordpage.js",
"src/controllers/dashboard/users/userprofilespage.js",
"src/controllers/home.js",
"src/controllers/list.js",
"src/controllers/edititemmetadata.js",
"src/controllers/favorites.js",
"src/controllers/hometab.js",
"src/controllers/movies/moviecollections.js",
"src/controllers/movies/moviegenres.js",
"src/controllers/movies/movies.js",
"src/controllers/movies/moviesrecommended.js",
"src/controllers/movies/movietrailers.js",
"src/controllers/playback/nowplaying.js",
"src/controllers/playback/videoosd.js",
"src/controllers/itemDetails/index.js",
"src/controllers/playback/queue/index.js",
"src/controllers/playback/video/index.js",
"src/controllers/searchpage.js",
"src/controllers/livetv/livetvguide.js",
"src/controllers/livetvtuner.js",
"src/controllers/livetv/livetvsuggested.js",
"src/controllers/livetvstatus.js",
"src/controllers/livetvguideprovider.js",
"src/controllers/livetvsettings.js",
"src/controllers/livetv/livetvrecordings.js",
"src/controllers/livetv/livetvschedule.js",
"src/controllers/livetv/livetvseriestimers.js",
"src/controllers/livetv/livetvchannels.js",
"src/controllers/shows/episodes.js",
"src/controllers/shows/tvgenres.js",
"src/controllers/shows/tvlatest.js",
"src/controllers/shows/tvrecommended.js",
"src/controllers/shows/tvshows.js",
"src/controllers/shows/tvstudios.js",
"src/controllers/shows/tvupcoming.js",
"src/controllers/user/display/index.js",
"src/controllers/user/home/index.js",
"src/controllers/user/menu/index.js",
"src/controllers/user/playback/index.js",
"src/controllers/user/profile/index.js",
"src/controllers/user/quickConnect/index.js",
"src/controllers/user/subtitles/index.js",
"src/controllers/wizard/finish/index.js",
"src/controllers/wizard/remote/index.js",
"src/controllers/wizard/settings/index.js",
"src/controllers/wizard/start/index.js",
"src/controllers/wizard/user/index.js",
"src/elements/emby-button/emby-button.js",
"src/elements/emby-button/paper-icon-button-light.js",
"src/elements/emby-checkbox/emby-checkbox.js",
"src/elements/emby-collapse/emby-collapse.js",
"src/elements/emby-input/emby-input.js",
"src/elements/emby-itemrefreshindicator/emby-itemrefreshindicator.js",
"src/elements/emby-itemscontainer/emby-itemscontainer.js",
"src/elements/emby-playstatebutton/emby-playstatebutton.js",
"src/elements/emby-programcell/emby-programcell.js",
"src/elements/emby-progressbar/emby-progressbar.js",
"src/elements/emby-progressring/emby-progressring.js",
"src/elements/emby-radio/emby-radio.js",
"src/elements/emby-ratingbutton/emby-ratingbutton.js",
"src/elements/emby-scrollbuttons/emby-scrollbuttons.js",
"src/elements/emby-scroller/emby-scroller.js",
"src/elements/emby-select/emby-select.js",
"src/elements/emby-slider/emby-slider.js",
"src/elements/emby-tabs/emby-tabs.js",
"src/elements/emby-textarea/emby-textarea.js",
"src/elements/emby-toggle/emby-toggle.js",
"src/libraries/screensavermanager.js",
"src/libraries/navdrawer/navdrawer.js",
"src/libraries/scroller.js",
"src/plugins/backdropScreensaver/plugin.js",
"src/plugins/bookPlayer/plugin.js",
"src/plugins/pdfPlayer/plugin.js",
"src/plugins/bookPlayer/tableOfContents.js",
"src/plugins/chromecastPlayer/chromecastHelper.js",
"src/plugins/photoPlayer/plugin.js",
"src/plugins/youtubePlayer/plugin.js",
"src/scripts/alphanumericshortcuts.js",
"src/scripts/autoBackdrops.js",
"src/scripts/autocast.js",
"src/scripts/browser.js",
"src/scripts/clientUtils.js",
"src/scripts/datetime.js",
"src/scripts/deleteHelper.js",
"src/scripts/dfnshelper.js", "src/scripts/dfnshelper.js",
"src/scripts/dom.js", "src/scripts/dom.js",
"src/scripts/editorsidebar.js",
"src/scripts/fileDownloader.js",
"src/scripts/filesystem.js", "src/scripts/filesystem.js",
"src/scripts/globalize.js",
"src/scripts/imagehelper.js", "src/scripts/imagehelper.js",
"src/scripts/itembynamedetailpage.js",
"src/scripts/inputManager.js", "src/scripts/inputManager.js",
"src/scripts/deleteHelper.js", "src/scripts/autoThemes.js",
"src/components/actionSheet/actionSheet.js", "src/scripts/themeManager.js",
"src/components/playmenu.js",
"src/components/indicators/indicators.js",
"src/scripts/keyboardNavigation.js", "src/scripts/keyboardNavigation.js",
"src/scripts/libraryMenu.js",
"src/scripts/libraryBrowser.js",
"src/scripts/livetvcomponents.js",
"src/scripts/mouseManager.js",
"src/scripts/multiDownload.js",
"src/scripts/playlists.js",
"src/scripts/scrollHelper.js",
"src/scripts/serverNotifications.js",
"src/scripts/routes.js",
"src/scripts/settings/appSettings.js", "src/scripts/settings/appSettings.js",
"src/scripts/settings/userSettings.js", "src/scripts/settings/userSettings.js",
"src/scripts/settings/webSettings.js" "src/scripts/settings/webSettings.js",
"src/scripts/shell.js",
"src/scripts/taskbutton.js",
"src/scripts/themeLoader.js",
"src/scripts/touchHelper.js"
], ],
"plugins": [ "plugins": [
"@babel/plugin-transform-modules-amd" "@babel/plugin-transform-modules-amd",
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-private-methods"
] ]
} }
] ]
@ -126,7 +380,7 @@
"last 2 Chrome versions", "last 2 Chrome versions",
"last 2 ChromeAndroid versions", "last 2 ChromeAndroid versions",
"last 2 Safari versions", "last 2 Safari versions",
"last 2 iOS versions", "iOS > 10",
"last 2 Edge versions", "last 2 Edge versions",
"Chrome 27", "Chrome 27",
"Chrome 38", "Chrome 38",
@ -134,9 +388,11 @@
"Chrome 53", "Chrome 53",
"Chrome 56", "Chrome 56",
"Chrome 63", "Chrome 63",
"Edge 18",
"Firefox ESR" "Firefox ESR"
], ],
"scripts": { "scripts": {
"start": "yarn serve",
"serve": "gulp serve --development", "serve": "gulp serve --development",
"prepare": "gulp --production", "prepare": "gulp --production",
"build:development": "gulp --development", "build:development": "gulp --development",

33
scripts/duplicates.py Normal file
View file

@ -0,0 +1,33 @@
import sys
import os
import json
# load every string in the source language
# print all duplicate values to a file
cwd = os.getcwd()
source = cwd + '/../src/strings/en-us.json'
reverse = {}
duplicates = {}
with open(source) as en:
strings = json.load(en)
for key, value in strings.items():
if value not in reverse:
reverse[value] = [key]
else:
reverse[value].append(key)
for key, value in reverse.items():
if len(value) > 1:
duplicates[key] = value
print('LENGTH: ' + str(len(duplicates)))
with open('duplicates.txt', 'w') as out:
for item in duplicates:
out.write(json.dumps(item) + ': ')
out.write(json.dumps(duplicates[item]) + '\n')
out.close()
print('DONE')

View file

@ -11,7 +11,7 @@ langlst = os.listdir(langdir)
keys = [] keys = []
with open('scout.txt', 'r') as f: with open('unused.txt', 'r') as f:
for line in f: for line in f:
keys.append(line.strip('\n')) keys.append(line.strip('\n'))

View file

@ -16,7 +16,7 @@ langlst.append('en-us.json')
dep = [] dep = []
def grep(key): def grep(key):
command = 'grep -r -E "(\(\\\"|\(\'|\{)%s(\\\"|\'|\})" --include=\*.{js,html} --exclude-dir=../src/strings ../src' % key command = 'grep -r -E "(\\\"|\'|\{)%s(\\\"|\'|\})" --include=\*.{js,html} --exclude-dir=../src/strings ../src' % key
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = p.stdout.readlines() output = p.stdout.readlines()
if output: if output:

View file

@ -235,6 +235,15 @@ div[data-role=controlgroup] a.ui-btn-active {
width: 50%; width: 50%;
} }
.localUsers .cardText-secondary {
white-space: pre-wrap;
height: 3em;
}
.customCssContainer textarea {
resize: none;
}
@media all and (min-width: 70em) { @media all and (min-width: 70em) {
.dashboardSections { .dashboardSections {
-webkit-flex-wrap: wrap; -webkit-flex-wrap: wrap;

View file

@ -30,6 +30,10 @@
align-items: flex-start; align-items: flex-start;
} }
.align-items-flex-end {
align-items: flex-end;
}
.justify-content-center { .justify-content-center {
justify-content: center; justify-content: center;
} }
@ -38,6 +42,10 @@
justify-content: flex-end; justify-content: flex-end;
} }
.justify-content-space-between {
justify-content: space-between;
}
.flex-wrap-wrap { .flex-wrap-wrap {
flex-wrap: wrap; flex-wrap: wrap;
} }

View file

@ -1,37 +0,0 @@
html {
font-family: "Noto Sans", sans-serif;
font-size: 93%;
-webkit-text-size-adjust: 100%;
text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
h1,
h2,
h3 {
font-family: "Noto Sans", sans-serif;
}
h1 {
font-weight: 400;
font-size: 1.8em;
}
h2 {
font-weight: 400;
font-size: 1.5em;
}
h3 {
font-weight: 400;
font-size: 1.17em;
}
.layout-tv {
font-size: 130%;
}
.layout-mobile {
font-size: 90%;
}

34
src/assets/css/fonts.scss Normal file
View file

@ -0,0 +1,34 @@
@mixin font($weight: null, $size: null) {
font-family: "Noto Sans", sans-serif;
font-weight: $weight;
font-size: $size;
}
html {
@include font;
text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
h1 {
@include font(400, 1.8em);
}
h2 {
@include font(400, 1.5em);
}
h3 {
@include font(400, 1.17em);
}
.layout-tv {
/* Per WebOS and Tizen guidelines, fonts must be 20px minimum.
This takes the 16px baseline and multiplies it by 1.25 to get 20px. */
font-size: 125%;
}
.layout-mobile {
font-size: 90%;
}

View file

@ -1,31 +0,0 @@
h1 {
font-weight: 400;
font-size: 1.8em;
}
.layout-desktop h1 {
font-size: 2em;
}
h2 {
font-weight: 400;
font-size: 1.5em;
}
h3 {
font-weight: 400;
font-size: 1.17em;
}
@media all and (min-height: 720px) {
html {
font-size: 20px;
}
}
/* This is supposed to be 1080p, but had to reduce the min height to account for possible browser chrome */
@media all and (min-height: 1000px) {
html {
font-size: 27px;
}
}

View file

@ -0,0 +1,31 @@
@mixin header-font($size: null) {
font-weight: 400;
font-size: $size;
}
html {
@media all and (min-height: 720px) {
font-size: 20px;
}
/* This is supposed to be 1080p, but had to reduce the min height to account for possible browser chrome */
@media all and (min-height: 1000px) {
font-size: 27px;
}
}
h1 {
@include header-font(1.8em);
.layout-desktop & {
font-size: 2em;
}
}
h2 {
@include header-font(1.8em);
}
h3 {
@include header-font(1.17em);
}

View file

@ -1,8 +0,0 @@
html {
font-size: 82% !important;
}
.formDialogFooter {
position: static !important;
margin: 0 -1em !important;
}

3
src/assets/css/ios.scss Normal file
View file

@ -0,0 +1,3 @@
html {
font-size: 82% !important;
}

View file

@ -24,14 +24,14 @@
padding-top: 7em !important; padding-top: 7em !important;
} }
.layout-mobile .libraryPage {
padding-top: 4em !important;
}
.itemDetailPage { .itemDetailPage {
padding-top: 0 !important; padding-top: 0 !important;
} }
.layout-tv .itemDetailPage {
padding-top: 4.2em !important;
}
.standalonePage { .standalonePage {
padding-top: 4.5em !important; padding-top: 4.5em !important;
} }
@ -164,6 +164,13 @@
display: flex; display: flex;
flex-direction: column; flex-direction: column;
contain: layout style paint; contain: layout style paint;
transition: background ease-in-out 0.5s;
}
.layout-tv .skinHeader {
/* In TV layout, it makes more sense to keep the top bar at the top of the page
Having it follow the view only makes us lose vertical space, while not being focusable */
position: relative;
} }
.hiddenViewMenuBar .skinHeader { .hiddenViewMenuBar .skinHeader {
@ -178,6 +185,10 @@
width: 100%; width: 100%;
} }
.layout-tv .sectionTabs {
width: 55%;
}
.selectedMediaFolder { .selectedMediaFolder {
background-color: #f2f2f2 !important; background-color: #f2f2f2 !important;
} }
@ -235,12 +246,6 @@
text-align: center; text-align: center;
} }
.layout-desktop .searchTabButton,
.layout-mobile .searchTabButton,
.layout-tv .headerSearchButton {
display: none !important;
}
.mainDrawer-scrollContainer { .mainDrawer-scrollContainer {
padding-bottom: 10vh; padding-bottom: 10vh;
} }
@ -272,7 +277,7 @@
} }
} }
@media all and (max-width: 84em) { @media all and (max-width: 100em) {
.withSectionTabs .headerTop { .withSectionTabs .headerTop {
padding-bottom: 0.55em; padding-bottom: 0.55em;
} }
@ -280,9 +285,13 @@
.sectionTabs { .sectionTabs {
font-size: 83.5%; font-size: 83.5%;
} }
.layout-tv .sectionTabs {
width: 100%;
}
} }
@media all and (min-width: 84em) { @media all and (min-width: 100em) {
.headerTop { .headerTop {
padding: 0.8em 0.8em; padding: 0.8em 0.8em;
} }
@ -438,16 +447,17 @@
background-repeat: no-repeat; background-repeat: no-repeat;
background-position: center; background-position: center;
background-attachment: fixed; background-attachment: fixed;
height: 50vh; height: 40vh;
position: relative; position: relative;
animation: backdrop-fadein 800ms ease-in normal both;
} }
.layout-mobile .itemBackdrop { .layout-mobile .itemBackdrop {
background-attachment: scroll; background-attachment: scroll;
height: 26.5vh;
} }
.layout-desktop .itemBackdrop::after, .layout-desktop .itemBackdrop::after {
.layout-tv .itemBackdrop::after {
content: ""; content: "";
width: 100%; width: 100%;
height: 100%; height: 100%;
@ -455,18 +465,28 @@
display: block; display: block;
} }
.layout-desktop .noBackdrop .itemBackdrop, .layout-tv .itemBackdrop,
.layout-tv .noBackdrop .itemBackdrop { .layout-desktop .noBackdrop .itemBackdrop {
display: none; display: none;
} }
.detailPageContent { .detailPageContent {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding-left: 2%; padding-left: 32.45vw;
padding-right: 2%; padding-right: 2%;
} }
.layout-mobile .detailPageContent {
padding-left: 5%;
padding-right: 5%;
}
.layout-desktop .detailPageContent .emby-scroller,
.layout-tv .detailPageContent .emby-scroller {
margin-left: 0;
}
.layout-desktop .noBackdrop .detailPageContent, .layout-desktop .noBackdrop .detailPageContent,
.layout-tv .noBackdrop .detailPageContent { .layout-tv .noBackdrop .detailPageContent {
margin-top: 2.5em; margin-top: 2.5em;
@ -477,6 +497,10 @@
margin-top: 0; margin-top: 0;
} }
.detailSectionContent a {
color: inherit;
}
.personBackdrop { .personBackdrop {
background-size: contain; background-size: contain;
} }
@ -495,7 +519,23 @@
.parentName { .parentName {
display: block; display: block;
margin-bottom: 0.5em; margin: 0 0 0;
}
.layout-mobile .parentName {
margin: 0.6em 0 0;
}
.musicParentName {
margin: 0.15em 0 0.2em;
}
.layout-mobile .musicParentName {
margin: -0.25em 0 0.25em;
}
.layout-mobile .itemExternalLinks {
display: none;
} }
.mainDetailButtons { .mainDetailButtons {
@ -503,8 +543,6 @@
-webkit-box-align: center; -webkit-box-align: center;
-webkit-align-items: center; -webkit-align-items: center;
align-items: center; align-items: center;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
margin: 1em 0; margin: 1em 0;
} }
@ -520,6 +558,35 @@
font-weight: 600; font-weight: 600;
} }
.itemName.originalTitle {
margin: 0.2em 0 0.2em;
}
.itemName.parentNameLast {
margin: 0 0 0;
}
.layout-mobile .itemName.parentNameLast {
margin: 0.4em 0 0.4em;
}
.layout-mobile h1.itemName,
.layout-mobile h1.parentName {
font-size: 1.6em;
}
.itemName.parentNameLast.withOriginalTitle {
margin: 0 0 0;
}
.layout-mobile .itemName.parentNameLast.withOriginalTitle {
margin: 0.6em 0 0;
}
.layout-mobile .itemName.originalTitle {
margin: 0.5em 0 0.5em;
}
.nameContainer { .nameContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -546,6 +613,19 @@
text-align: center; text-align: center;
} }
.layout-mobile .mainDetailButtons {
margin-top: 1em;
margin-bottom: 0.5em;
}
.subtitle {
margin: 0.15em 0 0.2em;
}
.layout-mobile .subtitle {
margin: 0.2em 0 0.2em;
}
.detailPagePrimaryContainer { .detailPagePrimaryContainer {
display: flex; display: flex;
align-items: center; align-items: center;
@ -553,10 +633,14 @@
z-index: 2; z-index: 2;
} }
.layout-tv .detailPagePrimaryContainer {
display: block;
}
.layout-mobile .detailPagePrimaryContainer { .layout-mobile .detailPagePrimaryContainer {
display: block; display: block;
position: relative; position: relative;
top: 0; padding: 0.5em 3.3% 0.5em;
} }
.layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer, .layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer,
@ -566,13 +650,18 @@
padding-left: 32.45vw; padding-left: 32.45vw;
} }
.layout-desktop .detailSticky, .layout-desktop .detailRibbon {
.layout-tv .detailSticky {
margin-top: -7.2em; margin-top: -7.2em;
height: 7.2em;
} }
.layout-desktop .noBackdrop .detailSticky, .layout-tv .detailRibbon {
.layout-tv .noBackdrop .detailSticky { margin-top: 0;
height: inherit;
}
.layout-desktop .noBackdrop .detailRibbon,
.layout-tv .noBackdrop .detailRibbon {
margin-top: 0; margin-top: 0;
} }
@ -584,6 +673,9 @@
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis; text-overflow: ellipsis;
text-align: left; text-align: left;
min-width: 0;
max-width: 100%;
overflow: hidden;
} }
.layout-mobile .infoText { .layout-mobile .infoText {
@ -594,12 +686,29 @@
margin: 1.25em 0; margin: 1.25em 0;
} }
.detailImageContainer { .layout-mobile .detailPageSecondaryContainer {
position: relative; margin: 1em 0;
margin-top: -25vh; }
.layout-mobile .detailImageContainer {
display: none;
}
.detailImageContainer .card {
position: absolute;
top: 50%;
float: left; float: left;
width: 25vw; width: 25vw;
z-index: 3; z-index: 3;
transform: translateY(-50%);
}
.detailImageContainer .card.backdropCard {
top: 35%;
}
.detailImageContainer .card.squareCard {
top: 40%;
} }
.layout-desktop .noBackdrop .detailImageContainer, .layout-desktop .noBackdrop .detailImageContainer,
@ -612,11 +721,11 @@
} }
.detailLogo { .detailLogo {
width: 30vw; width: 25vw;
height: 25vh; height: 16vh;
position: absolute; position: absolute;
top: 10vh; top: 10vh;
right: 20vw; right: 25vw;
background-size: contain; background-size: contain;
} }
@ -641,7 +750,8 @@ div.itemDetailGalleryLink.defaultCardBackground {
} }
.itemDetailGalleryLink.defaultCardBackground { .itemDetailGalleryLink.defaultCardBackground {
height: 23vw; /* Dirty hack to get it to look somewhat square. Less than ideal. */ /* Dirty hack to get it to look somewhat square. Less than ideal. */
height: 23vw;
} }
.itemDetailGalleryLink.defaultCardBackground > .material-icons { .itemDetailGalleryLink.defaultCardBackground > .material-icons {
@ -655,14 +765,18 @@ div.itemDetailGalleryLink.defaultCardBackground {
position: relative; position: relative;
} }
.layout-desktop .detailPageWrapperContainer, .layout-desktop .itemBackdrop {
.layout-tv .detailPageWrapperContainer { height: 40vh;
margin-top: 7.2em;
} }
.layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer, .layout-desktop .detailPageWrapperContainer,
.layout-desktop #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer { .layout-tv .detailPageWrapperContainer {
padding-left: 3.3%; margin-top: 0.1em;
}
.layout-desktop .detailImageContainer .card,
.layout-tv .detailImageContainer .card {
top: 10%;
} }
.btnPlaySimple { .btnPlaySimple {
@ -677,13 +791,8 @@ div.itemDetailGalleryLink.defaultCardBackground {
} }
.emby-button.detailFloatingButton { .emby-button.detailFloatingButton {
position: absolute; font-size: 1.4em;
background-color: rgba(0, 0, 0, 0.5) !important; margin-right: 0.5em !important;
z-index: 1;
top: 50%;
left: 50%;
margin: -2.2em 0 0 -2.2em;
padding: 0.4em !important;
color: rgba(255, 255, 255, 0.76); color: rgba(255, 255, 255, 0.76);
} }
@ -693,16 +802,12 @@ div.itemDetailGalleryLink.defaultCardBackground {
@media all and (max-width: 62.5em) { @media all and (max-width: 62.5em) {
.parentName { .parentName {
margin-bottom: 1em; margin-bottom: 0;
} }
.itemDetailPage { .itemDetailPage {
padding-top: 0 !important; padding-top: 0 !important;
} }
.detailimg-hidemobile {
display: none;
}
} }
@media all and (min-width: 31.25em) { @media all and (min-width: 31.25em) {
@ -750,7 +855,7 @@ div.itemDetailGalleryLink.defaultCardBackground {
-webkit-align-items: center; -webkit-align-items: center;
align-items: center; align-items: center;
margin: 0 !important; margin: 0 !important;
padding: 0.5em 0.7em !important; padding: 0.7em 0.7em !important;
} }
@media all and (min-width: 29em) { @media all and (min-width: 29em) {
@ -797,9 +902,9 @@ div.itemDetailGalleryLink.defaultCardBackground {
} }
.detailImageProgressContainer { .detailImageProgressContainer {
position: absolute;
bottom: 0; bottom: 0;
width: 22.786458333333332vw; margin-top: -0.4vw;
width: 100%;
} }
.detailButton-text { .detailButton-text {
@ -818,25 +923,7 @@ div.itemDetailGalleryLink.defaultCardBackground {
} }
} }
@media all and (min-width: 62.5em) { @media all and (min-width: 100em) {
.headerTop {
padding-left: 0.8em;
padding-right: 0.8em;
}
.headerTabs {
align-self: center;
width: auto;
align-items: center;
justify-content: center;
margin-top: -4.2em;
position: relative;
}
.detailFloatingButton {
display: none !important;
}
.personBackdrop { .personBackdrop {
display: none !important; display: none !important;
} }
@ -845,6 +932,11 @@ div.itemDetailGalleryLink.defaultCardBackground {
font-size: 108%; font-size: 108%;
margin: 1.25em 0; margin: 1.25em 0;
} }
.layout-tv .mainDetailButtons {
font-size: 108%;
margin: 1em 0 1.25em;
}
} }
@media all and (max-width: 50em) { @media all and (max-width: 50em) {
@ -866,6 +958,10 @@ div.itemDetailGalleryLink.defaultCardBackground {
} }
} }
.detailVerticalSection .emby-scrollbuttons {
padding-top: 0.4em;
}
.layout-tv .detailVerticalSection { .layout-tv .detailVerticalSection {
margin-bottom: 3.4em !important; margin-bottom: 3.4em !important;
} }
@ -954,6 +1050,10 @@ div.itemDetailGalleryLink.defaultCardBackground {
margin-bottom: 2.7em; margin-bottom: 2.7em;
} }
.layout-mobile .verticalSection-extrabottompadding {
margin-bottom: 1em;
}
.sectionTitleButton, .sectionTitleButton,
.sectionTitleIconButton { .sectionTitleIconButton {
margin-right: 0 !important; margin-right: 0 !important;
@ -979,7 +1079,13 @@ div.itemDetailGalleryLink.defaultCardBackground {
div:not(.sectionTitleContainer-cards) > .sectionTitle-cards { div:not(.sectionTitleContainer-cards) > .sectionTitle-cards {
margin: 0; margin: 0;
padding-top: 1.25em; padding-top: 0.5em;
padding-bottom: 0.2em;
}
.layout-mobile :not(.sectionTitleContainer-cards) > .sectionTitle-cards {
margin: 0;
padding-top: 0.5em;
} }
.sectionTitleButton { .sectionTitleButton {
@ -1046,13 +1152,13 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards {
} }
.layout-tv .padded-top-focusscale { .layout-tv .padded-top-focusscale {
padding-top: 1em; padding-top: 1.5em;
margin-top: -1em; margin-top: -1.5em;
} }
.layout-tv .padded-bottom-focusscale { .layout-tv .padded-bottom-focusscale {
padding-bottom: 1em; padding-bottom: 1.5em;
margin-bottom: -1em; margin-bottom: -1.5em;
} }
@media all and (min-height: 31.25em) { @media all and (min-height: 31.25em) {
@ -1132,6 +1238,12 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards {
.trackSelections .selectContainer .selectLabel { .trackSelections .selectContainer .selectLabel {
margin: 0 0.2em 0 0; margin: 0 0.2em 0 0;
line-height: 1.75;
}
.layout-mobile .detailsGroupItem .label,
.layout-mobile .trackSelections .selectContainer .selectLabel {
flex-basis: 4.5em;
} }
.trackSelections .selectContainer .detailTrackSelect { .trackSelections .selectContainer .detailTrackSelect {
@ -1144,3 +1256,21 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards {
margin-top: 0; margin-top: 0;
font-size: 1.4em; font-size: 1.4em;
} }
.overview-controls {
display: flex;
justify-content: flex-end;
}
.detail-clamp-text {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: 12;
-webkit-box-orient: vertical;
}
@media all and (min-width: 40em) {
.detail-clamp-text {
-webkit-line-clamp: 6;
}
}

View file

@ -2,8 +2,8 @@
padding-bottom: 15em; padding-bottom: 15em;
} }
@media all and (min-width: 62.5em) {
#guideTab { #guideTab {
@media all and (min-width: 62.5em) {
padding-left: 0.5em; padding-left: 0.5em;
} }
} }

View file

@ -12,6 +12,7 @@
.hiddenScrollX, .hiddenScrollX,
.layout-tv .scrollX { .layout-tv .scrollX {
-ms-overflow-style: none; -ms-overflow-style: none;
scrollbar-width: none;
} }
.hiddenScrollX-forced { .hiddenScrollX-forced {
@ -40,6 +41,7 @@
.hiddenScrollY, .hiddenScrollY,
.layout-tv .smoothScrollY { .layout-tv .smoothScrollY {
-ms-overflow-style: none; -ms-overflow-style: none;
scrollbar-width: none;
/* Can't do this because it not only hides the scrollbar, but also prevents scrolling */ /* Can't do this because it not only hides the scrollbar, but also prevents scrolling */

View file

@ -1,10 +1,21 @@
body, @mixin fullpage {
html {
margin: 0; margin: 0;
padding: 0; padding: 0;
height: 100%; height: 100%;
} }
html {
@include fullpage;
line-height: 1.35;
}
body {
@include fullpage;
overflow-x: hidden;
background-color: transparent !important;
-webkit-font-smoothing: antialiased;
}
.clipForScreenReader { .clipForScreenReader {
clip: rect(1px, 1px, 1px, 1px); clip: rect(1px, 1px, 1px, 1px);
clip-path: inset(50%); clip-path: inset(50%);
@ -18,7 +29,7 @@ html {
.material-icons { .material-icons {
/* Fix font ligatures on older WebOS versions */ /* Fix font ligatures on older WebOS versions */
-webkit-font-feature-settings: "liga"; font-feature-settings: "liga";
} }
.backgroundContainer { .backgroundContainer {
@ -30,26 +41,12 @@ html {
contain: strict; contain: strict;
} }
html {
line-height: 1.35;
}
.layout-mobile, .layout-mobile,
.layout-tv { .layout-tv {
-webkit-touch-callout: none; -webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; user-select: none;
} }
body {
overflow-x: hidden;
background-color: transparent !important;
-webkit-font-smoothing: antialiased;
}
.mainAnimatedPage { .mainAnimatedPage {
contain: style size !important; contain: style size !important;
} }
@ -62,7 +59,7 @@ body {
overflow-y: hidden !important; overflow-y: hidden !important;
} }
div[data-role=page] { div[data-role="page"] {
outline: 0; outline: 0;
} }
@ -75,11 +72,11 @@ div[data-role=page] {
padding-left: 0.15em; padding-left: 0.15em;
font-weight: 400; font-weight: 400;
white-space: normal !important; white-space: normal !important;
}
.fieldDescription + .fieldDescription { + .fieldDescription {
margin-top: 0.3em; margin-top: 0.3em;
} }
}
.content-primary, .content-primary,
.padded-bottom-page, .padded-bottom-page,
@ -89,9 +86,14 @@ div[data-role=page] {
padding-bottom: 5em !important; padding-bottom: 5em !important;
} }
.readOnlyContent {
@media all and (min-width: 50em) { @media all and (min-width: 50em) {
.readOnlyContent, max-width: 54em;
}
}
form { form {
@media all and (min-width: 50em) {
max-width: 54em; max-width: 54em;
} }
} }
@ -111,15 +113,20 @@ div[data-role=page] {
.headroom { .headroom {
will-change: transform; will-change: transform;
transition: transform 200ms linear; transition: transform 200ms linear;
}
.headroom--pinned { &--pinned {
transform: translateY(0%); transform: translateY(0%);
} }
.headroom--unpinned { &--unpinned {
transform: translateY(-100%); transform: translateY(-100%);
} }
}
.drawerContent {
/* make sure the bottom of the drawer is visible when music is playing */
padding-bottom: 4em;
}
.force-scroll { .force-scroll {
overflow-y: scroll; overflow-y: scroll;
@ -128,3 +135,17 @@ div[data-role=page] {
.hide-scroll { .hide-scroll {
overflow-y: hidden; overflow-y: hidden;
} }
.w-100 {
width: 100%;
}
.margin-auto-x {
margin-left: auto;
margin-right: auto;
}
.margin-auto-y {
margin-top: auto;
margin-bottom: auto;
}

View file

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Before After
Before After

View file

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24"><title>Microsoft Edge icon</title><path d="M21.86 17.86q.14 0 .25.12.1.13.1.25t-.11.33l-.32.46-.43.53-.44.5q-.21.25-.38.42l-.22.23q-.58.53-1.34 1.04-.76.51-1.6.91-.86.4-1.74.64t-1.67.24q-.9 0-1.69-.28-.8-.28-1.48-.78-.68-.5-1.22-1.17-.53-.66-.92-1.44-.38-.77-.58-1.6-.2-.83-.2-1.67 0-1 .32-1.96.33-.97.87-1.8.14.95.55 1.77.41.82 1.02 1.5.6.68 1.38 1.21.78.54 1.64.9.86.36 1.77.56.92.2 1.8.2 1.12 0 2.18-.24 1.06-.23 2.06-.72l.2-.1.2-.05zm-15.5-1.27q0 1.1.27 2.15.27 1.06.78 2.03.51.96 1.24 1.77.74.82 1.66 1.4-1.47-.2-2.8-.74-1.33-.55-2.48-1.37-1.15-.83-2.08-1.9-.92-1.07-1.58-2.33T.36 14.94Q0 13.54 0 12.06q0-.81.32-1.49.31-.68.83-1.23.53-.55 1.2-.96.66-.4 1.35-.66.74-.27 1.5-.39.78-.12 1.55-.12.7 0 1.42.1.72.12 1.4.35.68.23 1.32.57.63.35 1.16.83-.35 0-.7.07-.33.07-.65.23v-.02q-.63.28-1.2.74-.57.46-1.05 1.04-.48.58-.87 1.26-.38.67-.65 1.39-.27.71-.42 1.44-.15.72-.15 1.38zM11.96.06q1.7 0 3.33.39 1.63.38 3.07 1.15 1.43.77 2.62 1.93 1.18 1.16 1.98 2.7.49.94.76 1.96.28 1 .28 2.08 0 .89-.23 1.7-.24.8-.69 1.48-.45.68-1.1 1.22-.64.53-1.45.88-.54.24-1.11.36-.58.13-1.16.13-.42 0-.97-.03-.54-.03-1.1-.12-.55-.1-1.05-.28-.5-.19-.84-.5-.12-.09-.23-.24-.1-.16-.1-.33 0-.15.16-.35.16-.2.35-.5.2-.28.36-.68.16-.4.16-.95 0-1.06-.4-1.96-.4-.91-1.06-1.64-.66-.74-1.52-1.28-.86-.55-1.79-.89-.84-.3-1.72-.44-.87-.14-1.76-.14-1.55 0-3.06.45T.94 7.55q.71-1.74 1.81-3.13 1.1-1.38 2.52-2.35Q6.68 1.1 8.37.58q1.7-.52 3.58-.52Z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Before After
Before After

View file

@ -2,160 +2,158 @@
* require.js module definitions bundled by webpack * require.js module definitions bundled by webpack
*/ */
// Use define from require.js not webpack's define // Use define from require.js not webpack's define
var _define = window.define; const _define = window.define;
// document-register-element
var docRegister = require('document-register-element');
_define('document-register-element', function() {
return docRegister;
});
// fetch // fetch
var fetch = require('whatwg-fetch'); const fetch = require('whatwg-fetch');
_define('fetch', function() { _define('fetch', function() {
return fetch; return fetch;
}); });
// Blurhash
const blurhash = require('blurhash');
_define('blurhash', function() {
return blurhash;
});
// query-string // query-string
var query = require('query-string'); const query = require('query-string');
_define('queryString', function() { _define('queryString', function() {
return query; return query;
}); });
// flvjs // flvjs
var flvjs = require('flv.js/dist/flv').default; const flvjs = require('flv.js/dist/flv').default;
_define('flvjs', function() { _define('flvjs', function() {
return flvjs; return flvjs;
}); });
// jstree // jstree
var jstree = require('jstree'); const jstree = require('jstree');
require('jstree/dist/themes/default/style.css'); require('jstree/dist/themes/default/style.css');
_define('jstree', function() { _define('jstree', function() {
return jstree; return jstree;
}); });
// jquery // jquery
var jquery = require('jquery'); const jquery = require('jquery');
_define('jQuery', function() { _define('jQuery', function() {
return jquery; return jquery;
}); });
// hlsjs // hlsjs
var hlsjs = require('hls.js'); const hlsjs = require('hls.js');
_define('hlsjs', function() { _define('hlsjs', function() {
return hlsjs; return hlsjs;
}); });
// howler // howler
var howler = require('howler'); const howler = require('howler');
_define('howler', function() { _define('howler', function() {
return howler; return howler;
}); });
// resize-observer-polyfill // resize-observer-polyfill
var resize = require('resize-observer-polyfill').default; const resize = require('resize-observer-polyfill').default;
_define('resize-observer-polyfill', function() { _define('resize-observer-polyfill', function() {
return resize; return resize;
}); });
// shaka
var shaka = require('shaka-player');
_define('shaka', function() {
return shaka;
});
// swiper // swiper
var swiper = require('swiper/js/swiper'); const swiper = require('swiper/swiper-bundle');
require('swiper/css/swiper.min.css'); require('swiper/swiper-bundle.css');
_define('swiper', function() { _define('swiper', function() {
return swiper; return swiper;
}); });
// sortable // sortable
var sortable = require('sortablejs').default; const sortable = require('sortablejs').default;
_define('sortable', function() { _define('sortable', function() {
return sortable; return sortable;
}); });
// webcomponents // webcomponents
var webcomponents = require('webcomponents.js/webcomponents-lite'); const webcomponents = require('webcomponents.js/webcomponents-lite');
_define('webcomponents', function() { _define('webcomponents', function() {
return webcomponents; return webcomponents;
}); });
// libass-wasm // libass-wasm
var libassWasm = require('libass-wasm'); const libassWasm = require('libass-wasm');
_define('JavascriptSubtitlesOctopus', function() { _define('JavascriptSubtitlesOctopus', function() {
return libassWasm; return libassWasm;
}); });
// material-icons // material-icons
var materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css'); const materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css');
_define('material-icons', function() { _define('material-icons', function() {
return materialIcons; return materialIcons;
}); });
// noto font const epubjs = require('epubjs');
var noto = require('jellyfin-noto'); _define('epubjs', function () {
_define('jellyfin-noto', function () { return epubjs;
return noto; });
const pdfjs = require('pdfjs-dist/build/pdf');
_define('pdfjs', function () {
return pdfjs;
}); });
// page.js // page.js
var page = require('page'); const page = require('page');
_define('page', function() { _define('page', function() {
return page; return page;
}); });
// core-js // core-js
var polyfill = require('@babel/polyfill/dist/polyfill'); const polyfill = require('@babel/polyfill/dist/polyfill');
_define('polyfill', function () { _define('polyfill', function () {
return polyfill; return polyfill;
}); });
// domtokenlist-shim // domtokenlist-shim
var classlist = require('classlist.js'); const classlist = require('classlist.js');
_define('classlist-polyfill', function () { _define('classlist-polyfill', function () {
return classlist; return classlist;
}); });
// Date-FNS // Date-FNS
var dateFns = require('date-fns'); const dateFns = require('date-fns');
_define('date-fns', function () { _define('date-fns', function () {
return dateFns; return dateFns;
}); });
var dateFnsLocale = require('date-fns/locale'); const dateFnsLocale = require('date-fns/locale');
_define('date-fns/locale', function () { _define('date-fns/locale', function () {
return dateFnsLocale; return dateFnsLocale;
}); });
var fast_text_encoding = require('fast-text-encoding'); const fast_text_encoding = require('fast-text-encoding');
_define('fast-text-encoding', function () { _define('fast-text-encoding', function () {
return fast_text_encoding; return fast_text_encoding;
}); });
// intersection-observer // intersection-observer
var intersection_observer = require('intersection-observer'); const intersection_observer = require('intersection-observer');
_define('intersection-observer', function () { _define('intersection-observer', function () {
return intersection_observer; return intersection_observer;
}); });
// screenfull // screenfull
var screenfull = require('screenfull'); const screenfull = require('screenfull');
_define('screenfull', function () { _define('screenfull', function () {
return screenfull; return screenfull;
}); });
// headroom.js // headroom.js
var headroom = require('headroom.js/dist/headroom'); const headroom = require('headroom.js/dist/headroom');
_define('headroom', function () { _define('headroom', function () {
return headroom; return headroom;
}); });
// apiclient // apiclient
var apiclient = require('jellyfin-apiclient'); const apiclient = require('jellyfin-apiclient');
_define('apiclient', function () { _define('apiclient', function () {
return apiclient.ApiClient; return apiclient.ApiClient;
@ -176,3 +174,9 @@ _define('connectionManagerFactory', function () {
_define('appStorage', function () { _define('appStorage', function () {
return apiclient.AppStorage; return apiclient.AppStorage;
}); });
// libarchive.js
const libarchive = require('libarchive.js');
_define('libarchive', function () {
return libarchive;
});

View file

@ -1,9 +1,20 @@
define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-button-light', 'formDialogStyle'], function (dialogHelper, datetime, globalize) { /* eslint-disable indent */
'use strict';
/**
* Module for controlling user parental control from.
* @module components/accessSchedule/accessSchedule
*/
import dialogHelper from 'dialogHelper';
import datetime from 'datetime';
import globalize from 'globalize';
import 'emby-select';
import 'paper-icon-button-light';
import 'formDialogStyle';
function getDisplayTime(hours) { function getDisplayTime(hours) {
var minutes = 0; let minutes = 0;
var pct = hours % 1; const pct = hours % 1;
if (pct) { if (pct) {
minutes = parseInt(60 * pct); minutes = parseInt(60 * pct);
@ -13,32 +24,32 @@ define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-butt
} }
function populateHours(context) { function populateHours(context) {
var html = ''; let html = '';
for (var i = 0; i < 24; i++) { for (let i = 0; i < 24; i++) {
html += '<option value="' + i + '">' + getDisplayTime(i) + '</option>'; html += `<option value="${i}">${getDisplayTime(i)}</option>`;
} }
html += '<option value="24">' + getDisplayTime(0) + '</option>'; html += `<option value="24">${getDisplayTime(0)}</option>`;
context.querySelector('#selectStart').innerHTML = html; context.querySelector('#selectStart').innerHTML = html;
context.querySelector('#selectEnd').innerHTML = html; context.querySelector('#selectEnd').innerHTML = html;
} }
function loadSchedule(context, schedule) { function loadSchedule(context, {DayOfWeek, StartHour, EndHour}) {
context.querySelector('#selectDay').value = schedule.DayOfWeek || 'Sunday'; context.querySelector('#selectDay').value = DayOfWeek || 'Sunday';
context.querySelector('#selectStart').value = schedule.StartHour || 0; context.querySelector('#selectStart').value = StartHour || 0;
context.querySelector('#selectEnd').value = schedule.EndHour || 0; context.querySelector('#selectEnd').value = EndHour || 0;
} }
function submitSchedule(context, options) { function submitSchedule(context, options) {
var updatedSchedule = { const updatedSchedule = {
DayOfWeek: context.querySelector('#selectDay').value, DayOfWeek: context.querySelector('#selectDay').value,
StartHour: context.querySelector('#selectStart').value, StartHour: context.querySelector('#selectStart').value,
EndHour: context.querySelector('#selectEnd').value EndHour: context.querySelector('#selectEnd').value
}; };
if (parseFloat(updatedSchedule.StartHour) >= parseFloat(updatedSchedule.EndHour)) { if (parseFloat(updatedSchedule.StartHour) >= parseFloat(updatedSchedule.EndHour)) {
return void alert(globalize.translate('ErrorMessageStartHourGreaterThanEnd')); return void alert(globalize.translate('ErrorStartHourGreaterThanEnd'));
} }
context.submitted = true; context.submitted = true;
@ -46,44 +57,41 @@ define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-butt
dialogHelper.close(context); dialogHelper.close(context);
} }
return { export function show(options) {
show: function (options) { return new Promise((resolve, reject) => {
return new Promise(function (resolve, reject) { import('text!./accessSchedule.template.html').then(({default: template}) => {
var xhr = new XMLHttpRequest(); const dlg = dialogHelper.createDialog({
xhr.open('GET', 'components/accessSchedule/accessSchedule.template.html', true);
xhr.onload = function (e) {
var template = this.response;
var dlg = dialogHelper.createDialog({
removeOnClose: true, removeOnClose: true,
size: 'small' size: 'small'
}); });
dlg.classList.add('formDialog'); dlg.classList.add('formDialog');
var html = ''; let html = '';
html += globalize.translateDocument(template); html += globalize.translateHtml(template);
dlg.innerHTML = html; dlg.innerHTML = html;
populateHours(dlg); populateHours(dlg);
loadSchedule(dlg, options.schedule); loadSchedule(dlg, options.schedule);
dialogHelper.open(dlg); dialogHelper.open(dlg);
dlg.addEventListener('close', function () { dlg.addEventListener('close', () => {
if (dlg.submitted) { if (dlg.submitted) {
resolve(options.schedule); resolve(options.schedule);
} else { } else {
reject(); reject();
} }
}); });
dlg.querySelector('.btnCancel').addEventListener('click', function (e) { dlg.querySelector('.btnCancel').addEventListener('click', () => {
dialogHelper.close(dlg); dialogHelper.close(dlg);
}); });
dlg.querySelector('form').addEventListener('submit', function (e) { dlg.querySelector('form').addEventListener('submit', event => {
submitSchedule(dlg, options); submitSchedule(dlg, options);
e.preventDefault(); event.preventDefault();
return false; return false;
}); });
}; });
xhr.send();
}); });
} }
/* eslint-enable indent */
export default {
show: show
}; };
});

View file

@ -1,5 +1,5 @@
<div class="formDialogHeader"> <div class="formDialogHeader">
<button is="paper-icon-button-light" class="btnCancel autoSize" title="${LabelPrevious}" tabindex="-1"> <button is="paper-icon-button-light" class="btnCancel autoSize" title="${Previous}" tabindex="-1">
<span class="material-icons arrow_back" aria-hidden="true"></span> <span class="material-icons arrow_back" aria-hidden="true"></span>
</button> </button>
<h3 class="formDialogHeaderTitle"> <h3 class="formDialogHeaderTitle">
@ -12,13 +12,13 @@
<div class="selectContainer"> <div class="selectContainer">
<select is="emby-select" id="selectDay" label="${LabelAccessDay}"> <select is="emby-select" id="selectDay" label="${LabelAccessDay}">
<option value="Sunday">${OptionSunday}</option> <option value="Sunday">${Sunday}</option>
<option value="Monday">${OptionMonday}</option> <option value="Monday">${Monday}</option>
<option value="Tuesday">${OptionTuesday}</option> <option value="Tuesday">${Tuesday}</option>
<option value="Wednesday">${OptionWednesday}</option> <option value="Wednesday">${Wednesday}</option>
<option value="Thursday">${OptionThursday}</option> <option value="Thursday">${Thursday}</option>
<option value="Friday">${OptionFriday}</option> <option value="Friday">${Friday}</option>
<option value="Saturday">${OptionSaturday}</option> <option value="Saturday">${Saturday}</option>
<option value="Everyday">${OptionEveryday}</option> <option value="Everyday">${OptionEveryday}</option>
<option value="Weekday">${OptionWeekdays}</option> <option value="Weekday">${OptionWeekdays}</option>
<option value="Weekend">${OptionWeekends}</option> <option value="Weekend">${OptionWeekends}</option>
@ -33,7 +33,7 @@
<div class="formDialogFooter"> <div class="formDialogFooter">
<button is="emby-button" type="submit" class="raised button-submit block formDialogFooterItem"> <button is="emby-button" type="submit" class="raised button-submit block formDialogFooterItem">
<span>${ButtonAdd}</span> <span>${Add}</span>
</button> </button>
</div> </div>
</form> </form>

View file

@ -9,22 +9,14 @@ import 'scrollStyles';
import 'listViewStyle'; import 'listViewStyle';
function getOffsets(elems) { function getOffsets(elems) {
const results = [];
let results = [];
if (!document) { if (!document) {
return results; return results;
} }
let box; for (const elem of elems) {
for (let elem of elems) { const box = elem.getBoundingClientRect();
// 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({ results.push({
top: box.top, top: box.top,
@ -38,12 +30,11 @@ function getOffsets(elems) {
} }
function getPosition(options, dlg) { function getPosition(options, dlg) {
const windowSize = dom.getWindowSize(); const windowSize = dom.getWindowSize();
const windowHeight = windowSize.innerHeight; const windowHeight = windowSize.innerHeight;
const windowWidth = windowSize.innerWidth; const windowWidth = windowSize.innerWidth;
let pos = getOffsets([options.positionTo])[0]; const pos = getOffsets([options.positionTo])[0];
if (options.positionY !== 'top') { if (options.positionY !== 'top') {
pos.top += (pos.height || 0) / 2; pos.top += (pos.height || 0) / 2;
@ -80,19 +71,18 @@ function getPosition(options, dlg) {
} }
function centerFocus(elem, horiz, on) { function centerFocus(elem, horiz, on) {
require(['scrollHelper'], function (scrollHelper) { import('scrollHelper').then(({default: scrollHelper}) => {
const fn = on ? 'on' : 'off'; const fn = on ? 'on' : 'off';
scrollHelper.centerFocus[fn](elem, horiz); scrollHelper.centerFocus[fn](elem, horiz);
}); });
} }
export function show(options) { export function show(options) {
// items // items
// positionTo // positionTo
// showCancel // showCancel
// title // title
let dialogOptions = { const dialogOptions = {
removeOnClose: true, removeOnClose: true,
enableHistory: options.enableHistory, enableHistory: options.enableHistory,
scrollY: false scrollY: false
@ -105,7 +95,6 @@ export function show(options) {
isFullscreen = true; isFullscreen = true;
dialogOptions.autoFocus = true; dialogOptions.autoFocus = true;
} else { } else {
dialogOptions.modal = false; dialogOptions.modal = false;
dialogOptions.entryAnimation = options.entryAnimation; dialogOptions.entryAnimation = options.entryAnimation;
dialogOptions.exitAnimation = options.exitAnimation; dialogOptions.exitAnimation = options.exitAnimation;
@ -114,7 +103,7 @@ export function show(options) {
dialogOptions.autoFocus = false; dialogOptions.autoFocus = false;
} }
let dlg = dialogHelper.createDialog(dialogOptions); const dlg = dialogHelper.createDialog(dialogOptions);
if (isFullscreen) { if (isFullscreen) {
dlg.classList.add('actionsheet-fullscreen'); dlg.classList.add('actionsheet-fullscreen');
@ -140,10 +129,9 @@ export function show(options) {
} }
let renderIcon = false; let renderIcon = false;
let icons = []; const icons = [];
let itemIcon; let itemIcon;
for (let item of options.items) { for (const item of options.items) {
itemIcon = item.icon || (item.selected ? 'check' : null); itemIcon = item.icon || (item.selected ? 'check' : null);
if (itemIcon) { if (itemIcon) {
@ -153,7 +141,9 @@ export function show(options) {
} }
if (layoutManager.tv) { 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>'; 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 // If any items have an icon, give them all an icon just to make sure they're all lined up evenly
@ -166,7 +156,6 @@ export function show(options) {
} }
if (options.title) { if (options.title) {
html += '<h1 class="actionSheetTitle">' + options.title + '</h1>'; html += '<h1 class="actionSheetTitle">' + options.title + '</h1>';
} }
if (options.text) { if (options.text) {
@ -202,7 +191,6 @@ export function show(options) {
const item = options.items[i]; const item = options.items[i];
if (item.divider) { if (item.divider) {
html += '<div class="actionsheetDivider"></div>'; html += '<div class="actionsheetDivider"></div>';
continue; continue;
} }
@ -216,7 +204,7 @@ export function show(options) {
itemIcon = icons[i]; itemIcon = icons[i];
if (itemIcon) { if (itemIcon) {
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons ' + itemIcon + '"></span>'; html += `<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons ${itemIcon}"></span>`;
} else if (renderIcon && !center) { } else if (renderIcon && !center) {
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons check" style="visibility:hidden;"></span>'; html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons check" style="visibility:hidden;"></span>';
} }
@ -228,13 +216,13 @@ export function show(options) {
html += '</div>'; html += '</div>';
if (item.secondaryText) { if (item.secondaryText) {
html += '<div class="listItemBodyText secondary">' + item.secondaryText + '</div>'; html += `<div class="listItemBodyText secondary">${item.secondaryText}</div>`;
} }
html += '</div>'; html += '</div>';
if (item.asideText) { if (item.asideText) {
html += '<div class="listItemAside actionSheetItemAsideText">' + item.asideText + '</div>'; html += `<div class="listItemAside actionSheetItemAsideText">${item.asideText}</div>`;
} }
html += '</button>'; html += '</button>';
@ -242,7 +230,7 @@ export function show(options) {
if (options.showCancel) { if (options.showCancel) {
html += '<div class="buttons">'; html += '<div class="buttons">';
html += '<button is="emby-button" type="button" class="btnCloseActionSheet">' + globalize.translate('ButtonCancel') + '</button>'; html += `<button is="emby-button" type="button" class="btnCloseActionSheet">${globalize.translate('ButtonCancel')}</button>`;
html += '</div>'; html += '</div>';
} }
html += '</div>'; html += '</div>';
@ -253,15 +241,13 @@ export function show(options) {
centerFocus(dlg.querySelector('.actionSheetScroller'), false, true); centerFocus(dlg.querySelector('.actionSheetScroller'), false, true);
} }
let btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet'); const btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet');
if (btnCloseActionSheet) { if (btnCloseActionSheet) {
btnCloseActionSheet.addEventListener('click', function () { btnCloseActionSheet.addEventListener('click', function () {
dialogHelper.close(dlg); 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 selectedId;
let timeout; let timeout;
@ -272,26 +258,20 @@ export function show(options) {
} }
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
let isResolved; let isResolved;
dlg.addEventListener('click', function (e) { dlg.addEventListener('click', function (e) {
const actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem'); const actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem');
if (actionSheetMenuItem) { if (actionSheetMenuItem) {
selectedId = actionSheetMenuItem.getAttribute('data-id'); selectedId = actionSheetMenuItem.getAttribute('data-id');
if (options.resolveOnClick) { if (options.resolveOnClick) {
if (options.resolveOnClick.indexOf) { if (options.resolveOnClick.indexOf) {
if (options.resolveOnClick.indexOf(selectedId) !== -1) { if (options.resolveOnClick.indexOf(selectedId) !== -1) {
resolve(selectedId); resolve(selectedId);
isResolved = true; isResolved = true;
} }
} else { } else {
resolve(selectedId); resolve(selectedId);
isResolved = true; isResolved = true;
@ -300,11 +280,9 @@ export function show(options) {
dialogHelper.close(dlg); dialogHelper.close(dlg);
} }
}); });
dlg.addEventListener('close', function () { dlg.addEventListener('close', function () {
if (layoutManager.tv) { if (layoutManager.tv) {
centerFocus(dlg.querySelector('.actionSheetScroller'), false, false); centerFocus(dlg.querySelector('.actionSheetScroller'), false, false);
} }

View file

@ -1,13 +1,21 @@
define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings', 'serverNotifications', 'connectionManager', 'emby-button', 'listViewStyle'], function (events, globalize, dom, datefns, dfnshelper, userSettings, serverNotifications, connectionManager) { import events from 'events';
'use strict'; import globalize from 'globalize';
import dom from 'dom';
import * as datefns from 'date-fns';
import dfnshelper from 'dfnshelper';
import serverNotifications from 'serverNotifications';
import 'emby-button';
import 'listViewStyle';
/* eslint-disable indent */
function getEntryHtml(entry, apiClient) { function getEntryHtml(entry, apiClient) {
var html = ''; let html = '';
html += '<div class="listItem listItem-border">'; html += '<div class="listItem listItem-border">';
var color = '#00a4dc'; let color = '#00a4dc';
var icon = 'notifications'; let icon = 'notifications';
if ('Error' == entry.Severity || 'Fatal' == entry.Severity || 'Warn' == entry.Severity) { if (entry.Severity == 'Error' || entry.Severity == 'Fatal' || entry.Severity == 'Warn') {
color = '#cc0000'; color = '#cc0000';
icon = 'notification_important'; icon = 'notification_important';
} }
@ -34,10 +42,14 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
html += '</div>'; html += '</div>';
if (entry.Overview) { if (entry.Overview) {
html += '<button type="button" is="paper-icon-button-light" class="btnEntryInfo" data-id="' + entry.Id + '" title="' + globalize.translate('Info') + '"><span class="material-icons info"></span></button>'; html += `<button type="button" is="paper-icon-button-light" class="btnEntryInfo" data-id="${entry.Id}" title="${globalize.translate('Info')}">
<span class="material-icons info"></span>
</button>`;
} }
return html += '</div>'; html += '</div>';
return html;
} }
function renderList(elem, apiClient, result, startIndex, limit) { function renderList(elem, apiClient, result, startIndex, limit) {
@ -47,14 +59,15 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
} }
function reloadData(instance, elem, apiClient, startIndex, limit) { function reloadData(instance, elem, apiClient, startIndex, limit) {
if (null == startIndex) { if (startIndex == null) {
startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0'); startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0');
} }
limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7'); limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7');
var minDate = new Date(); const minDate = new Date();
var hasUserId = 'false' !== elem.getAttribute('data-useractivity'); const hasUserId = elem.getAttribute('data-useractivity') !== 'false';
// TODO: Use date-fns
if (hasUserId) { if (hasUserId) {
minDate.setTime(minDate.getTime() - 24 * 60 * 60 * 1000); // one day back minDate.setTime(minDate.getTime() - 24 * 60 * 60 * 1000); // one day back
} else { } else {
@ -70,7 +83,7 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
elem.setAttribute('data-activitystartindex', startIndex); elem.setAttribute('data-activitystartindex', startIndex);
elem.setAttribute('data-activitylimit', limit); elem.setAttribute('data-activitylimit', limit);
if (!startIndex) { if (!startIndex) {
var activityContainer = dom.parentWithClass(elem, 'activityContainer'); const activityContainer = dom.parentWithClass(elem, 'activityContainer');
if (activityContainer) { if (activityContainer) {
if (result.Items.length) { if (result.Items.length) {
@ -87,7 +100,7 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
} }
function onActivityLogUpdate(e, apiClient, data) { function onActivityLogUpdate(e, apiClient, data) {
var options = this.options; const options = this.options;
if (options && options.serverId === apiClient.serverId()) { if (options && options.serverId === apiClient.serverId()) {
reloadData(this, options.element, apiClient); reloadData(this, options.element, apiClient);
@ -95,14 +108,14 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
} }
function onListClick(e) { function onListClick(e) {
var btnEntryInfo = dom.parentWithClass(e.target, 'btnEntryInfo'); const btnEntryInfo = dom.parentWithClass(e.target, 'btnEntryInfo');
if (btnEntryInfo) { if (btnEntryInfo) {
var id = btnEntryInfo.getAttribute('data-id'); const id = btnEntryInfo.getAttribute('data-id');
var items = this.items; const items = this.items;
if (items) { if (items) {
var item = items.filter(function (i) { const item = items.filter(function (i) {
return i.Id.toString() === id; return i.Id.toString() === id;
})[0]; })[0];
@ -114,35 +127,35 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
} }
function showItemOverview(item) { function showItemOverview(item) {
require(['alert'], function (alert) { import('alert').then(({default: alert}) => {
alert({ alert({
text: item.Overview text: item.Overview
}); });
}); });
} }
function ActivityLog(options) { class ActivityLog {
constructor(options) {
this.options = options; this.options = options;
var element = options.element; const element = options.element;
element.classList.add('activityLogListWidget'); element.classList.add('activityLogListWidget');
element.addEventListener('click', onListClick.bind(this)); element.addEventListener('click', onListClick.bind(this));
var apiClient = connectionManager.getApiClient(options.serverId); const apiClient = window.connectionManager.getApiClient(options.serverId);
reloadData(this, element, apiClient); reloadData(this, element, apiClient);
var onUpdate = onActivityLogUpdate.bind(this); const onUpdate = onActivityLogUpdate.bind(this);
this.updateFn = onUpdate; this.updateFn = onUpdate;
events.on(serverNotifications, 'ActivityLogEntry', onUpdate); events.on(serverNotifications, 'ActivityLogEntry', onUpdate);
apiClient.sendMessage('ActivityLogEntryStart', '0,1500'); apiClient.sendMessage('ActivityLogEntryStart', '0,1500');
} }
destroy() {
ActivityLog.prototype.destroy = function () { const options = this.options;
var options = this.options;
if (options) { if (options) {
options.element.classList.remove('activityLogListWidget'); options.element.classList.remove('activityLogListWidget');
connectionManager.getApiClient(options.serverId).sendMessage('ActivityLogEntryStop', '0,1500'); window.connectionManager.getApiClient(options.serverId).sendMessage('ActivityLogEntryStop', '0,1500');
} }
var onUpdate = this.updateFn; const onUpdate = this.updateFn;
if (onUpdate) { if (onUpdate) {
events.off(serverNotifications, 'ActivityLogEntry', onUpdate); events.off(serverNotifications, 'ActivityLogEntry', onUpdate);
@ -150,7 +163,9 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
this.items = null; this.items = null;
this.options = null; this.options = null;
}; }
}
return ActivityLog; export default ActivityLog;
});
/* eslint-enable indent */

View file

@ -1,14 +1,16 @@
define(['browser', 'dialog', 'globalize'], function (browser, dialog, globalize) { import browser from 'browser';
'use strict'; import dialog from 'dialog';
import globalize from 'globalize';
/* eslint-disable indent */
function replaceAll(originalString, strReplace, strWith) { function replaceAll(originalString, strReplace, strWith) {
var reg = new RegExp(strReplace, 'ig'); const reg = new RegExp(strReplace, 'ig');
return originalString.replace(reg, strWith); return originalString.replace(reg, strWith);
} }
return function (text, title) { export default function (text, title) {
let options;
var options;
if (typeof text === 'string') { if (typeof text === 'string') {
options = { options = {
title: title, title: title,
@ -21,7 +23,7 @@ define(['browser', 'dialog', 'globalize'], function (browser, dialog, globalize)
if (browser.tv && window.alert) { if (browser.tv && window.alert) {
alert(replaceAll(options.text || '', '<br/>', '\n')); alert(replaceAll(options.text || '', '<br/>', '\n'));
} else { } else {
var items = []; const items = [];
items.push({ items.push({
name: globalize.translate('ButtonGotIt'), name: globalize.translate('ButtonGotIt'),
@ -31,7 +33,7 @@ define(['browser', 'dialog', 'globalize'], function (browser, dialog, globalize)
options.buttons = items; options.buttons = items;
return dialog(options).then(function (result) { return dialog.show(options).then(function (result) {
if (result === 'ok') { if (result === 'ok') {
return Promise.resolve(); return Promise.resolve();
} }
@ -41,5 +43,6 @@ define(['browser', 'dialog', 'globalize'], function (browser, dialog, globalize)
} }
return Promise.resolve(); return Promise.resolve();
}; }
});
/* eslint-enable indent */

View file

@ -1,11 +1,22 @@
define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-button-light', 'material-icons'], function (focusManager, layoutManager, dom) { /* eslint-disable indent */
'use strict';
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() { function focus() {
var scope = this; const scope = this;
var selected = scope.querySelector('.' + selectedButtonClass); const selected = scope.querySelector(`.${selectedButtonClass}`);
if (selected) { if (selected) {
focusManager.focus(selected); focusManager.focus(selected);
@ -15,8 +26,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
} }
function getAlphaPickerButtonClassName(vertical) { function getAlphaPickerButtonClassName(vertical) {
let alphaPickerButtonClassName = 'alphaPickerButton';
var alphaPickerButtonClassName = 'alphaPickerButton';
if (layoutManager.tv) { if (layoutManager.tv) {
alphaPickerButtonClassName += ' alphaPickerButton-tv'; alphaPickerButtonClassName += ' alphaPickerButton-tv';
@ -30,44 +40,42 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
} }
function getLetterButton(l, vertical) { function getLetterButton(l, vertical) {
return '<button data-value="' + l + '" class="' + getAlphaPickerButtonClassName(vertical) + '">' + l + '</button>'; return `<button data-value="${l}" class="${getAlphaPickerButtonClassName(vertical)}">${l}</button>`;
} }
function mapLetters(letters, vertical) { function mapLetters(letters, vertical) {
return letters.map(l => {
return letters.map(function (l) {
return getLetterButton(l, vertical); return getLetterButton(l, vertical);
}); });
} }
function render(element, options) { function render(element, options) {
element.classList.add('alphaPicker'); element.classList.add('alphaPicker');
if (layoutManager.tv) { if (layoutManager.tv) {
element.classList.add('alphaPicker-tv'); element.classList.add('alphaPicker-tv');
} }
var vertical = element.classList.contains('alphaPicker-vertical'); const vertical = element.classList.contains('alphaPicker-vertical');
if (!vertical) { if (!vertical) {
element.classList.add('focuscontainer-x'); element.classList.add('focuscontainer-x');
} }
var html = ''; let html = '';
var letters; let letters;
var alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical); const alphaPickerButtonClassName = getAlphaPickerButtonClassName(vertical);
var rowClassName = 'alphaPickerRow'; let rowClassName = 'alphaPickerRow';
if (vertical) { if (vertical) {
rowClassName += ' alphaPickerRow-vertical'; rowClassName += ' alphaPickerRow-vertical';
} }
html += '<div class="' + rowClassName + '">'; html += `<div class="${rowClassName}">`;
if (options.mode === 'keyboard') { if (options.mode === 'keyboard') {
html += '<button data-value=" " is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><span class="material-icons alphaPickerButtonIcon space_bar"></span></button>'; html += `<button data-value=" " is="paper-icon-button-light" class="${alphaPickerButtonClassName}"><span class="material-icons alphaPickerButtonIcon space_bar"></span></button>`;
} else { } else {
letters = ['#']; letters = ['#'];
html += mapLetters(letters, vertical).join(''); html += mapLetters(letters, vertical).join('');
@ -77,11 +85,11 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
html += mapLetters(letters, vertical).join(''); html += mapLetters(letters, vertical).join('');
if (options.mode === 'keyboard') { if (options.mode === 'keyboard') {
html += '<button data-value="backspace" is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><span class="material-icons alphaPickerButtonIcon backspace"></span></button>'; html += `<button data-value="backspace" is="paper-icon-button-light" class="${alphaPickerButtonClassName}"><span class="material-icons alphaPickerButtonIcon backspace"></span></button>`;
html += '</div>'; html += '</div>';
letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
html += '<div class="' + rowClassName + '">'; html += `<div class="${rowClassName}">`;
html += '<br/>'; html += '<br/>';
html += mapLetters(letters, vertical).join(''); html += mapLetters(letters, vertical).join('');
html += '</div>'; html += '</div>';
@ -95,74 +103,71 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
element.focus = focus; element.focus = focus;
} }
function AlphaPicker(options) { export class AlphaPicker {
constructor(options) {
const self = this;
var self = this;
this.options = options; this.options = options;
var element = options.element; const element = options.element;
var itemsContainer = options.itemsContainer; const itemsContainer = options.itemsContainer;
var itemClass = options.itemClass; const itemClass = options.itemClass;
var itemFocusValue; let itemFocusValue;
var itemFocusTimeout; let itemFocusTimeout;
function onItemFocusTimeout() { function onItemFocusTimeout() {
itemFocusTimeout = null; itemFocusTimeout = null;
self.value(itemFocusValue); self.value(itemFocusValue);
} }
var alphaFocusedElement; let alphaFocusedElement;
var alphaFocusTimeout; let alphaFocusTimeout;
function onAlphaFocusTimeout() { function onAlphaFocusTimeout() {
alphaFocusTimeout = null; alphaFocusTimeout = null;
if (document.activeElement === alphaFocusedElement) { if (document.activeElement === alphaFocusedElement) {
var value = alphaFocusedElement.getAttribute('data-value'); const value = alphaFocusedElement.getAttribute('data-value');
self.value(value, true); self.value(value, true);
} }
} }
function onAlphaPickerInKeyboardModeClick(e) { function onAlphaPickerInKeyboardModeClick(e) {
const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
if (alphaPickerButton) { if (alphaPickerButton) {
var value = alphaPickerButton.getAttribute('data-value'); const value = alphaPickerButton.getAttribute('data-value');
element.dispatchEvent(new CustomEvent('alphavalueclicked', { element.dispatchEvent(new CustomEvent('alphavalueclicked', {
cancelable: false, cancelable: false,
detail: { detail: {
value: value value
} }
})); }));
} }
} }
function onAlphaPickerClick(e) { function onAlphaPickerClick(e) {
const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
if (alphaPickerButton) { if (alphaPickerButton) {
var value = alphaPickerButton.getAttribute('data-value'); const value = alphaPickerButton.getAttribute('data-value');
if ((this._currentValue || '').toUpperCase() === value.toUpperCase()) { if ((this._currentValue || '').toUpperCase() === value.toUpperCase()) {
self.value(null, true); this.value(null, true);
} else { } else {
self.value(value, true); this.value(value, true);
} }
} }
} }
function onAlphaPickerFocusIn(e) { function onAlphaPickerFocusIn(e) {
if (alphaFocusTimeout) { if (alphaFocusTimeout) {
clearTimeout(alphaFocusTimeout); clearTimeout(alphaFocusTimeout);
alphaFocusTimeout = null; alphaFocusTimeout = null;
} }
var alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton'); const alphaPickerButton = dom.parentWithClass(e.target, 'alphaPickerButton');
if (alphaPickerButton) { if (alphaPickerButton) {
alphaFocusedElement = alphaPickerButton; alphaFocusedElement = alphaPickerButton;
@ -171,13 +176,11 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
} }
function onItemsFocusIn(e) { function onItemsFocusIn(e) {
const item = dom.parentWithClass(e.target, itemClass);
var item = dom.parentWithClass(e.target, itemClass);
if (item) { if (item) {
var prefix = item.getAttribute('data-prefix'); const prefix = item.getAttribute('data-prefix');
if (prefix && prefix.length) { if (prefix && prefix.length) {
itemFocusValue = prefix[0]; itemFocusValue = prefix[0];
if (itemFocusTimeout) { if (itemFocusTimeout) {
clearTimeout(itemFocusTimeout); clearTimeout(itemFocusTimeout);
@ -187,10 +190,8 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
} }
} }
self.enabled = function (enabled) { this.enabled = function (enabled) {
if (enabled) { if (enabled) {
if (itemsContainer) { if (itemsContainer) {
itemsContainer.addEventListener('focus', onItemsFocusIn, true); itemsContainer.addEventListener('focus', onItemsFocusIn, true);
} }
@ -204,9 +205,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
} else { } else {
element.addEventListener('click', onAlphaPickerClick.bind(this)); element.addEventListener('click', onAlphaPickerClick.bind(this));
} }
} else { } else {
if (itemsContainer) { if (itemsContainer) {
itemsContainer.removeEventListener('focus', onItemsFocusIn, true); itemsContainer.removeEventListener('focus', onItemsFocusIn, true);
} }
@ -223,25 +222,23 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
this.visible(true); this.visible(true);
} }
AlphaPicker.prototype.value = function (value, applyValue) { value(value, applyValue) {
const element = this.options.element;
var element = this.options.element; let btn;
var btn; let selected;
var selected;
if (value !== undefined) { if (value !== undefined) {
if (value != null) { if (value != null) {
value = value.toUpperCase(); value = value.toUpperCase();
this._currentValue = value; this._currentValue = value;
if (this.options.mode !== 'keyboard') { if (this.options.mode !== 'keyboard') {
selected = element.querySelector('.' + selectedButtonClass); selected = element.querySelector(`.${selectedButtonClass}`);
try { try {
btn = element.querySelector('.alphaPickerButton[data-value=\'' + value + '\']'); btn = element.querySelector(`.alphaPickerButton[data-value='${value}']`);
} catch (err) { } catch (err) {
console.error('error in querySelector: ' + err); console.error('error in querySelector:', err);
} }
if (btn && btn !== selected) { if (btn && btn !== selected) {
@ -254,7 +251,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
} else { } else {
this._currentValue = value; this._currentValue = value;
selected = element.querySelector('.' + selectedButtonClass); selected = element.querySelector(`.${selectedButtonClass}`);
if (selected) { if (selected) {
selected.classList.remove(selectedButtonClass); selected.classList.remove(selectedButtonClass);
} }
@ -265,57 +262,52 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
element.dispatchEvent(new CustomEvent('alphavaluechanged', { element.dispatchEvent(new CustomEvent('alphavaluechanged', {
cancelable: false, cancelable: false,
detail: { detail: {
value: value value
} }
})); }));
} }
return this._currentValue; return this._currentValue;
}; }
AlphaPicker.prototype.on = function (name, fn) { on(name, fn) {
var element = this.options.element; const element = this.options.element;
element.addEventListener(name, fn); element.addEventListener(name, fn);
}; }
AlphaPicker.prototype.off = function (name, fn) { off(name, fn) {
var element = this.options.element; const element = this.options.element;
element.removeEventListener(name, fn); element.removeEventListener(name, fn);
}; }
AlphaPicker.prototype.visible = function (visible) { visible(visible) {
const element = this.options.element;
var element = this.options.element;
element.style.visibility = visible ? 'visible' : 'hidden'; 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() {
const element = this.options.element;
const elems = element.querySelectorAll('.alphaPickerButton');
const values = [];
for (let i = 0, length = elems.length; i < length; i++) {
values.push(elems[i].getAttribute('data-value')); values.push(elems[i].getAttribute('data-value'));
} }
return values; return values;
}; }
AlphaPicker.prototype.focus = function () { focus() {
const element = this.options.element;
var element = this.options.element;
focusManager.autoFocus(element, true); focusManager.autoFocus(element, true);
}; }
AlphaPicker.prototype.destroy = function () { destroy() {
const element = this.options.element;
var element = this.options.element;
this.enabled(false); this.enabled(false);
element.classList.remove('focuscontainer-x'); element.classList.remove('focuscontainer-x');
this.options = null; this.options = null;
}; }
}
return AlphaPicker; /* eslint-enable indent */
}); export default AlphaPicker;

View file

@ -1,8 +1,7 @@
define(['browser', 'css!./appFooter'], function (browser) { import 'css!./appFooter';
'use strict';
function render(options) { function render(options) {
var elem = document.createElement('div'); const elem = document.createElement('div');
elem.classList.add('appfooter'); elem.classList.add('appfooter');
document.body.appendChild(elem); document.body.appendChild(elem);
@ -10,8 +9,9 @@ define(['browser', 'css!./appFooter'], function (browser) {
return elem; return elem;
} }
function appFooter(options) { class appFooter {
var self = this; constructor(options) {
const self = this;
self.element = render(options); self.element = render(options);
self.add = function (elem) { self.add = function (elem) {
@ -26,12 +26,11 @@ define(['browser', 'css!./appFooter'], function (browser) {
} }
}; };
} }
destroy() {
appFooter.prototype.destroy = function () { const self = this;
var self = this;
self.element = null; self.element = null;
}; }
}
return appFooter; export default appFooter;
});

File diff suppressed because it is too large Load diff

View file

@ -1,11 +1,15 @@
define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'globalize'], function (appSettings, browser, events, htmlMediaHelper, webSettings, globalize) { import appSettings from 'appSettings';
'use strict'; import browser from 'browser';
import events from 'events';
import * as htmlMediaHelper from 'htmlMediaHelper';
import * as webSettings from 'webSettings';
import globalize from 'globalize';
function getBaseProfileOptions(item) { function getBaseProfileOptions(item) {
var disableHlsVideoAudioCodecs = []; const disableHlsVideoAudioCodecs = [];
if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) { if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) {
if (browser.edge || browser.msie) { if (browser.edge) {
disableHlsVideoAudioCodecs.push('mp3'); disableHlsVideoAudioCodecs.push('mp3');
} }
@ -20,34 +24,16 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
}; };
} }
function getDeviceProfileForWindowsUwp(item) { function getDeviceProfile(item, options = {}) {
return new Promise(function (resolve, reject) {
require(['browserdeviceprofile', 'environments/windows-uwp/mediacaps'], function (profileBuilder, uwpMediaCaps) {
var profileOptions = getBaseProfileOptions(item);
profileOptions.supportsDts = uwpMediaCaps.supportsDTS();
profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby();
profileOptions.audioChannels = uwpMediaCaps.getAudioChannels();
resolve(profileBuilder(profileOptions));
});
});
}
function getDeviceProfile(item, options) {
options = options || {};
if (self.Windows) {
return getDeviceProfileForWindowsUwp(item);
}
return new Promise(function (resolve) { return new Promise(function (resolve) {
require(['browserdeviceprofile'], function (profileBuilder) { import('browserdeviceprofile').then(({default: profileBuilder}) => {
var profile; let profile;
if (window.NativeShell) { if (window.NativeShell) {
profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder);
} else { } else {
var builderOpts = getBaseProfileOptions(item); const builderOpts = getBaseProfileOptions(item);
builderOpts.enableSsaRender = (item && !options.isRetry && 'allcomplexformats' !== appSettings.get('subtitleburnin')); builderOpts.enableSsaRender = (item && !options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats');
profile = profileBuilder(builderOpts); profile = profileBuilder(builderOpts);
} }
@ -57,20 +43,20 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
} }
function escapeRegExp(str) { function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1');
} }
function replaceAll(originalString, strReplace, strWith) { function replaceAll(originalString, strReplace, strWith) {
var strReplace2 = escapeRegExp(strReplace); const strReplace2 = escapeRegExp(strReplace);
var reg = new RegExp(strReplace2, 'ig'); const reg = new RegExp(strReplace2, 'ig');
return originalString.replace(reg, strWith); return originalString.replace(reg, strWith);
} }
function generateDeviceId() { function generateDeviceId() {
var keys = []; const keys = [];
if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), self.btoa) { if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), window.btoa) {
var result = replaceAll(btoa(keys.join('|')), '=', '1'); const result = replaceAll(btoa(keys.join('|')), '=', '1');
return Promise.resolve(result); return Promise.resolve(result);
} }
@ -78,8 +64,8 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
} }
function getDeviceId() { function getDeviceId() {
var key = '_deviceId2'; const key = '_deviceId2';
var deviceId = appSettings.get(key); const deviceId = appSettings.get(key);
if (deviceId) { if (deviceId) {
return Promise.resolve(deviceId); return Promise.resolve(deviceId);
@ -92,20 +78,40 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
} }
function getDeviceName() { function getDeviceName() {
var deviceName; let deviceName;
deviceName = browser.tizen ? 'Samsung Smart TV' : browser.web0s ? 'LG Smart TV' : browser.operaTv ? 'Opera TV' : browser.xboxOne ? 'Xbox One' : browser.ps4 ? 'Sony PS4' : browser.chrome ? 'Chrome' : browser.edge ? 'Edge' : browser.firefox ? 'Firefox' : browser.msie ? 'Internet Explorer' : browser.opera ? 'Opera' : browser.safari ? 'Safari' : 'Web Browser'; if (browser.tizen) {
deviceName = 'Samsung Smart TV';
} else if (browser.web0s) {
deviceName = 'LG Smart TV';
} else if (browser.operaTv) {
deviceName = 'Opera TV';
} else if (browser.xboxOne) {
deviceName = 'Xbox One';
} else if (browser.ps4) {
deviceName = 'Sony PS4';
} else if (browser.chrome) {
deviceName = 'Chrome';
} else if (browser.edgeChromium) {
deviceName = 'Edge Chromium';
} else if (browser.edge) {
deviceName = 'Edge';
} else if (browser.firefox) {
deviceName = 'Firefox';
} else if (browser.opera) {
deviceName = 'Opera';
} else if (browser.safari) {
deviceName = 'Safari';
} else {
deviceName = 'Web Browser';
}
if (browser.ipad) { if (browser.ipad) {
deviceName += ' iPad'; deviceName += ' iPad';
} else { } else if (browser.iphone) {
if (browser.iphone) {
deviceName += ' iPhone'; deviceName += ' iPhone';
} else { } else if (browser.android) {
if (browser.android) {
deviceName += ' Android'; deviceName += ' Android';
} }
}
}
return deviceName; return deviceName;
} }
@ -123,27 +129,10 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
return false; return false;
} }
var element = document.documentElement; const element = document.documentElement;
return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement('video').webkitEnterFullscreen; return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement('video').webkitEnterFullscreen;
} }
function getSyncProfile() {
return new Promise(function (resolve) {
require(['browserdeviceprofile', 'appSettings'], function (profileBuilder, appSettings) {
var profile;
if (window.NativeShell) {
profile = window.NativeShell.AppHost.getSyncProfile(profileBuilder, appSettings);
} else {
profile = profileBuilder();
profile.MaxStaticMusicBitrate = appSettings.maxStaticMusicBitrate();
}
resolve(profile);
});
});
}
function getDefaultLayout() { function getDefaultLayout() {
return 'desktop'; return 'desktop';
} }
@ -162,14 +151,14 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
function supportsCue() { function supportsCue() {
try { try {
var video = document.createElement('video'); const video = document.createElement('video');
var style = document.createElement('style'); const style = document.createElement('style');
style.textContent = 'video::cue {background: inherit}'; style.textContent = 'video::cue {background: inherit}';
document.body.appendChild(style); document.body.appendChild(style);
document.body.appendChild(video); document.body.appendChild(video);
var cue = window.getComputedStyle(video, '::cue').background; const cue = window.getComputedStyle(video, '::cue').background;
document.body.removeChild(style); document.body.removeChild(style);
document.body.removeChild(video); document.body.removeChild(video);
@ -183,7 +172,6 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
function onAppVisible() { function onAppVisible() {
if (isHidden) { if (isHidden) {
isHidden = false; isHidden = false;
console.debug('triggering app resume event');
events.trigger(appHost, 'resume'); events.trigger(appHost, 'resume');
} }
} }
@ -191,12 +179,11 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
function onAppHidden() { function onAppHidden() {
if (!isHidden) { if (!isHidden) {
isHidden = true; isHidden = true;
console.debug('app is hidden');
} }
} }
var supportedFeatures = function () { const supportedFeatures = function () {
var features = []; const features = [];
if (navigator.share) { if (navigator.share) {
features.push('sharing'); features.push('sharing');
@ -239,12 +226,6 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
features.push('fullscreenchange'); features.push('fullscreenchange');
} }
if (browser.chrome || browser.edge && !browser.slow) {
if (!browser.noAnimation && !browser.edgeUwp && !browser.xboxOne) {
features.push('imageanalysis');
}
}
if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) { if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) {
features.push('physicalvolumecontrol'); features.push('physicalvolumecontrol');
} }
@ -263,11 +244,11 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
features.push('targetblank'); features.push('targetblank');
features.push('screensaver'); features.push('screensaver');
webSettings.enableMultiServer().then(enabled => { webSettings.getMultiServer().then(enabled => {
if (enabled) features.push('multiserver'); if (enabled) features.push('multiserver');
}); });
if (!browser.orsay && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { if (!browser.orsay && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) {
features.push('subtitleappearancesettings'); features.push('subtitleappearancesettings');
} }
@ -279,7 +260,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
features.push('fileinput'); features.push('fileinput');
} }
if (browser.chrome) { if (browser.chrome || browser.edgeChromium) {
features.push('chromecast'); features.push('chromecast');
} }
@ -305,7 +286,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
} }
} }
var exitPromise; let exitPromise;
/** /**
* Ask user for exit * Ask user for exit
@ -315,7 +296,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
return; return;
} }
require(['actionsheet'], function (actionsheet) { import('actionsheet').then(({default: actionsheet}) => {
exitPromise = actionsheet.show({ exitPromise = actionsheet.show({
title: globalize.translate('MessageConfirmAppExit'), title: globalize.translate('MessageConfirmAppExit'),
items: [ items: [
@ -332,16 +313,16 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
}); });
} }
var deviceId; let deviceId;
var deviceName; let deviceName;
var appName = 'Jellyfin Web'; const appName = 'Jellyfin Web';
var appVersion = '10.6.0'; const appVersion = '10.7.0';
var appHost = { const appHost = {
getWindowState: function () { getWindowState: function () {
return document.windowState || 'Normal'; return document.windowState || 'Normal';
}, },
setWindowState: function (state) { setWindowState: function () {
alert('setWindowState is not supported and should not be called'); alert('setWindowState is not supported and should not be called');
}, },
exit: function () { exit: function () {
@ -356,10 +337,9 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
return window.NativeShell.AppHost.supports(command); return window.NativeShell.AppHost.supports(command);
} }
return -1 !== supportedFeatures.indexOf(command.toLowerCase()); return supportedFeatures.indexOf(command.toLowerCase()) !== -1;
}, },
preferVisualCards: browser.android || browser.chrome, preferVisualCards: browser.android || browser.chrome,
getSyncProfile: getSyncProfile,
getDefaultLayout: function () { getDefaultLayout: function () {
if (window.NativeShell) { if (window.NativeShell) {
return window.NativeShell.AppHost.getDefaultLayout(); return window.NativeShell.AppHost.getDefaultLayout();
@ -393,24 +373,17 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
getPushTokenInfo: function () { getPushTokenInfo: function () {
return {}; return {};
}, },
setThemeColor: function (color) {
var metaThemeColor = document.querySelector('meta[name=theme-color]');
if (metaThemeColor) {
metaThemeColor.setAttribute('content', color);
}
},
setUserScalable: function (scalable) { setUserScalable: function (scalable) {
if (!browser.tv) { if (!browser.tv) {
var att = scalable ? 'width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes' : 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no'; const att = scalable ? 'width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes' : 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no';
document.querySelector('meta[name=viewport]').setAttribute('content', att); document.querySelector('meta[name=viewport]').setAttribute('content', att);
} }
} }
}; };
var isHidden = false; let isHidden = false;
var hidden; let hidden;
var visibilityChange; let visibilityChange;
if (typeof document.hidden !== 'undefined') { /* eslint-disable-line compat/compat */ if (typeof document.hidden !== 'undefined') { /* eslint-disable-line compat/compat */
hidden = 'hidden'; hidden = 'hidden';
@ -429,10 +402,9 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
} }
}, false); }, false);
if (self.addEventListener) { if (window.addEventListener) {
self.addEventListener('focus', onAppVisible); window.addEventListener('focus', onAppVisible);
self.addEventListener('blur', onAppHidden); window.addEventListener('blur', onAppHidden);
} }
return appHost; export default appHost;
});

View file

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

View file

@ -1,56 +0,0 @@
define(['connectionManager'], function (connectionManager) {
return function () {
var self = this;
self.name = 'Backdrop ScreenSaver';
self.type = 'screensaver';
self.id = 'backdropscreensaver';
self.supportsAnonymous = false;
var currentSlideshow;
self.show = function () {
var query = {
ImageTypes: 'Backdrop',
EnableImageTypes: 'Backdrop',
IncludeItemTypes: 'Movie,Series,MusicArtist',
SortBy: 'Random',
Recursive: true,
Fields: 'Taglines',
ImageTypeLimit: 1,
StartIndex: 0,
Limit: 200
};
var apiClient = connectionManager.currentApiClient();
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (result) {
if (result.Items.length) {
require(['slideshow'], function (slideshow) {
var newSlideShow = new slideshow({
showTitle: true,
cover: true,
items: result.Items
});
newSlideShow.show();
currentSlideshow = newSlideShow;
});
}
});
};
self.hide = function () {
if (currentSlideshow) {
currentSlideshow.hide();
currentSlideshow = null;
}
};
};
});

View file

@ -167,8 +167,9 @@ button::-moz-focus-inner {
position: relative; position: relative;
background-clip: content-box !important; background-clip: content-box !important;
color: inherit; color: inherit;
}
/* This is only needed for scalable cards */ .cardScalable .cardImageContainer {
height: 100%; height: 100%;
contain: strict; contain: strict;
} }
@ -192,9 +193,14 @@ button::-moz-focus-inner {
/* Needed in case this is a button */ /* Needed in case this is a button */
display: block; display: block;
/* Needed in case this is a button */
margin: 0 !important; margin: 0 !important;
border: 0 !important;
padding: 0 !important;
cursor: pointer;
color: inherit;
width: 100%;
font-family: inherit;
font-size: inherit;
/* Needed in safari */ /* Needed in safari */
height: 100%; height: 100%;
@ -203,27 +209,25 @@ button::-moz-focus-inner {
contain: strict; contain: strict;
} }
.cardContent-button { .defaultCardBackground {
border: 0 !important; display: flex;
padding: 0 !important;
cursor: pointer;
color: inherit;
width: 100%;
vertical-align: middle;
font-family: inherit;
font-size: inherit;
} }
.cardContent-button:not(.defaultCardBackground) { .cardContent:not(.defaultCardBackground) {
background-color: transparent; background-color: transparent;
} }
.cardBox:not(.visualCardBox) .cardPadder {
background-color: #242424;
}
.visualCardBox .cardContent { .visualCardBox .cardContent {
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-bottom-right-radius: 0; 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); box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37);
} }
@ -239,33 +243,13 @@ button::-moz-focus-inner {
border: none; border: none;
} }
.cardImage-img {
max-height: 100%;
max-width: 100%;
/* This is simply for lazy image purposes, to ensure the image is visible sooner when scrolling */
min-height: 70%;
min-width: 70%;
margin: auto;
}
.coveredImage-img {
width: 100%;
height: 100%;
}
.coveredImage-noscale-img {
max-height: none;
max-width: none;
}
.coveredImage { .coveredImage {
background-size: cover; background-size: cover;
background-position: center center; background-position: center center;
} }
.coveredImage-noScale { .coveredImage.coveredImage-contain {
background-size: cover; background-size: contain;
} }
.cardFooter { .cardFooter {
@ -372,6 +356,8 @@ button::-moz-focus-inner {
.cardDefaultText { .cardDefaultText {
white-space: normal; white-space: normal;
text-align: center; text-align: center;
font-size: 2em;
font-weight: bold;
} }
.cardImageContainer .cardImageIcon { .cardImageContainer .cardImageIcon {

View file

@ -7,7 +7,6 @@
import datetime from 'datetime'; import datetime from 'datetime';
import imageLoader from 'imageLoader'; import imageLoader from 'imageLoader';
import connectionManager from 'connectionManager';
import itemHelper from 'itemHelper'; import itemHelper from 'itemHelper';
import focusManager from 'focusManager'; import focusManager from 'focusManager';
import indicators from 'indicators'; import indicators from 'indicators';
@ -277,7 +276,7 @@ import 'programStyles';
*/ */
function getImageWidth(shape, screenWidth, isOrientationLandscape) { function getImageWidth(shape, screenWidth, isOrientationLandscape) {
const imagesPerRow = getPostersPerRow(shape, screenWidth, isOrientationLandscape); const imagesPerRow = getPostersPerRow(shape, screenWidth, isOrientationLandscape);
return Math.round(screenWidth / imagesPerRow) * 2; return Math.round(screenWidth / imagesPerRow);
} }
/** /**
@ -291,12 +290,10 @@ import 'programStyles';
const primaryImageAspectRatio = imageLoader.getPrimaryImageAspectRatio(items); const primaryImageAspectRatio = imageLoader.getPrimaryImageAspectRatio(items);
if (['auto', 'autohome', 'autooverflow', 'autoVertical'].includes(options.shape)) { if (['auto', 'autohome', 'autooverflow', 'autoVertical'].includes(options.shape)) {
const requestedShape = options.shape; const requestedShape = options.shape;
options.shape = null; options.shape = null;
if (primaryImageAspectRatio) { if (primaryImageAspectRatio) {
if (primaryImageAspectRatio >= 3) { if (primaryImageAspectRatio >= 3) {
options.shape = 'banner'; options.shape = 'banner';
options.coverImage = true; options.coverImage = true;
@ -364,18 +361,16 @@ import 'programStyles';
let hasOpenRow; let hasOpenRow;
let hasOpenSection; let hasOpenSection;
let sectionTitleTagName = options.sectionTitleTagName || 'div'; const sectionTitleTagName = options.sectionTitleTagName || 'div';
let apiClient; let apiClient;
let lastServerId; let lastServerId;
for (let i = 0; i < items.length; i++) { for (const [i, item] of items.entries()) {
const serverId = item.ServerId || options.serverId;
let item = items[i];
let serverId = item.ServerId || options.serverId;
if (serverId !== lastServerId) { if (serverId !== lastServerId) {
lastServerId = serverId; lastServerId = serverId;
apiClient = connectionManager.getApiClient(lastServerId); apiClient = window.connectionManager.getApiClient(lastServerId);
} }
if (options.indexBy) { if (options.indexBy) {
@ -396,7 +391,6 @@ import 'programStyles';
} }
if (newIndexValue !== currentIndexValue) { if (newIndexValue !== currentIndexValue) {
if (hasOpenRow) { if (hasOpenRow) {
html += '</div>'; html += '</div>';
hasOpenRow = false; hasOpenRow = false;
@ -404,7 +398,6 @@ import 'programStyles';
} }
if (hasOpenSection) { if (hasOpenSection) {
html += '</div>'; html += '</div>';
if (isVertical) { if (isVertical) {
@ -428,7 +421,6 @@ import 'programStyles';
} }
if (options.rows && itemsInRow === 0) { if (options.rows && itemsInRow === 0) {
if (hasOpenRow) { if (hasOpenRow) {
html += '</div>'; html += '</div>';
hasOpenRow = false; hasOpenRow = false;
@ -503,94 +495,49 @@ import 'programStyles';
const primaryImageAspectRatio = item.PrimaryImageAspectRatio; const primaryImageAspectRatio = item.PrimaryImageAspectRatio;
let forceName = false; let forceName = false;
let imgUrl = null; let imgUrl = null;
let imgTag = null;
let coverImage = false; let coverImage = false;
let uiAspect = null; let uiAspect = null;
let imgType = null;
let itemId = null;
if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) { if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) {
imgType = 'Thumb';
imgUrl = apiClient.getScaledImageUrl(item.Id, { imgTag = item.ImageTags.Thumb;
type: 'Thumb',
maxWidth: width,
tag: item.ImageTags.Thumb
});
} else if ((options.preferBanner || shape === 'banner') && item.ImageTags && item.ImageTags.Banner) { } else if ((options.preferBanner || shape === 'banner') && item.ImageTags && item.ImageTags.Banner) {
imgType = 'Banner';
imgUrl = apiClient.getScaledImageUrl(item.Id, { imgTag = item.ImageTags.Banner;
type: 'Banner',
maxWidth: width,
tag: item.ImageTags.Banner
});
} else if (options.preferDisc && item.ImageTags && item.ImageTags.Disc) { } else if (options.preferDisc && item.ImageTags && item.ImageTags.Disc) {
imgType = 'Disc';
imgUrl = apiClient.getScaledImageUrl(item.Id, { imgTag = item.ImageTags.Disc;
type: 'Disc',
maxWidth: width,
tag: item.ImageTags.Disc
});
} else if (options.preferLogo && item.ImageTags && item.ImageTags.Logo) { } else if (options.preferLogo && item.ImageTags && item.ImageTags.Logo) {
imgType = 'Logo';
imgUrl = apiClient.getScaledImageUrl(item.Id, { imgTag = item.ImageTags.Logo;
type: 'Logo',
maxWidth: width,
tag: item.ImageTags.Logo
});
} else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) { } else if (options.preferLogo && item.ParentLogoImageTag && item.ParentLogoItemId) {
imgType = 'Logo';
imgUrl = apiClient.getScaledImageUrl(item.ParentLogoItemId, { imgTag = item.ParentLogoImageTag;
type: 'Logo', itemId = item.ParentLogoItemId;
maxWidth: width,
tag: item.ParentLogoImageTag
});
} else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) { } else if (options.preferThumb && item.SeriesThumbImageTag && options.inheritThumb !== false) {
imgType = 'Thumb';
imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { imgTag = item.SeriesThumbImageTag;
type: 'Thumb', itemId = item.SeriesId;
maxWidth: width,
tag: item.SeriesThumbImageTag
});
} else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') { } else if (options.preferThumb && item.ParentThumbItemId && options.inheritThumb !== false && item.MediaType !== 'Photo') {
imgType = 'Thumb';
imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { imgTag = item.ParentThumbImageTag;
type: 'Thumb', itemId = item.ParentThumbItemId;
maxWidth: width,
tag: item.ParentThumbImageTag
});
} else if (options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length) { } else if (options.preferThumb && item.BackdropImageTags && item.BackdropImageTags.length) {
imgType = 'Backdrop';
imgUrl = apiClient.getScaledImageUrl(item.Id, { imgTag = item.BackdropImageTags[0];
type: 'Backdrop',
maxWidth: width,
tag: item.BackdropImageTags[0]
});
forceName = true; forceName = true;
} else if (options.preferThumb && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false && item.Type === 'Episode') { } else if (options.preferThumb && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false && item.Type === 'Episode') {
imgType = 'Backdrop';
imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { imgTag = item.ParentBackdropImageTags[0];
type: 'Backdrop', itemId = item.ParentBackdropItemId;
maxWidth: width, } else if (item.ImageTags && item.ImageTags.Primary && (item.Type !== 'Episode' || item.ChildCount !== 0)) {
tag: item.ParentBackdropImageTags[0] imgType = 'Primary';
}); imgTag = item.ImageTags.Primary;
} else if (item.ImageTags && item.ImageTags.Primary) {
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
imgUrl = apiClient.getScaledImageUrl(item.Id, {
type: 'Primary',
maxHeight: height,
maxWidth: width,
tag: item.ImageTags.Primary
});
if (options.preferThumb && options.showTitle !== false) { if (options.preferThumb && options.showTitle !== false) {
forceName = true; forceName = true;
} }
@ -601,18 +548,16 @@ import 'programStyles';
coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2; coverImage = (Math.abs(primaryImageAspectRatio - uiAspect) / uiAspect) <= 0.2;
} }
} }
} else if (item.SeriesPrimaryImageTag) {
imgType = 'Primary';
imgTag = item.SeriesPrimaryImageTag;
itemId = item.SeriesId;
} else if (item.PrimaryImageTag) { } else if (item.PrimaryImageTag) {
imgType = 'Primary';
imgTag = item.PrimaryImageTag;
itemId = item.PrimaryImageItemId;
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
imgUrl = apiClient.getScaledImageUrl(item.PrimaryImageItemId || item.Id || item.ItemId, {
type: 'Primary',
maxHeight: height,
maxWidth: width,
tag: item.PrimaryImageTag
});
if (options.preferThumb && options.showTitle !== false) { if (options.preferThumb && options.showTitle !== false) {
forceName = true; forceName = true;
} }
@ -624,30 +569,15 @@ import 'programStyles';
} }
} }
} else if (item.ParentPrimaryImageTag) { } else if (item.ParentPrimaryImageTag) {
imgType = 'Primary';
imgUrl = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, { imgTag = item.ParentPrimaryImageTag;
type: 'Primary', itemId = item.ParentPrimaryImageItemId;
maxWidth: width,
tag: item.ParentPrimaryImageTag
});
} else if (item.SeriesPrimaryImageTag) {
imgUrl = apiClient.getScaledImageUrl(item.SeriesId, {
type: 'Primary',
maxWidth: width,
tag: item.SeriesPrimaryImageTag
});
} else if (item.AlbumId && item.AlbumPrimaryImageTag) { } else if (item.AlbumId && item.AlbumPrimaryImageTag) {
imgType = 'Primary';
imgTag = item.AlbumPrimaryImageTag;
itemId = item.AlbumId;
height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null; height = width && primaryImageAspectRatio ? Math.round(width / primaryImageAspectRatio) : null;
imgUrl = apiClient.getScaledImageUrl(item.AlbumId, {
type: 'Primary',
maxHeight: height,
maxWidth: width,
tag: item.AlbumPrimaryImageTag
});
if (primaryImageAspectRatio) { if (primaryImageAspectRatio) {
uiAspect = getDesiredAspect(shape); uiAspect = getDesiredAspect(shape);
if (uiAspect) { if (uiAspect) {
@ -655,57 +585,46 @@ import 'programStyles';
} }
} }
} else if (item.Type === 'Season' && item.ImageTags && item.ImageTags.Thumb) { } else if (item.Type === 'Season' && item.ImageTags && item.ImageTags.Thumb) {
imgType = 'Thumb';
imgUrl = apiClient.getScaledImageUrl(item.Id, { imgTag = item.ImageTags.Thumb;
type: 'Thumb',
maxWidth: width,
tag: item.ImageTags.Thumb
});
} else if (item.BackdropImageTags && item.BackdropImageTags.length) { } else if (item.BackdropImageTags && item.BackdropImageTags.length) {
imgType = 'Backdrop';
imgUrl = apiClient.getScaledImageUrl(item.Id, { imgTag = item.BackdropImageTags[0];
type: 'Backdrop',
maxWidth: width,
tag: item.BackdropImageTags[0]
});
} else if (item.ImageTags && item.ImageTags.Thumb) { } else if (item.ImageTags && item.ImageTags.Thumb) {
imgType = 'Thumb';
imgUrl = apiClient.getScaledImageUrl(item.Id, { imgTag = item.ImageTags.Thumb;
type: 'Thumb',
maxWidth: width,
tag: item.ImageTags.Thumb
});
} else if (item.SeriesThumbImageTag && options.inheritThumb !== false) { } else if (item.SeriesThumbImageTag && options.inheritThumb !== false) {
imgType = 'Thumb';
imgUrl = apiClient.getScaledImageUrl(item.SeriesId, { imgTag = item.SeriesThumbImageTag;
type: 'Thumb', itemId = item.SeriesId;
maxWidth: width,
tag: item.SeriesThumbImageTag
});
} else if (item.ParentThumbItemId && options.inheritThumb !== false) { } else if (item.ParentThumbItemId && options.inheritThumb !== false) {
imgType = 'Thumb';
imgUrl = apiClient.getScaledImageUrl(item.ParentThumbItemId, { imgTag = item.ParentThumbImageTag;
type: 'Thumb', itemId = item.ParentThumbItemId;
maxWidth: width,
tag: item.ParentThumbImageTag
});
} else if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false) { } else if (item.ParentBackdropImageTags && item.ParentBackdropImageTags.length && options.inheritThumb !== false) {
imgType = 'Backdrop';
imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, { imgTag = item.ParentBackdropImageTags[0];
type: 'Backdrop', itemId = item.ParentBackdropItemId;
maxWidth: width,
tag: item.ParentBackdropImageTags[0]
});
} }
if (!itemId) {
itemId = item.Id;
}
if (imgTag && imgType) {
imgUrl = apiClient.getScaledImageUrl(itemId, {
type: imgType,
maxHeight: height,
maxWidth: width,
tag: imgTag
});
}
const blurHashes = options.imageBlurhashes || item.ImageBlurHashes || {};
return { return {
imgUrl: imgUrl, imgUrl: imgUrl,
blurhash: (blurHashes[imgType] || {})[imgTag],
forceName: forceName, forceName: forceName,
coverImage: coverImage coverImage: coverImage
}; };
@ -736,7 +655,7 @@ import 'programStyles';
for (let i = 0; i < character.length; i++) { for (let i = 0; i < character.length; i++) {
sum += parseInt(character.charAt(i)); sum += parseInt(character.charAt(i));
} }
let index = String(sum).substr(-1); const index = String(sum).substr(-1);
return (index % numRandomColors) + 1; return (index % numRandomColors) + 1;
} else { } else {
@ -761,9 +680,8 @@ import 'programStyles';
let valid = 0; let valid = 0;
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
let currentCssClass = cssClass; let currentCssClass = cssClass;
let text = lines[i]; const text = lines[i];
if (valid > 0 && isOuterFooter) { if (valid > 0 && isOuterFooter) {
currentCssClass += ' cardText-secondary'; currentCssClass += ' cardText-secondary';
@ -788,8 +706,7 @@ import 'programStyles';
} }
if (forceLines) { if (forceLines) {
const linesLength = maxLines || Math.min(lines.length, maxLines || lines.length);
let linesLength = maxLines || Math.min(lines.length, maxLines || lines.length);
while (valid < linesLength) { while (valid < linesLength) {
html += "<div class='" + cssClass + "'>&nbsp;</div>"; html += "<div class='" + cssClass + "'>&nbsp;</div>";
@ -820,7 +737,6 @@ import 'programStyles';
let airTimeText = ''; let airTimeText = '';
if (item.StartDate) { if (item.StartDate) {
try { try {
let date = datetime.parseISO8601Date(item.StartDate); let date = datetime.parseISO8601Date(item.StartDate);
@ -867,7 +783,6 @@ import 'programStyles';
const showOtherText = isOuterFooter ? !overlayText : overlayText; const showOtherText = isOuterFooter ? !overlayText : overlayText;
if (isOuterFooter && options.cardLayout && layoutManager.mobile) { if (isOuterFooter && options.cardLayout && layoutManager.mobile) {
if (options.cardFooterAside !== 'none') { if (options.cardFooterAside !== 'none') {
html += '<button is="paper-icon-button-light" class="itemAction btnCardOptions cardText-secondary" data-action="menu"><span class="material-icons more_vert"></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>';
} }
@ -882,9 +797,7 @@ import 'programStyles';
if (showOtherText) { if (showOtherText) {
if ((options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) { if ((options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) {
if (isOuterFooter && item.Type === 'Episode' && item.SeriesName) { if (isOuterFooter && item.Type === 'Episode' && item.SeriesName) {
if (item.SeriesId) { if (item.SeriesId) {
lines.push(getTextActionButton({ lines.push(getTextActionButton({
Id: item.SeriesId, Id: item.SeriesId,
@ -897,15 +810,12 @@ import 'programStyles';
lines.push(item.SeriesName); lines.push(item.SeriesName);
} }
} else { } else {
if (isUsingLiveTvNaming(item)) { if (isUsingLiveTvNaming(item)) {
lines.push(item.Name); lines.push(item.Name);
if (!item.EpisodeTitle) { if (!item.EpisodeTitle) {
titleAdded = true; titleAdded = true;
} }
} else { } else {
const parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || ''; const parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || '';
@ -923,7 +833,6 @@ import 'programStyles';
} }
if (showMediaTitle) { if (showMediaTitle) {
const name = options.showTitle === 'auto' && !item.IsFolder && item.MediaType === 'Photo' ? '' : itemHelper.getDisplayName(item, { const name = options.showTitle === 'auto' && !item.IsFolder && item.MediaType === 'Photo' ? '' : itemHelper.getDisplayName(item, {
includeParentInfo: options.includeParentInfoInTitle includeParentInfo: options.includeParentInfoInTitle
}); });
@ -940,7 +849,6 @@ import 'programStyles';
if (showOtherText) { if (showOtherText) {
if (options.showParentTitle && parentTitleUnderneath) { if (options.showParentTitle && parentTitleUnderneath) {
if (isOuterFooter && item.AlbumArtists && item.AlbumArtists.length) { if (isOuterFooter && item.AlbumArtists && item.AlbumArtists.length) {
item.AlbumArtists[0].Type = 'MusicArtist'; item.AlbumArtists[0].Type = 'MusicArtist';
item.AlbumArtists[0].IsFolder = true; item.AlbumArtists[0].IsFolder = true;
@ -974,7 +882,6 @@ import 'programStyles';
} }
if (options.showPremiereDate) { if (options.showPremiereDate) {
if (item.PremiereDate) { if (item.PremiereDate) {
try { try {
lines.push(datetime.toLocaleDateString( lines.push(datetime.toLocaleDateString(
@ -983,7 +890,6 @@ import 'programStyles';
)); ));
} catch (err) { } catch (err) {
lines.push(''); lines.push('');
} }
} else { } else {
lines.push(''); lines.push('');
@ -991,14 +897,10 @@ import 'programStyles';
} }
if (options.showYear || options.showSeriesYear) { if (options.showYear || options.showSeriesYear) {
if (item.Type === 'Series') { if (item.Type === 'Series') {
if (item.Status === 'Continuing') { if (item.Status === 'Continuing') {
lines.push(globalize.translate('SeriesYearToPresent', item.ProductionYear || '')); lines.push(globalize.translate('SeriesYearToPresent', item.ProductionYear || ''));
} else { } else {
if (item.EndDate && item.ProductionYear) { if (item.EndDate && item.ProductionYear) {
const endYear = datetime.parseISO8601Date(item.EndDate).getFullYear(); const endYear = datetime.parseISO8601Date(item.EndDate).getFullYear();
lines.push(item.ProductionYear + ((endYear === item.ProductionYear) ? '' : (' - ' + endYear))); lines.push(item.ProductionYear + ((endYear === item.ProductionYear) ? '' : (' - ' + endYear)));
@ -1012,9 +914,7 @@ import 'programStyles';
} }
if (options.showRuntime) { if (options.showRuntime) {
if (item.RunTimeTicks) { if (item.RunTimeTicks) {
lines.push(datetime.getDisplayRunningTime(item.RunTimeTicks)); lines.push(datetime.getDisplayRunningTime(item.RunTimeTicks));
} else { } else {
lines.push(''); lines.push('');
@ -1022,14 +922,11 @@ import 'programStyles';
} }
if (options.showAirTime) { if (options.showAirTime) {
lines.push(getAirTimeText(item, options.showAirDateTime, options.showAirEndTime) || ''); lines.push(getAirTimeText(item, options.showAirDateTime, options.showAirEndTime) || '');
} }
if (options.showChannelName) { if (options.showChannelName) {
if (item.ChannelId) { if (item.ChannelId) {
lines.push(getTextActionButton({ lines.push(getTextActionButton({
Id: item.ChannelId, Id: item.ChannelId,
@ -1046,7 +943,6 @@ import 'programStyles';
} }
if (options.showCurrentProgram && item.Type === 'TvChannel') { if (options.showCurrentProgram && item.Type === 'TvChannel') {
if (item.CurrentProgram) { if (item.CurrentProgram) {
lines.push(item.CurrentProgram.Name); lines.push(item.CurrentProgram.Name);
} else { } else {
@ -1055,7 +951,6 @@ import 'programStyles';
} }
if (options.showCurrentProgramTime && item.Type === 'TvChannel') { if (options.showCurrentProgramTime && item.Type === 'TvChannel') {
if (item.CurrentProgram) { if (item.CurrentProgram) {
lines.push(getAirTimeText(item.CurrentProgram, false, true) || ''); lines.push(getAirTimeText(item.CurrentProgram, false, true) || '');
} else { } else {
@ -1065,7 +960,6 @@ import 'programStyles';
if (options.showSeriesTimerTime) { if (options.showSeriesTimerTime) {
if (item.RecordAnyTime) { if (item.RecordAnyTime) {
lines.push(globalize.translate('Anytime')); lines.push(globalize.translate('Anytime'));
} else { } else {
lines.push(datetime.getDisplayTime(item.StartDate)); lines.push(datetime.getDisplayTime(item.StartDate));
@ -1091,6 +985,10 @@ import 'programStyles';
lines = []; lines = [];
} }
if (overlayText && showTitle) {
lines = [item.Name];
}
const addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile; const addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile;
html += getCardTextLines(lines, cssClass, !options.overlayText, isOuterFooter, options.cardLayout, addRightTextMargin, options.lines); html += getCardTextLines(lines, cssClass, !options.overlayText, isOuterFooter, options.cardLayout, addRightTextMargin, options.lines);
@ -1100,7 +998,6 @@ import 'programStyles';
} }
if (html) { if (html) {
if (!isOuterFooter || logoUrl || options.cardLayout) { if (!isOuterFooter || logoUrl || options.cardLayout) {
html = '<div class="' + footerClass + '">' + html; html = '<div class="' + footerClass + '">' + html;
@ -1142,31 +1039,25 @@ import 'programStyles';
* @returns {string} HTML markup for the item count indicator. * @returns {string} HTML markup for the item count indicator.
*/ */
function getItemCountsHtml(options, item) { function getItemCountsHtml(options, item) {
let counts = []; const counts = [];
let childText; let childText;
if (item.Type === 'Playlist') { if (item.Type === 'Playlist') {
childText = ''; childText = '';
if (item.RunTimeTicks) { if (item.RunTimeTicks) {
let minutes = item.RunTimeTicks / 600000000; let minutes = item.RunTimeTicks / 600000000;
minutes = minutes || 1; minutes = minutes || 1;
childText += globalize.translate('ValueMinutes', Math.round(minutes)); childText += globalize.translate('ValueMinutes', Math.round(minutes));
} else { } else {
childText += globalize.translate('ValueMinutes', 0); childText += globalize.translate('ValueMinutes', 0);
} }
counts.push(childText); counts.push(childText);
} else if (item.Type === 'Genre' || item.Type === 'Studio') { } else if (item.Type === 'Genre' || item.Type === 'Studio') {
if (item.MovieCount) { if (item.MovieCount) {
childText = item.MovieCount === 1 ? childText = item.MovieCount === 1 ?
globalize.translate('ValueOneMovie') : globalize.translate('ValueOneMovie') :
globalize.translate('ValueMovieCount', item.MovieCount); globalize.translate('ValueMovieCount', item.MovieCount);
@ -1175,7 +1066,6 @@ import 'programStyles';
} }
if (item.SeriesCount) { if (item.SeriesCount) {
childText = item.SeriesCount === 1 ? childText = item.SeriesCount === 1 ?
globalize.translate('ValueOneSeries') : globalize.translate('ValueOneSeries') :
globalize.translate('ValueSeriesCount', item.SeriesCount); globalize.translate('ValueSeriesCount', item.SeriesCount);
@ -1183,18 +1073,14 @@ import 'programStyles';
counts.push(childText); counts.push(childText);
} }
if (item.EpisodeCount) { if (item.EpisodeCount) {
childText = item.EpisodeCount === 1 ? childText = item.EpisodeCount === 1 ?
globalize.translate('ValueOneEpisode') : globalize.translate('ValueOneEpisode') :
globalize.translate('ValueEpisodeCount', item.EpisodeCount); globalize.translate('ValueEpisodeCount', item.EpisodeCount);
counts.push(childText); counts.push(childText);
} }
} else if (item.Type === 'MusicGenre' || options.context === 'MusicArtist') { } else if (item.Type === 'MusicGenre' || options.context === 'MusicArtist') {
if (item.AlbumCount) { if (item.AlbumCount) {
childText = item.AlbumCount === 1 ? childText = item.AlbumCount === 1 ?
globalize.translate('ValueOneAlbum') : globalize.translate('ValueOneAlbum') :
globalize.translate('ValueAlbumCount', item.AlbumCount); globalize.translate('ValueAlbumCount', item.AlbumCount);
@ -1202,7 +1088,6 @@ import 'programStyles';
counts.push(childText); counts.push(childText);
} }
if (item.SongCount) { if (item.SongCount) {
childText = item.SongCount === 1 ? childText = item.SongCount === 1 ?
globalize.translate('ValueOneSong') : globalize.translate('ValueOneSong') :
globalize.translate('ValueSongCount', item.SongCount); globalize.translate('ValueSongCount', item.SongCount);
@ -1210,16 +1095,13 @@ import 'programStyles';
counts.push(childText); counts.push(childText);
} }
if (item.MusicVideoCount) { if (item.MusicVideoCount) {
childText = item.MusicVideoCount === 1 ? childText = item.MusicVideoCount === 1 ?
globalize.translate('ValueOneMusicVideo') : globalize.translate('ValueOneMusicVideo') :
globalize.translate('ValueMusicVideoCount', item.MusicVideoCount); globalize.translate('ValueMusicVideoCount', item.MusicVideoCount);
counts.push(childText); counts.push(childText);
} }
} else if (item.Type === 'Series') { } else if (item.Type === 'Series') {
childText = item.RecursiveItemCount === 1 ? childText = item.RecursiveItemCount === 1 ?
globalize.translate('ValueOneEpisode') : globalize.translate('ValueOneEpisode') :
globalize.translate('ValueEpisodeCount', item.RecursiveItemCount); globalize.translate('ValueEpisodeCount', item.RecursiveItemCount);
@ -1235,10 +1117,11 @@ import 'programStyles';
/** /**
* Imports the refresh indicator element. * Imports the refresh indicator element.
*/ */
function requireRefreshIndicator() { function importRefreshIndicator() {
if (!refreshIndicatorLoaded) { if (!refreshIndicatorLoaded) {
refreshIndicatorLoaded = true; refreshIndicatorLoaded = true;
require(['emby-itemrefreshindicator']); /* eslint-disable-next-line @babel/no-unused-expressions */
import('emby-itemrefreshindicator');
} }
} }
@ -1272,13 +1155,11 @@ import 'programStyles';
let shape = options.shape; let shape = options.shape;
if (shape === 'mixed') { if (shape === 'mixed') {
shape = null; shape = null;
const primaryImageAspectRatio = item.PrimaryImageAspectRatio; const primaryImageAspectRatio = item.PrimaryImageAspectRatio;
if (primaryImageAspectRatio) { if (primaryImageAspectRatio) {
if (primaryImageAspectRatio >= 1.33) { if (primaryImageAspectRatio >= 1.33) {
shape = 'mixedBackdrop'; shape = 'mixedBackdrop';
} else if (primaryImageAspectRatio > 0.71) { } else if (primaryImageAspectRatio > 0.71) {
@ -1321,6 +1202,7 @@ import 'programStyles';
const imgInfo = getCardImageUrl(item, apiClient, options, shape); const imgInfo = getCardImageUrl(item, apiClient, options, shape);
const imgUrl = imgInfo.imgUrl; const imgUrl = imgInfo.imgUrl;
const blurhash = imgInfo.blurhash;
const forceName = imgInfo.forceName; const forceName = imgInfo.forceName;
@ -1333,8 +1215,8 @@ import 'programStyles';
if (coveredImage) { if (coveredImage) {
cardImageContainerClass += ' coveredImage'; cardImageContainerClass += ' coveredImage';
if (item.MediaType === 'Photo' || item.Type === 'PhotoAlbum' || item.Type === 'Folder' || item.ProgramInfo || item.Type === 'Program' || item.Type === 'Recording') { if (item.Type === 'TvChannel') {
cardImageContainerClass += ' coveredImage-noScale'; cardImageContainerClass += ' coveredImage-contain';
} }
} }
@ -1369,7 +1251,6 @@ import 'programStyles';
} }
if (overlayText) { if (overlayText) {
logoUrl = null; logoUrl = null;
footerCssClass = progressHtml ? 'innerCardFooter fullInnerCardFooter' : 'innerCardFooter'; footerCssClass = progressHtml ? 'innerCardFooter fullInnerCardFooter' : 'innerCardFooter';
@ -1384,7 +1265,7 @@ import 'programStyles';
} }
const mediaSourceCount = item.MediaSourceCount || 1; const mediaSourceCount = item.MediaSourceCount || 1;
if (mediaSourceCount > 1) { if (mediaSourceCount > 1 && options.disableIndicators !== true) {
innerCardFooter += '<div class="mediaSourceIndicator">' + mediaSourceCount + '</div>'; innerCardFooter += '<div class="mediaSourceIndicator">' + mediaSourceCount + '</div>';
} }
@ -1440,30 +1321,32 @@ import 'programStyles';
let cardBoxClose = ''; let cardBoxClose = '';
let cardScalableClose = ''; let cardScalableClose = '';
let cardContentClass = 'cardContent'; const cardContentClass = 'cardContent';
if (!options.cardLayout) {
cardContentClass += ' cardContent-shadow'; let blurhashAttrib = '';
if (blurhash && blurhash.length > 0) {
blurhashAttrib = 'data-blurhash="' + blurhash + '"';
} }
if (layoutManager.tv) { if (layoutManager.tv) {
// Don't use the IMG tag with safari because it puts a white border around it // Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + ' lazy" data-src="' + imgUrl + '">') : ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + '">'); cardImageContainerOpen = imgUrl ? ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + ' lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + '>') : ('<div class="' + cardImageContainerClass + ' ' + cardContentClass + '">');
cardImageContainerClose = '</div>'; cardImageContainerClose = '</div>';
} else { } else {
// Don't use the IMG tag with safari because it puts a white border around it // Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction lazy" data-src="' + imgUrl + '">') : ('<button data-action="' + action + '" class="cardContent-button ' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction">'); cardImageContainerOpen = imgUrl ? ('<button data-action="' + action + '" class="' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction lazy" data-src="' + imgUrl + '" ' + blurhashAttrib + '>') : ('<button data-action="' + action + '" class="' + cardImageContainerClass + ' ' + cardContentClass + ' itemAction">');
cardImageContainerClose = '</button>'; cardImageContainerClose = '</button>';
} }
let cardScalableClass = 'cardScalable'; const cardScalableClass = 'cardScalable';
cardImageContainerOpen = '<div class="' + cardBoxClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainerOpen; cardImageContainerOpen = '<div class="' + cardBoxClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder cardPadder-' + shape + '"></div>' + cardImageContainerOpen;
cardBoxClose = '</div>'; cardBoxClose = '</div>';
cardScalableClose = '</div>'; cardScalableClose = '</div>';
if (options.disableIndicators !== true) {
let indicatorsHtml = ''; let indicatorsHtml = '';
if (options.missingIndicator !== false) { if (options.missingIndicator !== false) {
@ -1476,7 +1359,6 @@ import 'programStyles';
indicatorsHtml += indicators.getTypeIndicator(item); indicatorsHtml += indicators.getTypeIndicator(item);
if (options.showGroupCount) { if (options.showGroupCount) {
indicatorsHtml += indicators.getChildCountIndicatorHtml(item, { indicatorsHtml += indicators.getChildCountIndicatorHtml(item, {
minCount: 1 minCount: 1
}); });
@ -1487,12 +1369,13 @@ import 'programStyles';
if (item.Type === 'CollectionFolder' || item.CollectionType) { if (item.Type === 'CollectionFolder' || item.CollectionType) {
const refreshClass = item.RefreshProgress ? '' : ' class="hide"'; const refreshClass = item.RefreshProgress ? '' : ' class="hide"';
indicatorsHtml += '<div is="emby-itemrefreshindicator"' + refreshClass + ' data-progress="' + (item.RefreshProgress || 0) + '" data-status="' + item.RefreshStatus + '"></div>'; indicatorsHtml += '<div is="emby-itemrefreshindicator"' + refreshClass + ' data-progress="' + (item.RefreshProgress || 0) + '" data-status="' + item.RefreshStatus + '"></div>';
requireRefreshIndicator(); importRefreshIndicator();
} }
if (indicatorsHtml) { if (indicatorsHtml) {
cardImageContainerOpen += '<div class="cardIndicators">' + indicatorsHtml + '</div>'; cardImageContainerOpen += '<div class="cardIndicators">' + indicatorsHtml + '</div>';
} }
}
if (!imgUrl) { if (!imgUrl) {
cardImageContainerOpen += getDefaultText(item, options); cardImageContainerOpen += getDefaultText(item, options);
@ -1539,8 +1422,8 @@ import 'programStyles';
let additionalCardContent = ''; let additionalCardContent = '';
if (layoutManager.desktop) { if (layoutManager.desktop && !options.disableHoverMenu) {
additionalCardContent += getHoverMenuHtml(item, action); additionalCardContent += getHoverMenuHtml(item, action, options);
} }
return '<' + tagName + ' data-index="' + index + '"' + timerAttributes + actionAttribute + ' data-isfolder="' + (item.IsFolder || false) + '" data-serverid="' + (item.ServerId || options.serverId) + '" data-id="' + (item.Id || item.ItemId) + '" data-type="' + item.Type + '"' + mediaTypeData + collectionTypeData + channelIdData + positionTicksData + collectionIdData + playlistIdData + contextData + parentIdData + ' data-prefix="' + prefix + '" class="' + className + '">' + cardImageContainerOpen + innerCardFooter + cardImageContainerClose + overlayButtons + additionalCardContent + cardScalableClose + outerCardFooter + cardBoxClose + '</' + tagName + '>'; return '<' + tagName + ' data-index="' + index + '"' + timerAttributes + actionAttribute + ' data-isfolder="' + (item.IsFolder || false) + '" data-serverid="' + (item.ServerId || options.serverId) + '" data-id="' + (item.Id || item.ItemId) + '" data-type="' + item.Type + '"' + mediaTypeData + collectionTypeData + channelIdData + positionTicksData + collectionIdData + playlistIdData + contextData + parentIdData + ' data-prefix="' + prefix + '" class="' + className + '">' + cardImageContainerOpen + innerCardFooter + cardImageContainerClose + overlayButtons + additionalCardContent + cardScalableClose + outerCardFooter + cardBoxClose + '</' + tagName + '>';
@ -1550,9 +1433,10 @@ import 'programStyles';
* Generates HTML markup for the card overlay. * Generates HTML markup for the card overlay.
* @param {object} item - Item used to generate the card overlay. * @param {object} item - Item used to generate the card overlay.
* @param {string} action - Action assigned to the overlay. * @param {string} action - Action assigned to the overlay.
* @param {Array} options - Card builder options.
* @returns {string} HTML markup of the card overlay. * @returns {string} HTML markup of the card overlay.
*/ */
function getHoverMenuHtml(item, action) { function getHoverMenuHtml(item, action, options) {
let html = ''; let html = '';
html += '<div class="cardOverlayContainer itemAction" data-action="' + action + '">'; html += '<div class="cardOverlayContainer itemAction" data-action="' + action + '">';
@ -1568,20 +1452,20 @@ import 'programStyles';
const userData = item.UserData || {}; const userData = item.UserData || {};
if (itemHelper.canMarkPlayed(item)) { if (itemHelper.canMarkPlayed(item)) {
require(['emby-playstatebutton']); /* eslint-disable-next-line @babel/no-unused-expressions */
import('emby-playstatebutton');
html += '<button is="emby-playstatebutton" type="button" data-action="none" class="' + btnCssClass + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-played="' + (userData.Played) + '"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover check"></span></button>'; html += '<button is="emby-playstatebutton" type="button" data-action="none" class="' + btnCssClass + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-itemtype="' + item.Type + '" data-played="' + (userData.Played) + '"><span class="material-icons cardOverlayButtonIcon cardOverlayButtonIcon-hover check"></span></button>';
} }
if (itemHelper.canRate(item)) { if (itemHelper.canRate(item)) {
const likes = userData.Likes == null ? '' : userData.Likes; const likes = userData.Likes == null ? '' : userData.Likes;
require(['emby-ratingbutton']); /* eslint-disable-next-line @babel/no-unused-expressions */
import('emby-ratingbutton');
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="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_vert"></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>';
html += '</div>'; html += '</div>';
@ -1605,6 +1489,8 @@ import 'programStyles';
case 'MusicArtist': case 'MusicArtist':
case 'Person': case 'Person':
return '<span class="cardImageIcon material-icons person"></span>'; return '<span class="cardImageIcon material-icons person"></span>';
case 'Audio':
return '<span class="cardImageIcon material-icons audiotrack"></span>';
case 'Movie': case 'Movie':
return '<span class="cardImageIcon material-icons movie"></span>'; return '<span class="cardImageIcon material-icons movie"></span>';
case 'Series': case 'Series':
@ -1613,6 +1499,12 @@ import 'programStyles';
return '<span class="cardImageIcon material-icons book"></span>'; return '<span class="cardImageIcon material-icons book"></span>';
case 'Folder': case 'Folder':
return '<span class="cardImageIcon material-icons folder"></span>'; return '<span class="cardImageIcon material-icons folder"></span>';
case 'BoxSet':
return '<span class="cardImageIcon material-icons collections"></span>';
case 'Playlist':
return '<span class="cardImageIcon material-icons view_list"></span>';
case 'PhotoAlbum':
return '<span class="cardImageIcon material-icons photo_album"></span>';
} }
if (options && options.defaultCardImageIcon) { if (options && options.defaultCardImageIcon) {
@ -1646,7 +1538,6 @@ import 'programStyles';
const html = buildCardsHtmlInternal(items, options); const html = buildCardsHtmlInternal(items, options);
if (html) { if (html) {
if (options.itemsContainer.cardBuilderHtml !== html) { if (options.itemsContainer.cardBuilderHtml !== html) {
options.itemsContainer.innerHTML = html; options.itemsContainer.innerHTML = html;
@ -1659,7 +1550,6 @@ import 'programStyles';
imageLoader.lazyChildren(options.itemsContainer); imageLoader.lazyChildren(options.itemsContainer);
} else { } else {
options.itemsContainer.innerHTML = html; options.itemsContainer.innerHTML = html;
options.itemsContainer.cardBuilderHtml = null; options.itemsContainer.cardBuilderHtml = null;
} }
@ -1683,7 +1573,6 @@ import 'programStyles';
indicatorsElem = card.querySelector('.cardIndicators'); indicatorsElem = card.querySelector('.cardIndicators');
if (!indicatorsElem) { if (!indicatorsElem) {
const cardImageContainer = card.querySelector('.cardImageContainer'); const cardImageContainer = card.querySelector('.cardImageContainer');
indicatorsElem = document.createElement('div'); indicatorsElem = document.createElement('div');
indicatorsElem.classList.add('cardIndicators'); indicatorsElem.classList.add('cardIndicators');
@ -1707,11 +1596,9 @@ import 'programStyles';
let itemProgressBar = null; let itemProgressBar = null;
if (userData.Played) { if (userData.Played) {
playedIndicator = card.querySelector('.playedIndicator'); playedIndicator = card.querySelector('.playedIndicator');
if (!playedIndicator) { if (!playedIndicator) {
playedIndicator = document.createElement('div'); playedIndicator = document.createElement('div');
playedIndicator.classList.add('playedIndicator'); playedIndicator.classList.add('playedIndicator');
playedIndicator.classList.add('indicator'); playedIndicator.classList.add('indicator');
@ -1720,10 +1607,8 @@ import 'programStyles';
} }
playedIndicator.innerHTML = '<span class="material-icons indicatorIcon check"></span>'; playedIndicator.innerHTML = '<span class="material-icons indicatorIcon check"></span>';
} else { } else {
playedIndicator = card.querySelector('.playedIndicator'); playedIndicator = card.querySelector('.playedIndicator');
if (playedIndicator) { if (playedIndicator) {
playedIndicator.parentNode.removeChild(playedIndicator); playedIndicator.parentNode.removeChild(playedIndicator);
} }
} }
@ -1731,7 +1616,6 @@ import 'programStyles';
countIndicator = card.querySelector('.countIndicator'); countIndicator = card.querySelector('.countIndicator');
if (!countIndicator) { if (!countIndicator) {
countIndicator = document.createElement('div'); countIndicator = document.createElement('div');
countIndicator.classList.add('countIndicator'); countIndicator.classList.add('countIndicator');
indicatorsElem = ensureIndicators(card, indicatorsElem); indicatorsElem = ensureIndicators(card, indicatorsElem);
@ -1739,10 +1623,8 @@ import 'programStyles';
} }
countIndicator.innerHTML = userData.UnplayedItemCount; countIndicator.innerHTML = userData.UnplayedItemCount;
} else if (enableCountIndicator) { } else if (enableCountIndicator) {
countIndicator = card.querySelector('.countIndicator'); countIndicator = card.querySelector('.countIndicator');
if (countIndicator) { if (countIndicator) {
countIndicator.parentNode.removeChild(countIndicator); countIndicator.parentNode.removeChild(countIndicator);
} }
} }
@ -1754,7 +1636,6 @@ import 'programStyles';
}); });
if (progressHtml) { if (progressHtml) {
itemProgressBar = card.querySelector('.itemProgressBar'); itemProgressBar = card.querySelector('.itemProgressBar');
if (!itemProgressBar) { if (!itemProgressBar) {
@ -1773,7 +1654,6 @@ import 'programStyles';
itemProgressBar.innerHTML = progressHtml; itemProgressBar.innerHTML = progressHtml;
} else { } else {
itemProgressBar = card.querySelector('.itemProgressBar'); itemProgressBar = card.querySelector('.itemProgressBar');
if (itemProgressBar) { if (itemProgressBar) {
itemProgressBar.parentNode.removeChild(itemProgressBar); itemProgressBar.parentNode.removeChild(itemProgressBar);
@ -1804,7 +1684,7 @@ import 'programStyles';
const cells = itemsContainer.querySelectorAll('.card[data-id="' + programId + '"]'); const cells = itemsContainer.querySelectorAll('.card[data-id="' + programId + '"]');
for (let i = 0, length = cells.length; i < length; i++) { for (let i = 0, length = cells.length; i < length; i++) {
let cell = cells[i]; const cell = cells[i];
const icon = cell.querySelector('.timerIndicator'); const icon = cell.querySelector('.timerIndicator');
if (!icon) { if (!icon) {
const indicatorsElem = ensureIndicators(cell); const indicatorsElem = ensureIndicators(cell);
@ -1823,8 +1703,8 @@ import 'programStyles';
const cells = itemsContainer.querySelectorAll('.card[data-timerid="' + timerId + '"]'); const cells = itemsContainer.querySelectorAll('.card[data-timerid="' + timerId + '"]');
for (let i = 0; i < cells.length; i++) { for (let i = 0; i < cells.length; i++) {
let cell = cells[i]; const cell = cells[i];
let icon = cell.querySelector('.timerIndicator'); const icon = cell.querySelector('.timerIndicator');
if (icon) { if (icon) {
icon.parentNode.removeChild(icon); icon.parentNode.removeChild(icon);
} }
@ -1841,8 +1721,8 @@ import 'programStyles';
const cells = itemsContainer.querySelectorAll('.card[data-seriestimerid="' + cancelledTimerId + '"]'); const cells = itemsContainer.querySelectorAll('.card[data-seriestimerid="' + cancelledTimerId + '"]');
for (let i = 0; i < cells.length; i++) { for (let i = 0; i < cells.length; i++) {
let cell = cells[i]; const cell = cells[i];
let icon = cell.querySelector('.timerIndicator'); const icon = cell.querySelector('.timerIndicator');
if (icon) { if (icon) {
icon.parentNode.removeChild(icon); icon.parentNode.removeChild(icon);
} }

View file

@ -1,13 +1,21 @@
define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browser'], function (datetime, imageLoader, connectionManager, layoutManager, browser) { /* eslint-disable indent */
'use strict';
var enableFocusTransform = !browser.slow && !browser.edge; /**
* Module for building cards from item data.
* @module components/cardBuilder/chaptercardbuilder
*/
import datetime from 'datetime';
import imageLoader from 'imageLoader';
import layoutManager from 'layoutManager';
import browser from 'browser';
const enableFocusTransform = !browser.slow && !browser.edge;
function buildChapterCardsHtml(item, chapters, options) { function buildChapterCardsHtml(item, chapters, options) {
// TODO move card creation code to Card component // TODO move card creation code to Card component
var className = 'card itemAction chapterCard'; let className = 'card itemAction chapterCard';
if (layoutManager.tv) { if (layoutManager.tv) {
className += ' show-focus'; className += ' show-focus';
@ -17,38 +25,36 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse
} }
} }
var mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || []; const mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || [];
var videoStream = mediaStreams.filter(function (i) { const videoStream = mediaStreams.filter(({Type}) => {
return i.Type === 'Video'; return Type === 'Video';
})[0] || {}; })[0] || {};
var shape = (options.backdropShape || 'backdrop'); let shape = (options.backdropShape || 'backdrop');
if (videoStream.Width && videoStream.Height) { if (videoStream.Width && videoStream.Height) {
if ((videoStream.Width / videoStream.Height) <= 1.2) { if ((videoStream.Width / videoStream.Height) <= 1.2) {
shape = (options.squareShape || 'square'); shape = (options.squareShape || 'square');
} }
} }
className += ' ' + shape + 'Card'; className += ` ${shape}Card`;
if (options.block || options.rows) { if (options.block || options.rows) {
className += ' block'; className += ' block';
} }
var html = ''; let html = '';
var itemsInRow = 0; let itemsInRow = 0;
var apiClient = connectionManager.getApiClient(item.ServerId); const apiClient = window.connectionManager.getApiClient(item.ServerId);
for (var i = 0, length = chapters.length; i < length; i++) {
for (let i = 0, length = chapters.length; i < length; i++) {
if (options.rows && itemsInRow === 0) { if (options.rows && itemsInRow === 0) {
html += '<div class="cardColumn">'; html += '<div class="cardColumn">';
} }
var chapter = chapters[i]; const chapter = chapters[i];
html += buildChapterCard(item, apiClient, chapter, i, options, className, shape); html += buildChapterCard(item, apiClient, chapter, i, options, className, shape);
itemsInRow++; itemsInRow++;
@ -62,51 +68,45 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse
return html; return html;
} }
function getImgUrl(item, chapter, index, maxWidth, apiClient) { function getImgUrl({Id}, {ImageTag}, index, maxWidth, apiClient) {
if (ImageTag) {
return apiClient.getScaledImageUrl(Id, {
if (chapter.ImageTag) { maxWidth: maxWidth,
tag: ImageTag,
return apiClient.getScaledImageUrl(item.Id, {
maxWidth: maxWidth * 2,
tag: chapter.ImageTag,
type: 'Chapter', type: 'Chapter',
index: index index
}); });
} }
return null; return null;
} }
function buildChapterCard(item, apiClient, chapter, index, options, className, shape) { function buildChapterCard(item, apiClient, chapter, index, {width, coverImage}, className, shape) {
const imgUrl = getImgUrl(item, chapter, index, width || 400, apiClient);
var imgUrl = getImgUrl(item, chapter, index, options.width || 400, apiClient); let cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer';
if (coverImage) {
var cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer';
if (options.coverImage) {
cardImageContainerClass += ' coveredImage'; cardImageContainerClass += ' coveredImage';
} }
var dataAttributes = ' data-action="play" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-positionticks="' + chapter.StartPositionTicks + '"'; const dataAttributes = ` data-action="play" data-isfolder="${item.IsFolder}" data-id="${item.Id}" data-serverid="${item.ServerId}" data-type="${item.Type}" data-mediatype="${item.MediaType}" data-positionticks="${chapter.StartPositionTicks}"`;
var cardImageContainer = imgUrl ? ('<div class="' + cardImageContainerClass + ' lazy" data-src="' + imgUrl + '">') : ('<div class="' + cardImageContainerClass + '">'); let cardImageContainer = imgUrl ? (`<div class="${cardImageContainerClass} lazy" data-src="${imgUrl}">`) : (`<div class="${cardImageContainerClass}">`);
if (!imgUrl) { if (!imgUrl) {
cardImageContainer += '<span class="material-icons cardImageIcon local_movies"></span>'; cardImageContainer += '<span class="material-icons cardImageIcon local_movies"></span>';
} }
var nameHtml = ''; let nameHtml = '';
nameHtml += '<div class="cardText">' + chapter.Name + '</div>'; nameHtml += `<div class="cardText">${chapter.Name}</div>`;
nameHtml += '<div class="cardText">' + datetime.getDisplayRunningTime(chapter.StartPositionTicks) + '</div>'; nameHtml += `<div class="cardText">${datetime.getDisplayRunningTime(chapter.StartPositionTicks)}</div>`;
var cardBoxCssClass = 'cardBox'; const cardBoxCssClass = 'cardBox';
var cardScalableClass = 'cardScalable'; const cardScalableClass = 'cardScalable';
var html = '<button type="button" class="' + className + '"' + dataAttributes + '><div class="' + cardBoxCssClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainer + '</div><div class="innerCardFooter">' + nameHtml + '</div></div></div></button>'; return `<button type="button" class="${className}"${dataAttributes}><div class="${cardBoxCssClass}"><div class="${cardScalableClass}"><div class="cardPadder-${shape}"></div>${cardImageContainer}</div><div class="innerCardFooter">${nameHtml}</div></div></div></button>`;
return html;
} }
function buildChapterCards(item, chapters, options) { export function buildChapterCards(item, chapters, options) {
if (options.parentContainer) { if (options.parentContainer) {
// Abort if the container has been disposed // Abort if the container has been disposed
if (!document.body.contains(options.parentContainer)) { if (!document.body.contains(options.parentContainer)) {
@ -121,15 +121,16 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse
} }
} }
var html = buildChapterCardsHtml(item, chapters, options); const html = buildChapterCardsHtml(item, chapters, options);
options.itemsContainer.innerHTML = html; options.itemsContainer.innerHTML = html;
imageLoader.lazyChildren(options.itemsContainer); imageLoader.lazyChildren(options.itemsContainer);
} }
return { /* eslint-enable indent */
export default {
buildChapterCards: buildChapterCards buildChapterCards: buildChapterCards
}; };
});

View file

@ -1,8 +1,13 @@
define(['cardBuilder'], function (cardBuilder) { /* eslint-disable indent */
'use strict';
function buildPeopleCards(items, options) { /**
* Module for building cards from item data.
* @module components/cardBuilder/peoplecardbuilder
*/
import cardBuilder from 'cardBuilder';
export function buildPeopleCards(items, options) {
options = Object.assign(options || {}, { options = Object.assign(options || {}, {
cardLayout: false, cardLayout: false,
centerText: true, centerText: true,
@ -15,8 +20,8 @@ define(['cardBuilder'], function (cardBuilder) {
cardBuilder.buildCards(items, options); cardBuilder.buildCards(items, options);
} }
return { /* eslint-enable indent */
export default {
buildPeopleCards: buildPeopleCards buildPeopleCards: buildPeopleCards
}; };
});

View file

@ -1,23 +1,16 @@
define([], function() { class CastSenderApi {
'use strict'; load() {
if (window.appMode === 'cordova' || window.appMode === 'android') { if (window.appMode === 'cordova' || window.appMode === 'android') {
return {
load: function () {
window.chrome = window.chrome || {}; window.chrome = window.chrome || {};
return Promise.resolve(); return Promise.resolve();
}
};
} else { } else {
var ccLoaded = false; let ccLoaded = false;
return {
load: function () {
if (ccLoaded) { if (ccLoaded) {
return Promise.resolve(); return Promise.resolve();
} }
return new Promise(function (resolve, reject) { return new Promise(function (resolve) {
var fileref = document.createElement('script'); const fileref = document.createElement('script');
fileref.setAttribute('type', 'text/javascript'); fileref.setAttribute('type', 'text/javascript');
fileref.onload = function () { fileref.onload = function () {
@ -29,6 +22,7 @@ define([], function() {
document.querySelector('head').appendChild(fileref); document.querySelector('head').appendChild(fileref);
}); });
} }
};
} }
}); }
export default CastSenderApi;

View file

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

View file

@ -1,234 +0,0 @@
define(['events'], function (events) {
'use strict';
// LinkParser
//
// https://github.com/ravisorg/LinkParser
//
// Locate and extract almost any URL within a string. Handles protocol-less domains, IPv4 and
// IPv6, unrecognised TLDs, and more.
//
// This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.
// http://creativecommons.org/licenses/by-sa/4.0/
(function () {
// Original URL regex from the Android android.text.util.Linkify function, found here:
// http://stackoverflow.com/a/19696443
//
// However there were problems with it, most probably related to the fact it was
// written in 2007, and it's been highly modified.
//
// 1) I didn't like the fact that it was tied to specific TLDs, since new ones
// are being added all the time it wouldn't be reasonable to expect developer to
// be continually updating their regular expressions.
//
// 2) It didn't allow unicode characters in the domains which are now allowed in
// many languages, (including some IDN TLDs). Again these are constantly being
// added to and it doesn't seem reasonable to hard-code them. Note this ended up
// not being possible in standard JS due to the way it handles multibyte strings.
// It is possible using XRegExp, however a big performance hit results. Disabled
// for now.
//
// 3) It didn't allow for IPv6 hostnames
// IPv6 regex from http://stackoverflow.com/a/17871737
//
// 4) It was very poorly commented
//
// 5) It wasn't as smart as it could have been about what should be part of a
// URL and what should be part of human language.
var protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)';
var credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters)
+ "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters)
+ '\\@)';
// IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452
// by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License
// http://intermapper.com/
var ipv6 = '('
+ '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))'
+ '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
+ '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
+ '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
+ ')(%.+)?';
var ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])';
// This would have been a lot cleaner if JS RegExp supported conditionals...
var linkRegExpString =
// begin match for protocol / username / password / host
'(?:'
// ============================
// If we have a recognized protocol at the beginning of the URL, we're
// more relaxed about what we accept, because we assume the user wants
// this to be a URL, and we're not accidentally matching human language
+ protocols + '?'
// optional username:password@
+ credentials + '?'
// IP address (both v4 and v6)
+ '(?:'
// IPv6
+ ipv6
// IPv4
+ '|' + ipv4
+ ')'
// end match for protocol / username / password / host
+ ')'
// optional port number
+ '(?:\\:\\d{1,5})?'
// plus optional path and query params (no unicode allowed here?)
+ '(?:'
+ '\\/(?:'
// some characters we'll accept because it's unlikely human language
// would use them after a URL unless they were part of the url
+ '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])'
+ '|(?:\\%[a-f0-9]{2})'
// some characters are much more likely to be used AFTER a url and
// were not intended to be included in the url itself. Mostly end
// of sentence type things. It's also likely that the URL would
// still work if any of these characters were missing from the end
// because we parsed it incorrectly. For these characters to be accepted
// they must be followed by another character that we're reasonably
// sure is part of the url
+ "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))"
+ ')*'
+ '|\\b|\$'
+ ')';
// regex = XRegExp(regex,'gi');
var linkRegExp = RegExp(linkRegExpString, 'gi');
var protocolRegExp = RegExp('^' + protocols, 'i');
// if url doesn't begin with a known protocol, add http by default
function ensureProtocol(url) {
if (!url.match(protocolRegExp)) {
url = 'http://' + url;
}
return url;
}
// look for links in the text
var LinkParser = {
parse: function (text) {
var links = [];
var match;
// eslint-disable-next-line no-cond-assign
while (match = linkRegExp.exec(text)) {
console.debug(match);
var txt = match[0];
var pos = match.index;
var len = txt.length;
var url = ensureProtocol(text);
links.push({ 'pos': pos, 'text': txt, 'len': len, 'url': url });
}
return links;
}
};
window.LinkParser = LinkParser;
})();
var cache = {};
function isValidIpAddress(address) {
var links = LinkParser.parse(address);
return links.length == 1;
}
function isLocalIpAddress(address) {
address = address.toLowerCase();
if (address.indexOf('127.0.0.1') !== -1) {
return true;
}
if (address.indexOf('localhost') !== -1) {
return true;
}
return false;
}
function getServerAddress(apiClient) {
var serverAddress = apiClient.serverAddress();
if (isValidIpAddress(serverAddress) && !isLocalIpAddress(serverAddress)) {
return Promise.resolve(serverAddress);
}
var cachedValue = getCachedValue(serverAddress);
if (cachedValue) {
return Promise.resolve(cachedValue);
}
return apiClient.getEndpointInfo().then(function (endpoint) {
if (endpoint.IsInNetwork) {
return apiClient.getPublicSystemInfo().then(function (info) {
var localAddress = info.LocalAddress;
if (!localAddress) {
console.debug('No valid local address returned, defaulting to external one');
localAddress = serverAddress;
}
addToCache(serverAddress, localAddress);
return localAddress;
});
} else {
addToCache(serverAddress, serverAddress);
return serverAddress;
}
});
}
function clearCache() {
cache = {};
}
function addToCache(key, value) {
cache[key] = {
value: value,
time: new Date().getTime()
};
}
function getCachedValue(key) {
var obj = cache[key];
if (obj && (new Date().getTime() - obj.time) < 180000) {
return obj.value;
}
return null;
}
events.on(ConnectionManager, 'localusersignedin', clearCache);
events.on(ConnectionManager, 'localusersignedout', clearCache);
return {
getServerAddress: getServerAddress
};
});

View file

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

View file

@ -1,13 +1,16 @@
define(['browser', 'dialog', 'globalize'], function(browser, dialog, globalize) { import browser from 'browser';
'use strict'; import dialog from 'dialog';
import globalize from 'globalize';
/* eslint-disable indent */
export default (() => {
function replaceAll(str, find, replace) { function replaceAll(str, find, replace) {
return str.split(find).join(replace); return str.split(find).join(replace);
} }
if (browser.tv && window.confirm) { if (browser.tv && window.confirm) {
// Use the native confirm dialog // Use the native confirm dialog
return function (options) { return options => {
if (typeof options === 'string') { if (typeof options === 'string') {
options = { options = {
title: '', title: '',
@ -15,8 +18,8 @@ define(['browser', 'dialog', 'globalize'], function(browser, dialog, globalize)
}; };
} }
var text = replaceAll(options.text || '', '<br/>', '\n'); const text = replaceAll(options.text || '', '<br/>', '\n');
var result = confirm(text); const result = window.confirm(text);
if (result) { if (result) {
return Promise.resolve(); return Promise.resolve();
@ -26,8 +29,8 @@ define(['browser', 'dialog', 'globalize'], function(browser, dialog, globalize)
}; };
} else { } else {
// Use our own dialog // Use our own dialog
return function (text, title) { return (text, title) => {
var options; let options;
if (typeof text === 'string') { if (typeof text === 'string') {
options = { options = {
title: title, title: title,
@ -37,7 +40,7 @@ define(['browser', 'dialog', 'globalize'], function(browser, dialog, globalize)
options = text; options = text;
} }
var items = []; const items = [];
items.push({ items.push({
name: options.cancelText || globalize.translate('ButtonCancel'), name: options.cancelText || globalize.translate('ButtonCancel'),
@ -53,7 +56,7 @@ define(['browser', 'dialog', 'globalize'], function(browser, dialog, globalize)
options.buttons = items; options.buttons = items;
return dialog(options).then(function (result) { return dialog.show(options).then(result => {
if (result === 'ok') { if (result === 'ok') {
return Promise.resolve(); return Promise.resolve();
} }
@ -62,4 +65,5 @@ define(['browser', 'dialog', 'globalize'], function(browser, dialog, globalize)
}); });
}; };
} }
}); })();
/* eslint-enable indent */

View file

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

View file

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

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