Merge branch 'master' into improve-builds
This commit is contained in:
commit
446dadc0eb
140 changed files with 4232 additions and 3479 deletions
|
@ -2,8 +2,7 @@ trigger:
|
||||||
batch: true
|
batch: true
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
- master
|
- '*'
|
||||||
- release-*
|
|
||||||
tags:
|
tags:
|
||||||
include:
|
include:
|
||||||
- '*'
|
- '*'
|
||||||
|
@ -13,12 +12,9 @@ pr:
|
||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: build
|
- job: Build
|
||||||
displayName: 'Build'
|
displayName: 'Build'
|
||||||
|
|
||||||
pool:
|
|
||||||
vmImage: 'ubuntu-latest'
|
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
Development:
|
Development:
|
||||||
|
@ -27,13 +23,15 @@ jobs:
|
||||||
BuildConfiguration: production
|
BuildConfiguration: production
|
||||||
Standalone:
|
Standalone:
|
||||||
BuildConfiguration: standalone
|
BuildConfiguration: standalone
|
||||||
maxParallel: 3
|
|
||||||
|
pool:
|
||||||
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
displayName: 'Install Node'
|
displayName: 'Install Node'
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '12.x'
|
||||||
|
|
||||||
- task: Cache@2
|
- task: Cache@2
|
||||||
displayName: 'Check Cache'
|
displayName: 'Check Cache'
|
||||||
|
@ -63,16 +61,14 @@ jobs:
|
||||||
|
|
||||||
- script: 'mv dist jellyfin-web'
|
- script: 'mv dist jellyfin-web'
|
||||||
displayName: 'Rename Directory'
|
displayName: 'Rename Directory'
|
||||||
condition: succeeded()
|
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@1
|
- task: PublishPipelineArtifact@1
|
||||||
displayName: 'Publish Release'
|
displayName: 'Publish Release'
|
||||||
condition: succeeded()
|
|
||||||
inputs:
|
inputs:
|
||||||
targetPath: '$(Build.SourcesDirectory)/jellyfin-web'
|
targetPath: '$(Build.SourcesDirectory)/jellyfin-web'
|
||||||
artifactName: 'jellyfin-web-$(BuildConfiguration)'
|
artifactName: 'jellyfin-web-$(BuildConfiguration)'
|
||||||
|
|
||||||
- job: lint
|
- job: Lint
|
||||||
displayName: 'Lint'
|
displayName: 'Lint'
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
|
@ -82,7 +78,7 @@ jobs:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
displayName: 'Install Node'
|
displayName: 'Install Node'
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '12.x'
|
||||||
|
|
||||||
- task: Cache@2
|
- task: Cache@2
|
||||||
displayName: 'Check Cache'
|
displayName: 'Check Cache'
|
||||||
|
@ -95,7 +91,7 @@ jobs:
|
||||||
displayName: 'Install Dependencies'
|
displayName: 'Install Dependencies'
|
||||||
condition: ne(variables.CACHE_RESTORED, 'true')
|
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||||
|
|
||||||
- script: 'yarn run lint'
|
- script: 'yarn run lint --quiet'
|
||||||
displayName: 'Run ESLint'
|
displayName: 'Run ESLint'
|
||||||
|
|
||||||
- script: 'yarn run stylelint'
|
- script: 'yarn run stylelint'
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
env:
|
env:
|
||||||
es6: true
|
|
||||||
browser: true
|
|
||||||
amd: true
|
amd: true
|
||||||
|
browser: true
|
||||||
|
es6: true
|
||||||
|
es2017: true
|
||||||
|
es2020: true
|
||||||
|
|
||||||
|
parserOptions:
|
||||||
|
ecmaVersion: 2020
|
||||||
|
sourceType: module
|
||||||
|
ecmaFeatures:
|
||||||
|
impliedStrict: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- promise
|
||||||
|
- import
|
||||||
|
- eslint-comments
|
||||||
|
|
||||||
|
extends:
|
||||||
|
- eslint:recommended
|
||||||
|
- plugin:promise/recommended
|
||||||
|
- plugin:import/errors
|
||||||
|
- plugin:import/warnings
|
||||||
|
- plugin:eslint-comments/recommended
|
||||||
|
|
||||||
globals:
|
globals:
|
||||||
# New browser globals
|
# Browser globals
|
||||||
DataView: readonly
|
|
||||||
MediaMetadata: readonly
|
MediaMetadata: readonly
|
||||||
Promise: readonly
|
|
||||||
# Deprecated browser globals
|
|
||||||
DocumentTouch: readonly
|
|
||||||
# Tizen globals
|
# Tizen globals
|
||||||
tizen: readonly
|
tizen: readonly
|
||||||
webapis: readonly
|
webapis: readonly
|
||||||
|
@ -18,7 +34,6 @@ globals:
|
||||||
# Dependency globals
|
# Dependency globals
|
||||||
$: readonly
|
$: readonly
|
||||||
jQuery: readonly
|
jQuery: readonly
|
||||||
queryString: readonly
|
|
||||||
requirejs: readonly
|
requirejs: readonly
|
||||||
# Jellyfin globals
|
# Jellyfin globals
|
||||||
ApiClient: writable
|
ApiClient: writable
|
||||||
|
@ -34,8 +49,7 @@ globals:
|
||||||
getWindowLocationSearch: writable
|
getWindowLocationSearch: writable
|
||||||
Globalize: writable
|
Globalize: writable
|
||||||
Hls: writable
|
Hls: writable
|
||||||
humaneDate: writable
|
dfnshelper: writable
|
||||||
humaneElapsed: writable
|
|
||||||
LibraryMenu: writable
|
LibraryMenu: writable
|
||||||
LinkParser: writable
|
LinkParser: writable
|
||||||
LiveTvHelpers: writable
|
LiveTvHelpers: writable
|
||||||
|
@ -46,9 +60,6 @@ globals:
|
||||||
UserParentalControlPage: writable
|
UserParentalControlPage: writable
|
||||||
Windows: readonly
|
Windows: readonly
|
||||||
|
|
||||||
extends:
|
|
||||||
- eslint:recommended
|
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
block-spacing: ["error"]
|
block-spacing: ["error"]
|
||||||
brace-style: ["error"]
|
brace-style: ["error"]
|
||||||
|
@ -63,9 +74,14 @@ rules:
|
||||||
no-multiple-empty-lines: ["error", { "max": 1 }]
|
no-multiple-empty-lines: ["error", { "max": 1 }]
|
||||||
no-trailing-spaces: ["error"]
|
no-trailing-spaces: ["error"]
|
||||||
one-var: ["error", "never"]
|
one-var: ["error", "never"]
|
||||||
semi: ["warn"]
|
semi: ["error"]
|
||||||
space-before-blocks: ["error"]
|
space-before-blocks: ["error"]
|
||||||
# TODO: Fix warnings and remove these rules
|
# TODO: Fix warnings and remove these rules
|
||||||
no-redeclare: ["warn"]
|
no-redeclare: ["warn"]
|
||||||
no-unused-vars: ["warn"]
|
no-unused-vars: ["warn"]
|
||||||
no-useless-escape: ["warn"]
|
no-useless-escape: ["warn"]
|
||||||
|
promise/catch-or-return: ["warn"]
|
||||||
|
promise/always-return: ["warn"]
|
||||||
|
promise/no-return-wrap: ["warn"]
|
||||||
|
# TODO: Remove after ES6 migration is complete
|
||||||
|
import/no-unresolved: ["warn"]
|
||||||
|
|
583
.gitignore
vendored
583
.gitignore
vendored
|
@ -1,580 +1,11 @@
|
||||||
|
# config
|
||||||
|
config.json
|
||||||
|
|
||||||
# Created by https://www.gitignore.io/api/node,rider,macos,linux,windows,visualstudio,visualstudiocode
|
# npm
|
||||||
# Edit at https://www.gitignore.io/?templates=node,rider,macos,linux,windows,visualstudio,visualstudiocode
|
|
||||||
|
|
||||||
### Linux ###
|
|
||||||
*~
|
|
||||||
|
|
||||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
|
||||||
.fuse_hidden*
|
|
||||||
|
|
||||||
# KDE directory preferences
|
|
||||||
.directory
|
|
||||||
|
|
||||||
# Linux trash folder which might appear on any partition or disk
|
|
||||||
.Trash-*
|
|
||||||
|
|
||||||
# .nfs files are created when an open file is removed but is still being accessed
|
|
||||||
.nfs*
|
|
||||||
|
|
||||||
### macOS ###
|
|
||||||
# General
|
|
||||||
.DS_Store
|
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
|
|
||||||
# Icon must end with two \r
|
|
||||||
Icon
|
|
||||||
|
|
||||||
# Thumbnails
|
|
||||||
._*
|
|
||||||
|
|
||||||
# Files that might appear in the root of a volume
|
|
||||||
.DocumentRevisions-V100
|
|
||||||
.fseventsd
|
|
||||||
.Spotlight-V100
|
|
||||||
.TemporaryItems
|
|
||||||
.Trashes
|
|
||||||
.VolumeIcon.icns
|
|
||||||
.com.apple.timemachine.donotpresent
|
|
||||||
|
|
||||||
# Directories potentially created on remote AFP share
|
|
||||||
.AppleDB
|
|
||||||
.AppleDesktop
|
|
||||||
Network Trash Folder
|
|
||||||
Temporary Items
|
|
||||||
.apdisk
|
|
||||||
|
|
||||||
### Node ###
|
|
||||||
# Logs
|
|
||||||
logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
|
||||||
lib-cov
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# nyc test coverage
|
|
||||||
.nyc_output
|
|
||||||
|
|
||||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
|
||||||
.grunt
|
|
||||||
|
|
||||||
# node-waf configuration
|
|
||||||
.lock-wscript
|
|
||||||
|
|
||||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
|
||||||
build/Release
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
node_modules/
|
|
||||||
jspm_packages/
|
|
||||||
|
|
||||||
# Dependency lockfile
|
|
||||||
package-lock.json
|
|
||||||
|
|
||||||
# TypeScript v1 declaration files
|
|
||||||
typings/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# dotenv environment variables file
|
|
||||||
.env
|
|
||||||
.env.test
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
|
|
||||||
# next.js build output
|
|
||||||
.next
|
|
||||||
|
|
||||||
# nuxt.js build output
|
|
||||||
.nuxt
|
|
||||||
|
|
||||||
# vuepress build output
|
|
||||||
.vuepress/dist
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless/
|
|
||||||
|
|
||||||
# FuseBox cache
|
|
||||||
.fusebox/
|
|
||||||
|
|
||||||
# DynamoDB Local files
|
|
||||||
.dynamodb/
|
|
||||||
|
|
||||||
### Rider ###
|
|
||||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
|
|
||||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
|
||||||
|
|
||||||
# User-specific stuff
|
|
||||||
.idea/**/workspace.xml
|
|
||||||
.idea/**/tasks.xml
|
|
||||||
.idea/**/usage.statistics.xml
|
|
||||||
.idea/**/dictionaries
|
|
||||||
.idea/**/shelf
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
.idea/**/contentModel.xml
|
|
||||||
|
|
||||||
# Sensitive or high-churn files
|
|
||||||
.idea/**/dataSources/
|
|
||||||
.idea/**/dataSources.ids
|
|
||||||
.idea/**/dataSources.local.xml
|
|
||||||
.idea/**/sqlDataSources.xml
|
|
||||||
.idea/**/dynamic.xml
|
|
||||||
.idea/**/uiDesigner.xml
|
|
||||||
.idea/**/dbnavigator.xml
|
|
||||||
|
|
||||||
# Gradle
|
|
||||||
.idea/**/gradle.xml
|
|
||||||
.idea/**/libraries
|
|
||||||
|
|
||||||
# Gradle and Maven with auto-import
|
|
||||||
# When using Gradle or Maven with auto-import, you should exclude module files,
|
|
||||||
# since they will be recreated, and may cause churn. Uncomment if using
|
|
||||||
# auto-import.
|
|
||||||
# .idea/modules.xml
|
|
||||||
# .idea/*.iml
|
|
||||||
# .idea/modules
|
|
||||||
|
|
||||||
# CMake
|
|
||||||
cmake-build-*/
|
|
||||||
|
|
||||||
# Mongo Explorer plugin
|
|
||||||
.idea/**/mongoSettings.xml
|
|
||||||
|
|
||||||
# File-based project format
|
|
||||||
*.iws
|
|
||||||
|
|
||||||
# IntelliJ
|
|
||||||
out/
|
|
||||||
|
|
||||||
# mpeltonen/sbt-idea plugin
|
|
||||||
.idea_modules/
|
|
||||||
|
|
||||||
# JIRA plugin
|
|
||||||
atlassian-ide-plugin.xml
|
|
||||||
|
|
||||||
# Cursive Clojure plugin
|
|
||||||
.idea/replstate.xml
|
|
||||||
|
|
||||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
|
||||||
com_crashlytics_export_strings.xml
|
|
||||||
crashlytics.properties
|
|
||||||
crashlytics-build.properties
|
|
||||||
fabric.properties
|
|
||||||
|
|
||||||
# Editor-based Rest Client
|
|
||||||
.idea/httpRequests
|
|
||||||
|
|
||||||
# Android studio 3.1+ serialized cache file
|
|
||||||
.idea/caches/build_file_checksums.ser
|
|
||||||
|
|
||||||
### VisualStudioCode ###
|
|
||||||
.vscode/*
|
|
||||||
!.vscode/settings.json
|
|
||||||
!.vscode/tasks.json
|
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
|
||||||
|
|
||||||
### VisualStudioCode Patch ###
|
|
||||||
# Ignore all local history of files
|
|
||||||
.history
|
|
||||||
|
|
||||||
### Windows ###
|
|
||||||
# Windows thumbnail cache files
|
|
||||||
Thumbs.db
|
|
||||||
ehthumbs.db
|
|
||||||
ehthumbs_vista.db
|
|
||||||
|
|
||||||
# Dump file
|
|
||||||
*.stackdump
|
|
||||||
|
|
||||||
# Folder config file
|
|
||||||
[Dd]esktop.ini
|
|
||||||
|
|
||||||
# Recycle Bin used on file shares
|
|
||||||
$RECYCLE.BIN/
|
|
||||||
|
|
||||||
# Windows Installer files
|
|
||||||
*.cab
|
|
||||||
*.msi
|
|
||||||
*.msix
|
|
||||||
*.msm
|
|
||||||
*.msp
|
|
||||||
|
|
||||||
# Windows shortcuts
|
|
||||||
*.lnk
|
|
||||||
|
|
||||||
### VisualStudio ###
|
|
||||||
## Ignore Visual Studio temporary files, build results, and
|
|
||||||
## files generated by popular Visual Studio add-ons.
|
|
||||||
##
|
|
||||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
|
||||||
|
|
||||||
# User-specific files
|
|
||||||
*.rsuser
|
|
||||||
*.suo
|
|
||||||
*.user
|
|
||||||
*.userosscache
|
|
||||||
*.sln.docstates
|
|
||||||
|
|
||||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
|
||||||
*.userprefs
|
|
||||||
|
|
||||||
# Build results
|
|
||||||
[Dd]ebug/
|
|
||||||
[Dd]ebugPublic/
|
|
||||||
[Rr]elease/
|
|
||||||
[Rr]eleases/
|
|
||||||
x64/
|
|
||||||
x86/
|
|
||||||
[Aa][Rr][Mm]/
|
|
||||||
[Aa][Rr][Mm]64/
|
|
||||||
bld/
|
|
||||||
[Bb]in/
|
|
||||||
[Oo]bj/
|
|
||||||
[Ll]og/
|
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
|
||||||
.vs/
|
|
||||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
|
||||||
#wwwroot/
|
|
||||||
|
|
||||||
# Visual Studio 2017 auto generated files
|
|
||||||
Generated\ Files/
|
|
||||||
|
|
||||||
# MSTest test Results
|
|
||||||
[Tt]est[Rr]esult*/
|
|
||||||
[Bb]uild[Ll]og.*
|
|
||||||
|
|
||||||
# NUNIT
|
|
||||||
*.VisualState.xml
|
|
||||||
TestResult.xml
|
|
||||||
|
|
||||||
# Build Results of an ATL Project
|
|
||||||
[Dd]ebugPS/
|
|
||||||
[Rr]eleasePS/
|
|
||||||
dlldata.c
|
|
||||||
|
|
||||||
# Benchmark Results
|
|
||||||
BenchmarkDotNet.Artifacts/
|
|
||||||
|
|
||||||
# .NET Core
|
|
||||||
project.lock.json
|
|
||||||
project.fragment.lock.json
|
|
||||||
artifacts/
|
|
||||||
|
|
||||||
# StyleCop
|
|
||||||
StyleCopReport.xml
|
|
||||||
|
|
||||||
# Files built by Visual Studio
|
|
||||||
*_i.c
|
|
||||||
*_p.c
|
|
||||||
*_h.h
|
|
||||||
*.ilk
|
|
||||||
*.meta
|
|
||||||
*.obj
|
|
||||||
*.iobj
|
|
||||||
*.pch
|
|
||||||
*.pdb
|
|
||||||
*.ipdb
|
|
||||||
*.pgc
|
|
||||||
*.pgd
|
|
||||||
*.rsp
|
|
||||||
*.sbr
|
|
||||||
*.tlb
|
|
||||||
*.tli
|
|
||||||
*.tlh
|
|
||||||
*.tmp
|
|
||||||
*.tmp_proj
|
|
||||||
*_wpftmp.csproj
|
|
||||||
*.vspscc
|
|
||||||
*.vssscc
|
|
||||||
.builds
|
|
||||||
*.pidb
|
|
||||||
*.svclog
|
|
||||||
*.scc
|
|
||||||
|
|
||||||
# Chutzpah Test files
|
|
||||||
_Chutzpah*
|
|
||||||
|
|
||||||
# Visual C++ cache files
|
|
||||||
ipch/
|
|
||||||
*.aps
|
|
||||||
*.ncb
|
|
||||||
*.opendb
|
|
||||||
*.opensdf
|
|
||||||
*.sdf
|
|
||||||
*.cachefile
|
|
||||||
*.VC.db
|
|
||||||
*.VC.VC.opendb
|
|
||||||
|
|
||||||
# Visual Studio profiler
|
|
||||||
*.psess
|
|
||||||
*.vsp
|
|
||||||
*.vspx
|
|
||||||
*.sap
|
|
||||||
|
|
||||||
# Visual Studio Trace Files
|
|
||||||
*.e2e
|
|
||||||
|
|
||||||
# TFS 2012 Local Workspace
|
|
||||||
$tf/
|
|
||||||
|
|
||||||
# Guidance Automation Toolkit
|
|
||||||
*.gpState
|
|
||||||
|
|
||||||
# ReSharper is a .NET coding add-in
|
|
||||||
_ReSharper*/
|
|
||||||
*.[Rr]e[Ss]harper
|
|
||||||
*.DotSettings.user
|
|
||||||
|
|
||||||
# JustCode is a .NET coding add-in
|
|
||||||
.JustCode
|
|
||||||
|
|
||||||
# TeamCity is a build add-in
|
|
||||||
_TeamCity*
|
|
||||||
|
|
||||||
# DotCover is a Code Coverage Tool
|
|
||||||
*.dotCover
|
|
||||||
|
|
||||||
# AxoCover is a Code Coverage Tool
|
|
||||||
.axoCover/*
|
|
||||||
!.axoCover/settings.json
|
|
||||||
|
|
||||||
# Visual Studio code coverage results
|
|
||||||
*.coverage
|
|
||||||
*.coveragexml
|
|
||||||
|
|
||||||
# NCrunch
|
|
||||||
_NCrunch_*
|
|
||||||
.*crunch*.local.xml
|
|
||||||
nCrunchTemp_*
|
|
||||||
|
|
||||||
# MightyMoose
|
|
||||||
*.mm.*
|
|
||||||
AutoTest.Net/
|
|
||||||
|
|
||||||
# Web workbench (sass)
|
|
||||||
.sass-cache/
|
|
||||||
|
|
||||||
# Installshield output folder
|
|
||||||
[Ee]xpress/
|
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
|
||||||
DocProject/buildhelp/
|
|
||||||
DocProject/Help/*.HxT
|
|
||||||
DocProject/Help/*.HxC
|
|
||||||
DocProject/Help/*.hhc
|
|
||||||
DocProject/Help/*.hhk
|
|
||||||
DocProject/Help/*.hhp
|
|
||||||
DocProject/Help/Html2
|
|
||||||
DocProject/Help/html
|
|
||||||
|
|
||||||
# Click-Once directory
|
|
||||||
publish/
|
|
||||||
|
|
||||||
# Publish Web Output
|
|
||||||
*.[Pp]ublish.xml
|
|
||||||
*.azurePubxml
|
|
||||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
|
||||||
# but database connection strings (with potential passwords) will be unencrypted
|
|
||||||
*.pubxml
|
|
||||||
*.publishproj
|
|
||||||
|
|
||||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
|
||||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
|
||||||
# in these scripts will be unencrypted
|
|
||||||
PublishScripts/
|
|
||||||
|
|
||||||
# NuGet Packages
|
|
||||||
*.nupkg
|
|
||||||
# The packages folder can be ignored because of Package Restore
|
|
||||||
**/[Pp]ackages/*
|
|
||||||
# except build/, which is used as an MSBuild target.
|
|
||||||
!**/[Pp]ackages/build/
|
|
||||||
# Uncomment if necessary however generally it will be regenerated when needed
|
|
||||||
#!**/[Pp]ackages/repositories.config
|
|
||||||
# NuGet v3's project.json files produces more ignorable files
|
|
||||||
*.nuget.props
|
|
||||||
*.nuget.targets
|
|
||||||
|
|
||||||
# Microsoft Azure Build Output
|
|
||||||
csx/
|
|
||||||
*.build.csdef
|
|
||||||
|
|
||||||
# Microsoft Azure Emulator
|
|
||||||
ecf/
|
|
||||||
rcf/
|
|
||||||
|
|
||||||
# Windows Store app package directories and files
|
|
||||||
AppPackages/
|
|
||||||
BundleArtifacts/
|
|
||||||
Package.StoreAssociation.xml
|
|
||||||
_pkginfo.txt
|
|
||||||
*.appx
|
|
||||||
|
|
||||||
# Visual Studio cache files
|
|
||||||
# files ending in .cache can be ignored
|
|
||||||
*.[Cc]ache
|
|
||||||
# but keep track of directories ending in .cache
|
|
||||||
!?*.[Cc]ache/
|
|
||||||
|
|
||||||
# Others
|
|
||||||
ClientBin/
|
|
||||||
~$*
|
|
||||||
*.dbmdl
|
|
||||||
*.dbproj.schemaview
|
|
||||||
*.jfm
|
|
||||||
*.pfx
|
|
||||||
*.publishsettings
|
|
||||||
orleans.codegen.cs
|
|
||||||
|
|
||||||
# Including strong name files can present a security risk
|
|
||||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
|
||||||
#*.snk
|
|
||||||
|
|
||||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
|
||||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
|
||||||
#bower_components/
|
|
||||||
# ASP.NET Core default setup: bower directory is configured as wwwroot/lib/ and bower restore is true
|
|
||||||
**/wwwroot/lib/
|
|
||||||
|
|
||||||
# RIA/Silverlight projects
|
|
||||||
Generated_Code/
|
|
||||||
|
|
||||||
# Backup & report files from converting an old project file
|
|
||||||
# to a newer Visual Studio version. Backup files are not needed,
|
|
||||||
# because we have git ;-)
|
|
||||||
_UpgradeReport_Files/
|
|
||||||
Backup*/
|
|
||||||
UpgradeLog*.XML
|
|
||||||
UpgradeLog*.htm
|
|
||||||
ServiceFabricBackup/
|
|
||||||
*.rptproj.bak
|
|
||||||
|
|
||||||
# SQL Server files
|
|
||||||
*.mdf
|
|
||||||
*.ldf
|
|
||||||
*.ndf
|
|
||||||
|
|
||||||
# Business Intelligence projects
|
|
||||||
*.rdl.data
|
|
||||||
*.bim.layout
|
|
||||||
*.bim_*.settings
|
|
||||||
*.rptproj.rsuser
|
|
||||||
*- Backup*.rdl
|
|
||||||
|
|
||||||
# Microsoft Fakes
|
|
||||||
FakesAssemblies/
|
|
||||||
|
|
||||||
# GhostDoc plugin setting file
|
|
||||||
*.GhostDoc.xml
|
|
||||||
|
|
||||||
# Node.js Tools for Visual Studio
|
|
||||||
.ntvs_analysis.dat
|
|
||||||
|
|
||||||
# Visual Studio 6 build log
|
|
||||||
*.plg
|
|
||||||
|
|
||||||
# Visual Studio 6 workspace options file
|
|
||||||
*.opt
|
|
||||||
|
|
||||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
|
||||||
*.vbw
|
|
||||||
|
|
||||||
# Visual Studio LightSwitch build output
|
|
||||||
**/*.HTMLClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/GeneratedArtifacts
|
|
||||||
**/*.DesktopClient/ModelManifest.xml
|
|
||||||
**/*.Server/GeneratedArtifacts
|
|
||||||
**/*.Server/ModelManifest.xml
|
|
||||||
_Pvt_Extensions
|
|
||||||
|
|
||||||
# Paket dependency manager
|
|
||||||
.paket/paket.exe
|
|
||||||
paket-files/
|
|
||||||
|
|
||||||
# FAKE - F# Make
|
|
||||||
.fake/
|
|
||||||
|
|
||||||
# JetBrains Rider
|
|
||||||
.idea/
|
|
||||||
*.sln.iml
|
|
||||||
|
|
||||||
# CodeRush personal settings
|
|
||||||
.cr/personal
|
|
||||||
|
|
||||||
# Python Tools for Visual Studio (PTVS)
|
|
||||||
__pycache__/
|
|
||||||
*.pyc
|
|
||||||
|
|
||||||
# Cake - Uncomment if you are using it
|
|
||||||
# tools/**
|
|
||||||
# !tools/packages.config
|
|
||||||
|
|
||||||
# Tabs Studio
|
|
||||||
*.tss
|
|
||||||
|
|
||||||
# Telerik's JustMock configuration file
|
|
||||||
*.jmconfig
|
|
||||||
|
|
||||||
# BizTalk build output
|
|
||||||
*.btp.cs
|
|
||||||
*.btm.cs
|
|
||||||
*.odx.cs
|
|
||||||
*.xsd.cs
|
|
||||||
|
|
||||||
# OpenCover UI analysis results
|
|
||||||
OpenCover/
|
|
||||||
|
|
||||||
# Azure Stream Analytics local run output
|
|
||||||
ASALocalRun/
|
|
||||||
|
|
||||||
# MSBuild Binary and Structured Log
|
|
||||||
*.binlog
|
|
||||||
|
|
||||||
# NVidia Nsight GPU debugger configuration file
|
|
||||||
*.nvuser
|
|
||||||
|
|
||||||
# MFractors (Xamarin productivity tool) working folder
|
|
||||||
.mfractor/
|
|
||||||
|
|
||||||
# Local History for Visual Studio
|
|
||||||
.localhistory/
|
|
||||||
|
|
||||||
# BeatPulse healthcheck temp database
|
|
||||||
healthchecksdb
|
|
||||||
|
|
||||||
# End of https://www.gitignore.io/api/node,rider,macos,linux,windows,visualstudio,visualstudiocode
|
|
||||||
|
|
||||||
# dist for webpack output
|
|
||||||
dist
|
dist
|
||||||
web
|
web
|
||||||
node_modules
|
node_modules
|
||||||
|
|
||||||
|
# ide
|
||||||
|
.idea
|
||||||
|
.vscode
|
3
babel.config.json
Normal file
3
babel.config.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"presets": ["@babel/preset-env"]
|
||||||
|
}
|
152
gulpfile.js
152
gulpfile.js
|
@ -18,16 +18,42 @@ const stream = require('webpack-stream');
|
||||||
const inject = require('gulp-inject');
|
const inject = require('gulp-inject');
|
||||||
const postcss = require('gulp-postcss');
|
const postcss = require('gulp-postcss');
|
||||||
const sass = require('gulp-sass');
|
const sass = require('gulp-sass');
|
||||||
|
const gulpif = require('gulp-if');
|
||||||
|
const lazypipe = require('lazypipe');
|
||||||
|
|
||||||
sass.compiler = require('node-sass')
|
sass.compiler = require('node-sass');
|
||||||
|
|
||||||
|
|
||||||
|
let config;
|
||||||
if (mode.production()) {
|
if (mode.production()) {
|
||||||
var config = require('./webpack.prod.js');
|
config = require('./webpack.prod.js');
|
||||||
} else {
|
} else {
|
||||||
var config = require('./webpack.dev.js');
|
config = require('./webpack.dev.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
javascript: {
|
||||||
|
query: ['src/**/*.js', '!src/bundle.js', '!src/standalone.js', '!src/scripts/apploader.js']
|
||||||
|
},
|
||||||
|
apploader: {
|
||||||
|
query: ['src/standalone.js', 'src/scripts/apploader.js']
|
||||||
|
},
|
||||||
|
css: {
|
||||||
|
query: ['src/**/*.css', 'src/**/*.scss']
|
||||||
|
},
|
||||||
|
html: {
|
||||||
|
query: ['src/**/*.html', '!src/index.html']
|
||||||
|
},
|
||||||
|
images: {
|
||||||
|
query: ['src/**/*.png', 'src/**/*.jpg', 'src/**/*.gif', 'src/**/*.svg']
|
||||||
|
},
|
||||||
|
copy: {
|
||||||
|
query: ['src/**/*.json', 'src/**/*.ico']
|
||||||
|
},
|
||||||
|
injectBundle: {
|
||||||
|
query: 'src/index.html'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function serve() {
|
function serve() {
|
||||||
browserSync.init({
|
browserSync.init({
|
||||||
server: {
|
server: {
|
||||||
|
@ -36,51 +62,99 @@ function serve() {
|
||||||
port: 8080
|
port: 8080
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(['src/**/*.js', '!src/bundle.js'], javascript);
|
let events = ['add', 'change'];
|
||||||
watch('src/bundle.js', webpack);
|
|
||||||
watch('src/**/*.css', css);
|
|
||||||
watch(['src/**/*.html', '!src/index.html'], html);
|
|
||||||
watch(['src/**/*.png', 'src/**/*.jpg', 'src/**/*.gif', 'src/**/*.svg'], images);
|
|
||||||
watch(['src/**/*.json', 'src/**/*.ico'], copy);
|
|
||||||
watch('src/index.html', injectBundle);
|
|
||||||
watch(['src/standalone.js', 'src/scripts/apploader.js'], standalone);
|
|
||||||
}
|
|
||||||
|
|
||||||
function standalone() {
|
watch(options.javascript.query).on('all', function (event, path) {
|
||||||
return src(['src/standalone.js', 'src/scripts/apploader.js'], { base: './src/' })
|
if (events.includes(event)) {
|
||||||
.pipe(concat('scripts/apploader.js'))
|
javascript(path);
|
||||||
.pipe(dest('dist/'));
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(options.apploader.query, apploader(true));
|
||||||
|
|
||||||
|
watch('src/bundle.js', webpack);
|
||||||
|
|
||||||
|
watch(options.css.query).on('all', function (event, path) {
|
||||||
|
if (events.includes(event)) {
|
||||||
|
css(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(options.html.query).on('all', function (event, path) {
|
||||||
|
if (events.includes(event)) {
|
||||||
|
html(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(options.images.query).on('all', function (event, path) {
|
||||||
|
if (events.includes(event)) {
|
||||||
|
images(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(options.copy.query).on('all', function (event, path) {
|
||||||
|
if (events.includes(event)) {
|
||||||
|
copy(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(options.injectBundle.query, injectBundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
function clean() {
|
function clean() {
|
||||||
return del(['dist/']);
|
return del(['dist/']);
|
||||||
}
|
}
|
||||||
|
|
||||||
function javascript() {
|
let pipelineJavascript = lazypipe()
|
||||||
return src(['src/**/*.js', '!src/bundle.js'], { base: './src/' })
|
.pipe(function () {
|
||||||
.pipe(mode.development(sourcemaps.init({ loadMaps: true })))
|
return mode.development(sourcemaps.init({ loadMaps: true }));
|
||||||
.pipe(babel({
|
})
|
||||||
|
.pipe(function () {
|
||||||
|
return babel({
|
||||||
presets: [
|
presets: [
|
||||||
['@babel/preset-env']
|
['@babel/preset-env']
|
||||||
]
|
]
|
||||||
}))
|
});
|
||||||
.pipe(terser({
|
})
|
||||||
|
.pipe(function () {
|
||||||
|
return terser({
|
||||||
keep_fnames: true,
|
keep_fnames: true,
|
||||||
mangle: false
|
mangle: false
|
||||||
}))
|
});
|
||||||
.pipe(mode.development(sourcemaps.write('.')))
|
})
|
||||||
|
.pipe(function () {
|
||||||
|
return mode.development(sourcemaps.write('.'));
|
||||||
|
});
|
||||||
|
|
||||||
|
function javascript(query) {
|
||||||
|
return src(typeof query !== 'function' ? query : options.javascript.query, { base: './src/' })
|
||||||
|
.pipe(pipelineJavascript())
|
||||||
.pipe(dest('dist/'))
|
.pipe(dest('dist/'))
|
||||||
.pipe(browserSync.stream());
|
.pipe(browserSync.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apploader(standalone) {
|
||||||
|
function task() {
|
||||||
|
return src(options.apploader.query, { base: './src/' })
|
||||||
|
.pipe(gulpif(standalone, concat('scripts/apploader.js')))
|
||||||
|
.pipe(pipelineJavascript())
|
||||||
|
.pipe(dest('dist/'))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
};
|
||||||
|
|
||||||
|
task.displayName = 'apploader';
|
||||||
|
|
||||||
|
return task;
|
||||||
|
}
|
||||||
|
|
||||||
function webpack() {
|
function webpack() {
|
||||||
return stream(config)
|
return stream(config)
|
||||||
.pipe(dest('dist/'))
|
.pipe(dest('dist/'))
|
||||||
.pipe(browserSync.stream());
|
.pipe(browserSync.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
function css() {
|
function css(query) {
|
||||||
return src(['src/**/*.css', 'src/**/*.scss'], { base: './src/' })
|
return src(typeof query !== 'function' ? query : options.css.query, { base: './src/' })
|
||||||
.pipe(mode.development(sourcemaps.init({ loadMaps: true })))
|
.pipe(mode.development(sourcemaps.init({ loadMaps: true })))
|
||||||
.pipe(sass().on('error', sass.logError))
|
.pipe(sass().on('error', sass.logError))
|
||||||
.pipe(postcss())
|
.pipe(postcss())
|
||||||
|
@ -89,28 +163,28 @@ function css() {
|
||||||
.pipe(browserSync.stream());
|
.pipe(browserSync.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
function html() {
|
function html(query) {
|
||||||
return src(['src/**/*.html', '!src/index.html'], { base: './src/' })
|
return src(typeof query !== 'function' ? query : options.html.query, { base: './src/' })
|
||||||
.pipe(mode.production(htmlmin({ collapseWhitespace: true })))
|
.pipe(mode.production(htmlmin({ collapseWhitespace: true })))
|
||||||
.pipe(dest('dist/'))
|
.pipe(dest('dist/'))
|
||||||
.pipe(browserSync.stream());
|
.pipe(browserSync.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
function images() {
|
function images(query) {
|
||||||
return src(['src/**/*.png', 'src/**/*.jpg', 'src/**/*.gif', 'src/**/*.svg'], { base: './src/' })
|
return src(typeof query !== 'function' ? query : options.images.query, { base: './src/' })
|
||||||
.pipe(mode.production(imagemin()))
|
.pipe(mode.production(imagemin()))
|
||||||
.pipe(dest('dist/'))
|
.pipe(dest('dist/'))
|
||||||
.pipe(browserSync.stream());
|
.pipe(browserSync.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
function copy() {
|
function copy(query) {
|
||||||
return src(['src/**/*.json', 'src/**/*.ico'], { base: './src/' })
|
return src(typeof query !== 'function' ? query : options.copy.query, { base: './src/' })
|
||||||
.pipe(dest('dist/'))
|
.pipe(dest('dist/'))
|
||||||
.pipe(browserSync.stream());
|
.pipe(browserSync.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
function injectBundle() {
|
function injectBundle() {
|
||||||
return src('src/index.html', { 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 }
|
||||||
))
|
))
|
||||||
|
@ -118,6 +192,10 @@ function injectBundle() {
|
||||||
.pipe(browserSync.stream());
|
.pipe(browserSync.stream());
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.default = series(clean, parallel(javascript, webpack, css, html, images, copy), injectBundle)
|
function build(standalone) {
|
||||||
exports.standalone = series(exports.default, standalone)
|
return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy), injectBundle);
|
||||||
exports.serve = series(exports.standalone, serve)
|
}
|
||||||
|
|
||||||
|
exports.default = build(false);
|
||||||
|
exports.standalone = build(true);
|
||||||
|
exports.serve = series(exports.standalone, serve);
|
||||||
|
|
39
package.json
39
package.json
|
@ -6,6 +6,7 @@
|
||||||
"license": "GPL-2.0-or-later",
|
"license": "GPL-2.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.8.6",
|
"@babel/core": "^7.8.6",
|
||||||
|
"@babel/plugin-transform-modules-amd": "^7.8.3",
|
||||||
"@babel/polyfill": "^7.8.7",
|
"@babel/polyfill": "^7.8.7",
|
||||||
"@babel/preset-env": "^7.8.6",
|
"@babel/preset-env": "^7.8.6",
|
||||||
"autoprefixer": "^9.7.4",
|
"autoprefixer": "^9.7.4",
|
||||||
|
@ -17,12 +18,16 @@
|
||||||
"cssnano": "^4.1.10",
|
"cssnano": "^4.1.10",
|
||||||
"del": "^5.1.0",
|
"del": "^5.1.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"file-loader": "^5.0.2",
|
"eslint-plugin-eslint-comments": "^3.1.2",
|
||||||
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"file-loader": "^6.0.0",
|
||||||
"gulp": "^4.0.2",
|
"gulp": "^4.0.2",
|
||||||
"gulp-babel": "^8.0.0",
|
"gulp-babel": "^8.0.0",
|
||||||
"gulp-cli": "^2.2.0",
|
"gulp-cli": "^2.2.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-imagemin": "^7.1.0",
|
"gulp-imagemin": "^7.1.0",
|
||||||
"gulp-inject": "^5.0.5",
|
"gulp-inject": "^5.0.5",
|
||||||
"gulp-mode": "^1.0.2",
|
"gulp-mode": "^1.0.2",
|
||||||
|
@ -30,7 +35,8 @@
|
||||||
"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.2.0",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"html-webpack-plugin": "^4.0.2",
|
||||||
|
"lazypipe": "^1.0.2",
|
||||||
"node-sass": "^4.13.1",
|
"node-sass": "^4.13.1",
|
||||||
"postcss-loader": "^3.0.0",
|
"postcss-loader": "^3.0.0",
|
||||||
"postcss-preset-env": "^6.7.0",
|
"postcss-preset-env": "^6.7.0",
|
||||||
|
@ -48,7 +54,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"alameda": "^1.4.0",
|
"alameda": "^1.4.0",
|
||||||
|
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
|
||||||
"core-js": "^3.6.4",
|
"core-js": "^3.6.4",
|
||||||
|
"date-fns": "^2.11.1",
|
||||||
"document-register-element": "^1.14.3",
|
"document-register-element": "^1.14.3",
|
||||||
"flv.js": "^1.5.0",
|
"flv.js": "^1.5.0",
|
||||||
"hls.js": "^0.13.1",
|
"hls.js": "^0.13.1",
|
||||||
|
@ -56,14 +64,13 @@
|
||||||
"jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto",
|
"jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto",
|
||||||
"jquery": "^3.4.1",
|
"jquery": "^3.4.1",
|
||||||
"jstree": "^3.3.7",
|
"jstree": "^3.3.7",
|
||||||
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus",
|
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-cordova",
|
||||||
"libjass": "^0.11.0",
|
|
||||||
"material-design-icons-iconfont": "^5.0.1",
|
"material-design-icons-iconfont": "^5.0.1",
|
||||||
"native-promise-only": "^0.8.0-a",
|
"native-promise-only": "^0.8.0-a",
|
||||||
"page": "^1.11.5",
|
"page": "^1.11.5",
|
||||||
"query-string": "^6.11.1",
|
"query-string": "^6.11.1",
|
||||||
"resize-observer-polyfill": "^1.5.1",
|
"resize-observer-polyfill": "^1.5.1",
|
||||||
"shaka-player": "^2.5.9",
|
"shaka-player": "^2.5.10",
|
||||||
"sortablejs": "^1.10.2",
|
"sortablejs": "^1.10.2",
|
||||||
"swiper": "^5.3.1",
|
"swiper": "^5.3.1",
|
||||||
"webcomponents.js": "^0.7.24",
|
"webcomponents.js": "^0.7.24",
|
||||||
|
@ -72,6 +79,28 @@
|
||||||
"babel": {
|
"babel": {
|
||||||
"presets": [
|
"presets": [
|
||||||
"@babel/preset-env"
|
"@babel/preset-env"
|
||||||
|
],
|
||||||
|
"overrides": [
|
||||||
|
{
|
||||||
|
"test": [
|
||||||
|
"src/components/autoFocuser.js",
|
||||||
|
"src/components/cardbuilder/cardBuilder.js",
|
||||||
|
"src/components/dom.js",
|
||||||
|
"src/components/filedownloader.js",
|
||||||
|
"src/components/filesystem.js",
|
||||||
|
"src/components/input/keyboardnavigation.js",
|
||||||
|
"src/components/scrollManager.js",
|
||||||
|
"src/components/sanatizefilename.js",
|
||||||
|
"src/scripts/settings/webSettings.js",
|
||||||
|
"src/scripts/settings/appSettings.js",
|
||||||
|
"src/scripts/settings/userSettings.js",
|
||||||
|
"src/scripts/imagehelper.js",
|
||||||
|
"src/scripts/dfnshelper.js"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-transform-modules-amd"
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
|
|
@ -63,6 +63,10 @@ progress[aria-valuenow]::before {
|
||||||
}
|
}
|
||||||
|
|
||||||
.adminDrawerLogo {
|
.adminDrawerLogo {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-mobile .adminDrawerLogo {
|
||||||
padding: 1.5em 1em 1.2em;
|
padding: 1.5em 1em 1.2em;
|
||||||
border-bottom: 1px solid #e0e0e0;
|
border-bottom: 1px solid #e0e0e0;
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
|
@ -161,7 +165,7 @@ div[data-role=controlgroup] a.ui-btn-active {
|
||||||
|
|
||||||
@media all and (min-width: 40em) {
|
@media all and (min-width: 40em) {
|
||||||
.content-primary {
|
.content-primary {
|
||||||
padding-top: 7em;
|
padding-top: 4.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.withTabs .content-primary {
|
.withTabs .content-primary {
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.libraryPage {
|
.libraryPage {
|
||||||
padding-top: 7em !important;
|
padding-top: 7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.itemDetailPage {
|
.itemDetailPage {
|
||||||
|
@ -242,7 +242,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.mainDrawer-scrollContainer {
|
.mainDrawer-scrollContainer {
|
||||||
padding-bottom: 10vh;
|
margin-bottom: 10vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (min-width: 40em) {
|
@media all and (min-width: 40em) {
|
||||||
|
@ -313,7 +313,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboardDocument .mainDrawer-scrollContainer {
|
.dashboardDocument .mainDrawer-scrollContainer {
|
||||||
margin-top: 6em !important;
|
margin-top: 4.6em !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1119,3 +1119,50 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards {
|
||||||
.itemsViewSettingsContainer > .button-flat {
|
.itemsViewSettingsContainer > .button-flat {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-mobile #myPreferencesMenuPage {
|
||||||
|
padding-top: 3.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemDetailsGroup {
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections {
|
||||||
|
max-width: 44em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailsGroupItem,
|
||||||
|
.trackSelections .selectContainer {
|
||||||
|
display: flex;
|
||||||
|
max-width: 44em;
|
||||||
|
margin: 0 0 0.5em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections .selectContainer {
|
||||||
|
margin: 0 0 0.3em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailsGroupItem .label,
|
||||||
|
.trackSelections .selectContainer .selectLabel {
|
||||||
|
cursor: default;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-basis: 6.25em;
|
||||||
|
margin: 0 0.6em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections .selectContainer .selectLabel {
|
||||||
|
margin: 0 0.2em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections .selectContainer .detailTrackSelect {
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections .selectContainer .selectArrowContainer .selectArrow {
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ _define("document-register-element", function() {
|
||||||
// fetch
|
// fetch
|
||||||
var fetch = require("whatwg-fetch");
|
var fetch = require("whatwg-fetch");
|
||||||
_define("fetch", function() {
|
_define("fetch", function() {
|
||||||
return fetch
|
return fetch;
|
||||||
});
|
});
|
||||||
|
|
||||||
// query-string
|
// query-string
|
||||||
|
@ -84,13 +84,6 @@ _define("webcomponents", function() {
|
||||||
return webcomponents;
|
return webcomponents;
|
||||||
});
|
});
|
||||||
|
|
||||||
// libjass
|
|
||||||
var libjass = require("libjass");
|
|
||||||
require("libjass/libjass.css");
|
|
||||||
_define("libjass", function() {
|
|
||||||
return libjass;
|
|
||||||
});
|
|
||||||
|
|
||||||
// libass-wasm
|
// libass-wasm
|
||||||
var libass_wasm = require("libass-wasm");
|
var libass_wasm = require("libass-wasm");
|
||||||
_define("JavascriptSubtitlesOctopus", function() {
|
_define("JavascriptSubtitlesOctopus", function() {
|
||||||
|
@ -119,3 +112,20 @@ var polyfill = require("@babel/polyfill/dist/polyfill");
|
||||||
_define("polyfill", function () {
|
_define("polyfill", function () {
|
||||||
return polyfill;
|
return polyfill;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// domtokenlist-shim
|
||||||
|
var classlist = require("classlist.js");
|
||||||
|
_define("classlist-polyfill", function () {
|
||||||
|
return classlist;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Date-FNS
|
||||||
|
var date_fns = require("date-fns");
|
||||||
|
_define("date-fns", function () {
|
||||||
|
return date_fns;
|
||||||
|
});
|
||||||
|
|
||||||
|
var date_fns_locale = require("date-fns/locale");
|
||||||
|
_define("date-fns/locale", function () {
|
||||||
|
return date_fns_locale;
|
||||||
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["events", "globalize", "dom", "datetime", "userSettings", "serverNotifications", "connectionManager", "emby-button", "listViewStyle"], function (events, globalize, dom, datetime, userSettings, serverNotifications, connectionManager) {
|
define(["events", "globalize", "dom", "date-fns", "dfnshelper", "userSettings", "serverNotifications", "connectionManager", "emby-button", "listViewStyle"], function (events, globalize, dom, datefns, dfnshelper, userSettings, serverNotifications, connectionManager) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function getEntryHtml(entry, apiClient) {
|
function getEntryHtml(entry, apiClient) {
|
||||||
|
@ -16,7 +16,7 @@ define(["events", "globalize", "dom", "datetime", "userSettings", "serverNotific
|
||||||
html += '<i class="listItemIcon material-icons" style="width:2em!important;height:2em!important;padding:0;color:transparent;background-color:' + color + ";background-image:url('" + apiClient.getUserImageUrl(entry.UserId, {
|
html += '<i class="listItemIcon material-icons" style="width:2em!important;height:2em!important;padding:0;color:transparent;background-color:' + color + ";background-image:url('" + apiClient.getUserImageUrl(entry.UserId, {
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
tag: entry.UserPrimaryImageTag
|
tag: entry.UserPrimaryImageTag
|
||||||
}) + "');background-repeat:no-repeat;background-position:center center;background-size: cover;\">dvr</i>"
|
}) + "');background-repeat:no-repeat;background-position:center center;background-size: cover;\">dvr</i>";
|
||||||
} else {
|
} else {
|
||||||
html += '<i class="listItemIcon material-icons" style="background-color:' + color + '">' + icon + '</i>';
|
html += '<i class="listItemIcon material-icons" style="background-color:' + color + '">' + icon + '</i>';
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,7 @@ define(["events", "globalize", "dom", "datetime", "userSettings", "serverNotific
|
||||||
html += entry.Name;
|
html += entry.Name;
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
html += '<div class="listItemBodyText secondary">';
|
html += '<div class="listItemBodyText secondary">';
|
||||||
var date = datetime.parseISO8601Date(entry.Date, true);
|
html += datefns.formatRelative(Date.parse(entry.Date), Date.parse(new Date()), { locale: dfnshelper.getLocale() });
|
||||||
html += datetime.toLocaleString(date).toLowerCase();
|
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
html += '<div class="listItemBodyText secondary listItemBodyText-nowrap">';
|
html += '<div class="listItemBodyText secondary listItemBodyText-nowrap">';
|
||||||
html += entry.ShortOverview || "";
|
html += entry.ShortOverview || "";
|
||||||
|
|
|
@ -511,9 +511,16 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
return baseRoute;
|
return baseRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var popstateOccurred = false;
|
||||||
|
window.addEventListener('popstate', function () {
|
||||||
|
popstateOccurred = true;
|
||||||
|
});
|
||||||
|
|
||||||
function getHandler(route) {
|
function getHandler(route) {
|
||||||
return function (ctx, next) {
|
return function (ctx, next) {
|
||||||
|
ctx.isBack = popstateOccurred;
|
||||||
handleRoute(ctx, next, route);
|
handleRoute(ctx, next, route);
|
||||||
|
popstateOccurred = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -570,8 +577,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
|
|
||||||
function showDirect(path) {
|
function showDirect(path) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
resolveOnNextShow = resolve, page.show(baseUrl()+path)
|
resolveOnNextShow = resolve, page.show(baseUrl()+path);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function show(path, options) {
|
function show(path, options) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSettings, browser, events, htmlMediaHelper) {
|
define(["appSettings", "browser", "events", "htmlMediaHelper", "webSettings"], function (appSettings, browser, events, htmlMediaHelper, webSettings) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function getBaseProfileOptions(item) {
|
function getBaseProfileOptions(item) {
|
||||||
|
@ -276,15 +276,17 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSet
|
||||||
features.push("otherapppromotions");
|
features.push("otherapppromotions");
|
||||||
features.push("displaymode");
|
features.push("displaymode");
|
||||||
features.push("targetblank");
|
features.push("targetblank");
|
||||||
// allows users to connect to more than one server
|
|
||||||
//features.push("multiserver");
|
|
||||||
features.push("screensaver");
|
features.push("screensaver");
|
||||||
|
|
||||||
if (!browser.orsay && !browser.tizen && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) {
|
webSettings.enableMultiServer().then(enabled => {
|
||||||
|
if (enabled) features.push("multiserver");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!browser.orsay && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) {
|
||||||
features.push("subtitleappearancesettings");
|
features.push("subtitleappearancesettings");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!browser.orsay && !browser.tizen) {
|
if (!browser.orsay) {
|
||||||
features.push("subtitleburnsettings");
|
features.push("subtitleburnsettings");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -381,7 +383,7 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSet
|
||||||
return window.NativeShell.AppHost.getDefaultLayout();
|
return window.NativeShell.AppHost.getDefaultLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
return getDefaultLayout()
|
return getDefaultLayout();
|
||||||
},
|
},
|
||||||
getDeviceProfile: getDeviceProfile,
|
getDeviceProfile: getDeviceProfile,
|
||||||
init: function () {
|
init: function () {
|
||||||
|
|
|
@ -1,22 +1,29 @@
|
||||||
define(["focusManager", "layoutManager"], function (focusManager, layoutManager) {
|
/* eslint-disable indent */
|
||||||
"use strict";
|
|
||||||
|
/**
|
||||||
|
* Module for performing auto-focus.
|
||||||
|
* @module components/autoFocuser
|
||||||
|
*/
|
||||||
|
|
||||||
|
import focusManager from "focusManager";
|
||||||
|
import layoutManager from "layoutManager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Previously selected element.
|
* Previously selected element.
|
||||||
*/
|
*/
|
||||||
var activeElement;
|
let activeElement;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if AutoFocuser is enabled.
|
* Returns _true_ if AutoFocuser is enabled.
|
||||||
*/
|
*/
|
||||||
function isEnabled() {
|
export function isEnabled() {
|
||||||
return layoutManager.tv;
|
return layoutManager.tv;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start AutoFocuser
|
* Start AutoFocuser.
|
||||||
*/
|
*/
|
||||||
function enable() {
|
export function enable() {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -28,24 +35,19 @@ define(["focusManager", "layoutManager"], function (focusManager, layoutManager)
|
||||||
console.debug("AutoFocuser enabled");
|
console.debug("AutoFocuser enabled");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an array from some source.
|
|
||||||
*/
|
|
||||||
var arrayFrom = Array.prototype.from || function (src) {
|
|
||||||
return Array.prototype.slice.call(src);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set focus on a suitable element, taking into account the previously selected.
|
* Set focus on a suitable element, taking into account the previously selected.
|
||||||
|
* @param {HTMLElement} [container] - Element to limit scope.
|
||||||
|
* @returns {HTMLElement} Focused element.
|
||||||
*/
|
*/
|
||||||
function autoFocus(container) {
|
export function autoFocus(container) {
|
||||||
if (!isEnabled()) {
|
if (!isEnabled()) {
|
||||||
return;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
container = container || document.body;
|
container = container || document.body;
|
||||||
|
|
||||||
var candidates = [];
|
let candidates = [];
|
||||||
|
|
||||||
if (activeElement) {
|
if (activeElement) {
|
||||||
// These elements are recreated
|
// These elements are recreated
|
||||||
|
@ -62,10 +64,10 @@ define(["focusManager", "layoutManager"], function (focusManager, layoutManager)
|
||||||
candidates.push(activeElement);
|
candidates.push(activeElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
candidates = candidates.concat(arrayFrom(container.querySelectorAll(".btnResume")));
|
candidates = candidates.concat(Array.from(container.querySelectorAll(".btnResume")));
|
||||||
candidates = candidates.concat(arrayFrom(container.querySelectorAll(".btnPlay")));
|
candidates = candidates.concat(Array.from(container.querySelectorAll(".btnPlay")));
|
||||||
|
|
||||||
var focusedElement;
|
let focusedElement;
|
||||||
|
|
||||||
candidates.every(function (element) {
|
candidates.every(function (element) {
|
||||||
if (focusManager.isCurrentlyFocusable(element)) {
|
if (focusManager.isCurrentlyFocusable(element)) {
|
||||||
|
@ -79,7 +81,7 @@ define(["focusManager", "layoutManager"], function (focusManager, layoutManager)
|
||||||
|
|
||||||
if (!focusedElement) {
|
if (!focusedElement) {
|
||||||
// FIXME: Multiple itemsContainers
|
// FIXME: Multiple itemsContainers
|
||||||
var itemsContainer = container.querySelector(".itemsContainer");
|
const itemsContainer = container.querySelector(".itemsContainer");
|
||||||
|
|
||||||
if (itemsContainer) {
|
if (itemsContainer) {
|
||||||
focusedElement = focusManager.autoFocus(itemsContainer);
|
focusedElement = focusManager.autoFocus(itemsContainer);
|
||||||
|
@ -93,9 +95,10 @@ define(["focusManager", "layoutManager"], function (focusManager, layoutManager)
|
||||||
return focusedElement;
|
return focusedElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
/* eslint-enable indent */
|
||||||
isEnabled: isEnabled,
|
|
||||||
enable: enable,
|
export default {
|
||||||
autoFocus: autoFocus
|
isEnabled: isEnabled,
|
||||||
};
|
enable: enable,
|
||||||
});
|
autoFocus: autoFocus
|
||||||
|
};
|
||||||
|
|
|
@ -52,5 +52,5 @@ define(["connectionManager"], function (connectionManager) {
|
||||||
currentSlideshow = null;
|
currentSlideshow = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,18 +1,7 @@
|
||||||
define(["dialogHelper", "loading", "connectionManager", "globalize", "actionsheet", "emby-input", "paper-icon-button-light", "emby-button", "listViewStyle", "material-icons", "formDialogStyle"], function (dialogHelper, loading, connectionManager, globalize, actionsheet) {
|
define(["dom", "dialogHelper", "loading", "connectionManager", "globalize", "actionsheet", "emby-input", "paper-icon-button-light", "emby-button", "listViewStyle", "material-icons", "formDialogStyle"], function (dom, dialogHelper, loading, connectionManager, globalize, actionsheet) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (options) {
|
return function (options) {
|
||||||
function parentWithClass(elem, className) {
|
|
||||||
while (!elem.classList || !elem.classList.contains(className)) {
|
|
||||||
elem = elem.parentNode;
|
|
||||||
if (!elem) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapChannel(button, channelId, providerChannelId) {
|
function mapChannel(button, channelId, providerChannelId) {
|
||||||
loading.show();
|
loading.show();
|
||||||
var providerId = options.providerId;
|
var providerId = options.providerId;
|
||||||
|
@ -26,7 +15,7 @@ define(["dialogHelper", "loading", "connectionManager", "globalize", "actionshee
|
||||||
},
|
},
|
||||||
dataType: "json"
|
dataType: "json"
|
||||||
}).then(function (mapping) {
|
}).then(function (mapping) {
|
||||||
var listItem = parentWithClass(button, "listItem");
|
var 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();
|
||||||
|
@ -34,7 +23,7 @@ define(["dialogHelper", "loading", "connectionManager", "globalize", "actionshee
|
||||||
}
|
}
|
||||||
|
|
||||||
function onChannelsElementClick(e) {
|
function onChannelsElementClick(e) {
|
||||||
var btnMap = parentWithClass(e.target, "btnMap");
|
var btnMap = dom.parentWithClass(e.target, "btnMap");
|
||||||
|
|
||||||
if (btnMap) {
|
if (btnMap) {
|
||||||
var channelId = btnMap.getAttribute("data-id");
|
var channelId = btnMap.getAttribute("data-id");
|
||||||
|
|
|
@ -188,9 +188,9 @@ define(['events'], function (events) {
|
||||||
return apiClient.getEndpointInfo().then(function (endpoint) {
|
return apiClient.getEndpointInfo().then(function (endpoint) {
|
||||||
if (endpoint.IsInNetwork) {
|
if (endpoint.IsInNetwork) {
|
||||||
return apiClient.getPublicSystemInfo().then(function (info) {
|
return apiClient.getPublicSystemInfo().then(function (info) {
|
||||||
var localAddress = info.LocalAddress
|
var localAddress = info.LocalAddress;
|
||||||
if (!localAddress) {
|
if (!localAddress) {
|
||||||
console.debug("No valid local address returned, defaulting to external one")
|
console.debug("No valid local address returned, defaulting to external one");
|
||||||
localAddress = serverAddress;
|
localAddress = serverAddress;
|
||||||
}
|
}
|
||||||
addToCache(serverAddress, localAddress);
|
addToCache(serverAddress, localAddress);
|
||||||
|
|
|
@ -1,25 +1,12 @@
|
||||||
define(['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 (dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize) {
|
define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-checkbox', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button', 'flexStyles'], function (dom, dialogHelper, loading, appHost, layoutManager, connectionManager, appRouter, globalize) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var currentServerId;
|
var currentServerId;
|
||||||
|
|
||||||
function parentWithClass(elem, className) {
|
|
||||||
|
|
||||||
while (!elem.classList || !elem.classList.contains(className)) {
|
|
||||||
elem = elem.parentNode;
|
|
||||||
|
|
||||||
if (!elem) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSubmit(e) {
|
function onSubmit(e) {
|
||||||
loading.show();
|
loading.show();
|
||||||
|
|
||||||
var panel = parentWithClass(this, 'dialog');
|
var panel = dom.parentWithClass(this, 'dialog');
|
||||||
|
|
||||||
var collectionId = panel.querySelector('#selectCollectionToAddTo').value;
|
var collectionId = panel.querySelector('#selectCollectionToAddTo').value;
|
||||||
|
|
||||||
|
|
|
@ -7,11 +7,11 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
systemInfo = info;
|
systemInfo = info;
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDialogClosed() {
|
function onDialogClosed() {
|
||||||
loading.hide()
|
loading.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshDirectoryBrowser(page, path, fileOptions, updatePathOnError) {
|
function refreshDirectoryBrowser(page, path, fileOptions, updatePathOnError) {
|
||||||
|
@ -24,7 +24,7 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
var promises = [];
|
var promises = [];
|
||||||
|
|
||||||
if ("Network" === path) {
|
if ("Network" === path) {
|
||||||
promises.push(ApiClient.getNetworkDevices())
|
promises.push(ApiClient.getNetworkDevices());
|
||||||
} else {
|
} else {
|
||||||
if (path) {
|
if (path) {
|
||||||
promises.push(ApiClient.getDirectoryContents(path, fileOptions));
|
promises.push(ApiClient.getDirectoryContents(path, fileOptions));
|
||||||
|
@ -89,7 +89,7 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
var instruction = options.instruction ? options.instruction + "<br/><br/>" : "";
|
var instruction = options.instruction ? options.instruction + "<br/><br/>" : "";
|
||||||
html += '<div class="infoBanner" style="margin-bottom:1.5em;">';
|
html += '<div class="infoBanner" style="margin-bottom:1.5em;">';
|
||||||
html += instruction;
|
html += instruction;
|
||||||
html += Globalize.translate("MessageDirectoryPickerInstruction").replace("{0}", "<b>\\\\server</b>").replace("{1}", "<b>\\\\192.168.1.101</b>");
|
html += Globalize.translate("MessageDirectoryPickerInstruction", "<b>\\\\server</b>", "<b>\\\\192.168.1.101</b>");
|
||||||
if ("bsd" === systemInfo.OperatingSystem.toLowerCase()) {
|
if ("bsd" === systemInfo.OperatingSystem.toLowerCase()) {
|
||||||
html += "<br/>";
|
html += "<br/>";
|
||||||
html += "<br/>";
|
html += "<br/>";
|
||||||
|
@ -101,7 +101,7 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
html += Globalize.translate("MessageDirectoryPickerLinuxInstruction");
|
html += Globalize.translate("MessageDirectoryPickerLinuxInstruction");
|
||||||
html += "<br/>";
|
html += "<br/>";
|
||||||
}
|
}
|
||||||
html += "</div>"
|
html += "</div>";
|
||||||
}
|
}
|
||||||
html += '<form style="margin:auto;">';
|
html += '<form style="margin:auto;">';
|
||||||
html += '<div class="inputContainer" style="display: flex; align-items: center;">';
|
html += '<div class="inputContainer" style="display: flex; align-items: center;">';
|
||||||
|
@ -144,13 +144,13 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
function alertText(text) {
|
function alertText(text) {
|
||||||
alertTextWithOptions({
|
alertTextWithOptions({
|
||||||
text: text
|
text: text
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function alertTextWithOptions(options) {
|
function alertTextWithOptions(options) {
|
||||||
require(["alert"], function(alert) {
|
require(["alert"], function(alert) {
|
||||||
alert(options)
|
alert(options);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function validatePath(path, validateWriteable, apiClient) {
|
function validatePath(path, validateWriteable, apiClient) {
|
||||||
|
@ -163,21 +163,20 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
}
|
}
|
||||||
}).catch(function(response) {
|
}).catch(function(response) {
|
||||||
if (response) {
|
if (response) {
|
||||||
// TODO All alerts (across the project), should use Globalize.translate()
|
|
||||||
if (response.status === 404) {
|
if (response.status === 404) {
|
||||||
alertText("The path could not be found. Please ensure the path is valid and try again.");
|
alertText(Globalize.translate("PathNotFound"));
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
if (response.status === 500) {
|
if (response.status === 500) {
|
||||||
if (validateWriteable) {
|
if (validateWriteable) {
|
||||||
alertText("Jellyfin Server requires write access to this folder. Please ensure write access and try again.");
|
alertText(Globalize.translate("WriteAccessRequired"));
|
||||||
} else {
|
} else {
|
||||||
alertText("The path could not be found. Please ensure the path is valid and try again.")
|
alertText(Globalize.translate("PathNotFound"));
|
||||||
}
|
}
|
||||||
return Promise.reject()
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return Promise.resolve()
|
return Promise.resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,7 +188,7 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
if (lnkPath.classList.contains("lnkFile")) {
|
if (lnkPath.classList.contains("lnkFile")) {
|
||||||
content.querySelector("#txtDirectoryPickerPath").value = path;
|
content.querySelector("#txtDirectoryPickerPath").value = path;
|
||||||
} else {
|
} else {
|
||||||
refreshDirectoryBrowser(content, path, fileOptions, true)
|
refreshDirectoryBrowser(content, path, fileOptions, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -276,7 +275,7 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
dlg.addEventListener("close", onDialogClosed);
|
dlg.addEventListener("close", onDialogClosed);
|
||||||
dialogHelper.open(dlg);
|
dialogHelper.open(dlg);
|
||||||
dlg.querySelector(".btnCloseDialog").addEventListener("click", function() {
|
dlg.querySelector(".btnCloseDialog").addEventListener("click", function() {
|
||||||
dialogHelper.close(dlg)
|
dialogHelper.close(dlg);
|
||||||
});
|
});
|
||||||
currentDialog = dlg;
|
currentDialog = dlg;
|
||||||
dlg.querySelector("#txtDirectoryPickerPath").value = initialPath;
|
dlg.querySelector("#txtDirectoryPickerPath").value = initialPath;
|
||||||
|
@ -294,9 +293,9 @@ define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-
|
||||||
if (currentDialog) {
|
if (currentDialog) {
|
||||||
dialogHelper.close(currentDialog);
|
dialogHelper.close(currentDialog);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
var systemInfo;
|
var systemInfo;
|
||||||
return directoryBrowser
|
return directoryBrowser;
|
||||||
});
|
});
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
|
|
||||||
<div class="selectContainer fldDateTimeLocale hide">
|
<div class="selectContainer fldDateTimeLocale hide">
|
||||||
<select is="emby-select" class="selectDateTimeLocale" label="${LabelDateTimeLocale}">
|
<select is="emby-select" class="selectDateTimeLocale" label="${LabelDateTimeLocale}">
|
||||||
<option value="">${AutoBasedOnLanguageSetting}</option>
|
<option value="">${Auto}</option>
|
||||||
<option value="ar">Arabic</option>
|
<option value="ar">Arabic</option>
|
||||||
<option value="be-BY">Belarusian (Belarus)</option>
|
<option value="be-BY">Belarusian (Belarus)</option>
|
||||||
<option value="bg-BG">Bulgarian (Bulgaria)</option>
|
<option value="bg-BG">Bulgarian (Bulgaria)</option>
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
define([], function () {
|
/* eslint-disable indent */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function parentWithAttribute(elem, name, value) {
|
/**
|
||||||
|
* Useful DOM utilities.
|
||||||
|
* @module components/dom
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns parent of element with specified attribute value.
|
||||||
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
||||||
|
* @param {string} name - Attribute name.
|
||||||
|
* @param {mixed} value - Attribute value.
|
||||||
|
* @returns {HTMLElement} Parent with specified attribute value.
|
||||||
|
*/
|
||||||
|
export function parentWithAttribute(elem, name, value) {
|
||||||
while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) {
|
while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) {
|
||||||
elem = elem.parentNode;
|
elem = elem.parentNode;
|
||||||
|
|
||||||
|
@ -14,8 +24,13 @@ define([], function () {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentWithTag(elem, tagNames) {
|
/**
|
||||||
|
* Returns parent of element with one of specified tag names.
|
||||||
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
||||||
|
* @param {(string|Array)} tagNames - Tag name or array of tag names.
|
||||||
|
* @returns {HTMLElement} Parent with one of specified tag names.
|
||||||
|
*/
|
||||||
|
export function parentWithTag(elem, tagNames) {
|
||||||
// accept both string and array passed in
|
// accept both string and array passed in
|
||||||
if (!Array.isArray(tagNames)) {
|
if (!Array.isArray(tagNames)) {
|
||||||
tagNames = [tagNames];
|
tagNames = [tagNames];
|
||||||
|
@ -32,9 +47,14 @@ define([], function () {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns _true_ if class list contains one of specified names.
|
||||||
|
* @param {DOMTokenList} classList - Class list.
|
||||||
|
* @param {Array} classNames - Array of class names.
|
||||||
|
* @returns {boolean} _true_ if class list contains one of specified names.
|
||||||
|
*/
|
||||||
function containsAnyClass(classList, classNames) {
|
function containsAnyClass(classList, classNames) {
|
||||||
|
for (let i = 0, length = classNames.length; i < length; i++) {
|
||||||
for (var i = 0, length = classNames.length; i < length; i++) {
|
|
||||||
if (classList.contains(classNames[i])) {
|
if (classList.contains(classNames[i])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -42,8 +62,13 @@ define([], function () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentWithClass(elem, classNames) {
|
/**
|
||||||
|
* Returns parent of element with one of specified class names.
|
||||||
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
||||||
|
* @param {(string|Array)} classNames - Class name or array of class names.
|
||||||
|
* @returns {HTMLElement} Parent with one of specified class names.
|
||||||
|
*/
|
||||||
|
export function parentWithClass(elem, classNames) {
|
||||||
// accept both string and array passed in
|
// accept both string and array passed in
|
||||||
if (!Array.isArray(classNames)) {
|
if (!Array.isArray(classNames)) {
|
||||||
classNames = [classNames];
|
classNames = [classNames];
|
||||||
|
@ -60,9 +85,9 @@ define([], function () {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportsCaptureOption = false;
|
let supportsCaptureOption = false;
|
||||||
try {
|
try {
|
||||||
var opts = Object.defineProperty({}, 'capture', {
|
const opts = Object.defineProperty({}, 'capture', {
|
||||||
// eslint-disable-next-line getter-return
|
// eslint-disable-next-line getter-return
|
||||||
get: function () {
|
get: function () {
|
||||||
supportsCaptureOption = true;
|
supportsCaptureOption = true;
|
||||||
|
@ -73,29 +98,58 @@ define([], function () {
|
||||||
console.debug('error checking capture support');
|
console.debug('error checking capture support');
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEventListenerWithOptions(target, type, handler, options) {
|
/**
|
||||||
var optionsOrCapture = options;
|
* Adds event listener to specified target.
|
||||||
|
* @param {EventTarget} target - Event target.
|
||||||
|
* @param {string} type - Event type.
|
||||||
|
* @param {function} handler - Event handler.
|
||||||
|
* @param {Object} [options] - Listener options.
|
||||||
|
*/
|
||||||
|
export function addEventListener(target, type, handler, options) {
|
||||||
|
let optionsOrCapture = options || {};
|
||||||
if (!supportsCaptureOption) {
|
if (!supportsCaptureOption) {
|
||||||
optionsOrCapture = options.capture;
|
optionsOrCapture = optionsOrCapture.capture;
|
||||||
}
|
}
|
||||||
target.addEventListener(type, handler, optionsOrCapture);
|
target.addEventListener(type, handler, optionsOrCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeEventListenerWithOptions(target, type, handler, options) {
|
/**
|
||||||
var optionsOrCapture = options;
|
* Removes event listener from specified target.
|
||||||
|
* @param {EventTarget} target - Event target.
|
||||||
|
* @param {string} type - Event type.
|
||||||
|
* @param {function} handler - Event handler.
|
||||||
|
* @param {Object} [options] - Listener options.
|
||||||
|
*/
|
||||||
|
export function removeEventListener(target, type, handler, options) {
|
||||||
|
let optionsOrCapture = options || {};
|
||||||
if (!supportsCaptureOption) {
|
if (!supportsCaptureOption) {
|
||||||
optionsOrCapture = options.capture;
|
optionsOrCapture = optionsOrCapture.capture;
|
||||||
}
|
}
|
||||||
target.removeEventListener(type, handler, optionsOrCapture);
|
target.removeEventListener(type, handler, optionsOrCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
var windowSize;
|
/**
|
||||||
var windowSizeEventsBound;
|
* Cached window size.
|
||||||
|
*/
|
||||||
|
let windowSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag of event listener bound.
|
||||||
|
*/
|
||||||
|
let windowSizeEventsBound;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets cached window size.
|
||||||
|
*/
|
||||||
function clearWindowSize() {
|
function clearWindowSize() {
|
||||||
windowSize = null;
|
windowSize = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWindowSize() {
|
/**
|
||||||
|
* Returns window size.
|
||||||
|
* @returns {Object} Window size.
|
||||||
|
*/
|
||||||
|
export function getWindowSize() {
|
||||||
if (!windowSize) {
|
if (!windowSize) {
|
||||||
windowSize = {
|
windowSize = {
|
||||||
innerHeight: window.innerHeight,
|
innerHeight: window.innerHeight,
|
||||||
|
@ -104,46 +158,60 @@ define([], function () {
|
||||||
|
|
||||||
if (!windowSizeEventsBound) {
|
if (!windowSizeEventsBound) {
|
||||||
windowSizeEventsBound = true;
|
windowSizeEventsBound = true;
|
||||||
addEventListenerWithOptions(window, "orientationchange", clearWindowSize, { passive: true });
|
addEventListener(window, "orientationchange", clearWindowSize, { passive: true });
|
||||||
addEventListenerWithOptions(window, 'resize', clearWindowSize, { passive: true });
|
addEventListener(window, 'resize', clearWindowSize, { passive: true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return windowSize;
|
return windowSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
var standardWidths = [480, 720, 1280, 1440, 1920, 2560, 3840, 5120, 7680];
|
/**
|
||||||
function getScreenWidth() {
|
* Standard screen widths.
|
||||||
var width = window.innerWidth;
|
*/
|
||||||
var height = window.innerHeight;
|
const standardWidths = [480, 720, 1280, 1440, 1920, 2560, 3840, 5120, 7680];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns screen width.
|
||||||
|
* @returns {number} Screen width.
|
||||||
|
*/
|
||||||
|
export function getScreenWidth() {
|
||||||
|
let width = window.innerWidth;
|
||||||
|
const height = window.innerHeight;
|
||||||
|
|
||||||
if (height > width) {
|
if (height > width) {
|
||||||
width = height * (16.0 / 9.0);
|
width = height * (16.0 / 9.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
var closest = standardWidths.sort(function (a, b) {
|
const closest = standardWidths.sort(function (a, b) {
|
||||||
return Math.abs(width - a) - Math.abs(width - b);
|
return Math.abs(width - a) - Math.abs(width - b);
|
||||||
})[0];
|
})[0];
|
||||||
|
|
||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
var _animationEvent;
|
/**
|
||||||
function whichAnimationEvent() {
|
* Name of animation end event.
|
||||||
|
*/
|
||||||
|
let _animationEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns name of animation end event.
|
||||||
|
* @returns {string} Name of animation end event.
|
||||||
|
*/
|
||||||
|
export function whichAnimationEvent() {
|
||||||
if (_animationEvent) {
|
if (_animationEvent) {
|
||||||
return _animationEvent;
|
return _animationEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
var t;
|
const el = document.createElement("div");
|
||||||
var el = document.createElement("div");
|
const animations = {
|
||||||
var animations = {
|
|
||||||
"animation": "animationend",
|
"animation": "animationend",
|
||||||
"OAnimation": "oAnimationEnd",
|
"OAnimation": "oAnimationEnd",
|
||||||
"MozAnimation": "animationend",
|
"MozAnimation": "animationend",
|
||||||
"WebkitAnimation": "webkitAnimationEnd"
|
"WebkitAnimation": "webkitAnimationEnd"
|
||||||
};
|
};
|
||||||
for (t in animations) {
|
for (let t in animations) {
|
||||||
if (el.style[t] !== undefined) {
|
if (el.style[t] !== undefined) {
|
||||||
_animationEvent = animations[t];
|
_animationEvent = animations[t];
|
||||||
return animations[t];
|
return animations[t];
|
||||||
|
@ -154,26 +222,36 @@ define([], function () {
|
||||||
return _animationEvent;
|
return _animationEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
function whichAnimationCancelEvent() {
|
/**
|
||||||
|
* Returns name of animation cancel event.
|
||||||
|
* @returns {string} Name of animation cancel event.
|
||||||
|
*/
|
||||||
|
export function whichAnimationCancelEvent() {
|
||||||
return whichAnimationEvent().replace('animationend', 'animationcancel').replace('AnimationEnd', 'AnimationCancel');
|
return whichAnimationEvent().replace('animationend', 'animationcancel').replace('AnimationEnd', 'AnimationCancel');
|
||||||
}
|
}
|
||||||
|
|
||||||
var _transitionEvent;
|
/**
|
||||||
function whichTransitionEvent() {
|
* Name of transition end event.
|
||||||
|
*/
|
||||||
|
let _transitionEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns name of transition end event.
|
||||||
|
* @returns {string} Name of transition end event.
|
||||||
|
*/
|
||||||
|
export function whichTransitionEvent() {
|
||||||
if (_transitionEvent) {
|
if (_transitionEvent) {
|
||||||
return _transitionEvent;
|
return _transitionEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
var t;
|
const el = document.createElement("div");
|
||||||
var el = document.createElement("div");
|
const transitions = {
|
||||||
var transitions = {
|
|
||||||
"transition": "transitionend",
|
"transition": "transitionend",
|
||||||
"OTransition": "oTransitionEnd",
|
"OTransition": "oTransitionEnd",
|
||||||
"MozTransition": "transitionend",
|
"MozTransition": "transitionend",
|
||||||
"WebkitTransition": "webkitTransitionEnd"
|
"WebkitTransition": "webkitTransitionEnd"
|
||||||
};
|
};
|
||||||
for (t in transitions) {
|
for (let t in transitions) {
|
||||||
if (el.style[t] !== undefined) {
|
if (el.style[t] !== undefined) {
|
||||||
_transitionEvent = transitions[t];
|
_transitionEvent = transitions[t];
|
||||||
return transitions[t];
|
return transitions[t];
|
||||||
|
@ -184,16 +262,17 @@ define([], function () {
|
||||||
return _transitionEvent;
|
return _transitionEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
/* eslint-enable indent */
|
||||||
parentWithAttribute: parentWithAttribute,
|
|
||||||
parentWithClass: parentWithClass,
|
export default {
|
||||||
parentWithTag: parentWithTag,
|
parentWithAttribute: parentWithAttribute,
|
||||||
addEventListener: addEventListenerWithOptions,
|
parentWithClass: parentWithClass,
|
||||||
removeEventListener: removeEventListenerWithOptions,
|
parentWithTag: parentWithTag,
|
||||||
getWindowSize: getWindowSize,
|
addEventListener: addEventListener,
|
||||||
getScreenWidth: getScreenWidth,
|
removeEventListener: removeEventListener,
|
||||||
whichTransitionEvent: whichTransitionEvent,
|
getWindowSize: getWindowSize,
|
||||||
whichAnimationEvent: whichAnimationEvent,
|
getScreenWidth: getScreenWidth,
|
||||||
whichAnimationCancelEvent: whichAnimationCancelEvent
|
whichTransitionEvent: whichTransitionEvent,
|
||||||
};
|
whichAnimationEvent: whichAnimationEvent,
|
||||||
});
|
whichAnimationCancelEvent: whichAnimationCancelEvent
|
||||||
|
};
|
||||||
|
|
|
@ -1,18 +1,14 @@
|
||||||
define(['multi-download'], function (multiDownload) {
|
import multiDownload from "multi-download";
|
||||||
'use strict';
|
|
||||||
|
|
||||||
return {
|
export function download(items) {
|
||||||
download: function (items) {
|
|
||||||
|
|
||||||
if (window.NativeShell) {
|
if (window.NativeShell) {
|
||||||
items.map(function (item) {
|
items.map(function (item) {
|
||||||
window.NativeShell.downloadFile(item.url);
|
window.NativeShell.downloadFile(item.url);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
multiDownload(items.map(function (item) {
|
multiDownload(items.map(function (item) {
|
||||||
return item.url;
|
return item.url;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
define([], function () {
|
export function fileExists(path) {
|
||||||
'use strict';
|
if (window.NativeShell && window.NativeShell.FileSystem) {
|
||||||
|
return window.NativeShell.FileSystem.fileExists(path);
|
||||||
|
}
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
export function directoryExists(path) {
|
||||||
fileExists: function (path) {
|
if (window.NativeShell && window.NativeShell.FileSystem) {
|
||||||
if (window.NativeShell && window.NativeShell.FileSystem) {
|
return window.NativeShell.FileSystem.directoryExists(path);
|
||||||
return window.NativeShell.FileSystem.fileExists(path);
|
}
|
||||||
}
|
return Promise.reject();
|
||||||
return Promise.reject();
|
}
|
||||||
},
|
|
||||||
directoryExists: function (path) {
|
|
||||||
if (window.NativeShell && window.NativeShell.FileSystem) {
|
|
||||||
return window.NativeShell.FileSystem.directoryExists(path);
|
|
||||||
}
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["dialogHelper", "globalize", "connectionManager", "events", "browser", "require", "emby-checkbox", "emby-collapse", "css!./style"], function (dialogHelper, globalize, connectionManager, events, browser, require) {
|
define(["dom", "dialogHelper", "globalize", "connectionManager", "events", "browser", "require", "emby-checkbox", "emby-collapse", "css!./style"], function (dom, dialogHelper, globalize, connectionManager, events, browser, require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function renderOptions(context, selector, cssClass, items, isCheckedFn) {
|
function renderOptions(context, selector, cssClass, items, isCheckedFn) {
|
||||||
|
@ -106,16 +106,6 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", "
|
||||||
events.trigger(instance, "filterchange");
|
events.trigger(instance, "filterchange");
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentWithClass(elem, className) {
|
|
||||||
while (!elem.classList || !elem.classList.contains(className)) {
|
|
||||||
elem = elem.parentNode;
|
|
||||||
if (!elem) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setVisibility(context, options) {
|
function setVisibility(context, options) {
|
||||||
if (options.mode == "livetvchannels" || options.mode == "albums" || options.mode == "artists" || options.mode == "albumartists" || options.mode == "songs") {
|
if (options.mode == "livetvchannels" || options.mode == "albums" || options.mode == "artists" || options.mode == "albumartists" || options.mode == "songs") {
|
||||||
hideByClass(context, "videoStandard");
|
hideByClass(context, "videoStandard");
|
||||||
|
@ -320,7 +310,7 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", "
|
||||||
triggerChange(self);
|
triggerChange(self);
|
||||||
});
|
});
|
||||||
context.addEventListener("change", function (e) {
|
context.addEventListener("change", function (e) {
|
||||||
var chkGenreFilter = parentWithClass(e.target, "chkGenreFilter");
|
var chkGenreFilter = dom.parentWithClass(e.target, "chkGenreFilter");
|
||||||
if (chkGenreFilter) {
|
if (chkGenreFilter) {
|
||||||
var filterName = chkGenreFilter.getAttribute("data-filter");
|
var filterName = chkGenreFilter.getAttribute("data-filter");
|
||||||
var filters = query.Genres || "";
|
var filters = query.Genres || "";
|
||||||
|
@ -334,7 +324,7 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", "
|
||||||
triggerChange(self);
|
triggerChange(self);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var chkTagFilter = parentWithClass(e.target, "chkTagFilter");
|
var chkTagFilter = dom.parentWithClass(e.target, "chkTagFilter");
|
||||||
if (chkTagFilter) {
|
if (chkTagFilter) {
|
||||||
var filterName = chkTagFilter.getAttribute("data-filter");
|
var filterName = chkTagFilter.getAttribute("data-filter");
|
||||||
var filters = query.Tags || "";
|
var filters = query.Tags || "";
|
||||||
|
@ -348,7 +338,7 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", "
|
||||||
triggerChange(self);
|
triggerChange(self);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var chkYearFilter = parentWithClass(e.target, "chkYearFilter");
|
var chkYearFilter = dom.parentWithClass(e.target, "chkYearFilter");
|
||||||
if (chkYearFilter) {
|
if (chkYearFilter) {
|
||||||
var filterName = chkYearFilter.getAttribute("data-filter");
|
var filterName = chkYearFilter.getAttribute("data-filter");
|
||||||
var filters = query.Years || "";
|
var filters = query.Years || "";
|
||||||
|
@ -362,7 +352,7 @@ define(["dialogHelper", "globalize", "connectionManager", "events", "browser", "
|
||||||
triggerChange(self);
|
triggerChange(self);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var chkOfficialRatingFilter = parentWithClass(e.target, "chkOfficialRatingFilter");
|
var chkOfficialRatingFilter = dom.parentWithClass(e.target, "chkOfficialRatingFilter");
|
||||||
if (chkOfficialRatingFilter) {
|
if (chkOfficialRatingFilter) {
|
||||||
var filterName = chkOfficialRatingFilter.getAttribute("data-filter");
|
var filterName = chkOfficialRatingFilter.getAttribute("data-filter");
|
||||||
var filters = query.OfficialRatings || "";
|
var filters = query.OfficialRatings || "";
|
||||||
|
|
|
@ -64,18 +64,18 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
|
||||||
} else {
|
} else {
|
||||||
var noLibDescription;
|
var noLibDescription;
|
||||||
if (user['Policy'] && user['Policy']['IsAdministrator']) {
|
if (user['Policy'] && user['Policy']['IsAdministrator']) {
|
||||||
noLibDescription = Globalize.translate("NoCreatedLibraries", '<a id="button-createLibrary" class="button-link">', '</a>')
|
noLibDescription = Globalize.translate("NoCreatedLibraries", '<a id="button-createLibrary" class="button-link">', '</a>');
|
||||||
} else {
|
} else {
|
||||||
noLibDescription = Globalize.translate("AskAdminToCreateLibrary");
|
noLibDescription = Globalize.translate("AskAdminToCreateLibrary");
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '<div class="centerMessage padded-left padded-right">';
|
html += '<div class="centerMessage padded-left padded-right">';
|
||||||
html += '<h2>' + Globalize.translate("MessageNothingHere") + '</h2>';
|
html += '<h2>' + Globalize.translate("MessageNothingHere") + '</h2>';
|
||||||
html += '<p>' + noLibDescription + '</p>'
|
html += '<p>' + noLibDescription + '</p>';
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
elem.innerHTML = html;
|
elem.innerHTML = html;
|
||||||
|
|
||||||
var createNowLink = elem.querySelector("#button-createLibrary")
|
var createNowLink = elem.querySelector("#button-createLibrary");
|
||||||
if (createNowLink) {
|
if (createNowLink) {
|
||||||
createNowLink.addEventListener("click", function () {
|
createNowLink.addEventListener("click", function () {
|
||||||
Dashboard.navigate("library.html");
|
Dashboard.navigate("library.html");
|
||||||
|
@ -640,7 +640,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
|
||||||
|
|
||||||
if (enableScrollX()) {
|
if (enableScrollX()) {
|
||||||
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true">';
|
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true">';
|
||||||
html += '<div is="emby-itemscontainer" class="itemsContainer scrollSlider focuscontainer-x">'
|
html += '<div is="emby-itemscontainer" class="itemsContainer scrollSlider focuscontainer-x">';
|
||||||
} else {
|
} else {
|
||||||
html += '<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right vertical-wrap focuscontainer-x">';
|
html += '<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right vertical-wrap focuscontainer-x">';
|
||||||
}
|
}
|
||||||
|
@ -714,7 +714,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
|
||||||
|
|
||||||
if (enableScrollX()) {
|
if (enableScrollX()) {
|
||||||
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true">';
|
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true">';
|
||||||
html += '<div is="emby-itemscontainer" class="itemsContainer scrollSlider focuscontainer-x" data-monitor="videoplayback,markplayed">'
|
html += '<div is="emby-itemscontainer" class="itemsContainer scrollSlider focuscontainer-x" data-monitor="videoplayback,markplayed">';
|
||||||
} else {
|
} else {
|
||||||
html += '<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right vertical-wrap focuscontainer-x" data-monitor="videoplayback,markplayed">';
|
html += '<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right vertical-wrap focuscontainer-x" data-monitor="videoplayback,markplayed">';
|
||||||
}
|
}
|
||||||
|
@ -786,7 +786,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
|
||||||
|
|
||||||
if (enableScrollX()) {
|
if (enableScrollX()) {
|
||||||
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true">';
|
html += '<div is="emby-scroller" class="padded-top-focusscale padded-bottom-focusscale" data-centerfocus="true">';
|
||||||
html += '<div is="emby-itemscontainer" class="itemsContainer scrollSlider focuscontainer-x">'
|
html += '<div is="emby-itemscontainer" class="itemsContainer scrollSlider focuscontainer-x">';
|
||||||
} else {
|
} else {
|
||||||
html += '<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right vertical-wrap focuscontainer-x">';
|
html += '<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right vertical-wrap focuscontainer-x">';
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,13 +171,29 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
|
||||||
|
|
||||||
// Appending #t=xxx to the query string doesn't seem to work with HLS
|
// Appending #t=xxx to the query string doesn't seem to work with HLS
|
||||||
// For plain video files, not all browsers support it either
|
// For plain video files, not all browsers support it either
|
||||||
var delay = browser.safari ? 2500 : 0;
|
|
||||||
if (delay) {
|
if (element.duration >= seconds) {
|
||||||
setTimeout(function () {
|
// media is ready, seek immediately
|
||||||
setCurrentTimeIfNeeded(element, seconds);
|
|
||||||
}, delay);
|
|
||||||
} else {
|
|
||||||
setCurrentTimeIfNeeded(element, seconds);
|
setCurrentTimeIfNeeded(element, seconds);
|
||||||
|
} else {
|
||||||
|
// update video player position when media is ready to be sought
|
||||||
|
var events = ["durationchange", "loadeddata", "play", "loadedmetadata"];
|
||||||
|
var onMediaChange = function(e) {
|
||||||
|
if (element.currentTime === 0 && element.duration >= seconds) {
|
||||||
|
// seek only when video position is exactly zero,
|
||||||
|
// as this is true only if video hasn't started yet or
|
||||||
|
// user rewound to the very beginning
|
||||||
|
// (but rewinding cannot happen as the first event with media of non-empty duration)
|
||||||
|
console.debug(`seeking to ${seconds} on ${e.type} event`);
|
||||||
|
setCurrentTimeIfNeeded(element, seconds);
|
||||||
|
events.map(function(name) {
|
||||||
|
element.removeEventListener(name, onMediaChange);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
events.map(function (name) {
|
||||||
|
element.addEventListener(name, onMediaChange);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
if (track) {
|
if (track) {
|
||||||
var format = (track.Codec || '').toLowerCase();
|
var format = (track.Codec || '').toLowerCase();
|
||||||
if (format === 'ssa' || format === 'ass') {
|
if (format === 'ssa' || format === 'ass') {
|
||||||
// libjass is needed here
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -117,8 +116,9 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeTrackEventText(text) {
|
function normalizeTrackEventText(text, useHtml) {
|
||||||
return text.replace(/\\N/gi, '\n');
|
var result = text.replace(/\\N/gi, '\n').replace(/\r/gi, '');
|
||||||
|
return useHtml ? result.replace(/\n/gi, '<br>') : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function setTracks(elem, tracks, item, mediaSource) {
|
function setTracks(elem, tracks, item, mediaSource) {
|
||||||
|
@ -568,19 +568,19 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
self.resetSubtitleOffset = function() {
|
self.resetSubtitleOffset = function() {
|
||||||
currentTrackOffset = 0;
|
currentTrackOffset = 0;
|
||||||
showTrackOffset = false;
|
showTrackOffset = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
self.enableShowingSubtitleOffset = function() {
|
self.enableShowingSubtitleOffset = function() {
|
||||||
showTrackOffset = true;
|
showTrackOffset = true;
|
||||||
}
|
};
|
||||||
|
|
||||||
self.disableShowingSubtitleOffset = function() {
|
self.disableShowingSubtitleOffset = function() {
|
||||||
showTrackOffset = false;
|
showTrackOffset = false;
|
||||||
}
|
};
|
||||||
|
|
||||||
self.isShowingSubtitleOffsetEnabled = function() {
|
self.isShowingSubtitleOffsetEnabled = function() {
|
||||||
return showTrackOffset;
|
return showTrackOffset;
|
||||||
}
|
};
|
||||||
|
|
||||||
function getTextTrack() {
|
function getTextTrack() {
|
||||||
var videoElement = self._mediaElement;
|
var videoElement = self._mediaElement;
|
||||||
|
@ -652,7 +652,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
|
|
||||||
self.getSubtitleOffset = function() {
|
self.getSubtitleOffset = function() {
|
||||||
return currentTrackOffset;
|
return currentTrackOffset;
|
||||||
}
|
};
|
||||||
|
|
||||||
function isAudioStreamSupported(stream, deviceProfile) {
|
function isAudioStreamSupported(stream, deviceProfile) {
|
||||||
|
|
||||||
|
@ -1020,7 +1020,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
xhr.onerror = function (e) {
|
xhr.onerror = function (e) {
|
||||||
reject(e);
|
reject(e);
|
||||||
decrementFetchQueue();
|
decrementFetchQueue();
|
||||||
}
|
};
|
||||||
|
|
||||||
xhr.send();
|
xhr.send();
|
||||||
});
|
});
|
||||||
|
@ -1047,101 +1047,38 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
lastCustomTrackMs = 0;
|
lastCustomTrackMs = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderWithSubtitlesOctopus(videoElement, track, item) {
|
function renderSsaAss(videoElement, track, item) {
|
||||||
var attachments = self._currentPlayOptions.mediaSource.MediaAttachments || [];
|
var attachments = self._currentPlayOptions.mediaSource.MediaAttachments || [];
|
||||||
|
var apiClient = connectionManager.getApiClient(item);
|
||||||
var options = {
|
var options = {
|
||||||
video: videoElement,
|
video: videoElement,
|
||||||
subUrl: getTextTrackUrl(track, item),
|
subUrl: getTextTrackUrl(track, item),
|
||||||
fonts: attachments.map(function (i) {
|
fonts: attachments.map(function (i) {
|
||||||
return i.DeliveryUrl;
|
return apiClient.getUrl(i.DeliveryUrl);
|
||||||
}),
|
}),
|
||||||
workerUrl: appRouter.baseUrl() + "/libraries/subtitles-octopus-worker.js",
|
workerUrl: appRouter.baseUrl() + "/libraries/subtitles-octopus-worker.js",
|
||||||
legacyWorkerUrl: appRouter.baseUrl() + "/libraries/subtitles-octopus-worker-legacy.js",
|
legacyWorkerUrl: appRouter.baseUrl() + "/libraries/subtitles-octopus-worker-legacy.js",
|
||||||
onError: function() {
|
onError: function() {
|
||||||
htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror');
|
htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror');
|
||||||
}
|
},
|
||||||
|
|
||||||
|
// new octopus options; override all, even defaults
|
||||||
|
renderMode: 'blend',
|
||||||
|
dropAllAnimations: false,
|
||||||
|
libassMemoryLimit: 40,
|
||||||
|
libassGlyphLimit: 40,
|
||||||
|
targetFps: 24,
|
||||||
|
prescaleTradeoff: 0.8,
|
||||||
|
softHeightLimit: 1080,
|
||||||
|
hardHeightLimit: 2160,
|
||||||
|
resizeVariation: 0.2,
|
||||||
|
renderAhead: 90
|
||||||
};
|
};
|
||||||
require(['JavascriptSubtitlesOctopus'], function(SubtitlesOctopus) {
|
require(['JavascriptSubtitlesOctopus'], function(SubtitlesOctopus) {
|
||||||
currentSubtitlesOctopus = new SubtitlesOctopus(options);
|
currentSubtitlesOctopus = new SubtitlesOctopus(options);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderWithLibjass(videoElement, track, item) {
|
|
||||||
|
|
||||||
var rendererSettings = {};
|
|
||||||
|
|
||||||
if (browser.ps4) {
|
|
||||||
// Text outlines are not rendering very well
|
|
||||||
rendererSettings.enableSvg = false;
|
|
||||||
} else if (browser.edge || browser.msie) {
|
|
||||||
// svg not rendering at all
|
|
||||||
rendererSettings.enableSvg = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// probably safer to just disable everywhere
|
|
||||||
rendererSettings.enableSvg = false;
|
|
||||||
|
|
||||||
require(['libjass', 'ResizeObserver'], function (libjass, ResizeObserver) {
|
|
||||||
|
|
||||||
libjass.ASS.fromUrl(getTextTrackUrl(track, item)).then(function (ass) {
|
|
||||||
|
|
||||||
var clock = new libjass.renderers.ManualClock();
|
|
||||||
currentClock = clock;
|
|
||||||
|
|
||||||
// Create a DefaultRenderer using the video element and the ASS object
|
|
||||||
var renderer = new libjass.renderers.WebRenderer(ass, clock, videoElement.parentNode, rendererSettings);
|
|
||||||
|
|
||||||
currentAssRenderer = renderer;
|
|
||||||
|
|
||||||
renderer.addEventListener("ready", function () {
|
|
||||||
try {
|
|
||||||
renderer.resize(videoElement.offsetWidth, videoElement.offsetHeight, 0, 0);
|
|
||||||
|
|
||||||
if (!self._resizeObserver) {
|
|
||||||
self._resizeObserver = new ResizeObserver(onVideoResize, {});
|
|
||||||
self._resizeObserver.observe(videoElement);
|
|
||||||
}
|
|
||||||
//clock.pause();
|
|
||||||
} catch (ex) {
|
|
||||||
//alert(ex);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}, function () {
|
|
||||||
htmlMediaHelper.onErrorInternal(self, 'mediadecodeerror');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderSsaAss(videoElement, track, item) {
|
|
||||||
if (supportsCanvas() && supportsWebWorkers()) {
|
|
||||||
console.debug('rendering subtitles with SubtitlesOctopus');
|
|
||||||
renderWithSubtitlesOctopus(videoElement, track, item);
|
|
||||||
} else {
|
|
||||||
console.debug('rendering subtitles with libjass');
|
|
||||||
renderWithLibjass(videoElement, track, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onVideoResize() {
|
|
||||||
if (browser.iOS) {
|
|
||||||
// the new sizes will be delayed for about 500ms with wkwebview
|
|
||||||
setTimeout(resetVideoRendererSize, 500);
|
|
||||||
} else {
|
|
||||||
resetVideoRendererSize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetVideoRendererSize() {
|
|
||||||
var renderer = currentAssRenderer;
|
|
||||||
if (renderer) {
|
|
||||||
var videoElement = self._mediaElement;
|
|
||||||
var width = videoElement.offsetWidth;
|
|
||||||
var height = videoElement.offsetHeight;
|
|
||||||
console.debug('videoElement resized: ' + width + 'x' + height);
|
|
||||||
renderer.resize(width, height, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function requiresCustomSubtitlesElement() {
|
function requiresCustomSubtitlesElement() {
|
||||||
|
|
||||||
// after a system update, ps4 isn't showing anything when creating a track element dynamically
|
// after a system update, ps4 isn't showing anything when creating a track element dynamically
|
||||||
|
@ -1231,7 +1168,6 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
if (!itemHelper.isLocalItem(item) || track.IsExternal) {
|
if (!itemHelper.isLocalItem(item) || track.IsExternal) {
|
||||||
var format = (track.Codec || '').toLowerCase();
|
var format = (track.Codec || '').toLowerCase();
|
||||||
if (format === 'ssa' || format === 'ass') {
|
if (format === 'ssa' || format === 'ass') {
|
||||||
// libjass is needed here
|
|
||||||
renderSsaAss(videoElement, track, item);
|
renderSsaAss(videoElement, track, item);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1274,7 +1210,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
data.TrackEvents.forEach(function (trackEvent) {
|
data.TrackEvents.forEach(function (trackEvent) {
|
||||||
|
|
||||||
var trackCueObject = window.VTTCue || window.TextTrackCue;
|
var trackCueObject = window.VTTCue || window.TextTrackCue;
|
||||||
var cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text));
|
var cue = new trackCueObject(trackEvent.StartPositionTicks / 10000000, trackEvent.EndPositionTicks / 10000000, normalizeTrackEventText(trackEvent.Text, false));
|
||||||
|
|
||||||
trackElement.addCue(cue);
|
trackElement.addCue(cue);
|
||||||
});
|
});
|
||||||
|
@ -1315,8 +1251,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedTrackEvent && selectedTrackEvent.Text) {
|
if (selectedTrackEvent && selectedTrackEvent.Text) {
|
||||||
|
subtitleTextElement.innerHTML = normalizeTrackEventText(selectedTrackEvent.Text, true);
|
||||||
subtitleTextElement.innerHTML = normalizeTrackEventText(selectedTrackEvent.Text);
|
|
||||||
subtitleTextElement.classList.remove('hide');
|
subtitleTextElement.classList.remove('hide');
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
@ -1493,11 +1428,11 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
}
|
}
|
||||||
|
|
||||||
if (browser.safari || browser.iOS || browser.iPad) {
|
if (browser.safari || browser.iOS || browser.iPad) {
|
||||||
list.push('AirPlay')
|
list.push('AirPlay');
|
||||||
}
|
}
|
||||||
|
|
||||||
list.push('SetBrightness');
|
list.push('SetBrightness');
|
||||||
list.push("SetAspectRatio")
|
list.push("SetAspectRatio");
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
@ -1620,11 +1555,11 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
if (video) {
|
if (video) {
|
||||||
if (isEnabled) {
|
if (isEnabled) {
|
||||||
video.requestAirPlay().catch(function(err) {
|
video.requestAirPlay().catch(function(err) {
|
||||||
console.error("Error requesting AirPlay", err)
|
console.error("Error requesting AirPlay", err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
document.exitAirPLay().catch(function(err) {
|
document.exitAirPLay().catch(function(err) {
|
||||||
console.error("Error exiting AirPlay", err)
|
console.error("Error exiting AirPlay", err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1757,12 +1692,12 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
var mediaElement = this._mediaElement;
|
var mediaElement = this._mediaElement;
|
||||||
if (mediaElement) {
|
if (mediaElement) {
|
||||||
if ("auto" === val) {
|
if ("auto" === val) {
|
||||||
mediaElement.style.removeProperty("object-fit")
|
mediaElement.style.removeProperty("object-fit");
|
||||||
} else {
|
} else {
|
||||||
mediaElement.style["object-fit"] = val
|
mediaElement.style["object-fit"] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this._currentAspectRatio = val
|
this._currentAspectRatio = val;
|
||||||
};
|
};
|
||||||
|
|
||||||
HtmlVideoPlayer.prototype.getAspectRatio = function () {
|
HtmlVideoPlayer.prototype.getAspectRatio = function () {
|
||||||
|
@ -1779,7 +1714,7 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
}, {
|
}, {
|
||||||
name: "Fill",
|
name: "Fill",
|
||||||
id: "fill"
|
id: "fill"
|
||||||
}]
|
}];
|
||||||
};
|
};
|
||||||
|
|
||||||
HtmlVideoPlayer.prototype.togglePictureInPicture = function () {
|
HtmlVideoPlayer.prototype.togglePictureInPicture = function () {
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
define(["datetime"], function (datetime) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function humaneDate(date_str) {
|
|
||||||
var format;
|
|
||||||
var time_formats = [
|
|
||||||
[90, "a minute"],
|
|
||||||
[3600, "minutes", 60],
|
|
||||||
[5400, "an hour"],
|
|
||||||
[86400, "hours", 3600],
|
|
||||||
[129600, "a day"],
|
|
||||||
[604800, "days", 86400],
|
|
||||||
[907200, "a week"],
|
|
||||||
[2628e3, "weeks", 604800],
|
|
||||||
[3942e3, "a month"],
|
|
||||||
[31536e3, "months", 2628e3],
|
|
||||||
[47304e3, "a year"],
|
|
||||||
[31536e5, "years", 31536e3]
|
|
||||||
];
|
|
||||||
var dt = new Date();
|
|
||||||
var date = datetime.parseISO8601Date(date_str, true);
|
|
||||||
var seconds = (dt - date) / 1000.0;
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
if (seconds < 0) {
|
|
||||||
seconds = Math.abs(seconds);
|
|
||||||
}
|
|
||||||
// eslint-disable-next-line no-cond-assign
|
|
||||||
for (; format = time_formats[i++];) {
|
|
||||||
if (seconds < format[0]) {
|
|
||||||
if (2 == format.length) {
|
|
||||||
return format[1] + " ago";
|
|
||||||
}
|
|
||||||
|
|
||||||
return Math.round(seconds / format[2]) + " " + format[1] + " ago";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seconds > 47304e5) {
|
|
||||||
return Math.round(seconds / 47304e5) + " centuries ago";
|
|
||||||
}
|
|
||||||
|
|
||||||
return date_str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function humaneElapsed(firstDateStr, secondDateStr) {
|
|
||||||
// TODO replace this whole script with a library or something
|
|
||||||
var dateOne = new Date(firstDateStr);
|
|
||||||
var dateTwo = new Date(secondDateStr);
|
|
||||||
var delta = (dateTwo.getTime() - dateOne.getTime()) / 1e3;
|
|
||||||
var days = Math.floor(delta % 31536e3 / 86400);
|
|
||||||
var hours = Math.floor(delta % 31536e3 % 86400 / 3600);
|
|
||||||
var minutes = Math.floor(delta % 31536e3 % 86400 % 3600 / 60);
|
|
||||||
var seconds = Math.round(delta % 31536e3 % 86400 % 3600 % 60);
|
|
||||||
var elapsed = "";
|
|
||||||
elapsed += 1 == days ? days + " day " : "";
|
|
||||||
elapsed += days > 1 ? days + " days " : "";
|
|
||||||
elapsed += 1 == hours ? hours + " hour " : "";
|
|
||||||
elapsed += hours > 1 ? hours + " hours " : "";
|
|
||||||
elapsed += 1 == minutes ? minutes + " minute " : "";
|
|
||||||
elapsed += minutes > 1 ? minutes + " minutes " : "";
|
|
||||||
elapsed += elapsed.length > 0 ? "and " : "";
|
|
||||||
elapsed += 1 == seconds ? seconds + " second" : "";
|
|
||||||
elapsed += 0 == seconds || seconds > 1 ? seconds + " seconds" : "";
|
|
||||||
return elapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.humaneDate = humaneDate;
|
|
||||||
window.humaneElapsed = humaneElapsed;
|
|
||||||
return {
|
|
||||||
humaneDate: humaneDate,
|
|
||||||
humaneElapsed: humaneElapsed
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader', 'browser', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'emby-checkbox', 'paper-icon-button-light', 'emby-button', 'formDialogStyle', 'cardStyle'], function (loading, appHost, dialogHelper, connectionManager, imageLoader, browser, layoutManager, scrollHelper, globalize, require) {
|
define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader', 'browser', 'layoutManager', 'scrollHelper', 'globalize', 'require', 'emby-checkbox', 'paper-icon-button-light', 'emby-button', 'formDialogStyle', 'cardStyle'], function (dom, loading, appHost, dialogHelper, connectionManager, imageLoader, browser, layoutManager, scrollHelper, globalize, require) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var enableFocusTransform = !browser.slow && !browser.edge;
|
var enableFocusTransform = !browser.slow && !browser.edge;
|
||||||
|
@ -109,7 +109,7 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
html += '<span style="margin-right: 10px;">';
|
html += '<span style="margin-right: 10px;">';
|
||||||
|
|
||||||
var startAtDisplay = totalRecordCount ? startIndex + 1 : 0;
|
var startAtDisplay = totalRecordCount ? startIndex + 1 : 0;
|
||||||
html += startAtDisplay + '-' + recordsEnd + ' of ' + totalRecordCount;
|
html += globalize.translate("ListPaging", startAtDisplay, recordsEnd, totalRecordCount);
|
||||||
|
|
||||||
html += '</span>';
|
html += '</span>';
|
||||||
|
|
||||||
|
@ -126,21 +126,7 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentWithClass(elem, className) {
|
|
||||||
|
|
||||||
while (!elem.classList || !elem.classList.contains(className)) {
|
|
||||||
elem = elem.parentNode;
|
|
||||||
|
|
||||||
if (!elem) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadRemoteImage(page, apiClient, url, type, provider) {
|
function downloadRemoteImage(page, apiClient, url, type, provider) {
|
||||||
|
|
||||||
var options = getBaseRemoteOptions();
|
var options = getBaseRemoteOptions();
|
||||||
|
|
||||||
options.Type = type;
|
options.Type = type;
|
||||||
|
@ -152,7 +138,7 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
apiClient.downloadRemoteImage(options).then(function () {
|
apiClient.downloadRemoteImage(options).then(function () {
|
||||||
|
|
||||||
hasChanges = true;
|
hasChanges = true;
|
||||||
var dlg = parentWithClass(page, 'dialog');
|
var dlg = dom.parentWithClass(page, 'dialog');
|
||||||
dialogHelper.close(dlg);
|
dialogHelper.close(dlg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -162,7 +148,6 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRemoteImageHtml(image, imageType, apiClient) {
|
function getRemoteImageHtml(image, imageType, apiClient) {
|
||||||
|
|
||||||
var tagName = layoutManager.tv ? 'button' : 'div';
|
var tagName = layoutManager.tv ? 'button' : 'div';
|
||||||
var enableFooterButtons = !layoutManager.tv;
|
var enableFooterButtons = !layoutManager.tv;
|
||||||
|
|
||||||
|
@ -293,7 +278,6 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
}
|
}
|
||||||
|
|
||||||
function initEditor(page, apiClient) {
|
function initEditor(page, apiClient) {
|
||||||
|
|
||||||
page.querySelector('#selectBrowsableImageType').addEventListener('change', function () {
|
page.querySelector('#selectBrowsableImageType').addEventListener('change', function () {
|
||||||
browsableImageType = this.value;
|
browsableImageType = this.value;
|
||||||
browsableImageStartIndex = 0;
|
browsableImageStartIndex = 0;
|
||||||
|
@ -319,14 +303,14 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
|
|
||||||
page.addEventListener('click', function (e) {
|
page.addEventListener('click', function (e) {
|
||||||
|
|
||||||
var btnDownloadRemoteImage = parentWithClass(e.target, 'btnDownloadRemoteImage');
|
var btnDownloadRemoteImage = dom.parentWithClass(e.target, 'btnDownloadRemoteImage');
|
||||||
if (btnDownloadRemoteImage) {
|
if (btnDownloadRemoteImage) {
|
||||||
var card = parentWithClass(btnDownloadRemoteImage, 'card');
|
var card = dom.parentWithClass(btnDownloadRemoteImage, 'card');
|
||||||
downloadRemoteImage(page, apiClient, card.getAttribute('data-imageurl'), card.getAttribute('data-imagetype'), card.getAttribute('data-imageprovider'));
|
downloadRemoteImage(page, apiClient, card.getAttribute('data-imageurl'), card.getAttribute('data-imagetype'), card.getAttribute('data-imageprovider'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var btnImageCard = parentWithClass(e.target, 'btnImageCard');
|
var btnImageCard = dom.parentWithClass(e.target, 'btnImageCard');
|
||||||
if (btnImageCard) {
|
if (btnImageCard) {
|
||||||
downloadRemoteImage(page, apiClient, btnImageCard.getAttribute('data-imageurl'), btnImageCard.getAttribute('data-imagetype'), btnImageCard.getAttribute('data-imageprovider'));
|
downloadRemoteImage(page, apiClient, btnImageCard.getAttribute('data-imageurl'), btnImageCard.getAttribute('data-imagetype'), btnImageCard.getAttribute('data-imageprovider'));
|
||||||
}
|
}
|
||||||
|
@ -334,7 +318,6 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
}
|
}
|
||||||
|
|
||||||
function showEditor(itemId, serverId, itemType) {
|
function showEditor(itemId, serverId, itemType) {
|
||||||
|
|
||||||
loading.show();
|
loading.show();
|
||||||
|
|
||||||
require(['text!./imagedownloader.template.html'], function (template) {
|
require(['text!./imagedownloader.template.html'], function (template) {
|
||||||
|
@ -380,7 +363,6 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDialogClosed() {
|
function onDialogClosed() {
|
||||||
|
|
||||||
var dlg = this;
|
var dlg = this;
|
||||||
|
|
||||||
if (layoutManager.tv) {
|
if (layoutManager.tv) {
|
||||||
|
@ -397,9 +379,7 @@ define(['loading', 'apphost', 'dialogHelper', 'connectionManager', 'imageLoader'
|
||||||
|
|
||||||
return {
|
return {
|
||||||
show: function (itemId, serverId, itemType, imageType) {
|
show: function (itemId, serverId, itemType, imageType) {
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
|
|
||||||
currentResolve = resolve;
|
currentResolve = resolve;
|
||||||
currentReject = reject;
|
currentReject = reject;
|
||||||
hasChanges = false;
|
hasChanges = false;
|
||||||
|
|
|
@ -1,165 +1,168 @@
|
||||||
define(["inputManager", "layoutManager"], function (inputManager, layoutManager) {
|
/**
|
||||||
"use strict";
|
* Module for performing keyboard navigation.
|
||||||
|
* @module components/input/keyboardnavigation
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
import inputManager from "inputManager";
|
||||||
* Key name mapping.
|
import layoutManager from "layoutManager";
|
||||||
*/
|
|
||||||
// Add more to support old browsers
|
|
||||||
var KeyNames = {
|
|
||||||
13: "Enter",
|
|
||||||
19: "Pause",
|
|
||||||
27: "Escape",
|
|
||||||
32: "Space",
|
|
||||||
37: "ArrowLeft",
|
|
||||||
38: "ArrowUp",
|
|
||||||
39: "ArrowRight",
|
|
||||||
40: "ArrowDown",
|
|
||||||
// MediaRewind (Tizen/WebOS)
|
|
||||||
412: "MediaRewind",
|
|
||||||
// MediaStop (Tizen/WebOS)
|
|
||||||
413: "MediaStop",
|
|
||||||
// MediaPlay (Tizen/WebOS)
|
|
||||||
415: "MediaPlay",
|
|
||||||
// MediaFastForward (Tizen/WebOS)
|
|
||||||
417: "MediaFastForward",
|
|
||||||
// Back (WebOS)
|
|
||||||
461: "Back",
|
|
||||||
// Back (Tizen)
|
|
||||||
10009: "Back",
|
|
||||||
// MediaTrackPrevious (Tizen)
|
|
||||||
10232: "MediaTrackPrevious",
|
|
||||||
// MediaTrackNext (Tizen)
|
|
||||||
10233: "MediaTrackNext",
|
|
||||||
// MediaPlayPause (Tizen)
|
|
||||||
10252: "MediaPlayPause"
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Keys used for keyboard navigation.
|
* Key name mapping.
|
||||||
*/
|
*/
|
||||||
var NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"];
|
const KeyNames = {
|
||||||
|
13: "Enter",
|
||||||
|
19: "Pause",
|
||||||
|
27: "Escape",
|
||||||
|
32: "Space",
|
||||||
|
37: "ArrowLeft",
|
||||||
|
38: "ArrowUp",
|
||||||
|
39: "ArrowRight",
|
||||||
|
40: "ArrowDown",
|
||||||
|
// MediaRewind (Tizen/WebOS)
|
||||||
|
412: "MediaRewind",
|
||||||
|
// MediaStop (Tizen/WebOS)
|
||||||
|
413: "MediaStop",
|
||||||
|
// MediaPlay (Tizen/WebOS)
|
||||||
|
415: "MediaPlay",
|
||||||
|
// MediaFastForward (Tizen/WebOS)
|
||||||
|
417: "MediaFastForward",
|
||||||
|
// Back (WebOS)
|
||||||
|
461: "Back",
|
||||||
|
// Back (Tizen)
|
||||||
|
10009: "Back",
|
||||||
|
// MediaTrackPrevious (Tizen)
|
||||||
|
10232: "MediaTrackPrevious",
|
||||||
|
// MediaTrackNext (Tizen)
|
||||||
|
10233: "MediaTrackNext",
|
||||||
|
// MediaPlayPause (Tizen)
|
||||||
|
10252: "MediaPlayPause"
|
||||||
|
};
|
||||||
|
|
||||||
var hasFieldKey = false;
|
/**
|
||||||
try {
|
* Keys used for keyboard navigation.
|
||||||
hasFieldKey = "key" in new KeyboardEvent("keydown");
|
*/
|
||||||
} catch (e) {
|
const NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"];
|
||||||
console.error("error checking 'key' field");
|
|
||||||
|
let hasFieldKey = false;
|
||||||
|
try {
|
||||||
|
hasFieldKey = "key" in new KeyboardEvent("keydown");
|
||||||
|
} catch (e) {
|
||||||
|
console.error("error checking 'key' field");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasFieldKey) {
|
||||||
|
// Add [a..z]
|
||||||
|
for (let i = 65; i <= 90; i++) {
|
||||||
|
KeyNames[i] = String.fromCharCode(i).toLowerCase();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!hasFieldKey) {
|
/**
|
||||||
// Add [a..z]
|
* Returns key name from event.
|
||||||
for (var i = 65; i <= 90; i++) {
|
*
|
||||||
KeyNames[i] = String.fromCharCode(i).toLowerCase();
|
* @param {KeyboardEvent} event - Keyboard event.
|
||||||
|
* @return {string} Key name.
|
||||||
|
*/
|
||||||
|
export function getKeyName(event) {
|
||||||
|
return KeyNames[event.keyCode] || event.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns _true_ if key is used for navigation.
|
||||||
|
*
|
||||||
|
* @param {string} key - Key name.
|
||||||
|
* @return {boolean} _true_ if key is used for navigation.
|
||||||
|
*/
|
||||||
|
export function isNavigationKey(key) {
|
||||||
|
return NavigationKeys.indexOf(key) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enable() {
|
||||||
|
document.addEventListener("keydown", function (e) {
|
||||||
|
const key = getKeyName(e);
|
||||||
|
|
||||||
|
// Ignore navigation keys for non-TV
|
||||||
|
if (!layoutManager.tv && isNavigationKey(key)) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
let capture = true;
|
||||||
* Returns key name from event.
|
|
||||||
*
|
|
||||||
* @param {KeyboardEvent} keyboard event
|
|
||||||
* @return {string} key name
|
|
||||||
*/
|
|
||||||
function getKeyName(event) {
|
|
||||||
return KeyNames[event.keyCode] || event.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
switch (key) {
|
||||||
* Returns _true_ if key is used for navigation.
|
case "ArrowLeft":
|
||||||
*
|
inputManager.handle("left");
|
||||||
* @param {string} key name
|
break;
|
||||||
* @return {boolean} _true_ if key is used for navigation
|
case "ArrowUp":
|
||||||
*/
|
inputManager.handle("up");
|
||||||
function isNavigationKey(key) {
|
break;
|
||||||
return NavigationKeys.indexOf(key) != -1;
|
case "ArrowRight":
|
||||||
}
|
inputManager.handle("right");
|
||||||
|
break;
|
||||||
|
case "ArrowDown":
|
||||||
|
inputManager.handle("down");
|
||||||
|
break;
|
||||||
|
|
||||||
function enable() {
|
case "Back":
|
||||||
document.addEventListener("keydown", function (e) {
|
inputManager.handle("back");
|
||||||
var key = getKeyName(e);
|
break;
|
||||||
|
|
||||||
// Ignore navigation keys for non-TV
|
case "Escape":
|
||||||
if (!layoutManager.tv && isNavigationKey(key)) {
|
if (layoutManager.tv) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var capture = true;
|
|
||||||
|
|
||||||
switch (key) {
|
|
||||||
case "ArrowLeft":
|
|
||||||
inputManager.handle("left");
|
|
||||||
break;
|
|
||||||
case "ArrowUp":
|
|
||||||
inputManager.handle("up");
|
|
||||||
break;
|
|
||||||
case "ArrowRight":
|
|
||||||
inputManager.handle("right");
|
|
||||||
break;
|
|
||||||
case "ArrowDown":
|
|
||||||
inputManager.handle("down");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Back":
|
|
||||||
inputManager.handle("back");
|
inputManager.handle("back");
|
||||||
break;
|
} else {
|
||||||
|
|
||||||
case "Escape":
|
|
||||||
if (layoutManager.tv) {
|
|
||||||
inputManager.handle("back");
|
|
||||||
} else {
|
|
||||||
capture = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "MediaPlay":
|
|
||||||
inputManager.handle("play");
|
|
||||||
break;
|
|
||||||
case "Pause":
|
|
||||||
inputManager.handle("pause");
|
|
||||||
break;
|
|
||||||
case "MediaPlayPause":
|
|
||||||
inputManager.handle("playpause");
|
|
||||||
break;
|
|
||||||
case "MediaRewind":
|
|
||||||
inputManager.handle("rewind");
|
|
||||||
break;
|
|
||||||
case "MediaFastForward":
|
|
||||||
inputManager.handle("fastforward");
|
|
||||||
break;
|
|
||||||
case "MediaStop":
|
|
||||||
inputManager.handle("stop");
|
|
||||||
break;
|
|
||||||
case "MediaTrackPrevious":
|
|
||||||
inputManager.handle("previoustrack");
|
|
||||||
break;
|
|
||||||
case "MediaTrackNext":
|
|
||||||
inputManager.handle("nexttrack");
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
capture = false;
|
capture = false;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
if (capture) {
|
case "MediaPlay":
|
||||||
console.debug("disabling default event handling");
|
inputManager.handle("play");
|
||||||
e.preventDefault();
|
break;
|
||||||
}
|
case "Pause":
|
||||||
});
|
inputManager.handle("pause");
|
||||||
}
|
break;
|
||||||
|
case "MediaPlayPause":
|
||||||
|
inputManager.handle("playpause");
|
||||||
|
break;
|
||||||
|
case "MediaRewind":
|
||||||
|
inputManager.handle("rewind");
|
||||||
|
break;
|
||||||
|
case "MediaFastForward":
|
||||||
|
inputManager.handle("fastforward");
|
||||||
|
break;
|
||||||
|
case "MediaStop":
|
||||||
|
inputManager.handle("stop");
|
||||||
|
break;
|
||||||
|
case "MediaTrackPrevious":
|
||||||
|
inputManager.handle("previoustrack");
|
||||||
|
break;
|
||||||
|
case "MediaTrackNext":
|
||||||
|
inputManager.handle("nexttrack");
|
||||||
|
break;
|
||||||
|
|
||||||
// Gamepad initialisation. No script is required if no gamepads are present at init time, saving a bit of resources.
|
default:
|
||||||
// Whenever the gamepad is connected, we hand all the control of the gamepad to gamepadtokey.js by removing the event handler
|
capture = false;
|
||||||
function attachGamepadScript(e) {
|
}
|
||||||
console.log("Gamepad connected! Attaching gamepadtokey.js script");
|
|
||||||
window.removeEventListener("gamepadconnected", attachGamepadScript);
|
|
||||||
require(["components/input/gamepadtokey"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// No need to check for gamepads manually at load time, the eventhandler will be fired for that
|
if (capture) {
|
||||||
window.addEventListener("gamepadconnected", attachGamepadScript);
|
console.debug("disabling default event handling");
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
// Gamepad initialisation. No script is required if no gamepads are present at init time, saving a bit of resources.
|
||||||
enable: enable,
|
// Whenever the gamepad is connected, we hand all the control of the gamepad to gamepadtokey.js by removing the event handler
|
||||||
getKeyName: getKeyName,
|
function attachGamepadScript(e) {
|
||||||
isNavigationKey: isNavigationKey
|
console.log("Gamepad connected! Attaching gamepadtokey.js script");
|
||||||
};
|
window.removeEventListener("gamepadconnected", attachGamepadScript);
|
||||||
});
|
require(["components/input/gamepadtokey"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// No need to check for gamepads manually at load time, the eventhandler will be fired for that
|
||||||
|
window.addEventListener("gamepadconnected", attachGamepadScript);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
enable: enable,
|
||||||
|
getKeyName: getKeyName,
|
||||||
|
isNavigationKey: isNavigationKey
|
||||||
|
};
|
||||||
|
|
|
@ -116,7 +116,7 @@ define(["dialogHelper", "require", "layoutManager", "globalize", "userSettings",
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAttribute(label, value) {
|
function createAttribute(label, value) {
|
||||||
return '<span class="mediaInfoLabel">' + label + '</span><span class="mediaInfoAttribute">' + value + "</span>"
|
return '<span class="mediaInfoLabel">' + label + '</span><span class="mediaInfoAttribute">' + value + "</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
function showMediaInfoMore(itemId, serverId, template) {
|
function showMediaInfoMore(itemId, serverId, template) {
|
||||||
|
|
|
@ -296,8 +296,6 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize",
|
||||||
|
|
||||||
var html = "";
|
var html = "";
|
||||||
|
|
||||||
var providerIds = item.ProviderIds || {};
|
|
||||||
|
|
||||||
for (var i = 0, length = idList.length; i < length; i++) {
|
for (var i = 0, length = idList.length; i < length; i++) {
|
||||||
|
|
||||||
var idInfo = idList[i];
|
var idInfo = idList[i];
|
||||||
|
@ -306,9 +304,12 @@ define(["dialogHelper", "loading", "connectionManager", "require", "globalize",
|
||||||
|
|
||||||
html += '<div class="inputContainer">';
|
html += '<div class="inputContainer">';
|
||||||
|
|
||||||
var idLabel = globalize.translate("LabelDynamicExternalId").replace("{0}", idInfo.Name);
|
var fullName = idInfo.Name;
|
||||||
|
if (idInfo.Type) {
|
||||||
|
fullName = idInfo.Name + " " + globalize.translate(idInfo.Type);
|
||||||
|
}
|
||||||
|
|
||||||
var value = providerIds[idInfo.Key] || "";
|
var idLabel = globalize.translate("LabelDynamicExternalId", fullName);
|
||||||
|
|
||||||
html += '<input is="emby-input" class="txtLookupId" data-providerkey="' + idInfo.Key + '" id="' + id + '" label="' + idLabel + '"/>';
|
html += '<input is="emby-input" class="txtLookupId" data-providerkey="' + idInfo.Key + '" id="' + id + '" label="' + idLabel + '"/>';
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
html += "<option value='" + culture.TwoLetterISORegionName + "'>" + culture.DisplayName + "</option>";
|
html += "<option value='" + culture.TwoLetterISORegionName + "'>" + culture.DisplayName + "</option>";
|
||||||
}
|
}
|
||||||
select.innerHTML = html;
|
select.innerHTML = html;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateRefreshInterval(select) {
|
function populateRefreshInterval(select) {
|
||||||
|
@ -120,7 +120,7 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
html += plugin.Name;
|
html += plugin.Name;
|
||||||
html += "</h3>";
|
html += "</h3>";
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
i > 0 ? html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate("ButtonUp") + '" class="btnSortableMoveUp btnSortable" data-pluginindex="' + i + '"><i class="material-icons keyboard_arrow_up"></i></button>' : plugins.length > 1 && (html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate("ButtonDown") + '" class="btnSortableMoveDown btnSortable" data-pluginindex="' + i + '"><i class="material-icons keyboard_arrow_down"></i></button>'), html += "</div>"
|
i > 0 ? html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate("ButtonUp") + '" class="btnSortableMoveUp btnSortable" data-pluginindex="' + i + '"><i class="material-icons keyboard_arrow_up"></i></button>' : plugins.length > 1 && (html += '<button type="button" is="paper-icon-button-light" title="' + globalize.translate("ButtonDown") + '" class="btnSortableMoveDown btnSortable" data-pluginindex="' + i + '"><i class="material-icons keyboard_arrow_down"></i></button>'), html += "</div>";
|
||||||
}
|
}
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
html += '<div class="fieldDescription">' + globalize.translate("LabelMetadataDownloadersHelp") + "</div>";
|
html += '<div class="fieldDescription">' + globalize.translate("LabelMetadataDownloadersHelp") + "</div>";
|
||||||
|
@ -265,10 +265,10 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
renderMetadataFetchers(parent, availableOptions, {});
|
renderMetadataFetchers(parent, availableOptions, {});
|
||||||
renderSubtitleFetchers(parent, availableOptions, {});
|
renderSubtitleFetchers(parent, availableOptions, {});
|
||||||
renderImageFetchers(parent, availableOptions, {});
|
renderImageFetchers(parent, availableOptions, {});
|
||||||
availableOptions.SubtitleFetchers.length ? parent.querySelector(".subtitleDownloadSettings").classList.remove("hide") : parent.querySelector(".subtitleDownloadSettings").classList.add("hide")
|
availableOptions.SubtitleFetchers.length ? parent.querySelector(".subtitleDownloadSettings").classList.remove("hide") : parent.querySelector(".subtitleDownloadSettings").classList.add("hide");
|
||||||
}).catch(function() {
|
}).catch(function() {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function adjustSortableListElement(elem) {
|
function adjustSortableListElement(elem) {
|
||||||
|
@ -296,8 +296,8 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
Type: type
|
Type: type
|
||||||
}, currentLibraryOptions.TypeOptions.push(typeOptions));
|
}, currentLibraryOptions.TypeOptions.push(typeOptions));
|
||||||
var availableOptions = getTypeOptions(currentAvailableOptions || {}, type);
|
var availableOptions = getTypeOptions(currentAvailableOptions || {}, type);
|
||||||
(new ImageOptionsEditor).show(type, typeOptions, availableOptions)
|
(new ImageOptionsEditor).show(type, typeOptions, availableOptions);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onImageFetchersContainerClick(e) {
|
function onImageFetchersContainerClick(e) {
|
||||||
|
@ -315,12 +315,12 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
var list = dom.parentWithClass(li, "paperList");
|
var list = dom.parentWithClass(li, "paperList");
|
||||||
if (btnSortable.classList.contains("btnSortableMoveDown")) {
|
if (btnSortable.classList.contains("btnSortableMoveDown")) {
|
||||||
var next = li.nextSibling;
|
var next = li.nextSibling;
|
||||||
next && (li.parentNode.removeChild(li), next.parentNode.insertBefore(li, next.nextSibling))
|
next && (li.parentNode.removeChild(li), next.parentNode.insertBefore(li, next.nextSibling));
|
||||||
} else {
|
} else {
|
||||||
var prev = li.previousSibling;
|
var prev = li.previousSibling;
|
||||||
prev && (li.parentNode.removeChild(li), prev.parentNode.insertBefore(li, prev))
|
prev && (li.parentNode.removeChild(li), prev.parentNode.insertBefore(li, prev));
|
||||||
}
|
}
|
||||||
Array.prototype.forEach.call(list.querySelectorAll(".sortableOption"), adjustSortableListElement)
|
Array.prototype.forEach.call(list.querySelectorAll(".sortableOption"), adjustSortableListElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,13 +407,13 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
|
|
||||||
function setSubtitleFetchersIntoOptions(parent, options) {
|
function setSubtitleFetchersIntoOptions(parent, options) {
|
||||||
options.DisabledSubtitleFetchers = Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkSubtitleFetcher"), function(elem) {
|
options.DisabledSubtitleFetchers = Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkSubtitleFetcher"), function(elem) {
|
||||||
return !elem.checked
|
return !elem.checked;
|
||||||
}), function(elem) {
|
}), function(elem) {
|
||||||
return elem.getAttribute("data-pluginname")
|
return elem.getAttribute("data-pluginname");
|
||||||
});
|
});
|
||||||
|
|
||||||
options.SubtitleFetcherOrder = Array.prototype.map.call(parent.querySelectorAll(".subtitleFetcherItem"), function(elem) {
|
options.SubtitleFetcherOrder = Array.prototype.map.call(parent.querySelectorAll(".subtitleFetcherItem"), function(elem) {
|
||||||
return elem.getAttribute("data-pluginname")
|
return elem.getAttribute("data-pluginname");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -455,13 +455,13 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
}
|
}
|
||||||
|
|
||||||
typeOptions.ImageFetchers = Array.prototype.map.call(Array.prototype.filter.call(section.querySelectorAll(".chkImageFetcher"), function(elem) {
|
typeOptions.ImageFetchers = Array.prototype.map.call(Array.prototype.filter.call(section.querySelectorAll(".chkImageFetcher"), function(elem) {
|
||||||
return elem.checked
|
return elem.checked;
|
||||||
}), function(elem) {
|
}), function(elem) {
|
||||||
return elem.getAttribute("data-pluginname")
|
return elem.getAttribute("data-pluginname");
|
||||||
});
|
});
|
||||||
|
|
||||||
typeOptions.ImageFetcherOrder = Array.prototype.map.call(section.querySelectorAll(".imageFetcherItem"), function(elem) {
|
typeOptions.ImageFetcherOrder = Array.prototype.map.call(section.querySelectorAll(".imageFetcherItem"), function(elem) {
|
||||||
return elem.getAttribute("data-pluginname")
|
return elem.getAttribute("data-pluginname");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -505,20 +505,20 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
SaveSubtitlesWithMedia: parent.querySelector("#chkSaveSubtitlesLocally").checked,
|
SaveSubtitlesWithMedia: parent.querySelector("#chkSaveSubtitlesLocally").checked,
|
||||||
RequirePerfectSubtitleMatch: parent.querySelector("#chkRequirePerfectMatch").checked,
|
RequirePerfectSubtitleMatch: parent.querySelector("#chkRequirePerfectMatch").checked,
|
||||||
MetadataSavers: Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkMetadataSaver"), function(elem) {
|
MetadataSavers: Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkMetadataSaver"), function(elem) {
|
||||||
return elem.checked
|
return elem.checked;
|
||||||
}), function(elem) {
|
}), function(elem) {
|
||||||
return elem.getAttribute("data-pluginname")
|
return elem.getAttribute("data-pluginname");
|
||||||
}),
|
}),
|
||||||
TypeOptions: []
|
TypeOptions: []
|
||||||
};
|
};
|
||||||
|
|
||||||
options.LocalMetadataReaderOrder = Array.prototype.map.call(parent.querySelectorAll(".localReaderOption"), function(elem) {
|
options.LocalMetadataReaderOrder = Array.prototype.map.call(parent.querySelectorAll(".localReaderOption"), function(elem) {
|
||||||
return elem.getAttribute("data-pluginname")
|
return elem.getAttribute("data-pluginname");
|
||||||
});
|
});
|
||||||
options.SubtitleDownloadLanguages = Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkSubtitleLanguage"), function(elem) {
|
options.SubtitleDownloadLanguages = Array.prototype.map.call(Array.prototype.filter.call(parent.querySelectorAll(".chkSubtitleLanguage"), function(elem) {
|
||||||
return elem.checked
|
return elem.checked;
|
||||||
}), function(elem) {
|
}), function(elem) {
|
||||||
return elem.getAttribute("data-lang")
|
return elem.getAttribute("data-lang");
|
||||||
});
|
});
|
||||||
setSubtitleFetchersIntoOptions(parent, options);
|
setSubtitleFetchersIntoOptions(parent, options);
|
||||||
setMetadataFetchersIntoOptions(parent, options);
|
setMetadataFetchersIntoOptions(parent, options);
|
||||||
|
@ -531,7 +531,7 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
function getOrderedPlugins(plugins, configuredOrder) {
|
function getOrderedPlugins(plugins, configuredOrder) {
|
||||||
plugins = plugins.slice(0);
|
plugins = plugins.slice(0);
|
||||||
plugins.sort(function(a, b) {
|
plugins.sort(function(a, b) {
|
||||||
return a = configuredOrder.indexOf(a.Name), b = configuredOrder.indexOf(b.Name), a < b ? -1 : a > b ? 1 : 0
|
return a = configuredOrder.indexOf(a.Name), b = configuredOrder.indexOf(b.Name), a < b ? -1 : a > b ? 1 : 0;
|
||||||
});
|
});
|
||||||
return plugins;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
@ -558,10 +558,10 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
parent.querySelector("#chkSkipIfAudioTrackPresent").checked = options.SkipSubtitlesIfAudioTrackMatches;
|
parent.querySelector("#chkSkipIfAudioTrackPresent").checked = options.SkipSubtitlesIfAudioTrackMatches;
|
||||||
parent.querySelector("#chkRequirePerfectMatch").checked = options.RequirePerfectSubtitleMatch;
|
parent.querySelector("#chkRequirePerfectMatch").checked = options.RequirePerfectSubtitleMatch;
|
||||||
Array.prototype.forEach.call(parent.querySelectorAll(".chkMetadataSaver"), function(elem) {
|
Array.prototype.forEach.call(parent.querySelectorAll(".chkMetadataSaver"), function(elem) {
|
||||||
elem.checked = options.MetadataSavers ? -1 !== options.MetadataSavers.indexOf(elem.getAttribute("data-pluginname")) : "true" === elem.getAttribute("data-defaultenabled")
|
elem.checked = options.MetadataSavers ? -1 !== options.MetadataSavers.indexOf(elem.getAttribute("data-pluginname")) : "true" === elem.getAttribute("data-defaultenabled");
|
||||||
});
|
});
|
||||||
Array.prototype.forEach.call(parent.querySelectorAll(".chkSubtitleLanguage"), function(elem) {
|
Array.prototype.forEach.call(parent.querySelectorAll(".chkSubtitleLanguage"), function(elem) {
|
||||||
elem.checked = !!options.SubtitleDownloadLanguages && -1 !== options.SubtitleDownloadLanguages.indexOf(elem.getAttribute("data-lang"))
|
elem.checked = !!options.SubtitleDownloadLanguages && -1 !== options.SubtitleDownloadLanguages.indexOf(elem.getAttribute("data-lang"));
|
||||||
});
|
});
|
||||||
renderMetadataReaders(parent, getOrderedPlugins(parent.availableOptions.MetadataReaders, options.LocalMetadataReaderOrder || []));
|
renderMetadataReaders(parent, getOrderedPlugins(parent.availableOptions.MetadataReaders, options.LocalMetadataReaderOrder || []));
|
||||||
renderMetadataFetchers(parent, parent.availableOptions, options);
|
renderMetadataFetchers(parent, parent.availableOptions, options);
|
||||||
|
@ -578,5 +578,5 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
getLibraryOptions: getLibraryOptions,
|
getLibraryOptions: getLibraryOptions,
|
||||||
setLibraryOptions: setLibraryOptions,
|
setLibraryOptions: setLibraryOptions,
|
||||||
setAdvancedVisible: setAdvancedVisible
|
setAdvancedVisible: setAdvancedVisible
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -188,5 +188,5 @@ define(["pluginManager"], function (pluginManager) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -465,7 +465,12 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
|
||||||
var id = "txt1" + idInfo.Key;
|
var id = "txt1" + idInfo.Key;
|
||||||
var formatString = idInfo.UrlFormatString || '';
|
var formatString = idInfo.UrlFormatString || '';
|
||||||
|
|
||||||
var labelText = globalize.translate('LabelDynamicExternalId').replace('{0}', idInfo.Name);
|
var fullName = idInfo.Name;
|
||||||
|
if (idInfo.Type) {
|
||||||
|
fullName = idInfo.Name + " " + globalize.translate(idInfo.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
var labelText = globalize.translate('LabelDynamicExternalId', fullName);
|
||||||
|
|
||||||
html += '<div class="inputContainer">';
|
html += '<div class="inputContainer">';
|
||||||
html += '<div class="flex align-items-center">';
|
html += '<div class="flex align-items-center">';
|
||||||
|
|
|
@ -173,15 +173,15 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir
|
||||||
};
|
};
|
||||||
|
|
||||||
if (status === 'completed') {
|
if (status === 'completed') {
|
||||||
notification.title = globalize.translate('PackageInstallCompleted').replace('{0}', installation.Name + ' ' + installation.Version);
|
notification.title = globalize.translate('PackageInstallCompleted', installation.Name, installation.Version);
|
||||||
notification.vibrate = true;
|
notification.vibrate = true;
|
||||||
} else if (status === 'cancelled') {
|
} else if (status === 'cancelled') {
|
||||||
notification.title = globalize.translate('PackageInstallCancelled').replace('{0}', installation.Name + ' ' + installation.Version);
|
notification.title = globalize.translate('PackageInstallCancelled', installation.Name, installation.Version);
|
||||||
} else if (status === 'failed') {
|
} else if (status === 'failed') {
|
||||||
notification.title = globalize.translate('PackageInstallFailed').replace('{0}', installation.Name + ' ' + installation.Version);
|
notification.title = globalize.translate('PackageInstallFailed', installation.Name, installation.Version);
|
||||||
notification.vibrate = true;
|
notification.vibrate = true;
|
||||||
} else if (status === 'progress') {
|
} else if (status === 'progress') {
|
||||||
notification.title = globalize.translate('InstallingPackage').replace('{0}', installation.Name + ' ' + installation.Version);
|
notification.title = globalize.translate('InstallingPackage', installation.Name, installation.Version);
|
||||||
|
|
||||||
notification.actions =
|
notification.actions =
|
||||||
[
|
[
|
||||||
|
|
|
@ -1633,29 +1633,29 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
||||||
self.supportSubtitleOffset = function(player) {
|
self.supportSubtitleOffset = function(player) {
|
||||||
player = player || self._currentPlayer;
|
player = player || self._currentPlayer;
|
||||||
return player && 'setSubtitleOffset' in player;
|
return player && 'setSubtitleOffset' in player;
|
||||||
}
|
};
|
||||||
|
|
||||||
self.enableShowingSubtitleOffset = function(player) {
|
self.enableShowingSubtitleOffset = function(player) {
|
||||||
player = player || self._currentPlayer;
|
player = player || self._currentPlayer;
|
||||||
player.enableShowingSubtitleOffset();
|
player.enableShowingSubtitleOffset();
|
||||||
}
|
};
|
||||||
|
|
||||||
self.disableShowingSubtitleOffset = function(player) {
|
self.disableShowingSubtitleOffset = function(player) {
|
||||||
player = player || self._currentPlayer;
|
player = player || self._currentPlayer;
|
||||||
if (player.disableShowingSubtitleOffset) {
|
if (player.disableShowingSubtitleOffset) {
|
||||||
player.disableShowingSubtitleOffset();
|
player.disableShowingSubtitleOffset();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
self.isShowingSubtitleOffsetEnabled = function(player) {
|
self.isShowingSubtitleOffsetEnabled = function(player) {
|
||||||
player = player || self._currentPlayer;
|
player = player || self._currentPlayer;
|
||||||
return player.isShowingSubtitleOffsetEnabled();
|
return player.isShowingSubtitleOffsetEnabled();
|
||||||
}
|
};
|
||||||
|
|
||||||
self.isSubtitleStreamExternal = function(index, player) {
|
self.isSubtitleStreamExternal = function(index, player) {
|
||||||
var stream = getSubtitleStream(player, index);
|
var stream = getSubtitleStream(player, index);
|
||||||
return stream ? getDeliveryMethod(stream) === 'External' : false;
|
return stream ? getDeliveryMethod(stream) === 'External' : false;
|
||||||
}
|
};
|
||||||
|
|
||||||
self.setSubtitleOffset = function (value, player) {
|
self.setSubtitleOffset = function (value, player) {
|
||||||
player = player || self._currentPlayer;
|
player = player || self._currentPlayer;
|
||||||
|
@ -1669,12 +1669,12 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
||||||
if (player.getSubtitleOffset) {
|
if (player.getSubtitleOffset) {
|
||||||
return player.getSubtitleOffset();
|
return player.getSubtitleOffset();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
self.canHandleOffsetOnCurrentSubtitle = function(player) {
|
self.canHandleOffsetOnCurrentSubtitle = function(player) {
|
||||||
var index = self.getSubtitleStreamIndex(player);
|
var index = self.getSubtitleStreamIndex(player);
|
||||||
return index !== -1 && self.isSubtitleStreamExternal(index, player);
|
return index !== -1 && self.isSubtitleStreamExternal(index, player);
|
||||||
}
|
};
|
||||||
|
|
||||||
self.seek = function (ticks, player) {
|
self.seek = function (ticks, player) {
|
||||||
|
|
||||||
|
|
|
@ -1,24 +1,10 @@
|
||||||
define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', 'connectionManager', 'userSettings', 'appRouter', 'globalize', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (shell, dialogHelper, loading, layoutManager, playbackManager, connectionManager, userSettings, appRouter, globalize) {
|
define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager', 'connectionManager', 'userSettings', 'appRouter', 'globalize', 'emby-input', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (dom, shell, dialogHelper, loading, layoutManager, playbackManager, connectionManager, userSettings, appRouter, globalize) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var currentServerId;
|
var currentServerId;
|
||||||
|
|
||||||
function parentWithClass(elem, className) {
|
|
||||||
|
|
||||||
while (!elem.classList || !elem.classList.contains(className)) {
|
|
||||||
elem = elem.parentNode;
|
|
||||||
|
|
||||||
if (!elem) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onSubmit(e) {
|
function onSubmit(e) {
|
||||||
|
var panel = dom.parentWithClass(this, 'dialog');
|
||||||
var panel = parentWithClass(this, 'dialog');
|
|
||||||
|
|
||||||
var playlistId = panel.querySelector('#selectPlaylistToAddTo').value;
|
var playlistId = panel.querySelector('#selectPlaylistToAddTo').value;
|
||||||
var apiClient = connectionManager.getApiClient(currentServerId);
|
var apiClient = connectionManager.getApiClient(currentServerId);
|
||||||
|
@ -35,11 +21,9 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPlaylist(apiClient, dlg) {
|
function createPlaylist(apiClient, dlg) {
|
||||||
|
|
||||||
loading.show();
|
loading.show();
|
||||||
|
|
||||||
var url = apiClient.getUrl("Playlists", {
|
var url = apiClient.getUrl("Playlists", {
|
||||||
|
|
||||||
Name: dlg.querySelector('#txtNewPlaylistName').value,
|
Name: dlg.querySelector('#txtNewPlaylistName').value,
|
||||||
Ids: dlg.querySelector('.fldSelectedItemIds').value || '',
|
Ids: dlg.querySelector('.fldSelectedItemIds').value || '',
|
||||||
userId: apiClient.getCurrentUserId()
|
userId: apiClient.getCurrentUserId()
|
||||||
|
@ -50,9 +34,7 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
type: "POST",
|
type: "POST",
|
||||||
url: url,
|
url: url,
|
||||||
dataType: "json"
|
dataType: "json"
|
||||||
|
|
||||||
}).then(function (result) {
|
}).then(function (result) {
|
||||||
|
|
||||||
loading.hide();
|
loading.hide();
|
||||||
|
|
||||||
var id = result.Id;
|
var id = result.Id;
|
||||||
|
@ -63,16 +45,13 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
}
|
}
|
||||||
|
|
||||||
function redirectToPlaylist(apiClient, id) {
|
function redirectToPlaylist(apiClient, id) {
|
||||||
|
|
||||||
appRouter.showItem(id, apiClient.serverId());
|
appRouter.showItem(id, apiClient.serverId());
|
||||||
}
|
}
|
||||||
|
|
||||||
function addToPlaylist(apiClient, dlg, id) {
|
function addToPlaylist(apiClient, dlg, id) {
|
||||||
|
|
||||||
var itemIds = dlg.querySelector('.fldSelectedItemIds').value || '';
|
var itemIds = dlg.querySelector('.fldSelectedItemIds').value || '';
|
||||||
|
|
||||||
if (id === 'queue') {
|
if (id === 'queue') {
|
||||||
|
|
||||||
playbackManager.queue({
|
playbackManager.queue({
|
||||||
serverId: apiClient.serverId(),
|
serverId: apiClient.serverId(),
|
||||||
ids: itemIds.split(',')
|
ids: itemIds.split(',')
|
||||||
|
@ -85,7 +64,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
loading.show();
|
loading.show();
|
||||||
|
|
||||||
var url = apiClient.getUrl("Playlists/" + id + "/Items", {
|
var url = apiClient.getUrl("Playlists/" + id + "/Items", {
|
||||||
|
|
||||||
Ids: itemIds,
|
Ids: itemIds,
|
||||||
userId: apiClient.getCurrentUserId()
|
userId: apiClient.getCurrentUserId()
|
||||||
});
|
});
|
||||||
|
@ -95,7 +73,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
url: url
|
url: url
|
||||||
|
|
||||||
}).then(function () {
|
}).then(function () {
|
||||||
|
|
||||||
loading.hide();
|
loading.hide();
|
||||||
|
|
||||||
dlg.submitted = true;
|
dlg.submitted = true;
|
||||||
|
@ -108,7 +85,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
}
|
}
|
||||||
|
|
||||||
function populatePlaylists(editorOptions, panel) {
|
function populatePlaylists(editorOptions, panel) {
|
||||||
|
|
||||||
var select = panel.querySelector('#selectPlaylistToAddTo');
|
var select = panel.querySelector('#selectPlaylistToAddTo');
|
||||||
|
|
||||||
loading.hide();
|
loading.hide();
|
||||||
|
@ -116,7 +92,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
panel.querySelector('.newPlaylistInfo').classList.add('hide');
|
panel.querySelector('.newPlaylistInfo').classList.add('hide');
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
|
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
IncludeItemTypes: "Playlist",
|
IncludeItemTypes: "Playlist",
|
||||||
SortBy: 'SortName',
|
SortBy: 'SortName',
|
||||||
|
@ -125,7 +100,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
|
|
||||||
var apiClient = connectionManager.getApiClient(currentServerId);
|
var apiClient = connectionManager.getApiClient(currentServerId);
|
||||||
apiClient.getItems(apiClient.getCurrentUserId(), options).then(function (result) {
|
apiClient.getItems(apiClient.getCurrentUserId(), options).then(function (result) {
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
if (editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) {
|
if (editorOptions.enableAddToPlayQueue !== false && playbackManager.isPlaying()) {
|
||||||
|
@ -135,7 +109,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
html += '<option value="">' + globalize.translate('OptionNew') + '</option>';
|
html += '<option value="">' + globalize.translate('OptionNew') + '</option>';
|
||||||
|
|
||||||
html += result.Items.map(function (i) {
|
html += result.Items.map(function (i) {
|
||||||
|
|
||||||
return '<option value="' + i.Id + '">' + i.Name + '</option>';
|
return '<option value="' + i.Id + '">' + i.Name + '</option>';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -159,7 +132,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
}
|
}
|
||||||
|
|
||||||
function getEditorHtml(items) {
|
function getEditorHtml(items) {
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">';
|
html += '<div class="formDialogContent smoothScrollY" style="padding-top:2em;">';
|
||||||
|
@ -195,7 +167,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
}
|
}
|
||||||
|
|
||||||
function initEditor(content, options, items) {
|
function initEditor(content, options, items) {
|
||||||
|
|
||||||
content.querySelector('#selectPlaylistToAddTo').addEventListener('change', function () {
|
content.querySelector('#selectPlaylistToAddTo').addEventListener('change', function () {
|
||||||
if (this.value) {
|
if (this.value) {
|
||||||
content.querySelector('.newPlaylistInfo').classList.add('hide');
|
content.querySelector('.newPlaylistInfo').classList.add('hide');
|
||||||
|
@ -235,7 +206,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
}
|
}
|
||||||
|
|
||||||
PlaylistEditor.prototype.show = function (options) {
|
PlaylistEditor.prototype.show = function (options) {
|
||||||
|
|
||||||
var items = options.items || {};
|
var items = options.items || {};
|
||||||
currentServerId = options.serverId;
|
currentServerId = options.serverId;
|
||||||
|
|
||||||
|
@ -272,7 +242,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
initEditor(dlg, options, items);
|
initEditor(dlg, options, items);
|
||||||
|
|
||||||
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
||||||
|
|
||||||
dialogHelper.close(dlg);
|
dialogHelper.close(dlg);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -281,7 +250,6 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'playbackManager',
|
||||||
}
|
}
|
||||||
|
|
||||||
return dialogHelper.open(dlg).then(function () {
|
return dialogHelper.open(dlg).then(function () {
|
||||||
|
|
||||||
if (layoutManager.tv) {
|
if (layoutManager.tv) {
|
||||||
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
|
centerFocus(dlg.querySelector('.formDialogContent'), false, false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,6 @@
|
||||||
define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (shell, dialogHelper, loading, layoutManager, connectionManager, appRouter, globalize) {
|
define(['dom', 'shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager', 'appRouter', 'globalize', 'emby-input', 'emby-checkbox', 'paper-icon-button-light', 'emby-select', 'material-icons', 'css!./../formdialog', 'emby-button'], function (dom, shell, dialogHelper, loading, layoutManager, connectionManager, appRouter, globalize) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
function parentWithClass(elem, className) {
|
|
||||||
|
|
||||||
while (!elem.classList || !elem.classList.contains(className)) {
|
|
||||||
elem = elem.parentNode;
|
|
||||||
|
|
||||||
if (!elem) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getEditorHtml() {
|
function getEditorHtml() {
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
@ -65,7 +52,7 @@ define(['shell', 'dialogHelper', 'loading', 'layoutManager', 'connectionManager'
|
||||||
loading.show();
|
loading.show();
|
||||||
|
|
||||||
var instance = this;
|
var instance = this;
|
||||||
var dlg = parentWithClass(e.target, 'dialog');
|
var dlg = dom.parentWithClass(e.target, 'dialog');
|
||||||
var options = instance.options;
|
var options = instance.options;
|
||||||
|
|
||||||
var apiClient = connectionManager.getApiClient(options.serverId);
|
var apiClient = connectionManager.getApiClient(options.serverId);
|
||||||
|
|
|
@ -1,96 +1,90 @@
|
||||||
// From https://github.com/parshap/node-sanitize-filename
|
// From https://github.com/parshap/node-sanitize-filename
|
||||||
|
|
||||||
define([], function () {
|
const illegalRe = /[\/\?<>\\:\*\|":]/g;
|
||||||
'use strict';
|
// eslint-disable-next-line no-control-regex
|
||||||
|
const controlRe = /[\x00-\x1f\x80-\x9f]/g;
|
||||||
|
const reservedRe = /^\.+$/;
|
||||||
|
const windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
||||||
|
const windowsTrailingRe = /[\. ]+$/;
|
||||||
|
|
||||||
var illegalRe = /[\/\?<>\\:\*\|":]/g;
|
function isHighSurrogate(codePoint) {
|
||||||
// eslint-disable-next-line no-control-regex
|
return codePoint >= 0xd800 && codePoint <= 0xdbff;
|
||||||
var controlRe = /[\x00-\x1f\x80-\x9f]/g;
|
}
|
||||||
var reservedRe = /^\.+$/;
|
|
||||||
var windowsReservedRe = /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(\..*)?$/i;
|
|
||||||
var windowsTrailingRe = /[\. ]+$/;
|
|
||||||
|
|
||||||
function isHighSurrogate(codePoint) {
|
function isLowSurrogate(codePoint) {
|
||||||
return codePoint >= 0xd800 && codePoint <= 0xdbff;
|
return codePoint >= 0xdc00 && codePoint <= 0xdfff;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getByteLength(string) {
|
||||||
|
if (typeof string !== "string") {
|
||||||
|
throw new Error("Input must be string");
|
||||||
}
|
}
|
||||||
|
|
||||||
function isLowSurrogate(codePoint) {
|
const charLength = string.length;
|
||||||
return codePoint >= 0xdc00 && codePoint <= 0xdfff;
|
let byteLength = 0;
|
||||||
}
|
let codePoint = null;
|
||||||
|
let prevCodePoint = null;
|
||||||
function getByteLength(string) {
|
for (let i = 0; i < charLength; i++) {
|
||||||
if (typeof string !== "string") {
|
codePoint = string.charCodeAt(i);
|
||||||
throw new Error("Input must be string");
|
// handle 4-byte non-BMP chars
|
||||||
}
|
// low surrogate
|
||||||
|
if (isLowSurrogate(codePoint)) {
|
||||||
var charLength = string.length;
|
// when parsing previous hi-surrogate, 3 is added to byteLength
|
||||||
var byteLength = 0;
|
if (prevCodePoint != null && isHighSurrogate(prevCodePoint)) {
|
||||||
var codePoint = null;
|
|
||||||
var prevCodePoint = null;
|
|
||||||
for (var i = 0; i < charLength; i++) {
|
|
||||||
codePoint = string.charCodeAt(i);
|
|
||||||
// handle 4-byte non-BMP chars
|
|
||||||
// low surrogate
|
|
||||||
if (isLowSurrogate(codePoint)) {
|
|
||||||
// when parsing previous hi-surrogate, 3 is added to byteLength
|
|
||||||
if (prevCodePoint != null && isHighSurrogate(prevCodePoint)) {
|
|
||||||
byteLength += 1;
|
|
||||||
} else {
|
|
||||||
byteLength += 3;
|
|
||||||
}
|
|
||||||
} else if (codePoint <= 0x7f) {
|
|
||||||
byteLength += 1;
|
byteLength += 1;
|
||||||
} else if (codePoint >= 0x80 && codePoint <= 0x7ff) {
|
} else {
|
||||||
byteLength += 2;
|
|
||||||
} else if (codePoint >= 0x800 && codePoint <= 0xffff) {
|
|
||||||
byteLength += 3;
|
byteLength += 3;
|
||||||
}
|
}
|
||||||
prevCodePoint = codePoint;
|
} else if (codePoint <= 0x7f) {
|
||||||
|
byteLength += 1;
|
||||||
|
} else if (codePoint >= 0x80 && codePoint <= 0x7ff) {
|
||||||
|
byteLength += 2;
|
||||||
|
} else if (codePoint >= 0x800 && codePoint <= 0xffff) {
|
||||||
|
byteLength += 3;
|
||||||
}
|
}
|
||||||
|
prevCodePoint = codePoint;
|
||||||
return byteLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function truncate(string, byteLength) {
|
return byteLength;
|
||||||
if (typeof string !== "string") {
|
}
|
||||||
throw new Error("Input must be string");
|
|
||||||
}
|
|
||||||
|
|
||||||
var charLength = string.length;
|
function truncate(string, byteLength) {
|
||||||
var curByteLength = 0;
|
if (typeof string !== "string") {
|
||||||
var codePoint;
|
throw new Error("Input must be string");
|
||||||
var segment;
|
|
||||||
|
|
||||||
for (var i = 0; i < charLength; i += 1) {
|
|
||||||
codePoint = string.charCodeAt(i);
|
|
||||||
segment = string[i];
|
|
||||||
|
|
||||||
if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) {
|
|
||||||
i += 1;
|
|
||||||
segment += string[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
curByteLength += getByteLength(segment);
|
|
||||||
|
|
||||||
if (curByteLength === byteLength) {
|
|
||||||
return string.slice(0, i + 1);
|
|
||||||
} else if (curByteLength > byteLength) {
|
|
||||||
return string.slice(0, i - segment.length + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
const charLength = string.length;
|
||||||
sanitize: function (input, replacement) {
|
let curByteLength = 0;
|
||||||
var sanitized = input
|
let codePoint;
|
||||||
.replace(illegalRe, replacement)
|
let segment;
|
||||||
.replace(controlRe, replacement)
|
|
||||||
.replace(reservedRe, replacement)
|
for (let i = 0; i < charLength; i += 1) {
|
||||||
.replace(windowsReservedRe, replacement)
|
codePoint = string.charCodeAt(i);
|
||||||
.replace(windowsTrailingRe, replacement);
|
segment = string[i];
|
||||||
return truncate(sanitized, 255);
|
|
||||||
|
if (isHighSurrogate(codePoint) && isLowSurrogate(string.charCodeAt(i + 1))) {
|
||||||
|
i += 1;
|
||||||
|
segment += string[i];
|
||||||
}
|
}
|
||||||
};
|
|
||||||
});
|
curByteLength += getByteLength(segment);
|
||||||
|
|
||||||
|
if (curByteLength === byteLength) {
|
||||||
|
return string.slice(0, i + 1);
|
||||||
|
} else if (curByteLength > byteLength) {
|
||||||
|
return string.slice(0, i - segment.length + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function sanitize(input, replacement) {
|
||||||
|
const sanitized = input
|
||||||
|
.replace(illegalRe, replacement)
|
||||||
|
.replace(controlRe, replacement)
|
||||||
|
.replace(reservedRe, replacement)
|
||||||
|
.replace(windowsReservedRe, replacement)
|
||||||
|
.replace(windowsTrailingRe, replacement);
|
||||||
|
return truncate(sanitized, 255);
|
||||||
|
}
|
||||||
|
|
|
@ -1,38 +1,46 @@
|
||||||
define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManager) {
|
/* eslint-disable indent */
|
||||||
"use strict";
|
|
||||||
|
/**
|
||||||
|
* Module for controlling scroll behavior.
|
||||||
|
* @module components/scrollManager
|
||||||
|
*/
|
||||||
|
|
||||||
|
import dom from "dom";
|
||||||
|
import browser from "browser";
|
||||||
|
import layoutManager from "layoutManager";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scroll time in ms.
|
* Scroll time in ms.
|
||||||
*/
|
*/
|
||||||
var ScrollTime = 270;
|
const ScrollTime = 270;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Epsilon for comparing values.
|
* Epsilon for comparing values.
|
||||||
*/
|
*/
|
||||||
var Epsilon = 1e-6;
|
const Epsilon = 1e-6;
|
||||||
|
|
||||||
// FIXME: Need to scroll to top of page to fully show the top menu. This can be solved by some marker of top most elements or their containers
|
// FIXME: Need to scroll to top of page to fully show the top menu. This can be solved by some marker of top most elements or their containers
|
||||||
/**
|
/**
|
||||||
* Returns minimum vertical scroll.
|
* Returns minimum vertical scroll.
|
||||||
* Scroll less than that value will be zeroed.
|
* Scroll less than that value will be zeroed.
|
||||||
*
|
*
|
||||||
* @return {number} minimum vertical scroll
|
* @return {number} Minimum vertical scroll.
|
||||||
*/
|
*/
|
||||||
function minimumScrollY() {
|
function minimumScrollY() {
|
||||||
var topMenu = document.querySelector(".headerTop");
|
const topMenu = document.querySelector(".headerTop");
|
||||||
if (topMenu) {
|
if (topMenu) {
|
||||||
return topMenu.clientHeight;
|
return topMenu.clientHeight;
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportsSmoothScroll = "scrollBehavior" in document.documentElement.style;
|
const supportsSmoothScroll = "scrollBehavior" in document.documentElement.style;
|
||||||
|
|
||||||
var supportsScrollToOptions = false;
|
let supportsScrollToOptions = false;
|
||||||
try {
|
try {
|
||||||
var elem = document.createElement("div");
|
const elem = document.createElement("div");
|
||||||
|
|
||||||
var opts = Object.defineProperty({}, "behavior", {
|
const opts = Object.defineProperty({}, "behavior", {
|
||||||
// eslint-disable-next-line getter-return
|
// eslint-disable-next-line getter-return
|
||||||
get: function () {
|
get: function () {
|
||||||
supportsScrollToOptions = true;
|
supportsScrollToOptions = true;
|
||||||
|
@ -47,10 +55,10 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Returns value clamped by range [min, max].
|
* Returns value clamped by range [min, max].
|
||||||
*
|
*
|
||||||
* @param {number} value clamped value
|
* @param {number} value - Clamped value.
|
||||||
* @param {number} min begining of range
|
* @param {number} min - Begining of range.
|
||||||
* @param {number} max ending of range
|
* @param {number} max - Ending of range.
|
||||||
* @return {number} clamped value
|
* @return {number} Clamped value.
|
||||||
*/
|
*/
|
||||||
function clamp(value, min, max) {
|
function clamp(value, min, max) {
|
||||||
return value <= min ? min : value >= max ? max : value;
|
return value <= min ? min : value >= max ? max : value;
|
||||||
|
@ -60,15 +68,15 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
* Returns the required delta to fit range 1 into range 2.
|
* Returns the required delta to fit range 1 into range 2.
|
||||||
* In case of range 1 is bigger than range 2 returns delta to fit most out of range part.
|
* In case of range 1 is bigger than range 2 returns delta to fit most out of range part.
|
||||||
*
|
*
|
||||||
* @param {number} begin1 begining of range 1
|
* @param {number} begin1 - Begining of range 1.
|
||||||
* @param {number} end1 ending of range 1
|
* @param {number} end1 - Ending of range 1.
|
||||||
* @param {number} begin2 begining of range 2
|
* @param {number} begin2 - Begining of range 2.
|
||||||
* @param {number} end2 ending of range 2
|
* @param {number} end2 - Ending of range 2.
|
||||||
* @return {number} delta: <0 move range1 to the left, >0 - to the right
|
* @return {number} Delta: <0 move range1 to the left, >0 - to the right.
|
||||||
*/
|
*/
|
||||||
function fitRange(begin1, end1, begin2, end2) {
|
function fitRange(begin1, end1, begin2, end2) {
|
||||||
var delta1 = begin1 - begin2;
|
const delta1 = begin1 - begin2;
|
||||||
var delta2 = end2 - end1;
|
const delta2 = end2 - end1;
|
||||||
if (delta1 < 0 && delta1 < delta2) {
|
if (delta1 < 0 && delta1 < delta2) {
|
||||||
return -delta1;
|
return -delta1;
|
||||||
} else if (delta2 < 0) {
|
} else if (delta2 < 0) {
|
||||||
|
@ -80,13 +88,21 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Ease value.
|
* Ease value.
|
||||||
*
|
*
|
||||||
* @param {number} t value in range [0, 1]
|
* @param {number} t - Value in range [0, 1].
|
||||||
* @return {number} eased value in range [0, 1]
|
* @return {number} Eased value in range [0, 1].
|
||||||
*/
|
*/
|
||||||
function ease(t) {
|
function ease(t) {
|
||||||
return t*(2 - t); // easeOutQuad === ease-out
|
return t*(2 - t); // easeOutQuad === ease-out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Rect
|
||||||
|
* @property {number} left - X coordinate of top-left corner.
|
||||||
|
* @property {number} top - Y coordinate of top-left corner.
|
||||||
|
* @property {number} width - Width.
|
||||||
|
* @property {number} height - Height.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Document scroll wrapper helps to unify scrolling and fix issues of some browsers.
|
* Document scroll wrapper helps to unify scrolling and fix issues of some browsers.
|
||||||
*
|
*
|
||||||
|
@ -100,41 +116,68 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
*
|
*
|
||||||
* Tizen 5 Browser/Native: scrolls documentElement (and window); has a document.scrollingElement
|
* Tizen 5 Browser/Native: scrolls documentElement (and window); has a document.scrollingElement
|
||||||
*/
|
*/
|
||||||
function DocumentScroller() {
|
class DocumentScroller {
|
||||||
}
|
/**
|
||||||
|
* Horizontal scroll position.
|
||||||
DocumentScroller.prototype = {
|
* @type {number}
|
||||||
|
*/
|
||||||
get scrollLeft() {
|
get scrollLeft() {
|
||||||
return window.pageXOffset;
|
return window.pageXOffset;
|
||||||
},
|
}
|
||||||
|
|
||||||
set scrollLeft(val) {
|
set scrollLeft(val) {
|
||||||
window.scroll(val, window.pageYOffset);
|
window.scroll(val, window.pageYOffset);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical scroll position.
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
get scrollTop() {
|
get scrollTop() {
|
||||||
return window.pageYOffset;
|
return window.pageYOffset;
|
||||||
},
|
}
|
||||||
|
|
||||||
set scrollTop(val) {
|
set scrollTop(val) {
|
||||||
window.scroll(window.pageXOffset, val);
|
window.scroll(window.pageXOffset, val);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal scroll size (scroll width).
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
get scrollWidth() {
|
get scrollWidth() {
|
||||||
return Math.max(document.documentElement.scrollWidth, document.body.scrollWidth);
|
return Math.max(document.documentElement.scrollWidth, document.body.scrollWidth);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical scroll size (scroll height).
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
get scrollHeight() {
|
get scrollHeight() {
|
||||||
return Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
|
return Math.max(document.documentElement.scrollHeight, document.body.scrollHeight);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal client size (client width).
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
get clientWidth() {
|
get clientWidth() {
|
||||||
return Math.min(document.documentElement.clientWidth, document.body.clientWidth);
|
return Math.min(document.documentElement.clientWidth, document.body.clientWidth);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertical client size (client height).
|
||||||
|
* @type {number}
|
||||||
|
*/
|
||||||
get clientHeight() {
|
get clientHeight() {
|
||||||
return Math.min(document.documentElement.clientHeight, document.body.clientHeight);
|
return Math.min(document.documentElement.clientHeight, document.body.clientHeight);
|
||||||
},
|
}
|
||||||
|
|
||||||
getBoundingClientRect: function() {
|
/**
|
||||||
|
* Returns bounding client rect.
|
||||||
|
* @return {Rect} Bounding client rect.
|
||||||
|
*/
|
||||||
|
getBoundingClientRect() {
|
||||||
// Make valid viewport coordinates: documentElement.getBoundingClientRect returns rect of entire document relative to viewport
|
// Make valid viewport coordinates: documentElement.getBoundingClientRect returns rect of entire document relative to viewport
|
||||||
return {
|
return {
|
||||||
left: 0,
|
left: 0,
|
||||||
|
@ -142,26 +185,34 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
width: this.clientWidth,
|
width: this.clientWidth,
|
||||||
height: this.clientHeight
|
height: this.clientHeight
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
scrollTo: function() {
|
/**
|
||||||
|
* Scrolls window.
|
||||||
|
* @param {...mixed} args See window.scrollTo.
|
||||||
|
*/
|
||||||
|
scrollTo() {
|
||||||
window.scrollTo.apply(window, arguments);
|
window.scrollTo.apply(window, arguments);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
var documentScroller = new DocumentScroller();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns parent element that can be scrolled. If no such, returns documentElement.
|
* Default (document) scroller.
|
||||||
|
*/
|
||||||
|
const documentScroller = new DocumentScroller();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns parent element that can be scrolled. If no such, returns document scroller.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} element element for which parent is being searched
|
* @param {HTMLElement} element - Element for which parent is being searched.
|
||||||
* @param {boolean} vertical search for vertical scrollable parent
|
* @param {boolean} vertical - Search for vertical scrollable parent.
|
||||||
|
* @param {HTMLElement|DocumentScroller} Parent element that can be scrolled or document scroller.
|
||||||
*/
|
*/
|
||||||
function getScrollableParent(element, vertical) {
|
function getScrollableParent(element, vertical) {
|
||||||
if (element) {
|
if (element) {
|
||||||
var nameScroll = "scrollWidth";
|
let nameScroll = "scrollWidth";
|
||||||
var nameClient = "clientWidth";
|
let nameClient = "clientWidth";
|
||||||
var nameClass = "scrollX";
|
let nameClass = "scrollX";
|
||||||
|
|
||||||
if (vertical) {
|
if (vertical) {
|
||||||
nameScroll = "scrollHeight";
|
nameScroll = "scrollHeight";
|
||||||
|
@ -169,7 +220,7 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
nameClass = "scrollY";
|
nameClass = "scrollY";
|
||||||
}
|
}
|
||||||
|
|
||||||
var parent = element.parentElement;
|
let parent = element.parentElement;
|
||||||
|
|
||||||
while (parent) {
|
while (parent) {
|
||||||
// Skip 'emby-scroller' because it scrolls by itself
|
// Skip 'emby-scroller' because it scrolls by itself
|
||||||
|
@ -187,20 +238,20 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} ScrollerData
|
* @typedef {Object} ScrollerData
|
||||||
* @property {number} scrollPos current scroll position
|
* @property {number} scrollPos - Current scroll position.
|
||||||
* @property {number} scrollSize scroll size
|
* @property {number} scrollSize - Scroll size.
|
||||||
* @property {number} clientSize client size
|
* @property {number} clientSize - Client size.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns scroll data for specified orientation.
|
* Returns scroller data for specified orientation.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} scroller scroller
|
* @param {HTMLElement} scroller - Scroller.
|
||||||
* @param {boolean} vertical vertical scroll data
|
* @param {boolean} vertical - Vertical scroller data.
|
||||||
* @return {ScrollerData} scroll data
|
* @return {ScrollerData} Scroller data.
|
||||||
*/
|
*/
|
||||||
function getScrollerData(scroller, vertical) {
|
function getScrollerData(scroller, vertical) {
|
||||||
var data = {};
|
let data = {};
|
||||||
|
|
||||||
if (!vertical) {
|
if (!vertical) {
|
||||||
data.scrollPos = scroller.scrollLeft;
|
data.scrollPos = scroller.scrollLeft;
|
||||||
|
@ -218,14 +269,14 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Returns position of child of scroller for specified orientation.
|
* Returns position of child of scroller for specified orientation.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} scroller scroller
|
* @param {HTMLElement} scroller - Scroller.
|
||||||
* @param {HTMLElement} element child of scroller
|
* @param {HTMLElement} element - Child of scroller.
|
||||||
* @param {boolean} vertical vertical scroll
|
* @param {boolean} vertical - Vertical scroll.
|
||||||
* @return {number} child position
|
* @return {number} Child position.
|
||||||
*/
|
*/
|
||||||
function getScrollerChildPos(scroller, element, vertical) {
|
function getScrollerChildPos(scroller, element, vertical) {
|
||||||
var elementRect = element.getBoundingClientRect();
|
const elementRect = element.getBoundingClientRect();
|
||||||
var scrollerRect = scroller.getBoundingClientRect();
|
const scrollerRect = scroller.getBoundingClientRect();
|
||||||
|
|
||||||
if (!vertical) {
|
if (!vertical) {
|
||||||
return scroller.scrollLeft + elementRect.left - scrollerRect.left;
|
return scroller.scrollLeft + elementRect.left - scrollerRect.left;
|
||||||
|
@ -237,21 +288,21 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Returns scroll position for element.
|
* Returns scroll position for element.
|
||||||
*
|
*
|
||||||
* @param {ScrollerData} scrollerData scroller data
|
* @param {ScrollerData} scrollerData - Scroller data.
|
||||||
* @param {number} elementPos child element position
|
* @param {number} elementPos - Child element position.
|
||||||
* @param {number} elementSize child element size
|
* @param {number} elementSize - Child element size.
|
||||||
* @param {boolean} centered scroll to center
|
* @param {boolean} centered - Scroll to center.
|
||||||
* @return {number} scroll position
|
* @return {number} Scroll position.
|
||||||
*/
|
*/
|
||||||
function calcScroll(scrollerData, elementPos, elementSize, centered) {
|
function calcScroll(scrollerData, elementPos, elementSize, centered) {
|
||||||
var maxScroll = scrollerData.scrollSize - scrollerData.clientSize;
|
const maxScroll = scrollerData.scrollSize - scrollerData.clientSize;
|
||||||
|
|
||||||
var scroll;
|
let scroll;
|
||||||
|
|
||||||
if (centered) {
|
if (centered) {
|
||||||
scroll = elementPos + (elementSize - scrollerData.clientSize) / 2;
|
scroll = elementPos + (elementSize - scrollerData.clientSize) / 2;
|
||||||
} else {
|
} else {
|
||||||
var delta = fitRange(elementPos, elementPos + elementSize - 1, scrollerData.scrollPos, scrollerData.scrollPos + scrollerData.clientSize - 1);
|
const delta = fitRange(elementPos, elementPos + elementSize - 1, scrollerData.scrollPos, scrollerData.scrollPos + scrollerData.clientSize - 1);
|
||||||
scroll = scrollerData.scrollPos - delta;
|
scroll = scrollerData.scrollPos - delta;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,14 +312,14 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Calls scrollTo function in proper way.
|
* Calls scrollTo function in proper way.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} scroller scroller
|
* @param {HTMLElement} scroller - Scroller.
|
||||||
* @param {ScrollToOptions} options scroll options
|
* @param {ScrollToOptions} options - Scroll options.
|
||||||
*/
|
*/
|
||||||
function scrollToHelper(scroller, options) {
|
function scrollToHelper(scroller, options) {
|
||||||
if ("scrollTo" in scroller) {
|
if ("scrollTo" in scroller) {
|
||||||
if (!supportsScrollToOptions) {
|
if (!supportsScrollToOptions) {
|
||||||
var scrollX = (options.left !== undefined ? options.left : scroller.scrollLeft);
|
const scrollX = (options.left !== undefined ? options.left : scroller.scrollLeft);
|
||||||
var scrollY = (options.top !== undefined ? options.top : scroller.scrollTop);
|
const scrollY = (options.top !== undefined ? options.top : scroller.scrollTop);
|
||||||
scroller.scrollTo(scrollX, scrollY);
|
scroller.scrollTo(scrollX, scrollY);
|
||||||
} else {
|
} else {
|
||||||
scroller.scrollTo(options);
|
scroller.scrollTo(options);
|
||||||
|
@ -286,14 +337,14 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Performs built-in scroll.
|
* Performs built-in scroll.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} xScroller horizontal scroller
|
* @param {HTMLElement} xScroller - Horizontal scroller.
|
||||||
* @param {number} scrollX horizontal coordinate
|
* @param {number} scrollX - Horizontal coordinate.
|
||||||
* @param {HTMLElement} yScroller vertical scroller
|
* @param {HTMLElement} yScroller - Vertical scroller.
|
||||||
* @param {number} scrollY vertical coordinate
|
* @param {number} scrollY - Vertical coordinate.
|
||||||
* @param {boolean} smooth smooth scrolling
|
* @param {boolean} smooth - Smooth scrolling.
|
||||||
*/
|
*/
|
||||||
function builtinScroll(xScroller, scrollX, yScroller, scrollY, smooth) {
|
function builtinScroll(xScroller, scrollX, yScroller, scrollY, smooth) {
|
||||||
var scrollBehavior = smooth ? "smooth" : "instant";
|
const scrollBehavior = smooth ? "smooth" : "instant";
|
||||||
|
|
||||||
if (xScroller !== yScroller) {
|
if (xScroller !== yScroller) {
|
||||||
scrollToHelper(xScroller, {left: scrollX, behavior: scrollBehavior});
|
scrollToHelper(xScroller, {left: scrollX, behavior: scrollBehavior});
|
||||||
|
@ -303,7 +354,10 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var scrollTimer;
|
/**
|
||||||
|
* Requested frame for animated scroll.
|
||||||
|
*/
|
||||||
|
let scrollTimer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets scroll timer to stop scrolling.
|
* Resets scroll timer to stop scrolling.
|
||||||
|
@ -316,29 +370,29 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Performs animated scroll.
|
* Performs animated scroll.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} xScroller horizontal scroller
|
* @param {HTMLElement} xScroller - Horizontal scroller.
|
||||||
* @param {number} scrollX horizontal coordinate
|
* @param {number} scrollX - Horizontal coordinate.
|
||||||
* @param {HTMLElement} yScroller vertical scroller
|
* @param {HTMLElement} yScroller - Vertical scroller.
|
||||||
* @param {number} scrollY vertical coordinate
|
* @param {number} scrollY - Vertical coordinate.
|
||||||
*/
|
*/
|
||||||
function animateScroll(xScroller, scrollX, yScroller, scrollY) {
|
function animateScroll(xScroller, scrollX, yScroller, scrollY) {
|
||||||
|
|
||||||
var ox = xScroller.scrollLeft;
|
const ox = xScroller.scrollLeft;
|
||||||
var oy = yScroller.scrollTop;
|
const oy = yScroller.scrollTop;
|
||||||
var dx = scrollX - ox;
|
const dx = scrollX - ox;
|
||||||
var dy = scrollY - oy;
|
const dy = scrollY - oy;
|
||||||
|
|
||||||
if (Math.abs(dx) < Epsilon && Math.abs(dy) < Epsilon) {
|
if (Math.abs(dx) < Epsilon && Math.abs(dy) < Epsilon) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var start;
|
let start;
|
||||||
|
|
||||||
function scrollAnim(currentTimestamp) {
|
function scrollAnim(currentTimestamp) {
|
||||||
|
|
||||||
start = start || currentTimestamp;
|
start = start || currentTimestamp;
|
||||||
|
|
||||||
var k = Math.min(1, (currentTimestamp - start) / ScrollTime);
|
let k = Math.min(1, (currentTimestamp - start) / ScrollTime);
|
||||||
|
|
||||||
if (k === 1) {
|
if (k === 1) {
|
||||||
resetScrollTimer();
|
resetScrollTimer();
|
||||||
|
@ -348,8 +402,8 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
|
|
||||||
k = ease(k);
|
k = ease(k);
|
||||||
|
|
||||||
var x = ox + dx*k;
|
const x = ox + dx*k;
|
||||||
var y = oy + dy*k;
|
const y = oy + dy*k;
|
||||||
|
|
||||||
builtinScroll(xScroller, x, yScroller, y, false);
|
builtinScroll(xScroller, x, yScroller, y, false);
|
||||||
|
|
||||||
|
@ -362,11 +416,11 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Performs scroll.
|
* Performs scroll.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} xScroller horizontal scroller
|
* @param {HTMLElement} xScroller - Horizontal scroller.
|
||||||
* @param {number} scrollX horizontal coordinate
|
* @param {number} scrollX - Horizontal coordinate.
|
||||||
* @param {HTMLElement} yScroller vertical scroller
|
* @param {HTMLElement} yScroller - Vertical scroller.
|
||||||
* @param {number} scrollY vertical coordinate
|
* @param {number} scrollY - Vertical coordinate.
|
||||||
* @param {boolean} smooth smooth scrolling
|
* @param {boolean} smooth - Smooth scrolling.
|
||||||
*/
|
*/
|
||||||
function doScroll(xScroller, scrollX, yScroller, scrollY, smooth) {
|
function doScroll(xScroller, scrollX, yScroller, scrollY, smooth) {
|
||||||
|
|
||||||
|
@ -403,26 +457,26 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Returns true if scroll manager is enabled.
|
* Returns true if scroll manager is enabled.
|
||||||
*/
|
*/
|
||||||
var isEnabled = function() {
|
export function isEnabled() {
|
||||||
return layoutManager.tv;
|
return layoutManager.tv;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Scrolls the document to a given position.
|
* Scrolls the document to a given position.
|
||||||
*
|
*
|
||||||
* @param {number} scrollX horizontal coordinate
|
* @param {number} scrollX - Horizontal coordinate.
|
||||||
* @param {number} scrollY vertical coordinate
|
* @param {number} scrollY - Vertical coordinate.
|
||||||
* @param {boolean} [smooth=false] smooth scrolling
|
* @param {boolean} [smooth=false] - Smooth scrolling.
|
||||||
*/
|
*/
|
||||||
var scrollTo = function(scrollX, scrollY, smooth) {
|
export function scrollTo(scrollX, scrollY, smooth) {
|
||||||
|
|
||||||
smooth = !!smooth;
|
smooth = !!smooth;
|
||||||
|
|
||||||
// Scroller is document itself by default
|
// Scroller is document itself by default
|
||||||
var scroller = getScrollableParent(null, false);
|
const scroller = getScrollableParent(null, false);
|
||||||
|
|
||||||
var xScrollerData = getScrollerData(scroller, false);
|
const xScrollerData = getScrollerData(scroller, false);
|
||||||
var yScrollerData = getScrollerData(scroller, true);
|
const yScrollerData = getScrollerData(scroller, true);
|
||||||
|
|
||||||
scrollX = clamp(Math.round(scrollX), 0, xScrollerData.scrollSize - xScrollerData.clientSize);
|
scrollX = clamp(Math.round(scrollX), 0, xScrollerData.scrollSize - xScrollerData.clientSize);
|
||||||
scrollY = clamp(Math.round(scrollY), 0, yScrollerData.scrollSize - yScrollerData.clientSize);
|
scrollY = clamp(Math.round(scrollY), 0, yScrollerData.scrollSize - yScrollerData.clientSize);
|
||||||
|
@ -433,39 +487,39 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
/**
|
/**
|
||||||
* Scrolls the document to a given element.
|
* Scrolls the document to a given element.
|
||||||
*
|
*
|
||||||
* @param {HTMLElement} element target element of scroll task
|
* @param {HTMLElement} element - Target element of scroll task.
|
||||||
* @param {boolean} [smooth=false] smooth scrolling
|
* @param {boolean} [smooth=false] - Smooth scrolling.
|
||||||
*/
|
*/
|
||||||
var scrollToElement = function(element, smooth) {
|
export function scrollToElement(element, smooth) {
|
||||||
|
|
||||||
smooth = !!smooth;
|
smooth = !!smooth;
|
||||||
|
|
||||||
var scrollCenterX = true;
|
let scrollCenterX = true;
|
||||||
var scrollCenterY = true;
|
let scrollCenterY = true;
|
||||||
|
|
||||||
var offsetParent = element.offsetParent;
|
const offsetParent = element.offsetParent;
|
||||||
|
|
||||||
// In Firefox offsetParent.offsetParent is BODY
|
// In Firefox offsetParent.offsetParent is BODY
|
||||||
var isFixed = offsetParent && (!offsetParent.offsetParent || window.getComputedStyle(offsetParent).position === "fixed");
|
const isFixed = offsetParent && (!offsetParent.offsetParent || window.getComputedStyle(offsetParent).position === "fixed");
|
||||||
|
|
||||||
// Scroll fixed elements to nearest edge (or do not scroll at all)
|
// Scroll fixed elements to nearest edge (or do not scroll at all)
|
||||||
if (isFixed) {
|
if (isFixed) {
|
||||||
scrollCenterX = scrollCenterY = false;
|
scrollCenterX = scrollCenterY = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var xScroller = getScrollableParent(element, false);
|
const xScroller = getScrollableParent(element, false);
|
||||||
var yScroller = getScrollableParent(element, true);
|
const yScroller = getScrollableParent(element, true);
|
||||||
|
|
||||||
var elementRect = element.getBoundingClientRect();
|
const elementRect = element.getBoundingClientRect();
|
||||||
|
|
||||||
var xScrollerData = getScrollerData(xScroller, false);
|
const xScrollerData = getScrollerData(xScroller, false);
|
||||||
var yScrollerData = getScrollerData(yScroller, true);
|
const yScrollerData = getScrollerData(yScroller, true);
|
||||||
|
|
||||||
var xPos = getScrollerChildPos(xScroller, element, false);
|
const xPos = getScrollerChildPos(xScroller, element, false);
|
||||||
var yPos = getScrollerChildPos(yScroller, element, true);
|
const yPos = getScrollerChildPos(yScroller, element, true);
|
||||||
|
|
||||||
var scrollX = calcScroll(xScrollerData, xPos, elementRect.width, scrollCenterX);
|
const scrollX = calcScroll(xScrollerData, xPos, elementRect.width, scrollCenterX);
|
||||||
var scrollY = calcScroll(yScrollerData, yPos, elementRect.height, scrollCenterY);
|
let scrollY = calcScroll(yScrollerData, yPos, elementRect.height, scrollCenterY);
|
||||||
|
|
||||||
// HACK: Scroll to top for top menu because it is hidden
|
// HACK: Scroll to top for top menu because it is hidden
|
||||||
// FIXME: Need a marker to scroll top/bottom
|
// FIXME: Need a marker to scroll top/bottom
|
||||||
|
@ -490,9 +544,10 @@ define(["dom", "browser", "layoutManager"], function (dom, browser, layoutManage
|
||||||
}, {capture: true});
|
}, {capture: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
/* eslint-enable indent */
|
||||||
isEnabled: isEnabled,
|
|
||||||
scrollTo: scrollTo,
|
export default {
|
||||||
scrollToElement: scrollToElement
|
isEnabled: isEnabled,
|
||||||
};
|
scrollTo: scrollTo,
|
||||||
});
|
scrollToElement: scrollToElement
|
||||||
|
};
|
||||||
|
|
|
@ -10,12 +10,6 @@ define([], function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
},
|
},
|
||||||
canExec: false,
|
|
||||||
exec: function (options) {
|
|
||||||
// options.path
|
|
||||||
// options.arguments
|
|
||||||
return Promise.reject();
|
|
||||||
},
|
|
||||||
enableFullscreen: function () {
|
enableFullscreen: function () {
|
||||||
if (window.NativeShell) {
|
if (window.NativeShell) {
|
||||||
window.NativeShell.enableFullscreen();
|
window.NativeShell.enableFullscreen();
|
||||||
|
|
|
@ -116,8 +116,8 @@ define(['apphost', 'userSettings', 'browser', 'events', 'pluginManager', 'backdr
|
||||||
|
|
||||||
var linkUrl = info.stylesheetPath;
|
var linkUrl = info.stylesheetPath;
|
||||||
unloadTheme();
|
unloadTheme();
|
||||||
var link = document.createElement('link');
|
|
||||||
|
|
||||||
|
var link = document.createElement('link');
|
||||||
link.setAttribute('rel', 'stylesheet');
|
link.setAttribute('rel', 'stylesheet');
|
||||||
link.setAttribute('type', 'text/css');
|
link.setAttribute('type', 'text/css');
|
||||||
link.onload = function () {
|
link.onload = function () {
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'loading', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost, loading) {
|
/**
|
||||||
|
* Image viewer component
|
||||||
|
* @module components/slideshow/slideshow
|
||||||
|
*/
|
||||||
|
define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves an item's image URL from the API.
|
||||||
|
* @param {object|string} item - Item used to generate the image URL.
|
||||||
|
* @param {object} options - Options of the image.
|
||||||
|
* @param {object} apiClient - API client instance used to retrieve the image.
|
||||||
|
* @returns {null|string} URL of the item's image.
|
||||||
|
*/
|
||||||
function getImageUrl(item, options, apiClient) {
|
function getImageUrl(item, options, apiClient) {
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.type = options.type || "Primary";
|
options.type = options.type || "Primary";
|
||||||
|
|
||||||
|
@ -11,7 +21,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.ImageTags && item.ImageTags[options.type]) {
|
if (item.ImageTags && item.ImageTags[options.type]) {
|
||||||
|
|
||||||
options.tag = item.ImageTags[options.type];
|
options.tag = item.ImageTags[options.type];
|
||||||
return apiClient.getScaledImageUrl(item.Id, options);
|
return apiClient.getScaledImageUrl(item.Id, options);
|
||||||
}
|
}
|
||||||
|
@ -27,8 +36,14 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves a backdrop's image URL from the API.
|
||||||
|
* @param {object} item - Item used to generate the image URL.
|
||||||
|
* @param {object} options - Options of the image.
|
||||||
|
* @param {object} apiClient - API client instance used to retrieve the image.
|
||||||
|
* @returns {null|string} URL of the item's backdrop.
|
||||||
|
*/
|
||||||
function getBackdropImageUrl(item, options, apiClient) {
|
function getBackdropImageUrl(item, options, apiClient) {
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.type = options.type || "Backdrop";
|
options.type = options.type || "Backdrop";
|
||||||
|
|
||||||
|
@ -46,19 +61,19 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getImgUrl(item, original) {
|
/**
|
||||||
|
* Dispatches a request for an item's image to its respective handler.
|
||||||
|
* @param {object} item - Item used to generate the image URL.
|
||||||
|
* @returns {string} URL of the item's image.
|
||||||
|
*/
|
||||||
|
function getImgUrl(item) {
|
||||||
var apiClient = connectionManager.getApiClient(item.ServerId);
|
var apiClient = connectionManager.getApiClient(item.ServerId);
|
||||||
var imageOptions = {};
|
var imageOptions = {};
|
||||||
|
|
||||||
if (!original) {
|
|
||||||
imageOptions.maxWidth = screen.availWidth;
|
|
||||||
}
|
|
||||||
if (item.BackdropImageTags && item.BackdropImageTags.length) {
|
if (item.BackdropImageTags && item.BackdropImageTags.length) {
|
||||||
return getBackdropImageUrl(item, imageOptions, apiClient);
|
return getBackdropImageUrl(item, imageOptions, apiClient);
|
||||||
} else {
|
} else {
|
||||||
|
if (item.MediaType === 'Photo') {
|
||||||
if (item.MediaType === 'Photo' && original) {
|
|
||||||
return apiClient.getItemDownloadUrl(item.Id);
|
return apiClient.getItemDownloadUrl(item.Id);
|
||||||
}
|
}
|
||||||
imageOptions.type = "Primary";
|
imageOptions.type = "Primary";
|
||||||
|
@ -66,15 +81,25 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a button using the specified icon, classes and properties.
|
||||||
|
* @param {string} icon - Name of the material icon on the button
|
||||||
|
* @param {string} cssClass - CSS classes to assign to the button
|
||||||
|
* @param {boolean} canFocus - Flag to set the tabindex attribute on the button to -1.
|
||||||
|
* @param {boolean} autoFocus - Flag to set the autofocus attribute on the button.
|
||||||
|
* @returns {string} The HTML markup of the button.
|
||||||
|
*/
|
||||||
function getIcon(icon, cssClass, canFocus, autoFocus) {
|
function getIcon(icon, cssClass, canFocus, autoFocus) {
|
||||||
|
|
||||||
var tabIndex = canFocus ? '' : ' tabindex="-1"';
|
var tabIndex = canFocus ? '' : ' tabindex="-1"';
|
||||||
autoFocus = autoFocus ? ' autofocus' : '';
|
autoFocus = autoFocus ? ' autofocus' : '';
|
||||||
return '<button is="paper-icon-button-light" class="autoSize ' + cssClass + '"' + tabIndex + autoFocus + '><i class="material-icons slideshowButtonIcon ' + icon + '"></i></button>';
|
return '<button is="paper-icon-button-light" class="autoSize ' + cssClass + '"' + tabIndex + autoFocus + '><i class="material-icons slideshowButtonIcon ' + icon + '"></i></button>';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the viewport meta tag to enable or disable scaling by the user.
|
||||||
|
* @param {boolean} scalable - Flag to set the scalability of the viewport.
|
||||||
|
*/
|
||||||
function setUserScalable(scalable) {
|
function setUserScalable(scalable) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
appHost.setUserScalable(scalable);
|
appHost.setUserScalable(scalable);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -83,23 +108,31 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
}
|
}
|
||||||
|
|
||||||
return function (options) {
|
return function (options) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
/** Initialized instance of Swiper. */
|
||||||
var swiperInstance;
|
var swiperInstance;
|
||||||
var dlg;
|
/** Initialized instance of the dialog containing the Swiper instance. */
|
||||||
var currentTimeout;
|
var dialog;
|
||||||
var currentIntervalMs;
|
/** Options of the slideshow components */
|
||||||
var currentOptions;
|
var currentOptions;
|
||||||
var currentIndex;
|
/** ID of the timeout used to hide the OSD. */
|
||||||
|
var hideTimeout;
|
||||||
|
/** Last coordinates of the mouse pointer. */
|
||||||
|
var lastMouseMoveData;
|
||||||
|
/** Visibility status of the OSD. */
|
||||||
|
var _osdOpen = false;
|
||||||
|
|
||||||
// small hack since this is not possible anyway
|
// Use autoplay on Chromecast since it is non-interactive.
|
||||||
if (browser.chromecast) {
|
options.interactive = !browser.chromecast;
|
||||||
options.interactive = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the HTML markup for the dialog and the OSD.
|
||||||
|
* @param {Object} options - Options used to create the dialog and slideshow.
|
||||||
|
*/
|
||||||
function createElements(options) {
|
function createElements(options) {
|
||||||
|
currentOptions = options;
|
||||||
|
|
||||||
dlg = dialogHelper.createDialog({
|
dialog = dialogHelper.createDialog({
|
||||||
exitAnimationDuration: options.interactive ? 400 : 800,
|
exitAnimationDuration: options.interactive ? 400 : 800,
|
||||||
size: 'fullscreen',
|
size: 'fullscreen',
|
||||||
autoFocus: false,
|
autoFocus: false,
|
||||||
|
@ -108,17 +141,15 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
removeOnClose: true
|
removeOnClose: true
|
||||||
});
|
});
|
||||||
|
|
||||||
dlg.classList.add('slideshowDialog');
|
dialog.classList.add('slideshowDialog');
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
|
|
||||||
if (options.interactive) {
|
html += '<div class="slideshowSwiperContainer"><div class="swiper-wrapper"></div></div>';
|
||||||
|
|
||||||
|
if (options.interactive && !layoutManager.tv) {
|
||||||
var actionButtonsOnTop = layoutManager.mobile;
|
var actionButtonsOnTop = layoutManager.mobile;
|
||||||
|
|
||||||
html += '<div>';
|
|
||||||
html += '<div class="slideshowSwiperContainer"><div class="swiper-wrapper"></div></div>';
|
|
||||||
|
|
||||||
html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false);
|
html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false);
|
||||||
html += getIcon('keyboard_arrow_right', 'btnSlideshowNext slideshowButton hide-mouse-idle-tv', false);
|
html += getIcon('keyboard_arrow_right', 'btnSlideshowNext slideshowButton hide-mouse-idle-tv', false);
|
||||||
|
|
||||||
|
@ -137,7 +168,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
if (!actionButtonsOnTop) {
|
if (!actionButtonsOnTop) {
|
||||||
html += '<div class="slideshowBottomBar hide">';
|
html += '<div class="slideshowBottomBar hide">';
|
||||||
|
|
||||||
html += getIcon('pause', 'btnSlideshowPause slideshowButton', true, true);
|
html += getIcon('play_arrow', 'btnSlideshowPause slideshowButton', true, true);
|
||||||
if (appHost.supports('filedownload')) {
|
if (appHost.supports('filedownload')) {
|
||||||
html += getIcon('file_download', 'btnDownload slideshowButton', true);
|
html += getIcon('file_download', 'btnDownload slideshowButton', true);
|
||||||
}
|
}
|
||||||
|
@ -148,33 +179,28 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '</div>';
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
html += '<div class="slideshowImage"></div><h1 class="slideshowImageText"></h1>';
|
html += '<div class="slideshowImage"></div><h1 class="slideshowImageText"></h1>';
|
||||||
}
|
}
|
||||||
|
|
||||||
dlg.innerHTML = html;
|
dialog.innerHTML = html;
|
||||||
|
|
||||||
if (options.interactive) {
|
if (options.interactive && !layoutManager.tv) {
|
||||||
dlg.querySelector('.btnSlideshowExit').addEventListener('click', function (e) {
|
dialog.querySelector('.btnSlideshowExit').addEventListener('click', function (e) {
|
||||||
|
dialogHelper.close(dialog);
|
||||||
dialogHelper.close(dlg);
|
|
||||||
});
|
});
|
||||||
dlg.querySelector('.btnSlideshowNext').addEventListener('click', nextImage);
|
|
||||||
dlg.querySelector('.btnSlideshowPrevious').addEventListener('click', previousImage);
|
|
||||||
|
|
||||||
var btnPause = dlg.querySelector('.btnSlideshowPause');
|
var btnPause = dialog.querySelector('.btnSlideshowPause');
|
||||||
if (btnPause) {
|
if (btnPause) {
|
||||||
btnPause.addEventListener('click', playPause);
|
btnPause.addEventListener('click', playPause);
|
||||||
}
|
}
|
||||||
|
|
||||||
var btnDownload = dlg.querySelector('.btnDownload');
|
var btnDownload = dialog.querySelector('.btnDownload');
|
||||||
if (btnDownload) {
|
if (btnDownload) {
|
||||||
btnDownload.addEventListener('click', download);
|
btnDownload.addEventListener('click', download);
|
||||||
}
|
}
|
||||||
|
|
||||||
var btnShare = dlg.querySelector('.btnShare');
|
var btnShare = dialog.querySelector('.btnShare');
|
||||||
if (btnShare) {
|
if (btnShare) {
|
||||||
btnShare.addEventListener('click', share);
|
btnShare.addEventListener('click', share);
|
||||||
}
|
}
|
||||||
|
@ -182,78 +208,104 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
|
|
||||||
setUserScalable(true);
|
setUserScalable(true);
|
||||||
|
|
||||||
dialogHelper.open(dlg).then(function () {
|
dialogHelper.open(dialog).then(function () {
|
||||||
|
|
||||||
setUserScalable(false);
|
setUserScalable(false);
|
||||||
stopInterval();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
inputManager.on(window, onInputCommand);
|
inputManager.on(window, onInputCommand);
|
||||||
document.addEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove);
|
document.addEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove);
|
||||||
|
|
||||||
dlg.addEventListener('close', onDialogClosed);
|
dialog.addEventListener('close', onDialogClosed);
|
||||||
|
|
||||||
if (options.interactive) {
|
loadSwiper(dialog, options);
|
||||||
loadSwiper(dlg);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles OSD changes when the autoplay is started.
|
||||||
|
*/
|
||||||
function onAutoplayStart() {
|
function onAutoplayStart() {
|
||||||
var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i');
|
var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause i');
|
||||||
if (btnSlideshowPause) {
|
if (btnSlideshowPause) {
|
||||||
btnSlideshowPause.classList.remove("play_arrow");
|
btnSlideshowPause.classList.replace("play_arrow", "pause");
|
||||||
btnSlideshowPause.classList.add("pause");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles OSD changes when the autoplay is stopped.
|
||||||
|
*/
|
||||||
function onAutoplayStop() {
|
function onAutoplayStop() {
|
||||||
var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i');
|
var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause i');
|
||||||
if (btnSlideshowPause) {
|
if (btnSlideshowPause) {
|
||||||
btnSlideshowPause.classList.remove("pause");
|
btnSlideshowPause.classList.replace("pause", "play_arrow");
|
||||||
btnSlideshowPause.classList.add("play_arrow");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadSwiper(dlg) {
|
/**
|
||||||
|
* Initializes the Swiper instance and binds the relevant events.
|
||||||
|
* @param {HTMLElement} dialog - Element containing the dialog.
|
||||||
|
* @param {Object} options - Options used to initialize the Swiper instance.
|
||||||
|
*/
|
||||||
|
function loadSwiper(dialog, options) {
|
||||||
|
var slides;
|
||||||
if (currentOptions.slides) {
|
if (currentOptions.slides) {
|
||||||
dlg.querySelector('.swiper-wrapper').innerHTML = currentOptions.slides.map(getSwiperSlideHtmlFromSlide).join('');
|
slides = currentOptions.slides;
|
||||||
} else {
|
} else {
|
||||||
dlg.querySelector('.swiper-wrapper').innerHTML = currentOptions.items.map(getSwiperSlideHtmlFromItem).join('');
|
slides = currentOptions.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
require(['swiper'], function (Swiper) {
|
require(['swiper'], function (Swiper) {
|
||||||
|
swiperInstance = new Swiper(dialog.querySelector('.slideshowSwiperContainer'), {
|
||||||
swiperInstance = new Swiper(dlg.querySelector('.slideshowSwiperContainer'), {
|
|
||||||
// Optional parameters
|
|
||||||
direction: 'horizontal',
|
direction: 'horizontal',
|
||||||
loop: options.loop !== false,
|
// Loop is disabled due to the virtual slides option not supporting it.
|
||||||
autoplay: {
|
loop: false,
|
||||||
delay: options.interval || 8000
|
autoplay: !options.interactive,
|
||||||
|
keyboard: {
|
||||||
|
enabled: true
|
||||||
},
|
},
|
||||||
// Disable preloading of all images
|
preloadImages: true,
|
||||||
preloadImages: false,
|
slidesPerView: 1,
|
||||||
// Enable lazy loading
|
slidesPerColumn: 1,
|
||||||
lazy: true,
|
|
||||||
loadPrevNext: true,
|
|
||||||
disableOnInteraction: false,
|
|
||||||
initialSlide: options.startIndex || 0,
|
initialSlide: options.startIndex || 0,
|
||||||
speed: 240
|
speed: 240,
|
||||||
|
navigation: {
|
||||||
|
nextEl: '.btnSlideshowNext',
|
||||||
|
prevEl: '.btnSlideshowPrevious'
|
||||||
|
},
|
||||||
|
// Virtual slides reduce memory consumption for large libraries while allowing preloading of images;
|
||||||
|
virtual: {
|
||||||
|
slides: slides,
|
||||||
|
cache: true,
|
||||||
|
renderSlide: getSwiperSlideHtml,
|
||||||
|
addSlidesBefore: 1,
|
||||||
|
addSlidesAfter: 1
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
swiperInstance.on('autoplayStart', onAutoplayStart);
|
swiperInstance.on('autoplayStart', onAutoplayStart);
|
||||||
swiperInstance.on('autoplayStop', onAutoplayStop);
|
swiperInstance.on('autoplayStop', onAutoplayStop);
|
||||||
|
|
||||||
if (layoutManager.mobile) {
|
|
||||||
pause();
|
|
||||||
} else {
|
|
||||||
play();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSwiperSlideHtmlFromItem(item) {
|
/**
|
||||||
|
* Renders the HTML markup of a slide for an item or a slide.
|
||||||
|
* @param {Object} item - The item used to render the slide.
|
||||||
|
* @param {number} index - The index of the item in the Swiper instance.
|
||||||
|
* @returns {string} The HTML markup of the slide.
|
||||||
|
*/
|
||||||
|
function getSwiperSlideHtml(item, index) {
|
||||||
|
if (currentOptions.slides) {
|
||||||
|
return getSwiperSlideHtmlFromSlide(item);
|
||||||
|
} else {
|
||||||
|
return getSwiperSlideHtmlFromItem(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the HTML markup of a slide for an item.
|
||||||
|
* @param {Object} item - Item used to generate the slide.
|
||||||
|
* @returns {string} The HTML markup of the slide.
|
||||||
|
*/
|
||||||
|
function getSwiperSlideHtmlFromItem(item) {
|
||||||
return getSwiperSlideHtmlFromSlide({
|
return getSwiperSlideHtmlFromSlide({
|
||||||
imageUrl: getImgUrl(item),
|
imageUrl: getImgUrl(item),
|
||||||
originalImage: getImgUrl(item, true),
|
originalImage: getImgUrl(item, true),
|
||||||
|
@ -264,11 +316,17 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the HTML markup of a slide for a slide object.
|
||||||
|
* @param {Object} item - Slide object used to generate the slide.
|
||||||
|
* @returns {string} The HTML markup of the slide.
|
||||||
|
*/
|
||||||
function getSwiperSlideHtmlFromSlide(item) {
|
function getSwiperSlideHtmlFromSlide(item) {
|
||||||
|
|
||||||
var html = '';
|
var html = '';
|
||||||
html += '<div class="swiper-slide" data-imageurl="' + item.imageUrl + '" data-original="' + item.originalImage + '" data-itemid="' + item.Id + '" data-serverid="' + item.ServerId + '">';
|
html += '<div class="swiper-slide" data-original="' + item.originalImage + '" data-itemid="' + item.Id + '" data-serverid="' + item.ServerId + '">';
|
||||||
html += '<img data-src="' + item.imageUrl + '" class="swiper-lazy swiper-slide-img">';
|
html += '<div class="slider-zoom-container">';
|
||||||
|
html += '<img src="' + item.originalImage + '" class="swiper-slide-img">';
|
||||||
|
html += '</div>';
|
||||||
if (item.title || item.subtitle) {
|
if (item.title || item.subtitle) {
|
||||||
html += '<div class="slideText">';
|
html += '<div class="slideText">';
|
||||||
html += '<div class="slideTextInner">';
|
html += '<div class="slideTextInner">';
|
||||||
|
@ -290,42 +348,18 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
function previousImage() {
|
/**
|
||||||
if (swiperInstance) {
|
* Fetches the information of the currently displayed slide.
|
||||||
swiperInstance.slidePrev();
|
* @returns {null|{itemId: string, shareUrl: string, serverId: string, url: string}} Object containing the information of the currently displayed slide.
|
||||||
} else {
|
*/
|
||||||
stopInterval();
|
|
||||||
showNextImage(currentIndex - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextImage() {
|
|
||||||
if (swiperInstance) {
|
|
||||||
|
|
||||||
if (options.loop === false) {
|
|
||||||
|
|
||||||
if (swiperInstance.activeIndex >= swiperInstance.slides.length - 1) {
|
|
||||||
dialogHelper.close(dlg);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
swiperInstance.slideNext();
|
|
||||||
} else {
|
|
||||||
stopInterval();
|
|
||||||
showNextImage(currentIndex + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getCurrentImageInfo() {
|
function getCurrentImageInfo() {
|
||||||
|
|
||||||
if (swiperInstance) {
|
if (swiperInstance) {
|
||||||
var slide = document.querySelector('.swiper-slide-active');
|
var slide = document.querySelector('.swiper-slide-active');
|
||||||
|
|
||||||
if (slide) {
|
if (slide) {
|
||||||
return {
|
return {
|
||||||
url: slide.getAttribute('data-original'),
|
url: slide.getAttribute('data-original'),
|
||||||
shareUrl: slide.getAttribute('data-imageurl'),
|
shareUrl: slide.getAttribute('data-original'),
|
||||||
itemId: slide.getAttribute('data-itemid'),
|
itemId: slide.getAttribute('data-itemid'),
|
||||||
serverId: slide.getAttribute('data-serverid')
|
serverId: slide.getAttribute('data-serverid')
|
||||||
};
|
};
|
||||||
|
@ -336,8 +370,10 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts a download for the currently displayed slide.
|
||||||
|
*/
|
||||||
function download() {
|
function download() {
|
||||||
|
|
||||||
var imageInfo = getCurrentImageInfo();
|
var imageInfo = getCurrentImageInfo();
|
||||||
|
|
||||||
require(['fileDownloader'], function (fileDownloader) {
|
require(['fileDownloader'], function (fileDownloader) {
|
||||||
|
@ -345,8 +381,10 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shares the currently displayed slide using the browser's built-in sharing feature.
|
||||||
|
*/
|
||||||
function share() {
|
function share() {
|
||||||
|
|
||||||
var imageInfo = getCurrentImageInfo();
|
var imageInfo = getCurrentImageInfo();
|
||||||
|
|
||||||
navigator.share({
|
navigator.share({
|
||||||
|
@ -354,20 +392,29 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the autoplay feature of the Swiper instance.
|
||||||
|
*/
|
||||||
function play() {
|
function play() {
|
||||||
if (swiperInstance.autoplay) {
|
if (swiperInstance.autoplay) {
|
||||||
swiperInstance.autoplay.start();
|
swiperInstance.autoplay.start();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pauses the autoplay feature of the Swiper instance;
|
||||||
|
*/
|
||||||
function pause() {
|
function pause() {
|
||||||
if (swiperInstance.autoplay) {
|
if (swiperInstance.autoplay) {
|
||||||
swiperInstance.autoplay.stop();
|
swiperInstance.autoplay.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggles the autoplay feature of the Swiper instance.
|
||||||
|
*/
|
||||||
function playPause() {
|
function playPause() {
|
||||||
var paused = !dlg.querySelector('.btnSlideshowPause i').classList.contains("pause");
|
var paused = !dialog.querySelector('.btnSlideshowPause i').classList.contains("pause");
|
||||||
if (paused) {
|
if (paused) {
|
||||||
play();
|
play();
|
||||||
} else {
|
} else {
|
||||||
|
@ -375,8 +422,10 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes the dialog and destroys the Swiper instance.
|
||||||
|
*/
|
||||||
function onDialogClosed() {
|
function onDialogClosed() {
|
||||||
|
|
||||||
var swiper = swiperInstance;
|
var swiper = swiperInstance;
|
||||||
if (swiper) {
|
if (swiper) {
|
||||||
swiper.destroy(true, true);
|
swiper.destroy(true, true);
|
||||||
|
@ -387,53 +436,38 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove);
|
document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
function startInterval(options) {
|
/**
|
||||||
|
* Shows the OSD.
|
||||||
currentOptions = options;
|
*/
|
||||||
|
|
||||||
stopInterval();
|
|
||||||
createElements(options);
|
|
||||||
|
|
||||||
if (!options.interactive) {
|
|
||||||
currentIntervalMs = options.interval || 11000;
|
|
||||||
showNextImage(options.startIndex || 0, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var _osdOpen = false;
|
|
||||||
|
|
||||||
function isOsdOpen() {
|
|
||||||
return _osdOpen;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getOsdBottom() {
|
|
||||||
return dlg.querySelector('.slideshowBottomBar');
|
|
||||||
}
|
|
||||||
|
|
||||||
function showOsd() {
|
function showOsd() {
|
||||||
|
var bottom = dialog.querySelector('.slideshowBottomBar');
|
||||||
var bottom = getOsdBottom();
|
|
||||||
if (bottom) {
|
if (bottom) {
|
||||||
slideUpToShow(bottom);
|
slideUpToShow(bottom);
|
||||||
startHideTimer();
|
startHideTimer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the OSD.
|
||||||
|
*/
|
||||||
function hideOsd() {
|
function hideOsd() {
|
||||||
|
var bottom = dialog.querySelector('.slideshowBottomBar');
|
||||||
var bottom = getOsdBottom();
|
|
||||||
if (bottom) {
|
if (bottom) {
|
||||||
slideDownToHide(bottom);
|
slideDownToHide(bottom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var hideTimeout;
|
/**
|
||||||
|
* Starts the timer used to automatically hide the OSD.
|
||||||
|
*/
|
||||||
function startHideTimer() {
|
function startHideTimer() {
|
||||||
stopHideTimer();
|
stopHideTimer();
|
||||||
hideTimeout = setTimeout(hideOsd, 4000);
|
hideTimeout = setTimeout(hideOsd, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stops the timer used to automatically hide the OSD.
|
||||||
|
*/
|
||||||
function stopHideTimer() {
|
function stopHideTimer() {
|
||||||
if (hideTimeout) {
|
if (hideTimeout) {
|
||||||
clearTimeout(hideTimeout);
|
clearTimeout(hideTimeout);
|
||||||
|
@ -441,71 +475,76 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function slideUpToShow(elem) {
|
/**
|
||||||
|
* Shows the OSD by sliding it into view.
|
||||||
if (!elem.classList.contains('hide')) {
|
* @param {HTMLElement} element - Element containing the OSD.
|
||||||
|
*/
|
||||||
|
function slideUpToShow(element) {
|
||||||
|
if (!element.classList.contains('hide')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_osdOpen = true;
|
_osdOpen = true;
|
||||||
elem.classList.remove('hide');
|
element.classList.remove('hide');
|
||||||
|
|
||||||
var onFinish = function () {
|
var onFinish = function () {
|
||||||
focusManager.focus(elem.querySelector('.btnSlideshowPause'));
|
focusManager.focus(element.querySelector('.btnSlideshowPause'));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!elem.animate) {
|
if (!element.animate) {
|
||||||
onFinish();
|
onFinish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
|
|
||||||
var keyframes = [
|
var keyframes = [
|
||||||
{ transform: 'translate3d(0,' + elem.offsetHeight + 'px,0)', opacity: '.3', offset: 0 },
|
{ transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 0 },
|
||||||
{ transform: 'translate3d(0,0,0)', opacity: '1', offset: 1 }
|
{ transform: 'translate3d(0,0,0)', opacity: '1', offset: 1 }
|
||||||
];
|
];
|
||||||
var timing = { duration: 300, iterations: 1, easing: 'ease-out' };
|
var timing = { duration: 300, iterations: 1, easing: 'ease-out' };
|
||||||
elem.animate(keyframes, timing).onfinish = onFinish;
|
element.animate(keyframes, timing).onfinish = onFinish;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function slideDownToHide(elem) {
|
/**
|
||||||
|
* Hides the OSD by sliding it out of view.
|
||||||
if (elem.classList.contains('hide')) {
|
* @param {HTMLElement} element - Element containing the OSD.
|
||||||
|
*/
|
||||||
|
function slideDownToHide(element) {
|
||||||
|
if (element.classList.contains('hide')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var onFinish = function () {
|
var onFinish = function () {
|
||||||
elem.classList.add('hide');
|
element.classList.add('hide');
|
||||||
_osdOpen = false;
|
_osdOpen = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!elem.animate) {
|
if (!element.animate) {
|
||||||
onFinish();
|
onFinish();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
requestAnimationFrame(function () {
|
requestAnimationFrame(function () {
|
||||||
|
|
||||||
var keyframes = [
|
var keyframes = [
|
||||||
{ transform: 'translate3d(0,0,0)', opacity: '1', offset: 0 },
|
{ transform: 'translate3d(0,0,0)', opacity: '1', offset: 0 },
|
||||||
{ transform: 'translate3d(0,' + elem.offsetHeight + 'px,0)', opacity: '.3', offset: 1 }
|
{ transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 1 }
|
||||||
];
|
];
|
||||||
var timing = { duration: 300, iterations: 1, easing: 'ease-out' };
|
var timing = { duration: 300, iterations: 1, easing: 'ease-out' };
|
||||||
elem.animate(keyframes, timing).onfinish = onFinish;
|
element.animate(keyframes, timing).onfinish = onFinish;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
var lastMouseMoveData;
|
/**
|
||||||
|
* Shows the OSD when moving the mouse pointer or touching the screen.
|
||||||
function onPointerMove(e) {
|
* @param {Event} event - Pointer movement event.
|
||||||
|
*/
|
||||||
var pointerType = e.pointerType || (layoutManager.mobile ? 'touch' : 'mouse');
|
function onPointerMove(event) {
|
||||||
|
var pointerType = event.pointerType || (layoutManager.mobile ? 'touch' : 'mouse');
|
||||||
|
|
||||||
if (pointerType === 'mouse') {
|
if (pointerType === 'mouse') {
|
||||||
var eventX = e.screenX || 0;
|
var eventX = event.screenX || 0;
|
||||||
var eventY = e.screenY || 0;
|
var eventY = event.screenY || 0;
|
||||||
|
|
||||||
var obj = lastMouseMoveData;
|
var obj = lastMouseMoveData;
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
|
@ -528,125 +567,46 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onInputCommand(e) {
|
/**
|
||||||
|
* Dispatches keyboard inputs to their proper handlers.
|
||||||
switch (e.detail.command) {
|
* @param {Event} event - Keyboard input event.
|
||||||
|
*/
|
||||||
case 'left':
|
function onInputCommand(event) {
|
||||||
if (!isOsdOpen()) {
|
switch (event.detail.command) {
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
previousImage();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'right':
|
|
||||||
if (!isOsdOpen()) {
|
|
||||||
e.preventDefault();
|
|
||||||
e.stopPropagation();
|
|
||||||
nextImage();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'up':
|
case 'up':
|
||||||
case 'down':
|
case 'down':
|
||||||
case 'select':
|
case 'select':
|
||||||
case 'menu':
|
case 'menu':
|
||||||
case 'info':
|
case 'info':
|
||||||
case 'play':
|
|
||||||
case 'playpause':
|
|
||||||
case 'pause':
|
|
||||||
showOsd();
|
showOsd();
|
||||||
break;
|
break;
|
||||||
|
case 'play':
|
||||||
|
play();
|
||||||
|
break;
|
||||||
|
case 'pause':
|
||||||
|
pause();
|
||||||
|
break;
|
||||||
|
case 'playpause':
|
||||||
|
playPause();
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function showNextImage(index, skipPreload) {
|
/**
|
||||||
|
* Shows the slideshow component.
|
||||||
index = Math.max(0, index);
|
*/
|
||||||
if (index >= currentOptions.items.length) {
|
|
||||||
index = 0;
|
|
||||||
}
|
|
||||||
currentIndex = index;
|
|
||||||
|
|
||||||
var options = currentOptions;
|
|
||||||
var items = options.items;
|
|
||||||
var item = items[index];
|
|
||||||
var imgUrl = getImgUrl(item);
|
|
||||||
|
|
||||||
var onSrcLoaded = function () {
|
|
||||||
var cardImageContainer = dlg.querySelector('.slideshowImage');
|
|
||||||
|
|
||||||
var newCardImageContainer = document.createElement('div');
|
|
||||||
newCardImageContainer.className = cardImageContainer.className;
|
|
||||||
|
|
||||||
if (options.cover) {
|
|
||||||
newCardImageContainer.classList.add('slideshowImage-cover');
|
|
||||||
}
|
|
||||||
|
|
||||||
newCardImageContainer.style.backgroundImage = "url('" + imgUrl + "')";
|
|
||||||
newCardImageContainer.classList.add('hide');
|
|
||||||
cardImageContainer.parentNode.appendChild(newCardImageContainer);
|
|
||||||
|
|
||||||
if (options.showTitle) {
|
|
||||||
dlg.querySelector('.slideshowImageText').innerHTML = item.Name;
|
|
||||||
} else {
|
|
||||||
dlg.querySelector('.slideshowImageText').innerHTML = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
newCardImageContainer.classList.remove('hide');
|
|
||||||
var onAnimationFinished = function () {
|
|
||||||
|
|
||||||
var parentNode = cardImageContainer.parentNode;
|
|
||||||
if (parentNode) {
|
|
||||||
parentNode.removeChild(cardImageContainer);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (newCardImageContainer.animate) {
|
|
||||||
|
|
||||||
var keyframes = [
|
|
||||||
{ opacity: '0', offset: 0 },
|
|
||||||
{ opacity: '1', offset: 1 }
|
|
||||||
];
|
|
||||||
var timing = { duration: 1200, iterations: 1 };
|
|
||||||
newCardImageContainer.animate(keyframes, timing).onfinish = onAnimationFinished;
|
|
||||||
} else {
|
|
||||||
onAnimationFinished();
|
|
||||||
}
|
|
||||||
|
|
||||||
stopInterval();
|
|
||||||
currentTimeout = setTimeout(function () {
|
|
||||||
showNextImage(index + 1, true);
|
|
||||||
|
|
||||||
}, currentIntervalMs);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!skipPreload) {
|
|
||||||
var img = new Image();
|
|
||||||
img.onload = onSrcLoaded;
|
|
||||||
img.src = imgUrl;
|
|
||||||
} else {
|
|
||||||
onSrcLoaded();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function stopInterval() {
|
|
||||||
if (currentTimeout) {
|
|
||||||
clearTimeout(currentTimeout);
|
|
||||||
currentTimeout = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
self.show = function () {
|
self.show = function () {
|
||||||
startInterval(options);
|
createElements(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hides the slideshow element.
|
||||||
|
*/
|
||||||
self.hide = function () {
|
self.hide = function () {
|
||||||
|
var dialog = dialog;
|
||||||
var dialog = dlg;
|
|
||||||
if (dialog) {
|
if (dialog) {
|
||||||
|
|
||||||
dialogHelper.close(dialog);
|
dialogHelper.close(dialog);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,15 +2,11 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function populateLanguages(select, languages) {
|
function populateLanguages(select, languages) {
|
||||||
|
|
||||||
var html = "";
|
var html = "";
|
||||||
|
|
||||||
html += "<option value=''>" + globalize.translate('AnyLanguage') + "</option>";
|
html += "<option value=''>" + globalize.translate('AnyLanguage') + "</option>";
|
||||||
|
|
||||||
for (var i = 0, length = languages.length; i < length; i++) {
|
for (var i = 0, length = languages.length; i < length; i++) {
|
||||||
|
|
||||||
var culture = languages[i];
|
var culture = languages[i];
|
||||||
|
|
||||||
html += "<option value='" + culture.ThreeLetterISOLanguageName + "'>" + culture.DisplayName + "</option>";
|
html += "<option value='" + culture.ThreeLetterISOLanguageName + "'>" + culture.DisplayName + "</option>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,7 +14,6 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSubtitleAppearanceObject(context) {
|
function getSubtitleAppearanceObject(context) {
|
||||||
|
|
||||||
var appearanceSettings = {};
|
var appearanceSettings = {};
|
||||||
|
|
||||||
appearanceSettings.textSize = context.querySelector('#selectTextSize').value;
|
appearanceSettings.textSize = context.querySelector('#selectTextSize').value;
|
||||||
|
@ -102,14 +97,12 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit(e) {
|
function onSubmit(e) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var apiClient = connectionManager.getApiClient(self.options.serverId);
|
var apiClient = connectionManager.getApiClient(self.options.serverId);
|
||||||
var userId = self.options.userId;
|
var userId = self.options.userId;
|
||||||
var userSettings = self.options.userSettings;
|
var userSettings = self.options.userSettings;
|
||||||
|
|
||||||
userSettings.setUserInfo(userId, apiClient).then(function () {
|
userSettings.setUserInfo(userId, apiClient).then(function () {
|
||||||
|
|
||||||
var enableSaveConfirmation = self.options.enableSaveConfirmation;
|
var enableSaveConfirmation = self.options.enableSaveConfirmation;
|
||||||
save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation);
|
save(self, self.options.element, userId, userSettings, apiClient, enableSaveConfirmation);
|
||||||
});
|
});
|
||||||
|
@ -118,6 +111,7 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,9 +191,7 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
var userSettings = self.options.userSettings;
|
var userSettings = self.options.userSettings;
|
||||||
|
|
||||||
apiClient.getUser(userId).then(function (user) {
|
apiClient.getUser(userId).then(function (user) {
|
||||||
|
|
||||||
userSettings.setUserInfo(userId, apiClient).then(function () {
|
userSettings.setUserInfo(userId, apiClient).then(function () {
|
||||||
|
|
||||||
self.dataLoaded = true;
|
self.dataLoaded = true;
|
||||||
|
|
||||||
var appearanceSettings = userSettings.getSubtitleAppearanceSettings(self.options.appearanceKey);
|
var appearanceSettings = userSettings.getSubtitleAppearanceSettings(self.options.appearanceKey);
|
||||||
|
@ -214,7 +206,6 @@ define(['require', 'globalize', 'appSettings', 'apphost', 'focusManager', 'loadi
|
||||||
};
|
};
|
||||||
|
|
||||||
SubtitleSettings.prototype.destroy = function () {
|
SubtitleSettings.prototype.destroy = function () {
|
||||||
|
|
||||||
this.options = null;
|
this.options = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
<form style="margin:0 auto;">
|
<form style="margin:0 auto;">
|
||||||
|
|
||||||
<div class="verticalSection">
|
<div class="verticalSection">
|
||||||
|
|
||||||
<h2 class="sectionTitle">
|
<h2 class="sectionTitle">
|
||||||
${Subtitles}
|
${Subtitles}
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -9,6 +7,7 @@
|
||||||
<div class="selectContainer">
|
<div class="selectContainer">
|
||||||
<select is="emby-select" id="selectSubtitleLanguage" label="${LabelPreferredSubtitleLanguage}"></select>
|
<select is="emby-select" id="selectSubtitleLanguage" label="${LabelPreferredSubtitleLanguage}"></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="selectContainer">
|
<div class="selectContainer">
|
||||||
<select is="emby-select" id="selectSubtitlePlaybackMode" label="${LabelSubtitlePlaybackMode}">
|
<select is="emby-select" id="selectSubtitlePlaybackMode" label="${LabelSubtitlePlaybackMode}">
|
||||||
<option value="Default">${Default}</option>
|
<option value="Default">${Default}</option>
|
||||||
|
@ -23,6 +22,7 @@
|
||||||
<div class="fieldDescription subtitlesOnlyForcedHelp subtitlesHelp hide">${OnlyForcedSubtitlesHelp}</div>
|
<div class="fieldDescription subtitlesOnlyForcedHelp subtitlesHelp hide">${OnlyForcedSubtitlesHelp}</div>
|
||||||
<div class="fieldDescription subtitlesNoneHelp subtitlesHelp hide">${NoSubtitlesHelp}</div>
|
<div class="fieldDescription subtitlesNoneHelp subtitlesHelp hide">${NoSubtitlesHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="selectContainer fldBurnIn hide">
|
<div class="selectContainer fldBurnIn hide">
|
||||||
<select is="emby-select" id="selectSubtitleBurnIn" label="${LabelBurnSubtitles}">
|
<select is="emby-select" id="selectSubtitleBurnIn" label="${LabelBurnSubtitles}">
|
||||||
<option value="">${Auto}</option>
|
<option value="">${Auto}</option>
|
||||||
|
@ -34,7 +34,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="verticalSection subtitleAppearanceSection hide">
|
<div class="verticalSection subtitleAppearanceSection hide">
|
||||||
|
|
||||||
<h2 class="sectionTitle">
|
<h2 class="sectionTitle">
|
||||||
${HeaderSubtitleAppearance}
|
${HeaderSubtitleAppearance}
|
||||||
</h2>
|
</h2>
|
||||||
|
@ -61,6 +60,7 @@
|
||||||
<option value="extralarge">${ExtraLarge}</option>
|
<option value="extralarge">${ExtraLarge}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="selectContainer">
|
<div class="selectContainer">
|
||||||
<select is="emby-select" id="selectFont" label="${LabelFont}">
|
<select is="emby-select" id="selectFont" label="${LabelFont}">
|
||||||
<option value="">${Default}</option>
|
<option value="">${Default}</option>
|
||||||
|
@ -71,12 +71,15 @@
|
||||||
<option value="smallcaps">${SmallCaps}</option>
|
<option value="smallcaps">${SmallCaps}</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inputContainer hide">
|
<div class="inputContainer hide">
|
||||||
<input is="emby-input" id="inputTextBackground" label="${LabelTextBackgroundColor}" type="text" />
|
<input is="emby-input" id="inputTextBackground" label="${LabelTextBackgroundColor}" type="text" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inputContainer hide">
|
<div class="inputContainer hide">
|
||||||
<input is="emby-input" id="inputTextColor" label="${LabelTextColor}" type="text" />
|
<input is="emby-input" id="inputTextColor" label="${LabelTextColor}" type="text" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="selectContainer">
|
<div class="selectContainer">
|
||||||
<select is="emby-select" id="selectDropShadow" label="${LabelDropShadow}">
|
<select is="emby-select" id="selectDropShadow" label="${LabelDropShadow}">
|
||||||
<option value="none">${None}</option>
|
<option value="none">${None}</option>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['playbackManager', 'text!./subtitlesync.template.html', 'css!./subtitlesync'], function (playbackManager, template, css) {
|
define(['playbackManager', 'layoutManager', 'text!./subtitlesync.template.html', 'css!./subtitlesync'], function (playbackManager, layoutManager, template, css) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var player;
|
var player;
|
||||||
|
@ -10,6 +10,7 @@ define(['playbackManager', 'text!./subtitlesync.template.html', 'css!./subtitles
|
||||||
function init(instance) {
|
function init(instance) {
|
||||||
|
|
||||||
var parent = document.createElement('div');
|
var parent = document.createElement('div');
|
||||||
|
document.body.appendChild(parent);
|
||||||
parent.innerHTML = template;
|
parent.innerHTML = template;
|
||||||
|
|
||||||
subtitleSyncSlider = parent.querySelector(".subtitleSyncSlider");
|
subtitleSyncSlider = parent.querySelector(".subtitleSyncSlider");
|
||||||
|
@ -17,11 +18,19 @@ define(['playbackManager', 'text!./subtitlesync.template.html', 'css!./subtitles
|
||||||
subtitleSyncCloseButton = parent.querySelector(".subtitleSync-closeButton");
|
subtitleSyncCloseButton = parent.querySelector(".subtitleSync-closeButton");
|
||||||
subtitleSyncContainer = parent.querySelector(".subtitleSyncContainer");
|
subtitleSyncContainer = parent.querySelector(".subtitleSyncContainer");
|
||||||
|
|
||||||
|
if (layoutManager.tv) {
|
||||||
|
subtitleSyncSlider.classList.add("focusable");
|
||||||
|
// HACK: Delay to give time for registered element attach (Firefox)
|
||||||
|
setTimeout(function () {
|
||||||
|
subtitleSyncSlider.enableKeyboardDragging();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|
||||||
subtitleSyncContainer.classList.add("hide");
|
subtitleSyncContainer.classList.add("hide");
|
||||||
|
|
||||||
subtitleSyncTextField.updateOffset = function(offset) {
|
subtitleSyncTextField.updateOffset = function(offset) {
|
||||||
this.textContent = offset + "s";
|
this.textContent = offset + "s";
|
||||||
}
|
};
|
||||||
|
|
||||||
subtitleSyncTextField.addEventListener("keypress", function(event) {
|
subtitleSyncTextField.addEventListener("keypress", function(event) {
|
||||||
|
|
||||||
|
@ -57,7 +66,7 @@ define(['playbackManager', 'text!./subtitlesync.template.html', 'css!./subtitles
|
||||||
subtitleSyncSlider.updateOffset = function(percent) {
|
subtitleSyncSlider.updateOffset = function(percent) {
|
||||||
// default value is 0s = 50%
|
// default value is 0s = 50%
|
||||||
this.value = percent === undefined ? 50 : percent;
|
this.value = percent === undefined ? 50 : percent;
|
||||||
}
|
};
|
||||||
|
|
||||||
subtitleSyncSlider.addEventListener("change", function () {
|
subtitleSyncSlider.addEventListener("change", function () {
|
||||||
// set new offset
|
// set new offset
|
||||||
|
@ -87,8 +96,6 @@ define(['playbackManager', 'text!./subtitlesync.template.html', 'css!./subtitles
|
||||||
SubtitleSync.prototype.toggle("forceToHide");
|
SubtitleSync.prototype.toggle("forceToHide");
|
||||||
});
|
});
|
||||||
|
|
||||||
document.body.appendChild(parent);
|
|
||||||
|
|
||||||
instance.element = parent;
|
instance.element = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +132,7 @@ define(['playbackManager', 'text!./subtitlesync.template.html', 'css!./subtitles
|
||||||
elem.parentNode.removeChild(elem);
|
elem.parentNode.removeChild(elem);
|
||||||
this.element = null;
|
this.element = null;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
SubtitleSync.prototype.toggle = function(action) {
|
SubtitleSync.prototype.toggle = function(action) {
|
||||||
|
|
||||||
|
@ -159,7 +166,7 @@ define(['playbackManager', 'text!./subtitlesync.template.html', 'css!./subtitles
|
||||||
}
|
}
|
||||||
/* eslint-enable no-fallthrough */
|
/* eslint-enable no-fallthrough */
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
return SubtitleSync;
|
return SubtitleSync;
|
||||||
});
|
});
|
||||||
|
|
3
src/config.example.json
Normal file
3
src/config.example.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"multiserver": true
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globalize", "loading", "connectionManager", "playMethodHelper", "cardBuilder", "imageLoader", "components/activitylog", "scripts/imagehelper", "indicators", "humanedate", "listViewStyle", "emby-button", "flexStyles", "emby-button", "emby-itemscontainer"], function (datetime, events, itemHelper, serverNotifications, dom, globalize, loading, connectionManager, playMethodHelper, cardBuilder, imageLoader, ActivityLog, imageHelper, indicators) {
|
define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globalize", "date-fns", "dfnshelper", "loading", "connectionManager", "playMethodHelper", "cardBuilder", "imageLoader", "components/activitylog", "scripts/imagehelper", "indicators", "listViewStyle", "emby-button", "flexStyles", "emby-button", "emby-itemscontainer"], function (datetime, events, itemHelper, serverNotifications, dom, globalize, datefns, dfnshelper, loading, connectionManager, playMethodHelper, cardBuilder, imageLoader, ActivityLog, imageHelper, indicators) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function showPlaybackInfo(btn, session) {
|
function showPlaybackInfo(btn, session) {
|
||||||
|
@ -467,10 +467,11 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
getNowPlayingName: function (session) {
|
getNowPlayingName: function (session) {
|
||||||
var imgUrl = "";
|
var imgUrl = "";
|
||||||
var nowPlayingItem = session.NowPlayingItem;
|
var nowPlayingItem = session.NowPlayingItem;
|
||||||
|
// FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix
|
||||||
|
// how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
|
||||||
if (!nowPlayingItem) {
|
if (!nowPlayingItem) {
|
||||||
return {
|
return {
|
||||||
html: "Last seen " + humaneDate(session.LastActivityDate),
|
html: globalize.translate("LastSeen", datefns.formatDistanceToNow(Date.parse(session.LastActivityDate), dfnshelper.localeWithSuffix)),
|
||||||
image: imgUrl
|
image: imgUrl
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,5 +29,5 @@ define(["datetime", "loading", "apphost", "listViewStyle", "emby-button", "flexS
|
||||||
loading.hide();
|
loading.hide();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -49,12 +49,12 @@ define(["loading", "libraryMenu", "globalize", "listViewStyle", "emby-button"],
|
||||||
}
|
}
|
||||||
page.querySelector(".notificationList").innerHTML = html;
|
page.querySelector(".notificationList").innerHTML = html;
|
||||||
loading.hide();
|
loading.hide();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(view, params) {
|
return function(view, params) {
|
||||||
view.addEventListener("viewshow", function() {
|
view.addEventListener("viewshow", function() {
|
||||||
reload(view);
|
reload(view);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -84,7 +84,7 @@ define(["jQuery", "loading", "libraryMenu", "globalize", "connectionManager", "e
|
||||||
}
|
}
|
||||||
|
|
||||||
if (installedPlugin) {
|
if (installedPlugin) {
|
||||||
var currentVersionText = globalize.translate("MessageYouHaveVersionInstalled").replace("{0}", "<strong>" + installedPlugin.Version + "</strong>");
|
var currentVersionText = globalize.translate("MessageYouHaveVersionInstalled", "<strong>" + installedPlugin.Version + "</strong>");
|
||||||
$("#pCurrentVersion", page).show().html(currentVersionText);
|
$("#pCurrentVersion", page).show().html(currentVersionText);
|
||||||
} else {
|
} else {
|
||||||
$("#pCurrentVersion", page).hide().html("");
|
$("#pCurrentVersion", page).hide().html("");
|
||||||
|
|
|
@ -116,7 +116,7 @@ define(["loading", "libraryMenu", "globalize", "cardStyle", "emby-button", "emby
|
||||||
return ip.Id == plugin.guid;
|
return ip.Id == plugin.guid;
|
||||||
})[0];
|
})[0];
|
||||||
html += "<div class='cardText cardText-secondary'>";
|
html += "<div class='cardText cardText-secondary'>";
|
||||||
html += installedPlugin ? globalize.translate("LabelVersionInstalled").replace("{0}", installedPlugin.Version) : " ";
|
html += installedPlugin ? globalize.translate("LabelVersionInstalled", installedPlugin.Version) : " ";
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
|
|
|
@ -2,7 +2,7 @@ define(["loading", "libraryMenu", "dom", "globalize", "cardStyle", "emby-button"
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function deletePlugin(page, uniqueid, name) {
|
function deletePlugin(page, uniqueid, name) {
|
||||||
var msg = globalize.translate("UninstallPluginConfirmation").replace("{0}", name);
|
var msg = globalize.translate("UninstallPluginConfirmation", name);
|
||||||
|
|
||||||
require(["confirm"], function (confirm) {
|
require(["confirm"], function (confirm) {
|
||||||
confirm({
|
confirm({
|
||||||
|
|
|
@ -75,17 +75,19 @@ define(["jQuery", "loading", "datetime", "dom", "globalize", "emby-input", "emby
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
context.querySelector(".taskTriggers").innerHTML = html;
|
context.querySelector(".taskTriggers").innerHTML = html;
|
||||||
},
|
},
|
||||||
|
// TODO: Replace this mess with date-fns and remove datetime completely
|
||||||
getTriggerFriendlyName: function (trigger) {
|
getTriggerFriendlyName: function (trigger) {
|
||||||
if ("DailyTrigger" == trigger.Type) {
|
if ("DailyTrigger" == trigger.Type) {
|
||||||
return "Daily at " + ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks);
|
return globalize.translate("DailyAt", ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("WeeklyTrigger" == trigger.Type) {
|
if ("WeeklyTrigger" == trigger.Type) {
|
||||||
return trigger.DayOfWeek + "s at " + ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks);
|
// TODO: The day of week isn't localised as well
|
||||||
|
return globalize.translate("WeeklyAt", trigger.DayOfWeek, ScheduledTaskPage.getDisplayTime(trigger.TimeOfDayTicks));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("SystemEventTrigger" == trigger.Type && "WakeFromSleep" == trigger.SystemEvent) {
|
if ("SystemEventTrigger" == trigger.Type && "WakeFromSleep" == trigger.SystemEvent) {
|
||||||
return "On wake from sleep";
|
return globalize.translate("OnWakeFromSleep");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trigger.Type == "IntervalTrigger") {
|
if (trigger.Type == "IntervalTrigger") {
|
||||||
|
@ -93,23 +95,23 @@ define(["jQuery", "loading", "datetime", "dom", "globalize", "emby-input", "emby
|
||||||
var hours = trigger.IntervalTicks / 36e9;
|
var hours = trigger.IntervalTicks / 36e9;
|
||||||
|
|
||||||
if (hours == 0.25) {
|
if (hours == 0.25) {
|
||||||
return "Every 15 minutes";
|
return globalize.translate("EveryXMinutes", "15");
|
||||||
}
|
}
|
||||||
if (hours == 0.5) {
|
if (hours == 0.5) {
|
||||||
return "Every 30 minutes";
|
return globalize.translate("EveryXMinutes", "30");
|
||||||
}
|
}
|
||||||
if (hours == 0.75) {
|
if (hours == 0.75) {
|
||||||
return "Every 45 minutes";
|
return globalize.translate("EveryXMinutes", "45");
|
||||||
}
|
}
|
||||||
if (hours == 1) {
|
if (hours == 1) {
|
||||||
return "Every hour";
|
return globalize.translate("EveryHour");
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Every " + hours + " hours";
|
return globalize.translate("EveryXHours", hours);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trigger.Type == "StartupTrigger") {
|
if (trigger.Type == "StartupTrigger") {
|
||||||
return "On application startup";
|
return globalize.translate("OnApplicationStartup");
|
||||||
}
|
}
|
||||||
|
|
||||||
return trigger.Type;
|
return trigger.Type;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["jQuery", "loading", "events", "globalize", "serverNotifications", "humanedate", "listViewStyle", "emby-button"], function($, loading, events, globalize, serverNotifications) {
|
define(["jQuery", "loading", "events", "globalize", "serverNotifications", "date-fns", "dfnshelper", "listViewStyle", "emby-button"], function ($, loading, events, globalize, serverNotifications, datefns, dfnshelper) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function reloadList(page) {
|
function reloadList(page) {
|
||||||
|
@ -7,7 +7,7 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "huma
|
||||||
}).then(function(tasks) {
|
}).then(function(tasks) {
|
||||||
populateList(page, tasks);
|
populateList(page, tasks);
|
||||||
loading.hide();
|
loading.hide();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateList(page, tasks) {
|
function populateList(page, tasks) {
|
||||||
|
@ -66,7 +66,10 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "huma
|
||||||
var html = "";
|
var html = "";
|
||||||
if (task.State === "Idle") {
|
if (task.State === "Idle") {
|
||||||
if (task.LastExecutionResult) {
|
if (task.LastExecutionResult) {
|
||||||
html += globalize.translate("LabelScheduledTaskLastRan").replace("{0}", humaneDate(task.LastExecutionResult.EndTimeUtc)).replace("{1}", humaneElapsed(task.LastExecutionResult.StartTimeUtc, task.LastExecutionResult.EndTimeUtc));
|
var endtime = Date.parse(task.LastExecutionResult.EndTimeUtc);
|
||||||
|
var starttime = Date.parse(task.LastExecutionResult.StartTimeUtc);
|
||||||
|
html += globalize.translate("LabelScheduledTaskLastRan", datefns.formatDistanceToNow(endtime, dfnshelper.localeWithSuffix),
|
||||||
|
datefns.formatDistance(starttime, endtime, { locale: dfnshelper.getLocale() }));
|
||||||
if (task.LastExecutionResult.Status === "Failed") {
|
if (task.LastExecutionResult.Status === "Failed") {
|
||||||
html += " <span style='color:#FF0000;'>(" + globalize.translate("LabelFailed") + ")</span>";
|
html += " <span style='color:#FF0000;'>(" + globalize.translate("LabelFailed") + ")</span>";
|
||||||
} else if (task.LastExecutionResult.Status === "Cancelled") {
|
} else if (task.LastExecutionResult.Status === "Cancelled") {
|
||||||
|
@ -152,7 +155,7 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "huma
|
||||||
ApiClient.startScheduledTask(id).then(function() {
|
ApiClient.startScheduledTask(id).then(function() {
|
||||||
updateTaskButton(button, "Running");
|
updateTaskButton(button, "Running");
|
||||||
reloadList(view);
|
reloadList(view);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
$(".divScheduledTasks", view).on("click", ".btnStopTask", function() {
|
$(".divScheduledTasks", view).on("click", ".btnStopTask", function() {
|
||||||
|
@ -161,7 +164,7 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "huma
|
||||||
ApiClient.stopScheduledTask(id).then(function() {
|
ApiClient.stopScheduledTask(id).then(function() {
|
||||||
updateTaskButton(button, "");
|
updateTaskButton(button, "");
|
||||||
reloadList(view);
|
reloadList(view);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
view.addEventListener("viewbeforehide", function() {
|
view.addEventListener("viewbeforehide", function() {
|
||||||
|
@ -175,5 +178,5 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "huma
|
||||||
reloadList(view);
|
reloadList(view);
|
||||||
events.on(serverNotifications, "ScheduledTasksInfo", onScheduledTasksUpdate);
|
events.on(serverNotifications, "ScheduledTasksInfo", onScheduledTasksUpdate);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["loading", "dom", "libraryMenu", "globalize", "scripts/imagehelper", "humanedate", "emby-button", "emby-itemscontainer", "cardStyle"], function (loading, dom, libraryMenu, globalize, imageHelper) {
|
define(["loading", "dom", "libraryMenu", "globalize", "scripts/imagehelper", "date-fns", "dfnshelper", "emby-button", "emby-itemscontainer", "cardStyle"], function (loading, dom, libraryMenu, globalize, imageHelper, datefns, dfnshelper) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function canDelete(deviceId) {
|
function canDelete(deviceId) {
|
||||||
|
@ -103,7 +103,7 @@ define(["loading", "dom", "libraryMenu", "globalize", "scripts/imagehelper", "hu
|
||||||
|
|
||||||
if (device.LastUserName) {
|
if (device.LastUserName) {
|
||||||
deviceHtml += device.LastUserName;
|
deviceHtml += device.LastUserName;
|
||||||
deviceHtml += ", " + humaneDate(device.DateLastActivity);
|
deviceHtml += ", " + datefns.formatDistanceToNow(Date.parse(device.DateLastActivity), dfnshelper.localeWithSuffix);
|
||||||
}
|
}
|
||||||
|
|
||||||
deviceHtml += " ";
|
deviceHtml += " ";
|
||||||
|
|
|
@ -258,14 +258,14 @@ define(["jQuery", "loading", "fnchecked", "emby-select", "emby-button", "emby-in
|
||||||
|
|
||||||
html += "<div>";
|
html += "<div>";
|
||||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||||
html += "<p>" + Globalize.translate("ValueContainer").replace("{0}", profile.Container || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueContainer", profile.Container || allText) + "</p>";
|
||||||
|
|
||||||
if ("Video" == profile.Type) {
|
if ("Video" == profile.Type) {
|
||||||
html += "<p>" + Globalize.translate("ValueVideoCodec").replace("{0}", profile.VideoCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueVideoCodec", profile.VideoCodec || allText) + "</p>";
|
||||||
html += "<p>" + Globalize.translate("ValueAudioCodec").replace("{0}", profile.AudioCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueAudioCodec", profile.AudioCodec || allText) + "</p>";
|
||||||
} else {
|
} else {
|
||||||
if ("Audio" == profile.Type) {
|
if ("Audio" == profile.Type) {
|
||||||
html += "<p>" + Globalize.translate("ValueCodec").replace("{0}", profile.AudioCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueCodec", profile.AudioCodec || allText) + "</p>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -319,14 +319,14 @@ define(["jQuery", "loading", "fnchecked", "emby-select", "emby-button", "emby-in
|
||||||
html += "<div>";
|
html += "<div>";
|
||||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||||
html += "<p>Protocol: " + (profile.Protocol || "Http") + "</p>";
|
html += "<p>Protocol: " + (profile.Protocol || "Http") + "</p>";
|
||||||
html += "<p>" + Globalize.translate("ValueContainer").replace("{0}", profile.Container || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueContainer", profile.Container || allText) + "</p>";
|
||||||
|
|
||||||
if ("Video" == profile.Type) {
|
if ("Video" == profile.Type) {
|
||||||
html += "<p>" + Globalize.translate("ValueVideoCodec").replace("{0}", profile.VideoCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueVideoCodec", profile.VideoCodec || allText) + "</p>";
|
||||||
html += "<p>" + Globalize.translate("ValueAudioCodec").replace("{0}", profile.AudioCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueAudioCodec", profile.AudioCodec || allText) + "</p>";
|
||||||
} else {
|
} else {
|
||||||
if ("Audio" == profile.Type) {
|
if ("Audio" == profile.Type) {
|
||||||
html += "<p>" + Globalize.translate("ValueCodec").replace("{0}", profile.AudioCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueCodec", profile.AudioCodec || allText) + "</p>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,11 +404,11 @@ define(["jQuery", "loading", "fnchecked", "emby-select", "emby-button", "emby-in
|
||||||
|
|
||||||
html += "<div>";
|
html += "<div>";
|
||||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||||
html += "<p>" + Globalize.translate("ValueContainer").replace("{0}", profile.Container || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueContainer", profile.Container || allText) + "</p>";
|
||||||
|
|
||||||
if (profile.Conditions && profile.Conditions.length) {
|
if (profile.Conditions && profile.Conditions.length) {
|
||||||
html += "<p>";
|
html += "<p>";
|
||||||
html += Globalize.translate("ValueConditions").replace("{0}", profile.Conditions.map(function (c) {
|
html += Globalize.translate("ValueConditions", profile.Conditions.map(function (c) {
|
||||||
return c.Property;
|
return c.Property;
|
||||||
}).join(", "));
|
}).join(", "));
|
||||||
html += "</p>";
|
html += "</p>";
|
||||||
|
@ -476,11 +476,11 @@ define(["jQuery", "loading", "fnchecked", "emby-select", "emby-button", "emby-in
|
||||||
|
|
||||||
html += "<div>";
|
html += "<div>";
|
||||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||||
html += "<p>" + Globalize.translate("ValueCodec").replace("{0}", profile.Codec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueCodec", profile.Codec || allText) + "</p>";
|
||||||
|
|
||||||
if (profile.Conditions && profile.Conditions.length) {
|
if (profile.Conditions && profile.Conditions.length) {
|
||||||
html += "<p>";
|
html += "<p>";
|
||||||
html += Globalize.translate("ValueConditions").replace("{0}", profile.Conditions.map(function (c) {
|
html += Globalize.translate("ValueConditions", profile.Conditions.map(function (c) {
|
||||||
return c.Property;
|
return c.Property;
|
||||||
}).join(", "));
|
}).join(", "));
|
||||||
html += "</p>";
|
html += "</p>";
|
||||||
|
@ -547,20 +547,20 @@ define(["jQuery", "loading", "fnchecked", "emby-select", "emby-button", "emby-in
|
||||||
|
|
||||||
html += "<div>";
|
html += "<div>";
|
||||||
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
html += '<a is="emby-linkbutton" href="#" class="lnkEditSubProfile" data-profileindex="' + i + '">';
|
||||||
html += "<p>" + Globalize.translate("ValueContainer").replace("{0}", profile.Container || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueContainer", profile.Container || allText) + "</p>";
|
||||||
|
|
||||||
if ("Video" == profile.Type) {
|
if ("Video" == profile.Type) {
|
||||||
html += "<p>" + Globalize.translate("ValueVideoCodec").replace("{0}", profile.VideoCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueVideoCodec", profile.VideoCodec || allText) + "</p>";
|
||||||
html += "<p>" + Globalize.translate("ValueAudioCodec").replace("{0}", profile.AudioCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueAudioCodec", profile.AudioCodec || allText) + "</p>";
|
||||||
} else {
|
} else {
|
||||||
if ("Audio" == profile.Type) {
|
if ("Audio" == profile.Type) {
|
||||||
html += "<p>" + Globalize.translate("ValueCodec").replace("{0}", profile.AudioCodec || allText) + "</p>";
|
html += "<p>" + Globalize.translate("ValueCodec", profile.AudioCodec || allText) + "</p>";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (profile.Conditions && profile.Conditions.length) {
|
if (profile.Conditions && profile.Conditions.length) {
|
||||||
html += "<p>";
|
html += "<p>";
|
||||||
html += Globalize.translate("ValueConditions").replace("{0}", profile.Conditions.map(function (c) {
|
html += Globalize.translate("ValueConditions", profile.Conditions.map(function (c) {
|
||||||
return c.Property;
|
return c.Property;
|
||||||
}).join(", "));
|
}).join(", "));
|
||||||
html += "</p>";
|
html += "</p>";
|
||||||
|
|
|
@ -14,6 +14,7 @@ define(["jQuery", "loading", "globalize", "dom", "libraryMenu"], function ($, lo
|
||||||
$("#txtVaapiDevice", page).val(config.VaapiDevice || "");
|
$("#txtVaapiDevice", page).val(config.VaapiDevice || "");
|
||||||
page.querySelector("#selectEncoderPreset").value = config.EncoderPreset || "";
|
page.querySelector("#selectEncoderPreset").value = config.EncoderPreset || "";
|
||||||
page.querySelector("#txtH264Crf").value = config.H264Crf || "";
|
page.querySelector("#txtH264Crf").value = config.H264Crf || "";
|
||||||
|
page.querySelector("#selectDeinterlaceMethod").value = config.DeinterlaceMethod || "";
|
||||||
page.querySelector("#chkEnableSubtitleExtraction").checked = config.EnableSubtitleExtraction || false;
|
page.querySelector("#chkEnableSubtitleExtraction").checked = config.EnableSubtitleExtraction || false;
|
||||||
page.querySelector("#chkEnableThrottling").checked = config.EnableThrottling || false;
|
page.querySelector("#chkEnableThrottling").checked = config.EnableThrottling || false;
|
||||||
page.querySelector("#selectVideoDecoder").dispatchEvent(new CustomEvent("change", {
|
page.querySelector("#selectVideoDecoder").dispatchEvent(new CustomEvent("change", {
|
||||||
|
@ -58,6 +59,7 @@ define(["jQuery", "loading", "globalize", "dom", "libraryMenu"], function ($, lo
|
||||||
config.VaapiDevice = $("#txtVaapiDevice", form).val();
|
config.VaapiDevice = $("#txtVaapiDevice", form).val();
|
||||||
config.EncoderPreset = form.querySelector("#selectEncoderPreset").value;
|
config.EncoderPreset = form.querySelector("#selectEncoderPreset").value;
|
||||||
config.H264Crf = parseInt(form.querySelector("#txtH264Crf").value || "0");
|
config.H264Crf = parseInt(form.querySelector("#txtH264Crf").value || "0");
|
||||||
|
config.DeinterlaceMethod = form.querySelector("#selectDeinterlaceMethod").value;
|
||||||
config.EnableSubtitleExtraction = form.querySelector("#chkEnableSubtitleExtraction").checked;
|
config.EnableSubtitleExtraction = form.querySelector("#chkEnableSubtitleExtraction").checked;
|
||||||
config.EnableThrottling = form.querySelector("#chkEnableThrottling").checked;
|
config.EnableThrottling = form.querySelector("#chkEnableThrottling").checked;
|
||||||
config.HardwareDecodingCodecs = Array.prototype.map.call(Array.prototype.filter.call(form.querySelectorAll(".chkDecodeCodec"), function (c) {
|
config.HardwareDecodingCodecs = Array.prototype.map.call(Array.prototype.filter.call(form.querySelectorAll(".chkDecodeCodec"), function (c) {
|
||||||
|
|
|
@ -591,7 +591,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
try {
|
try {
|
||||||
var birthday = datetime.parseISO8601Date(item.PremiereDate, true).toDateString();
|
var birthday = datetime.parseISO8601Date(item.PremiereDate, true).toDateString();
|
||||||
itemBirthday.classList.remove("hide");
|
itemBirthday.classList.remove("hide");
|
||||||
itemBirthday.innerHTML = globalize.translate("BirthDateValue").replace("{0}", birthday);
|
itemBirthday.innerHTML = globalize.translate("BirthDateValue", birthday);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
itemBirthday.classList.add("hide");
|
itemBirthday.classList.add("hide");
|
||||||
}
|
}
|
||||||
|
@ -605,7 +605,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
try {
|
try {
|
||||||
var deathday = datetime.parseISO8601Date(item.EndDate, true).toDateString();
|
var deathday = datetime.parseISO8601Date(item.EndDate, true).toDateString();
|
||||||
itemDeathDate.classList.remove("hide");
|
itemDeathDate.classList.remove("hide");
|
||||||
itemDeathDate.innerHTML = globalize.translate("DeathDateValue").replace("{0}", deathday);
|
itemDeathDate.innerHTML = globalize.translate("DeathDateValue", deathday);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
itemDeathDate.classList.add("hide");
|
itemDeathDate.classList.add("hide");
|
||||||
}
|
}
|
||||||
|
@ -618,7 +618,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
if ("Person" == item.Type && item.ProductionLocations && item.ProductionLocations.length) {
|
if ("Person" == item.Type && item.ProductionLocations && item.ProductionLocations.length) {
|
||||||
var gmap = '<a is="emby-linkbutton" class="button-link textlink" target="_blank" href="https://maps.google.com/maps?q=' + item.ProductionLocations[0] + '">' + item.ProductionLocations[0] + "</a>";
|
var gmap = '<a is="emby-linkbutton" class="button-link textlink" target="_blank" href="https://maps.google.com/maps?q=' + item.ProductionLocations[0] + '">' + item.ProductionLocations[0] + "</a>";
|
||||||
itemBirthLocation.classList.remove("hide");
|
itemBirthLocation.classList.remove("hide");
|
||||||
itemBirthLocation.innerHTML = globalize.translate("BirthPlaceValue").replace("{0}", gmap);
|
itemBirthLocation.innerHTML = globalize.translate("BirthPlaceValue", gmap);
|
||||||
} else {
|
} else {
|
||||||
itemBirthLocation.classList.add("hide");
|
itemBirthLocation.classList.add("hide");
|
||||||
}
|
}
|
||||||
|
@ -703,26 +703,9 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderUserInfo(page, item) {
|
|
||||||
var lastPlayedElement = page.querySelector(".itemLastPlayed");
|
|
||||||
|
|
||||||
if (item.UserData && item.UserData.LastPlayedDate) {
|
|
||||||
lastPlayedElement.classList.remove("hide");
|
|
||||||
var datePlayed = datetime.parseISO8601Date(item.UserData.LastPlayedDate);
|
|
||||||
lastPlayedElement.innerHTML = globalize.translate("DatePlayed") + ": " + datetime.toLocaleDateString(datePlayed) + " " + datetime.getDisplayTime(datePlayed);
|
|
||||||
} else {
|
|
||||||
lastPlayedElement.classList.add("hide");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderLinks(linksElem, item) {
|
function renderLinks(linksElem, item) {
|
||||||
var html = [];
|
var html = [];
|
||||||
|
|
||||||
if (item.DateCreated && itemHelper.enableDateAddedDisplay(item)) {
|
|
||||||
var dateCreated = datetime.parseISO8601Date(item.DateCreated);
|
|
||||||
html.push(globalize.translate("AddedOnValue", datetime.toLocaleDateString(dateCreated) + " " + datetime.getDisplayTime(dateCreated)));
|
|
||||||
}
|
|
||||||
|
|
||||||
var links = [];
|
var links = [];
|
||||||
|
|
||||||
if (!layoutManager.tv && item.HomePageUrl) {
|
if (!layoutManager.tv && item.HomePageUrl) {
|
||||||
|
@ -736,7 +719,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
}
|
}
|
||||||
|
|
||||||
if (links.length) {
|
if (links.length) {
|
||||||
html.push(globalize.translate("LinksValue", links.join(", ")));
|
html.push(links.join(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
linksElem.innerHTML = html.join(", ");
|
linksElem.innerHTML = html.join(", ");
|
||||||
|
@ -1032,13 +1015,17 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
context: context
|
context: context
|
||||||
}) + '">' + p.Name + "</a>";
|
}) + '">' + p.Name + "</a>";
|
||||||
}).join(", ");
|
}).join(", ");
|
||||||
var elem = page.querySelector(".genres");
|
|
||||||
elem.innerHTML = globalize.translate(genres.length > 1 ? "GenresValue" : "GenreValue", html);
|
|
||||||
|
|
||||||
|
var genresLabel = page.querySelector(".genresLabel");
|
||||||
|
genresLabel.innerHTML = globalize.translate(genres.length > 1 ? "Genres" : "Genre");
|
||||||
|
var genresValue = page.querySelector(".genres");
|
||||||
|
genresValue.innerHTML = html;
|
||||||
|
|
||||||
|
var genresGroup = page.querySelector(".genresGroup");
|
||||||
if (genres.length) {
|
if (genres.length) {
|
||||||
elem.classList.remove("hide");
|
genresGroup.classList.remove("hide");
|
||||||
} else {
|
} else {
|
||||||
elem.classList.add("hide");
|
genresGroup.classList.add("hide");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1056,13 +1043,17 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
context: context
|
context: context
|
||||||
}) + '">' + p.Name + "</a>";
|
}) + '">' + p.Name + "</a>";
|
||||||
}).join(", ");
|
}).join(", ");
|
||||||
var elem = page.querySelector(".directors");
|
|
||||||
elem.innerHTML = globalize.translate(directors.length > 1 ? "DirectorsValue" : "DirectorValue", html);
|
|
||||||
|
|
||||||
|
var directorsLabel = page.querySelector(".directorsLabel");
|
||||||
|
directorsLabel.innerHTML = globalize.translate(directors.length > 1 ? "Directors" : "Director");
|
||||||
|
var directorsValue = page.querySelector(".directors");
|
||||||
|
directorsValue.innerHTML = html;
|
||||||
|
|
||||||
|
var directorsGroup = page.querySelector(".directorsGroup");
|
||||||
if (directors.length) {
|
if (directors.length) {
|
||||||
elem.classList.remove("hide");
|
directorsGroup.classList.remove("hide");
|
||||||
} else {
|
} else {
|
||||||
elem.classList.add("hide");
|
directorsGroup.classList.add("hide");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1120,7 +1111,6 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
|
|
||||||
reloadUserDataButtons(page, item);
|
reloadUserDataButtons(page, item);
|
||||||
renderLinks(externalLinksElem, item);
|
renderLinks(externalLinksElem, item);
|
||||||
renderUserInfo(page, item);
|
|
||||||
renderTags(page, item);
|
renderTags(page, item);
|
||||||
renderSeriesAirTime(page, item, isStatic);
|
renderSeriesAirTime(page, item, isStatic);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ define(["globalize", "loading", "libraryMenu", "emby-checkbox", "emby-button", "
|
||||||
}, {
|
}, {
|
||||||
href: "metadatanfo.html",
|
href: "metadatanfo.html",
|
||||||
name: Globalize.translate("TabNfoSettings")
|
name: Globalize.translate("TabNfoSettings")
|
||||||
}]
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
return function(view, params) {
|
return function(view, params) {
|
||||||
|
@ -27,7 +27,7 @@ define(["globalize", "loading", "libraryMenu", "emby-checkbox", "emby-button", "
|
||||||
view.querySelector("#chkSaveMetadataHidden").checked = config.SaveMetadataHidden;
|
view.querySelector("#chkSaveMetadataHidden").checked = config.SaveMetadataHidden;
|
||||||
});
|
});
|
||||||
ApiClient.getNamedConfiguration("metadata").then(function(metadata) {
|
ApiClient.getNamedConfiguration("metadata").then(function(metadata) {
|
||||||
loadMetadataConfig(this, metadata)
|
loadMetadataConfig(this, metadata);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,5 +67,5 @@ define(["globalize", "loading", "libraryMenu", "emby-checkbox", "emby-button", "
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -48,7 +48,7 @@ define(["layoutManager", "cardBuilder", "apphost", "imageLoader", "loading", "sc
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBackdropShape() {
|
function getBackdropShape() {
|
||||||
return enableScrollX() ? "overflowBackdrop" : "backdrop"
|
return enableScrollX() ? "overflowBackdrop" : "backdrop";
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderActiveRecordings(context, promise) {
|
function renderActiveRecordings(context, promise) {
|
||||||
|
|
|
@ -7,10 +7,10 @@ define(["jQuery", "dom", "loading", "libraryMenu", "listViewStyle"], function($,
|
||||||
html += "<option value=''></option>";
|
html += "<option value=''></option>";
|
||||||
for (var i = 0, length = languages.length; i < length; i++) {
|
for (var i = 0, length = languages.length; i < length; i++) {
|
||||||
var culture = languages[i];
|
var culture = languages[i];
|
||||||
html += "<option value='" + culture.TwoLetterISOLanguageName + "'>" + culture.DisplayName + "</option>"
|
html += "<option value='" + culture.TwoLetterISOLanguageName + "'>" + culture.DisplayName + "</option>";
|
||||||
}
|
}
|
||||||
select.innerHTML = html
|
select.innerHTML = html;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function populateCountries(select) {
|
function populateCountries(select) {
|
||||||
|
@ -19,25 +19,25 @@ define(["jQuery", "dom", "loading", "libraryMenu", "listViewStyle"], function($,
|
||||||
html += "<option value=''></option>";
|
html += "<option value=''></option>";
|
||||||
for (var i = 0, length = allCountries.length; i < length; i++) {
|
for (var i = 0, length = allCountries.length; i < length; i++) {
|
||||||
var culture = allCountries[i];
|
var culture = allCountries[i];
|
||||||
html += "<option value='" + culture.TwoLetterISORegionName + "'>" + culture.DisplayName + "</option>"
|
html += "<option value='" + culture.TwoLetterISORegionName + "'>" + culture.DisplayName + "</option>";
|
||||||
}
|
}
|
||||||
select.innerHTML = html
|
select.innerHTML = html;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadPage(page) {
|
function loadPage(page) {
|
||||||
var promises = [ApiClient.getServerConfiguration(), populateLanguages(page.querySelector("#selectLanguage")), populateCountries(page.querySelector("#selectCountry"))];
|
var promises = [ApiClient.getServerConfiguration(), populateLanguages(page.querySelector("#selectLanguage")), populateCountries(page.querySelector("#selectCountry"))];
|
||||||
Promise.all(promises).then(function(responses) {
|
Promise.all(promises).then(function(responses) {
|
||||||
var config = responses[0];
|
var config = responses[0];
|
||||||
page.querySelector("#selectLanguage").value = config.PreferredMetadataLanguage || "", page.querySelector("#selectCountry").value = config.MetadataCountryCode || "", loading.hide()
|
page.querySelector("#selectLanguage").value = config.PreferredMetadataLanguage || "", page.querySelector("#selectCountry").value = config.MetadataCountryCode || "", loading.hide();
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onSubmit() {
|
function onSubmit() {
|
||||||
var form = this;
|
var form = this;
|
||||||
return loading.show(), ApiClient.getServerConfiguration().then(function(config) {
|
return loading.show(), ApiClient.getServerConfiguration().then(function(config) {
|
||||||
config.PreferredMetadataLanguage = form.querySelector("#selectLanguage").value, config.MetadataCountryCode = form.querySelector("#selectCountry").value, ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult)
|
config.PreferredMetadataLanguage = form.querySelector("#selectLanguage").value, config.MetadataCountryCode = form.querySelector("#selectCountry").value, ApiClient.updateServerConfiguration(config).then(Dashboard.processServerConfigurationUpdateResult);
|
||||||
}), !1
|
}), !1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTabs() {
|
function getTabs() {
|
||||||
|
@ -53,12 +53,12 @@ define(["jQuery", "dom", "loading", "libraryMenu", "listViewStyle"], function($,
|
||||||
}, {
|
}, {
|
||||||
href: "metadatanfo.html",
|
href: "metadatanfo.html",
|
||||||
name: Globalize.translate("TabNfoSettings")
|
name: Globalize.translate("TabNfoSettings")
|
||||||
}]
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).on("pageinit", "#metadataImagesConfigurationPage", function() {
|
$(document).on("pageinit", "#metadataImagesConfigurationPage", function() {
|
||||||
$(".metadataImagesConfigurationForm").off("submit", onSubmit).on("submit", onSubmit)
|
$(".metadataImagesConfigurationForm").off("submit", onSubmit).on("submit", onSubmit);
|
||||||
}).on("pageshow", "#metadataImagesConfigurationPage", function() {
|
}).on("pageshow", "#metadataImagesConfigurationPage", function() {
|
||||||
libraryMenu.setTabs("metadata", 2, getTabs), loading.show(), loadPage(this)
|
libraryMenu.setTabs("metadata", 2, getTabs), loading.show(), loadPage(this);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -91,21 +91,21 @@ define(["events", "layoutManager", "inputManager", "userSettings", "libraryMenu"
|
||||||
|
|
||||||
switch (recommendation.RecommendationType) {
|
switch (recommendation.RecommendationType) {
|
||||||
case "SimilarToRecentlyPlayed":
|
case "SimilarToRecentlyPlayed":
|
||||||
title = Globalize.translate("RecommendationBecauseYouWatched").replace("{0}", recommendation.BaselineItemName);
|
title = Globalize.translate("RecommendationBecauseYouWatched", recommendation.BaselineItemName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "SimilarToLikedItem":
|
case "SimilarToLikedItem":
|
||||||
title = Globalize.translate("RecommendationBecauseYouLike").replace("{0}", recommendation.BaselineItemName);
|
title = Globalize.translate("RecommendationBecauseYouLike", recommendation.BaselineItemName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "HasDirectorFromRecentlyPlayed":
|
case "HasDirectorFromRecentlyPlayed":
|
||||||
case "HasLikedDirector":
|
case "HasLikedDirector":
|
||||||
title = Globalize.translate("RecommendationDirectedBy").replace("{0}", recommendation.BaselineItemName);
|
title = Globalize.translate("RecommendationDirectedBy", recommendation.BaselineItemName);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "HasActorFromRecentlyPlayed":
|
case "HasActorFromRecentlyPlayed":
|
||||||
case "HasLikedActor":
|
case "HasLikedActor":
|
||||||
title = Globalize.translate("RecommendationStarring").replace("{0}", recommendation.BaselineItemName);
|
title = Globalize.translate("RecommendationStarring", recommendation.BaselineItemName);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ define(["jQuery", "loading", "libraryMenu"], function ($, loading, libraryMenu)
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).on("pageinit", "#playbackConfigurationPage", function () {
|
$(document).on("pageinit", "#playbackConfigurationPage", function () {
|
||||||
$(".playbackConfigurationForm").off("submit", onSubmit).on("submit", onSubmit)
|
$(".playbackConfigurationForm").off("submit", onSubmit).on("submit", onSubmit);
|
||||||
}).on("pageshow", "#playbackConfigurationPage", function () {
|
}).on("pageshow", "#playbackConfigurationPage", function () {
|
||||||
loading.show();
|
loading.show();
|
||||||
libraryMenu.setTabs("playback", 1, getTabs);
|
libraryMenu.setTabs("playback", 1, getTabs);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["displaySettings", "userSettingsBuilder", "userSettings", "autoFocuser"], function (DisplaySettings, userSettingsBuilder, currentUserSettings, autoFocuser) {
|
define(["displaySettings", "userSettings", "autoFocuser"], function (DisplaySettings, userSettings, autoFocuser) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -11,7 +11,7 @@ define(["displaySettings", "userSettingsBuilder", "userSettings", "autoFocuser"]
|
||||||
var settingsInstance;
|
var settingsInstance;
|
||||||
var hasChanges;
|
var hasChanges;
|
||||||
var userId = params.userId || ApiClient.getCurrentUserId();
|
var userId = params.userId || ApiClient.getCurrentUserId();
|
||||||
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettingsBuilder();
|
var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings();
|
||||||
view.addEventListener("viewshow", function () {
|
view.addEventListener("viewshow", function () {
|
||||||
window.addEventListener("beforeunload", onBeforeUnload);
|
window.addEventListener("beforeunload", onBeforeUnload);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ define(["displaySettings", "userSettingsBuilder", "userSettings", "autoFocuser"]
|
||||||
serverId: ApiClient.serverId(),
|
serverId: ApiClient.serverId(),
|
||||||
userId: userId,
|
userId: userId,
|
||||||
element: view.querySelector(".settingsContainer"),
|
element: view.querySelector(".settingsContainer"),
|
||||||
userSettings: userSettings,
|
userSettings: currentSettings,
|
||||||
enableSaveButton: false,
|
enableSaveButton: false,
|
||||||
enableSaveConfirmation: false,
|
enableSaveConfirmation: false,
|
||||||
autoFocus: autoFocuser.isEnabled()
|
autoFocus: autoFocuser.isEnabled()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["homescreenSettings", "userSettingsBuilder", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (HomescreenSettings, userSettingsBuilder, dom, globalize, loading, currentUserSettings, autoFocuser) {
|
define(["homescreenSettings", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (HomescreenSettings, dom, globalize, loading, userSettings, autoFocuser) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -11,7 +11,7 @@ define(["homescreenSettings", "userSettingsBuilder", "dom", "globalize", "loadin
|
||||||
var homescreenSettingsInstance;
|
var homescreenSettingsInstance;
|
||||||
var hasChanges;
|
var hasChanges;
|
||||||
var userId = params.userId || ApiClient.getCurrentUserId();
|
var userId = params.userId || ApiClient.getCurrentUserId();
|
||||||
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettingsBuilder();
|
var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings();
|
||||||
view.addEventListener("viewshow", function () {
|
view.addEventListener("viewshow", function () {
|
||||||
window.addEventListener("beforeunload", onBeforeUnload);
|
window.addEventListener("beforeunload", onBeforeUnload);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ define(["homescreenSettings", "userSettingsBuilder", "dom", "globalize", "loadin
|
||||||
serverId: ApiClient.serverId(),
|
serverId: ApiClient.serverId(),
|
||||||
userId: userId,
|
userId: userId,
|
||||||
element: view.querySelector(".homeScreenSettingsContainer"),
|
element: view.querySelector(".homeScreenSettingsContainer"),
|
||||||
userSettings: userSettings,
|
userSettings: currentSettings,
|
||||||
enableSaveButton: false,
|
enableSaveButton: false,
|
||||||
enableSaveConfirmation: false,
|
enableSaveConfirmation: false,
|
||||||
autoFocus: autoFocuser.isEnabled()
|
autoFocus: autoFocuser.isEnabled()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["apphost", "connectionManager", "listViewStyle", "emby-button"], function(appHost, connectionManager) {
|
define(["apphost", "connectionManager", "layoutManager", "listViewStyle", "emby-button"], function(appHost, connectionManager, layoutManager) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function(view, params) {
|
return function(view, params) {
|
||||||
|
@ -10,6 +10,10 @@ define(["apphost", "connectionManager", "listViewStyle", "emby-button"], functio
|
||||||
Dashboard.selectServer();
|
Dashboard.selectServer();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
view.querySelector(".clientSettings").addEventListener("click", function () {
|
||||||
|
window.NativeShell.openClientSettings();
|
||||||
|
});
|
||||||
|
|
||||||
view.addEventListener("viewshow", function() {
|
view.addEventListener("viewshow", function() {
|
||||||
// this page can also be used by admins to change user preferences from the user edit page
|
// this page can also be used by admins to change user preferences from the user edit page
|
||||||
var userId = params.userId || Dashboard.getCurrentUserId();
|
var userId = params.userId || Dashboard.getCurrentUserId();
|
||||||
|
@ -21,6 +25,12 @@ define(["apphost", "connectionManager", "listViewStyle", "emby-button"], functio
|
||||||
page.querySelector(".lnkPlaybackPreferences").setAttribute("href", "mypreferencesplayback.html?userId=" + userId);
|
page.querySelector(".lnkPlaybackPreferences").setAttribute("href", "mypreferencesplayback.html?userId=" + userId);
|
||||||
page.querySelector(".lnkSubtitlePreferences").setAttribute("href", "mypreferencessubtitles.html?userId=" + userId);
|
page.querySelector(".lnkSubtitlePreferences").setAttribute("href", "mypreferencessubtitles.html?userId=" + userId);
|
||||||
|
|
||||||
|
if (window.NativeShell && window.NativeShell.AppHost.supports("clientsettings")) {
|
||||||
|
page.querySelector(".clientSettings").classList.remove("hide");
|
||||||
|
} else {
|
||||||
|
page.querySelector(".clientSettings").classList.add("hide");
|
||||||
|
}
|
||||||
|
|
||||||
if (appHost.supports("multiserver")) {
|
if (appHost.supports("multiserver")) {
|
||||||
page.querySelector(".selectServer").classList.remove("hide");
|
page.querySelector(".selectServer").classList.remove("hide");
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,6 +43,12 @@ define(["apphost", "connectionManager", "listViewStyle", "emby-button"], functio
|
||||||
page.querySelector(".adminSection").classList.add("hide");
|
page.querySelector(".adminSection").classList.add("hide");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (layoutManager.mobile) {
|
||||||
|
page.querySelector(".headerUsername").classList.add("hide");
|
||||||
|
page.querySelector(".adminSection").classList.add("hide");
|
||||||
|
page.querySelector(".userSection").classList.add("hide");
|
||||||
|
}
|
||||||
|
|
||||||
ApiClient.getUser(userId).then(function(user) {
|
ApiClient.getUser(userId).then(function(user) {
|
||||||
page.querySelector(".headerUsername").innerHTML = user.Name;
|
page.querySelector(".headerUsername").innerHTML = user.Name;
|
||||||
if (!user.Policy.IsAdministrator) {
|
if (!user.Policy.IsAdministrator) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["playbackSettings", "userSettingsBuilder", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (PlaybackSettings, userSettingsBuilder, dom, globalize, loading, currentUserSettings, autoFocuser) {
|
define(["playbackSettings", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (PlaybackSettings, dom, globalize, loading, userSettings, autoFocuser) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -11,7 +11,7 @@ define(["playbackSettings", "userSettingsBuilder", "dom", "globalize", "loading"
|
||||||
var settingsInstance;
|
var settingsInstance;
|
||||||
var hasChanges;
|
var hasChanges;
|
||||||
var userId = params.userId || ApiClient.getCurrentUserId();
|
var userId = params.userId || ApiClient.getCurrentUserId();
|
||||||
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettingsBuilder();
|
var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings();
|
||||||
view.addEventListener("viewshow", function () {
|
view.addEventListener("viewshow", function () {
|
||||||
window.addEventListener("beforeunload", onBeforeUnload);
|
window.addEventListener("beforeunload", onBeforeUnload);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ define(["playbackSettings", "userSettingsBuilder", "dom", "globalize", "loading"
|
||||||
serverId: ApiClient.serverId(),
|
serverId: ApiClient.serverId(),
|
||||||
userId: userId,
|
userId: userId,
|
||||||
element: view.querySelector(".settingsContainer"),
|
element: view.querySelector(".settingsContainer"),
|
||||||
userSettings: userSettings,
|
userSettings: currentSettings,
|
||||||
enableSaveButton: false,
|
enableSaveButton: false,
|
||||||
enableSaveConfirmation: false,
|
enableSaveConfirmation: false,
|
||||||
autoFocus: autoFocuser.isEnabled()
|
autoFocus: autoFocuser.isEnabled()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["subtitleSettings", "userSettingsBuilder", "userSettings", "autoFocuser"], function (SubtitleSettings, userSettingsBuilder, currentUserSettings, autoFocuser) {
|
define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSettings, userSettings, autoFocuser) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -11,7 +11,7 @@ define(["subtitleSettings", "userSettingsBuilder", "userSettings", "autoFocuser"
|
||||||
var subtitleSettingsInstance;
|
var subtitleSettingsInstance;
|
||||||
var hasChanges;
|
var hasChanges;
|
||||||
var userId = params.userId || ApiClient.getCurrentUserId();
|
var userId = params.userId || ApiClient.getCurrentUserId();
|
||||||
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettingsBuilder();
|
var currentSettings = userId === ApiClient.getCurrentUserId() ? userSettings : new userSettings();
|
||||||
view.addEventListener("viewshow", function () {
|
view.addEventListener("viewshow", function () {
|
||||||
window.addEventListener("beforeunload", onBeforeUnload);
|
window.addEventListener("beforeunload", onBeforeUnload);
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@ define(["subtitleSettings", "userSettingsBuilder", "userSettings", "autoFocuser"
|
||||||
serverId: ApiClient.serverId(),
|
serverId: ApiClient.serverId(),
|
||||||
userId: userId,
|
userId: userId,
|
||||||
element: view.querySelector(".settingsContainer"),
|
element: view.querySelector(".settingsContainer"),
|
||||||
userSettings: userSettings,
|
userSettings: currentSettings,
|
||||||
enableSaveButton: false,
|
enableSaveButton: false,
|
||||||
enableSaveConfirmation: false,
|
enableSaveConfirmation: false,
|
||||||
autoFocus: autoFocuser.isEnabled()
|
autoFocus: autoFocuser.isEnabled()
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["loading", "dom", "globalize", "humanedate", "paper-icon-button-light", "cardStyle", "emby-button", "indicators", "flexStyles"], function (loading, dom, globalize) {
|
define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-button-light", "cardStyle", "emby-button", "indicators", "flexStyles"], function (loading, dom, globalize, datefns, dfnshelper) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function deleteUser(page, id) {
|
function deleteUser(page, id) {
|
||||||
|
@ -125,10 +125,11 @@ define(["loading", "dom", "globalize", "humanedate", "paper-icon-button-light",
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
return html + "</div>";
|
return html + "</div>";
|
||||||
}
|
}
|
||||||
|
// FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix
|
||||||
|
// how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
|
||||||
function getLastSeenText(lastActivityDate) {
|
function getLastSeenText(lastActivityDate) {
|
||||||
if (lastActivityDate) {
|
if (lastActivityDate) {
|
||||||
return "Last seen " + humaneDate(lastActivityDate);
|
return globalize.translate("LastSeen", datefns.formatDistanceToNow(Date.parse(lastActivityDate), dfnshelper.localeWithSuffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
height: 1.83em;
|
height: 1.83em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 2px solid currentcolor;
|
border: 0.14em solid currentcolor;
|
||||||
border-radius: 0.14em;
|
border-radius: 0.14em;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio-label-block {
|
.radio-label-block {
|
||||||
|
@ -31,67 +30,82 @@
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__outer-circle {
|
.mdl-radio__circles {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 4px;
|
margin-right: 0.54em;
|
||||||
left: 0;
|
width: 1.08em;
|
||||||
display: inline-block;
|
height: 1.08em;
|
||||||
box-sizing: border-box;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
margin: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 2px solid currentcolor;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 2;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__button:checked + .mdl-radio__label + .mdl-radio__outer-circle {
|
.mdl-radio__circles svg {
|
||||||
border: 2px solid #00a4dc;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__button:disabled + .mdl-radio__label + .mdl-radio__outer-circle {
|
.mdl-radio__button:disabled + .mdl-radio__circles {
|
||||||
border: 2px solid rgba(0, 0, 0, 0.26);
|
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mdl-radio__button:disabled + .mdl-radio__circles .mdl-radio__outer-circle {
|
||||||
|
color: rgba(0, 0, 0, 0.26);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio.show-focus .mdl-radio__button:focus + .mdl-radio__circles .mdl-radio__outer-circle {
|
||||||
|
color: #00a4dc;
|
||||||
|
}
|
||||||
|
|
||||||
.mdl-radio__inner-circle {
|
.mdl-radio__inner-circle {
|
||||||
position: absolute;
|
transition-duration: 0.2s;
|
||||||
z-index: 1;
|
|
||||||
margin: 0;
|
|
||||||
top: 8px;
|
|
||||||
left: 4px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition-duration: 0.28s;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-property: -webkit-transform;
|
transition-property: -webkit-transform;
|
||||||
transition-property: transform;
|
transition-property: transform;
|
||||||
transition-property: transform, -webkit-transform;
|
transition-property: transform, -webkit-transform;
|
||||||
-webkit-transform: scale3d(0, 0, 0);
|
-webkit-transform: scale(0);
|
||||||
transform: scale3d(0, 0, 0);
|
transform: scale(0);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio__button:checked + .mdl-radio__circles .mdl-radio__inner-circle {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio__button:disabled + .mdl-radio__circles .mdl-radio__inner-circle {
|
||||||
|
color: rgba(0, 0, 0, 0.26);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio.show-focus .mdl-radio__button:focus + .mdl-radio__circles .mdl-radio__inner-circle {
|
||||||
|
color: #00a4dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio__focus-circle {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #00a4dc;
|
background: #00a4dc;
|
||||||
|
opacity: 0.26;
|
||||||
|
transition-duration: 0.2s;
|
||||||
|
transition-property: -webkit-transform;
|
||||||
|
transition-property: transform;
|
||||||
|
transition-property: transform, -webkit-transform;
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__button:checked + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle {
|
.mdl-radio.show-focus .mdl-radio__button:focus + .mdl-radio__circles .mdl-radio__focus-circle {
|
||||||
-webkit-transform: scale3d(1, 1, 1);
|
-webkit-transform: scale(1.75);
|
||||||
transform: scale3d(1, 1, 1);
|
transform: scale(1.75);
|
||||||
}
|
|
||||||
|
|
||||||
.mdl-radio__button:disabled + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle {
|
|
||||||
background: rgba(0, 0, 0, 0.26);
|
|
||||||
cursor: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdl-radio__button:focus + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle {
|
|
||||||
box-shadow: 0 0 0 10px rgba(255, 255, 255, 0.76);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdl-radio__button:checked:focus + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle {
|
|
||||||
box-shadow: 0 0 0 10px rgba(0, 164, 220, 0.26);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__label {
|
.mdl-radio__label {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['css!./emby-radio', 'registerElement'], function () {
|
define(['layoutManager', 'css!./emby-radio', 'registerElement'], function (layoutManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var EmbyRadioPrototype = Object.create(HTMLInputElement.prototype);
|
var EmbyRadioPrototype = Object.create(HTMLInputElement.prototype);
|
||||||
|
@ -23,6 +23,7 @@ define(['css!./emby-radio', 'registerElement'], function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
EmbyRadioPrototype.attachedCallback = function () {
|
EmbyRadioPrototype.attachedCallback = function () {
|
||||||
|
var showFocus = !layoutManager.mobile;
|
||||||
|
|
||||||
if (this.getAttribute('data-radio') === 'true') {
|
if (this.getAttribute('data-radio') === 'true') {
|
||||||
return;
|
return;
|
||||||
|
@ -37,13 +38,36 @@ define(['css!./emby-radio', 'registerElement'], function () {
|
||||||
labelElement.classList.add('mdl-radio');
|
labelElement.classList.add('mdl-radio');
|
||||||
labelElement.classList.add('mdl-js-radio');
|
labelElement.classList.add('mdl-js-radio');
|
||||||
labelElement.classList.add('mdl-js-ripple-effect');
|
labelElement.classList.add('mdl-js-ripple-effect');
|
||||||
|
if (showFocus) {
|
||||||
|
labelElement.classList.add('show-focus');
|
||||||
|
}
|
||||||
|
|
||||||
var labelTextElement = labelElement.querySelector('span');
|
var labelTextElement = labelElement.querySelector('span');
|
||||||
|
|
||||||
labelTextElement.classList.add('radioButtonLabel');
|
labelTextElement.classList.add('radioButtonLabel');
|
||||||
labelTextElement.classList.add('mdl-radio__label');
|
labelTextElement.classList.add('mdl-radio__label');
|
||||||
|
|
||||||
labelElement.insertAdjacentHTML('beforeend', '<span class="mdl-radio__outer-circle"></span><span class="mdl-radio__inner-circle"></span>');
|
var html = '';
|
||||||
|
|
||||||
|
html += '<div class="mdl-radio__circles">';
|
||||||
|
|
||||||
|
html += '<svg>';
|
||||||
|
html += '<defs>';
|
||||||
|
html += '<clipPath id="cutoff">';
|
||||||
|
html += '<circle cx="50%" cy="50%" r="50%" />';
|
||||||
|
html += '</clipPath>';
|
||||||
|
html += '</defs>';
|
||||||
|
html += '<circle class="mdl-radio__outer-circle" cx="50%" cy="50%" r="50%" fill="none" stroke="currentcolor" stroke-width="0.26em" clip-path="url(#cutoff)" />';
|
||||||
|
html += '<circle class="mdl-radio__inner-circle" cx="50%" cy="50%" r="25%" fill="currentcolor" />';
|
||||||
|
html += '</svg>';
|
||||||
|
|
||||||
|
if (showFocus) {
|
||||||
|
html += '<div class="mdl-radio__focus-circle"></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
this.insertAdjacentHTML('afterend', html);
|
||||||
|
|
||||||
this.addEventListener('keydown', onKeyDown);
|
this.addEventListener('keydown', onKeyDown);
|
||||||
};
|
};
|
||||||
|
|
|
@ -109,7 +109,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectArrow {
|
.selectArrow {
|
||||||
margin-top: 0.35em;
|
margin-top: 1.2em;
|
||||||
font-size: 1.7em;
|
font-size: 1.7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
|
||||||
this.parentNode.insertBefore(label, this);
|
this.parentNode.insertBefore(label, this);
|
||||||
|
|
||||||
if (this.classList.contains('emby-select-withcolor')) {
|
if (this.classList.contains('emby-select-withcolor')) {
|
||||||
this.parentNode.insertAdjacentHTML('beforeend', '<div class="selectArrowContainer"><div style="visibility:hidden;">0</div><i class="selectArrow material-icons keyboard_arrow_down"></i></div>');
|
this.parentNode.insertAdjacentHTML('beforeend', '<div class="selectArrowContainer"><div style="visibility:hidden;display:none;">0</div><i class="selectArrow material-icons keyboard_arrow_down"></i></div>');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -402,7 +402,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
|
||||||
this.addEventListener('keydown', onKeyDown);
|
this.addEventListener('keydown', onKeyDown);
|
||||||
this.keyboardDraggingEnabled = true;
|
this.keyboardDraggingEnabled = true;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set steps for keyboard input.
|
* Set steps for keyboard input.
|
||||||
|
@ -413,7 +413,7 @@ define(['browser', 'dom', 'layoutManager', 'keyboardnavigation', 'css!./emby-sli
|
||||||
EmbySliderPrototype.setKeyboardSteps = function (stepDown, stepUp) {
|
EmbySliderPrototype.setKeyboardSteps = function (stepDown, stepUp) {
|
||||||
this.keyboardStepDown = stepDown || stepUp || 1;
|
this.keyboardStepDown = stepDown || stepUp || 1;
|
||||||
this.keyboardStepUp = stepUp || stepDown || 1;
|
this.keyboardStepUp = stepUp || stepDown || 1;
|
||||||
}
|
};
|
||||||
|
|
||||||
function setRange(elem, startPercent, endPercent) {
|
function setRange(elem, startPercent, endPercent) {
|
||||||
|
|
||||||
|
|
|
@ -136,6 +136,14 @@
|
||||||
<div class="fieldDescription">${H264CrfHelp}</div>
|
<div class="fieldDescription">${H264CrfHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="selectContainer">
|
||||||
|
<select is="emby-select" id="selectDeinterlaceMethod" label="${LabelDeinterlaceMethod}">
|
||||||
|
<option value="yadif">${Yadif}</option>
|
||||||
|
<option value="yadif_bob">${YadifBob}</option>
|
||||||
|
</select>
|
||||||
|
<div class="fieldDescription">${DeinterlaceMethodHelp}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
<label>
|
<label>
|
||||||
<input is="emby-checkbox" type="checkbox" id="chkEnableSubtitleExtraction" />
|
<input is="emby-checkbox" type="checkbox" id="chkEnableSubtitleExtraction" />
|
||||||
|
|
|
@ -111,22 +111,32 @@
|
||||||
<div class="detailImageContainer padded-left"></div>
|
<div class="detailImageContainer padded-left"></div>
|
||||||
<div class="detailPageContent">
|
<div class="detailPageContent">
|
||||||
<div class="detailPagePrimaryContent padded-left padded-right">
|
<div class="detailPagePrimaryContent padded-left padded-right">
|
||||||
<div class="detailSection" style="margin-bottom: 0;">
|
<div class="detailSection">
|
||||||
<div class="itemMiscInfo nativeName hide"></div>
|
<div class="itemMiscInfo nativeName hide"></div>
|
||||||
<div class="genres hide" style="margin: .7em 0;font-size:92%;"></div>
|
|
||||||
<div class="directors hide" style="margin: .7em 0;font-size:92%;"></div>
|
|
||||||
|
|
||||||
<form class="trackSelections flex align-items-center flex-wrap-wrap hide focuscontainer-x">
|
<div class="itemDetailsGroup">
|
||||||
<div class="selectContainer selectContainer-inline selectSourceContainer hide trackSelectionFieldContainer flex-shrink-zero">
|
<div class="detailsGroupItem genresGroup hide">
|
||||||
|
<div class="genresLabel label"></div>
|
||||||
|
<div class="genres content"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detailsGroupItem directorsGroup hide">
|
||||||
|
<div class="directorsLabel label"></div>
|
||||||
|
<div class="directors content"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form class="trackSelections hide focuscontainer-x">
|
||||||
|
<div class="selectContainer selectSourceContainer hide trackSelectionFieldContainer flex-shrink-zero">
|
||||||
<select is="emby-select" class="selectSource detailTrackSelect" label=""></select>
|
<select is="emby-select" class="selectSource detailTrackSelect" label=""></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="selectContainer selectContainer-inline selectVideoContainer hide trackSelectionFieldContainer flex-shrink-zero">
|
<div class="selectContainer selectVideoContainer hide trackSelectionFieldContainer flex-shrink-zero">
|
||||||
<select is="emby-select" class="selectVideo detailTrackSelect" label=""></select>
|
<select is="emby-select" class="selectVideo detailTrackSelect" label=""></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="selectContainer selectContainer-inline selectAudioContainer hide trackSelectionFieldContainer flex-shrink-zero">
|
<div class="selectContainer selectAudioContainer hide trackSelectionFieldContainer flex-shrink-zero">
|
||||||
<select is="emby-select" class="selectAudio detailTrackSelect" label=""></select>
|
<select is="emby-select" class="selectAudio detailTrackSelect" label=""></select>
|
||||||
</div>
|
</div>
|
||||||
<div class="selectContainer selectContainer-inline selectSubtitlesContainer hide trackSelectionFieldContainer">
|
<div class="selectContainer selectSubtitlesContainer hide trackSelectionFieldContainer">
|
||||||
<select is="emby-select" class="selectSubtitles detailTrackSelect" label=""></select>
|
<select is="emby-select" class="selectSubtitles detailTrackSelect" label=""></select>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -176,8 +186,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="additionalPartsCollapsible" class="verticalSection detailVerticalSection hide">
|
<div id="additionalPartsCollapsible" class="verticalSection detailVerticalSection hide">
|
||||||
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderAdditionalParts}</h2>
|
<h2 class="sectionTitle sectionTitle-cards padded-left padded-right">${HeaderAdditionalParts}</h2>
|
||||||
<div id="additionalPartsContent" is="emby-itemscontainer" class="itemsContainer vertical-wrap"></div>
|
<div id="additionalPartsContent" is="emby-itemscontainer" class="itemsContainer vertical-wrap padded-left padded-right"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="verticalSection itemVerticalSection moreFromSeasonSection hide">
|
<div class="verticalSection itemVerticalSection moreFromSeasonSection hide">
|
||||||
|
@ -203,17 +213,17 @@
|
||||||
|
|
||||||
<div id="seriesScheduleSection" class="verticalSection detailVerticalSection hide">
|
<div id="seriesScheduleSection" class="verticalSection detailVerticalSection hide">
|
||||||
<h2 class="sectionTitle padded-left padded-right">${HeaderUpcomingOnTV}</h2>
|
<h2 class="sectionTitle padded-left padded-right">${HeaderUpcomingOnTV}</h2>
|
||||||
<div id="seriesScheduleList" is="emby-itemscontainer" class="itemsContainer vertical-list"></div>
|
<div id="seriesScheduleList" is="emby-itemscontainer" class="itemsContainer vertical-list padded-left padded-right"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="specialsCollapsible" class="verticalSection detailVerticalSection hide">
|
<div id="specialsCollapsible" class="verticalSection detailVerticalSection hide">
|
||||||
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderSpecialFeatures}</h2>
|
<h2 class="sectionTitle sectionTitle-cards padded-left padded-right">${HeaderSpecialFeatures}</h2>
|
||||||
<div id="specialsContent" is="emby-itemscontainer" class="itemsContainer vertical-wrap"></div>
|
<div id="specialsContent" is="emby-itemscontainer" class="itemsContainer vertical-wrap padded-left padded-right"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="musicVideosCollapsible" class="verticalSection detailVerticalSection hide">
|
<div id="musicVideosCollapsible" class="verticalSection detailVerticalSection hide">
|
||||||
<h2 class="sectionTitle sectionTitle-cards padded-left">${HeaderMusicVideos}</h2>
|
<h2 class="sectionTitle sectionTitle-cards padded-left padded-right">${HeaderMusicVideos}</h2>
|
||||||
<div id="musicVideosContent" is="emby-itemscontainer" class="itemsContainer vertical-wrap"></div>
|
<div id="musicVideosContent" is="emby-itemscontainer" class="itemsContainer vertical-wrap padded-left padded-right"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="scenesCollapsible" class="verticalSection itemVerticalSection verticalSection-extrabottompadding hide">
|
<div id="scenesCollapsible" class="verticalSection itemVerticalSection verticalSection-extrabottompadding hide">
|
||||||
|
|
|
@ -2,19 +2,19 @@ Dashboard.confirm = function(message, title, callback) {
|
||||||
"use strict";
|
"use strict";
|
||||||
require(["confirm"], function(confirm) {
|
require(["confirm"], function(confirm) {
|
||||||
confirm(message, title).then(function() {
|
confirm(message, title).then(function() {
|
||||||
callback(!0)
|
callback(!0);
|
||||||
}, function() {
|
}, function() {
|
||||||
callback(!1)
|
callback(!1);
|
||||||
})
|
});
|
||||||
})
|
});
|
||||||
}, Dashboard.showLoadingMsg = function() {
|
}, Dashboard.showLoadingMsg = function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
require(["loading"], function(loading) {
|
require(["loading"], function(loading) {
|
||||||
loading.show()
|
loading.show();
|
||||||
})
|
});
|
||||||
}, Dashboard.hideLoadingMsg = function() {
|
}, Dashboard.hideLoadingMsg = function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
require(["loading"], function(loading) {
|
require(["loading"], function(loading) {
|
||||||
loading.hide()
|
loading.hide();
|
||||||
})
|
});
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,9 +2,9 @@ define(["jQuery"], function($) {
|
||||||
"use strict";
|
"use strict";
|
||||||
$.fn.checked = function(value) {
|
$.fn.checked = function(value) {
|
||||||
return !0 === value || !1 === value ? $(this).each(function() {
|
return !0 === value || !1 === value ? $(this).each(function() {
|
||||||
this.checked = value
|
this.checked = value;
|
||||||
}) : this.length && this[0].checked
|
}) : this.length && this[0].checked;
|
||||||
}, $.fn.checkboxradio = function() {
|
}, $.fn.checkboxradio = function() {
|
||||||
return this
|
return this;
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
define(["jQuery"], function($) {
|
define(["jQuery"], function($) {
|
||||||
"use strict";
|
"use strict";
|
||||||
$.fn.selectmenu = function() {
|
$.fn.selectmenu = function() {
|
||||||
return this
|
return this;
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inputContainer">
|
<div class="inputContainer">
|
||||||
<input is="emby-input" type="text" id="txtManualName" required="required" label="${LabelUser}" autocomplete="username" />
|
<input is="emby-input" type="text" id="txtManualName" required="required" label="${LabelUser}" autocomplete="username" autocapitalize="off" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="inputContainer">
|
<div class="inputContainer">
|
||||||
|
|
|
@ -47,6 +47,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a is="emby-linkbutton" data-ripple="false" href="#" style="display:block;padding:0;margin:0;" class="clientSettings listItem-border">
|
||||||
|
<div class="listItem">
|
||||||
|
<i class="material-icons listItemIcon listItemIcon-transparent devices_other"></i>
|
||||||
|
<div class="listItemBody">
|
||||||
|
<div class="listItemBodyText">${ClientSettings}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="adminSection verticalSection verticalSection-extrabottompadding">
|
<div class="adminSection verticalSection verticalSection-extrabottompadding">
|
||||||
<h2 class="sectionTitle" style="padding-left:.25em;">${HeaderAdmin}</h2>
|
<h2 class="sectionTitle" style="padding-left:.25em;">${HeaderAdmin}</h2>
|
||||||
|
|
|
@ -292,7 +292,7 @@ define([], function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof document !== 'undefined') {
|
if (typeof document !== 'undefined') {
|
||||||
if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
|
if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0)) {
|
||||||
browser.touch = true;
|
browser.touch = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -433,13 +433,9 @@ define(['browser'], function (browser) {
|
||||||
|
|
||||||
var supportsDts = browser.tizen || browser.orsay || browser.web0s || options.supportsDts;
|
var supportsDts = browser.tizen || browser.orsay || browser.web0s || options.supportsDts;
|
||||||
|
|
||||||
if (self.tizen && self.tizen.systeminfo) {
|
// DTS audio not supported in 2018 models (Tizen 4.0)
|
||||||
var v = tizen.systeminfo.getCapability('http://tizen.org/feature/platform.version');
|
if (browser.tizenVersion >= 4) {
|
||||||
|
supportsDts = false;
|
||||||
// DTS audio not supported in 2018 models (Tizen 4.0)
|
|
||||||
if (v && parseFloat(v) >= parseFloat('4.0')) {
|
|
||||||
supportsDts = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (supportsDts) {
|
if (supportsDts) {
|
||||||
|
@ -766,6 +762,11 @@ define(['browser'], function (browser) {
|
||||||
maxH264Level = 51;
|
maxH264Level = 51;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Support H264 Level 52 (Tizen 5.0) - app only
|
||||||
|
if (browser.tizenVersion >= 5 && window.NativeShell) {
|
||||||
|
maxH264Level = 52;
|
||||||
|
}
|
||||||
|
|
||||||
if (browser.tizen || browser.orsay ||
|
if (browser.tizen || browser.orsay ||
|
||||||
videoTestElement.canPlayType('video/mp4; codecs="avc1.6e0033"').replace(/no/, '')) {
|
videoTestElement.canPlayType('video/mp4; codecs="avc1.6e0033"').replace(/no/, '')) {
|
||||||
|
|
||||||
|
|
104
src/scripts/dfnshelper.js
Normal file
104
src/scripts/dfnshelper.js
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
import { ar, be, bg, ca, cs, da, de, el, enGB, enUS, es, faIR, fi, fr, frCA, he, hi, hr, hu, id, it, kk, ko, lt, ms, nb, nl, pl, ptBR, pt, ro, ru, sk, sl, sv, tr, uk, vi, zhCN, zhTW } from 'date-fns/locale';
|
||||||
|
import globalize from 'globalize';
|
||||||
|
|
||||||
|
export function getLocale() {
|
||||||
|
switch (globalize.getCurrentLocale()) {
|
||||||
|
case 'ar':
|
||||||
|
return ar;
|
||||||
|
case 'be-by':
|
||||||
|
return be;
|
||||||
|
case 'bg-bg':
|
||||||
|
return bg;
|
||||||
|
case 'ca':
|
||||||
|
return ca;
|
||||||
|
case 'cs':
|
||||||
|
return cs;
|
||||||
|
case 'da':
|
||||||
|
return da;
|
||||||
|
case 'de':
|
||||||
|
return de;
|
||||||
|
case 'el':
|
||||||
|
return el;
|
||||||
|
case 'en-gb':
|
||||||
|
return enGB;
|
||||||
|
case 'en-us':
|
||||||
|
return enUS;
|
||||||
|
case 'es':
|
||||||
|
return es;
|
||||||
|
case 'es-ar':
|
||||||
|
return es;
|
||||||
|
case 'es-mx':
|
||||||
|
return es;
|
||||||
|
case 'fa':
|
||||||
|
return faIR;
|
||||||
|
case 'fi':
|
||||||
|
return fi;
|
||||||
|
case 'fr':
|
||||||
|
return fr;
|
||||||
|
case 'fr-ca':
|
||||||
|
return frCA;
|
||||||
|
case 'gsw':
|
||||||
|
return de;
|
||||||
|
case 'he':
|
||||||
|
return he;
|
||||||
|
case 'hi-in':
|
||||||
|
return hi;
|
||||||
|
case 'hr':
|
||||||
|
return hr;
|
||||||
|
case 'hu':
|
||||||
|
return hu;
|
||||||
|
case 'id':
|
||||||
|
return id;
|
||||||
|
case 'it':
|
||||||
|
return it;
|
||||||
|
case 'kk':
|
||||||
|
return kk;
|
||||||
|
case 'ko':
|
||||||
|
return ko;
|
||||||
|
case 'lt-lt':
|
||||||
|
return lt;
|
||||||
|
case 'ms':
|
||||||
|
return ms;
|
||||||
|
case 'nb':
|
||||||
|
return nb;
|
||||||
|
case 'nl':
|
||||||
|
return nl;
|
||||||
|
case 'pl':
|
||||||
|
return pl;
|
||||||
|
case 'pt-br':
|
||||||
|
return ptBR;
|
||||||
|
case 'pt-pt':
|
||||||
|
return pt;
|
||||||
|
case 'ro':
|
||||||
|
return ro;
|
||||||
|
case 'ru':
|
||||||
|
return ru;
|
||||||
|
case 'sk':
|
||||||
|
return sk;
|
||||||
|
case 'sl-si':
|
||||||
|
return sl;
|
||||||
|
case 'sv':
|
||||||
|
return sv;
|
||||||
|
case 'tr':
|
||||||
|
return tr;
|
||||||
|
case 'uk':
|
||||||
|
return uk;
|
||||||
|
case 'vi':
|
||||||
|
return vi;
|
||||||
|
case 'zh-cn':
|
||||||
|
return zhCN;
|
||||||
|
case 'zh-hk':
|
||||||
|
return zhCN;
|
||||||
|
case 'zh-tw':
|
||||||
|
return zhTW;
|
||||||
|
default:
|
||||||
|
return enUS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const localeWithSuffix = { addSuffix: true, locale: getLocale() };
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getLocale: getLocale,
|
||||||
|
localeWithSuffix: localeWithSuffix
|
||||||
|
};
|
|
@ -1,4 +1,4 @@
|
||||||
define(['connectionManager', 'userSettings', 'events'], function (connectionManager, userSettings, events) {
|
define(['userSettings', 'events'], function (userSettings, events) {
|
||||||
'use strict';
|
'use strict';
|
||||||
var fallbackCulture = 'en-us';
|
var fallbackCulture = 'en-us';
|
||||||
|
|
||||||
|
@ -253,7 +253,6 @@ define(['connectionManager', 'userSettings', 'events'], function (connectionMana
|
||||||
|
|
||||||
updateCurrentCulture();
|
updateCurrentCulture();
|
||||||
|
|
||||||
events.on(connectionManager, 'localusersignedin', updateCurrentCulture);
|
|
||||||
events.on(userSettings, 'change', function (e, name) {
|
events.on(userSettings, 'change', function (e, name) {
|
||||||
if (name === 'language' || name === 'datetimelocale') {
|
if (name === 'language' || name === 'datetimelocale') {
|
||||||
updateCurrentCulture();
|
updateCurrentCulture();
|
||||||
|
@ -269,6 +268,7 @@ define(['connectionManager', 'userSettings', 'events'], function (connectionMana
|
||||||
defaultModule: defaultModule,
|
defaultModule: defaultModule,
|
||||||
getCurrentLocale: getCurrentLocale,
|
getCurrentLocale: getCurrentLocale,
|
||||||
getCurrentDateTimeLocale: getCurrentDateTimeLocale,
|
getCurrentDateTimeLocale: getCurrentDateTimeLocale,
|
||||||
register: register
|
register: register,
|
||||||
|
updateCurrentCulture: updateCurrentCulture
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
define(["browser"], function (browser) {
|
/* eslint-disable indent */
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function getDeviceIcon(device) {
|
import browser from 'browser';
|
||||||
|
|
||||||
|
export function getDeviceIcon(device) {
|
||||||
var baseUrl = "assets/img/devices/";
|
var baseUrl = "assets/img/devices/";
|
||||||
switch (device.AppName || device.Client) {
|
switch (device.AppName || device.Client) {
|
||||||
case "Samsung Smart TV":
|
case "Samsung Smart TV":
|
||||||
|
@ -42,7 +43,7 @@ define(["browser"], function (browser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLibraryIcon(library) {
|
export function getLibraryIcon(library) {
|
||||||
switch (library) {
|
switch (library) {
|
||||||
case "movies":
|
case "movies":
|
||||||
return "video_library";
|
return "video_library";
|
||||||
|
@ -71,8 +72,9 @@ define(["browser"], function (browser) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
/* eslint-enable indent */
|
||||||
getDeviceIcon: getDeviceIcon,
|
|
||||||
getLibraryIcon: getLibraryIcon
|
export default {
|
||||||
};
|
getDeviceIcon: getDeviceIcon,
|
||||||
});
|
getLibraryIcon: getLibraryIcon
|
||||||
|
};
|
||||||
|
|
|
@ -83,7 +83,7 @@ define(["userSettings"], function (userSettings) {
|
||||||
|
|
||||||
if (html += '<div class="listPaging">', showControls) {
|
if (html += '<div class="listPaging">', showControls) {
|
||||||
html += '<span style="vertical-align:middle;">';
|
html += '<span style="vertical-align:middle;">';
|
||||||
html += (totalRecordCount ? startIndex + 1 : 0) + "-" + recordsEnd + " of " + totalRecordCount;
|
html += Globalize.translate("ListPaging", (totalRecordCount ? startIndex + 1 : 0), recordsEnd, totalRecordCount);
|
||||||
html += "</span>";
|
html += "</span>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -243,15 +243,20 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", "
|
||||||
html += '<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder" data-itemid="selectserver" href="selectserver.html?showuser=1"><i class="material-icons navMenuOptionIcon">wifi</i><span class="navMenuOptionText">' + globalize.translate("ButtonSelectServer") + "</span></a>";
|
html += '<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder" data-itemid="selectserver" href="selectserver.html?showuser=1"><i class="material-icons navMenuOptionIcon">wifi</i><span class="navMenuOptionText">' + globalize.translate("ButtonSelectServer") + "</span></a>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
html += '<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder btnSettings" data-itemid="settings" href="#"><i class="material-icons navMenuOptionIcon settings"></i><span class="navMenuOptionText">' + globalize.translate("ButtonSettings") + "</span></a>";
|
||||||
html += '<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder btnLogout" data-itemid="logout" href="#"><i class="material-icons navMenuOptionIcon exit_to_app"></i><span class="navMenuOptionText">' + globalize.translate("ButtonSignOut") + "</span></a>";
|
html += '<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder btnLogout" data-itemid="logout" href="#"><i class="material-icons navMenuOptionIcon exit_to_app"></i><span class="navMenuOptionText">' + globalize.translate("ButtonSignOut") + "</span></a>";
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
// add buttons to navigation drawer
|
// add buttons to navigation drawer
|
||||||
navDrawerScrollContainer.innerHTML = html;
|
navDrawerScrollContainer.innerHTML = html;
|
||||||
// bind logout button click to method
|
|
||||||
var btnLogout = navDrawerScrollContainer.querySelector(".btnLogout");
|
|
||||||
|
|
||||||
|
var btnSettings = navDrawerScrollContainer.querySelector(".btnSettings");
|
||||||
|
if (btnSettings) {
|
||||||
|
btnSettings.addEventListener("click", onSettingsClick);
|
||||||
|
}
|
||||||
|
|
||||||
|
var btnLogout = navDrawerScrollContainer.querySelector(".btnLogout");
|
||||||
if (btnLogout) {
|
if (btnLogout) {
|
||||||
btnLogout.addEventListener("click", onLogoutClick);
|
btnLogout.addEventListener("click", onLogoutClick);
|
||||||
}
|
}
|
||||||
|
@ -598,6 +603,10 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", "
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onSettingsClick() {
|
||||||
|
Dashboard.navigate("mypreferencesmenu.html");
|
||||||
|
}
|
||||||
|
|
||||||
function onLogoutClick() {
|
function onLogoutClick() {
|
||||||
Dashboard.logout();
|
Dashboard.logout();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
define(['appStorage', 'events'], function (appStorage, events) {
|
/* eslint-disable indent */
|
||||||
'use strict';
|
|
||||||
|
import appStorage from 'appStorage';
|
||||||
|
import events from 'events';
|
||||||
|
|
||||||
function getKey(name, userId) {
|
function getKey(name, userId) {
|
||||||
if (userId) {
|
if (userId) {
|
||||||
|
@ -9,18 +11,23 @@ define(['appStorage', 'events'], function (appStorage, events) {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function AppSettings() {
|
export function enableAutoLogin(val) {
|
||||||
}
|
|
||||||
|
|
||||||
AppSettings.prototype.enableAutoLogin = function (val) {
|
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
this.set('enableAutoLogin', val.toString());
|
this.set('enableAutoLogin', val.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('enableAutoLogin') !== 'false';
|
return this.get('enableAutoLogin') !== 'false';
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.enableAutomaticBitrateDetection = function (isInNetwork, mediaType, val) {
|
export function enableSystemExternalPlayers(val) {
|
||||||
|
if (val !== null) {
|
||||||
|
this.set('enableSystemExternalPlayers', val.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.get('enableSystemExternalPlayers') === 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enableAutomaticBitrateDetection(isInNetwork, mediaType, val) {
|
||||||
var key = 'enableautobitratebitrate-' + mediaType + '-' + isInNetwork;
|
var key = 'enableautobitratebitrate-' + mediaType + '-' + isInNetwork;
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
if (isInNetwork && mediaType === 'Audio') {
|
if (isInNetwork && mediaType === 'Audio') {
|
||||||
|
@ -35,9 +42,9 @@ define(['appStorage', 'events'], function (appStorage, events) {
|
||||||
} else {
|
} else {
|
||||||
return this.get(key) !== 'false';
|
return this.get(key) !== 'false';
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.maxStreamingBitrate = function (isInNetwork, mediaType, val) {
|
export function maxStreamingBitrate(isInNetwork, mediaType, val) {
|
||||||
var key = 'maxbitrate-' + mediaType + '-' + isInNetwork;
|
var key = 'maxbitrate-' + mediaType + '-' + isInNetwork;
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
if (isInNetwork && mediaType === 'Audio') {
|
if (isInNetwork && mediaType === 'Audio') {
|
||||||
|
@ -53,43 +60,43 @@ define(['appStorage', 'events'], function (appStorage, events) {
|
||||||
} else {
|
} else {
|
||||||
return parseInt(this.get(key) || '0') || 1500000;
|
return parseInt(this.get(key) || '0') || 1500000;
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.maxStaticMusicBitrate = function (val) {
|
export function maxStaticMusicBitrate(val) {
|
||||||
if (val !== undefined) {
|
if (val !== undefined) {
|
||||||
this.set('maxStaticMusicBitrate', val);
|
this.set('maxStaticMusicBitrate', val);
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultValue = 320000;
|
var defaultValue = 320000;
|
||||||
return parseInt(this.get('maxStaticMusicBitrate') || defaultValue.toString()) || defaultValue;
|
return parseInt(this.get('maxStaticMusicBitrate') || defaultValue.toString()) || defaultValue;
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.maxChromecastBitrate = function (val) {
|
export function maxChromecastBitrate(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
this.set('chromecastBitrate1', val);
|
this.set('chromecastBitrate1', val);
|
||||||
}
|
}
|
||||||
|
|
||||||
val = this.get('chromecastBitrate1');
|
val = this.get('chromecastBitrate1');
|
||||||
return val ? parseInt(val) : null;
|
return val ? parseInt(val) : null;
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.syncOnlyOnWifi = function (val) {
|
export function syncOnlyOnWifi(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
this.set('syncOnlyOnWifi', val.toString());
|
this.set('syncOnlyOnWifi', val.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('syncOnlyOnWifi') !== 'false';
|
return this.get('syncOnlyOnWifi') !== 'false';
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.syncPath = function (val) {
|
export function syncPath(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
this.set('syncPath', val);
|
this.set('syncPath', val);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('syncPath');
|
return this.get('syncPath');
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.cameraUploadServers = function (val) {
|
export function cameraUploadServers(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
this.set('cameraUploadServers', val.join(','));
|
this.set('cameraUploadServers', val.join(','));
|
||||||
}
|
}
|
||||||
|
@ -100,36 +107,42 @@ define(['appStorage', 'events'], function (appStorage, events) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return [];
|
return [];
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.runAtStartup = function (val) {
|
export function runAtStartup(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
this.set('runatstartup', val.toString());
|
this.set('runatstartup', val.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('runatstartup') === 'true';
|
return this.get('runatstartup') === 'true';
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.set = function (name, value, userId) {
|
export function set(name, value, userId) {
|
||||||
var currentValue = this.get(name, userId);
|
var currentValue = this.get(name, userId);
|
||||||
appStorage.setItem(getKey(name, userId), value);
|
appStorage.setItem(getKey(name, userId), value);
|
||||||
|
|
||||||
if (currentValue !== value) {
|
if (currentValue !== value) {
|
||||||
events.trigger(this, 'change', [name]);
|
events.trigger(this, 'change', [name]);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.get = function (name, userId) {
|
export function get(name, userId) {
|
||||||
return appStorage.getItem(getKey(name, userId));
|
return appStorage.getItem(getKey(name, userId));
|
||||||
};
|
}
|
||||||
|
|
||||||
AppSettings.prototype.enableSystemExternalPlayers = function (val) {
|
/* eslint-enable indent */
|
||||||
if (val != null) {
|
|
||||||
this.set('enableSystemExternalPlayers', val.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.get('enableSystemExternalPlayers') === 'true';
|
export default {
|
||||||
};
|
enableAutoLogin: enableAutoLogin,
|
||||||
|
enableSystemExternalPlayers: enableSystemExternalPlayers,
|
||||||
return new AppSettings();
|
enableAutomaticBitrateDetection: enableAutomaticBitrateDetection,
|
||||||
});
|
maxStreamingBitrate: maxStreamingBitrate,
|
||||||
|
maxStaticMusicBitrate: maxStaticMusicBitrate,
|
||||||
|
maxChromecastBitrate: maxChromecastBitrate,
|
||||||
|
syncOnlyOnWifi: syncOnlyOnWifi,
|
||||||
|
syncPath: syncPath,
|
||||||
|
cameraUploadServers: cameraUploadServers,
|
||||||
|
runAtStartup: runAtStartup,
|
||||||
|
set: set,
|
||||||
|
get: get
|
||||||
|
};
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
define(['appSettings', 'events'], function (appSettings, events) {
|
/* eslint-disable indent */
|
||||||
'use strict';
|
|
||||||
|
import appSettings from 'appSettings';
|
||||||
|
import events from 'events';
|
||||||
|
|
||||||
function onSaveTimeout() {
|
function onSaveTimeout() {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
@ -15,10 +17,7 @@ define(['appSettings', 'events'], function (appSettings, events) {
|
||||||
instance.saveTimeout = setTimeout(onSaveTimeout.bind(instance), 50);
|
instance.saveTimeout = setTimeout(onSaveTimeout.bind(instance), 50);
|
||||||
}
|
}
|
||||||
|
|
||||||
function UserSettings() {
|
export function setUserInfo(userId, apiClient) {
|
||||||
}
|
|
||||||
|
|
||||||
UserSettings.prototype.setUserInfo = function (userId, apiClient) {
|
|
||||||
if (this.saveTimeout) {
|
if (this.saveTimeout) {
|
||||||
clearTimeout(this.saveTimeout);
|
clearTimeout(this.saveTimeout);
|
||||||
}
|
}
|
||||||
|
@ -37,17 +36,17 @@ define(['appSettings', 'events'], function (appSettings, events) {
|
||||||
result.CustomPrefs = result.CustomPrefs || {};
|
result.CustomPrefs = result.CustomPrefs || {};
|
||||||
self.displayPrefs = result;
|
self.displayPrefs = result;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.getData = function () {
|
export function getData() {
|
||||||
return this.displayPrefs;
|
return this.displayPrefs;
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.importFrom = function (instance) {
|
export function importFrom(instance) {
|
||||||
this.displayPrefs = instance.getData();
|
this.displayPrefs = instance.getData();
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.set = function (name, value, enableOnServer) {
|
export function set(name, value, enableOnServer) {
|
||||||
var userId = this.currentUserId;
|
var userId = this.currentUserId;
|
||||||
var currentValue = this.get(name, enableOnServer);
|
var currentValue = this.get(name, enableOnServer);
|
||||||
var result = appSettings.set(name, value, userId);
|
var result = appSettings.set(name, value, userId);
|
||||||
|
@ -62,18 +61,18 @@ define(['appSettings', 'events'], function (appSettings, events) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.get = function (name, enableOnServer) {
|
export function get(name, enableOnServer) {
|
||||||
var userId = this.currentUserId;
|
var userId = this.currentUserId;
|
||||||
if (enableOnServer !== false && this.displayPrefs) {
|
if (enableOnServer !== false && this.displayPrefs) {
|
||||||
return this.displayPrefs.CustomPrefs[name];
|
return this.displayPrefs.CustomPrefs[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
return appSettings.get(name, userId);
|
return appSettings.get(name, userId);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.serverConfig = function (config) {
|
export function serverConfig(config) {
|
||||||
var apiClient = this.currentApiClient;
|
var apiClient = this.currentApiClient;
|
||||||
if (config) {
|
if (config) {
|
||||||
return apiClient.updateUserConfiguration(this.currentUserId, config);
|
return apiClient.updateUserConfiguration(this.currentUserId, config);
|
||||||
|
@ -82,135 +81,135 @@ define(['appSettings', 'events'], function (appSettings, events) {
|
||||||
return apiClient.getUser(this.currentUserId).then(function (user) {
|
return apiClient.getUser(this.currentUserId).then(function (user) {
|
||||||
return user.Configuration;
|
return user.Configuration;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.enableCinemaMode = function (val) {
|
export function enableCinemaMode(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('enableCinemaMode', val.toString(), false);
|
return this.set('enableCinemaMode', val.toString(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
val = this.get('enableCinemaMode', false);
|
val = this.get('enableCinemaMode', false);
|
||||||
return val !== 'false';
|
return val !== 'false';
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.enableNextVideoInfoOverlay = function (val) {
|
export function enableNextVideoInfoOverlay(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('enableNextVideoInfoOverlay', val.toString());
|
return this.set('enableNextVideoInfoOverlay', val.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
val = this.get('enableNextVideoInfoOverlay', false);
|
val = this.get('enableNextVideoInfoOverlay', false);
|
||||||
return val !== 'false';
|
return val !== 'false';
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.enableThemeSongs = function (val) {
|
export function enableThemeSongs(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('enableThemeSongs', val.toString(), false);
|
return this.set('enableThemeSongs', val.toString(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
val = this.get('enableThemeSongs', false);
|
val = this.get('enableThemeSongs', false);
|
||||||
return val !== 'false';
|
return val !== 'false';
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.enableThemeVideos = function (val) {
|
export function enableThemeVideos(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('enableThemeVideos', val.toString(), false);
|
return this.set('enableThemeVideos', val.toString(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
val = this.get('enableThemeVideos', false);
|
val = this.get('enableThemeVideos', false);
|
||||||
return val !== 'false';
|
return val !== 'false';
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.enableFastFadein = function (val) {
|
export function enableFastFadein(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('fastFadein', val.toString(), false);
|
return this.set('fastFadein', val.toString(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
val = this.get('fastFadein', false);
|
val = this.get('fastFadein', false);
|
||||||
return val !== 'false';
|
return val !== 'false';
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.enableBackdrops = function (val) {
|
export function enableBackdrops(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('enableBackdrops', val.toString(), false);
|
return this.set('enableBackdrops', val.toString(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
val = this.get('enableBackdrops', false);
|
val = this.get('enableBackdrops', false);
|
||||||
return val !== 'false';
|
return val !== 'false';
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.language = function (val) {
|
export function language(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('language', val.toString(), false);
|
return this.set('language', val.toString(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('language', false);
|
return this.get('language', false);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.dateTimeLocale = function (val) {
|
export function dateTimeLocale(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('datetimelocale', val.toString(), false);
|
return this.set('datetimelocale', val.toString(), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('datetimelocale', false);
|
return this.get('datetimelocale', false);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.skipBackLength = function (val) {
|
export function skipBackLength(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('skipBackLength', val.toString());
|
return this.set('skipBackLength', val.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseInt(this.get('skipBackLength') || '10000');
|
return parseInt(this.get('skipBackLength') || '10000');
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.skipForwardLength = function (val) {
|
export function skipForwardLength(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('skipForwardLength', val.toString());
|
return this.set('skipForwardLength', val.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseInt(this.get('skipForwardLength') || '30000');
|
return parseInt(this.get('skipForwardLength') || '30000');
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.dashboardTheme = function (val) {
|
export function dashboardTheme(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('dashboardTheme', val);
|
return this.set('dashboardTheme', val);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('dashboardTheme');
|
return this.get('dashboardTheme');
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.skin = function (val) {
|
export function skin(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('skin', val, false);
|
return this.set('skin', val, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('skin', false);
|
return this.get('skin', false);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.theme = function (val) {
|
export function theme(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('appTheme', val, false);
|
return this.set('appTheme', val, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('appTheme', false);
|
return this.get('appTheme', false);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.screensaver = function (val) {
|
export function screensaver(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('screensaver', val, false);
|
return this.set('screensaver', val, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('screensaver', false);
|
return this.get('screensaver', false);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.soundEffects = function (val) {
|
export function soundEffects(val) {
|
||||||
if (val != null) {
|
if (val != null) {
|
||||||
return this.set('soundeffects', val, false);
|
return this.set('soundeffects', val, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.get('soundeffects', false);
|
return this.get('soundeffects', false);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.loadQuerySettings = function (key, query) {
|
export function loadQuerySettings(key, query) {
|
||||||
var values = this.get(key);
|
var values = this.get(key);
|
||||||
if (values) {
|
if (values) {
|
||||||
values = JSON.parse(values);
|
values = JSON.parse(values);
|
||||||
|
@ -218,9 +217,9 @@ define(['appSettings', 'events'], function (appSettings, events) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return query;
|
return query;
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.saveQuerySettings = function (key, query) {
|
export function saveQuerySettings(key, query) {
|
||||||
var values = {};
|
var values = {};
|
||||||
if (query.SortBy) {
|
if (query.SortBy) {
|
||||||
values.SortBy = query.SortBy;
|
values.SortBy = query.SortBy;
|
||||||
|
@ -231,25 +230,24 @@ define(['appSettings', 'events'], function (appSettings, events) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.set(key, JSON.stringify(values));
|
return this.set(key, JSON.stringify(values));
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.getSubtitleAppearanceSettings = function (key) {
|
export function getSubtitleAppearanceSettings(key) {
|
||||||
key = key || 'localplayersubtitleappearance3';
|
key = key || 'localplayersubtitleappearance3';
|
||||||
return JSON.parse(this.get(key, false) || '{}');
|
return JSON.parse(this.get(key, false) || '{}');
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.setSubtitleAppearanceSettings = function (value, key) {
|
export function setSubtitleAppearanceSettings(value, key) {
|
||||||
key = key || 'localplayersubtitleappearance3';
|
key = key || 'localplayersubtitleappearance3';
|
||||||
return this.set(key, JSON.stringify(value), false);
|
return this.set(key, JSON.stringify(value), false);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.setFilter = function (key, value) {
|
export function setFilter(key, value) {
|
||||||
return this.set(key, value, true);
|
return this.set(key, value, true);
|
||||||
};
|
}
|
||||||
|
|
||||||
UserSettings.prototype.getFilter = function (key) {
|
export function getFilter(key) {
|
||||||
return this.get(key, true);
|
return this.get(key, true);
|
||||||
};
|
}
|
||||||
|
|
||||||
return UserSettings;
|
/* eslint-enable indent */
|
||||||
});
|
|
15
src/scripts/settings/webSettings.js
Normal file
15
src/scripts/settings/webSettings.js
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
let data;
|
||||||
|
|
||||||
|
function getConfig() {
|
||||||
|
if (data) return Promise.resolve(data);
|
||||||
|
return fetch("/config.json?nocache=" + new Date().getUTCMilliseconds()).then(function (response) {
|
||||||
|
data = response.json();
|
||||||
|
return data;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function enableMultiServer() {
|
||||||
|
return getConfig().then(config => {
|
||||||
|
return config.multiserver;
|
||||||
|
});
|
||||||
|
}
|
|
@ -323,11 +323,11 @@ var AppInfo = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getElementsPath() {
|
function getElementsPath() {
|
||||||
return "elements"
|
return "elements";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getScriptsPath() {
|
function getScriptsPath() {
|
||||||
return "scripts"
|
return "scripts";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPlaybackManager(playbackManager) {
|
function getPlaybackManager(playbackManager) {
|
||||||
|
@ -452,6 +452,9 @@ var AppInfo = {};
|
||||||
require(["autoFocuser"], function(autoFocuser) {
|
require(["autoFocuser"], function(autoFocuser) {
|
||||||
autoFocuser.enable();
|
autoFocuser.enable();
|
||||||
});
|
});
|
||||||
|
require(['globalize', 'connectionManager', 'events'], function (globalize, connectionManager, events) {
|
||||||
|
events.on(connectionManager, 'localusersignedin', globalize.updateCurrentCulture);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -574,6 +577,7 @@ var AppInfo = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
require(["mediaSession", "serverNotifications"]);
|
require(["mediaSession", "serverNotifications"]);
|
||||||
|
require(["date-fns", "date-fns/locale"]);
|
||||||
|
|
||||||
if (!browser.tv && !browser.xboxOne) {
|
if (!browser.tv && !browser.xboxOne) {
|
||||||
require(["components/playback/playbackorientation"]);
|
require(["components/playback/playbackorientation"]);
|
||||||
|
@ -647,12 +651,12 @@ var AppInfo = {};
|
||||||
inputManager: "scripts/inputManager",
|
inputManager: "scripts/inputManager",
|
||||||
datetime: "scripts/datetime",
|
datetime: "scripts/datetime",
|
||||||
globalize: "scripts/globalize",
|
globalize: "scripts/globalize",
|
||||||
|
dfnshelper: "scripts/dfnshelper",
|
||||||
libraryMenu: "scripts/librarymenu",
|
libraryMenu: "scripts/librarymenu",
|
||||||
playlisteditor: componentsPath + "/playlisteditor/playlisteditor",
|
playlisteditor: componentsPath + "/playlisteditor/playlisteditor",
|
||||||
medialibrarycreator: componentsPath + "/medialibrarycreator/medialibrarycreator",
|
medialibrarycreator: componentsPath + "/medialibrarycreator/medialibrarycreator",
|
||||||
medialibraryeditor: componentsPath + "/medialibraryeditor/medialibraryeditor",
|
medialibraryeditor: componentsPath + "/medialibraryeditor/medialibraryeditor",
|
||||||
imageoptionseditor: componentsPath + "/imageoptionseditor/imageoptionseditor",
|
imageoptionseditor: componentsPath + "/imageoptionseditor/imageoptionseditor",
|
||||||
humanedate: componentsPath + "/humanedate",
|
|
||||||
apphost: componentsPath + "/apphost",
|
apphost: componentsPath + "/apphost",
|
||||||
visibleinviewport: componentsPath + "/visibleinviewport",
|
visibleinviewport: componentsPath + "/visibleinviewport",
|
||||||
qualityoptions: componentsPath + "/qualityoptions",
|
qualityoptions: componentsPath + "/qualityoptions",
|
||||||
|
@ -690,12 +694,13 @@ var AppInfo = {};
|
||||||
"swiper",
|
"swiper",
|
||||||
"queryString",
|
"queryString",
|
||||||
"sortable",
|
"sortable",
|
||||||
"libjass",
|
|
||||||
"webcomponents",
|
"webcomponents",
|
||||||
"material-icons",
|
"material-icons",
|
||||||
"jellyfin-noto",
|
"jellyfin-noto",
|
||||||
|
"date-fns",
|
||||||
"page",
|
"page",
|
||||||
"polyfill"
|
"polyfill",
|
||||||
|
"classlist-polyfill"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
urlArgs: urlArgs,
|
urlArgs: urlArgs,
|
||||||
|
@ -704,6 +709,7 @@ var AppInfo = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
require(["polyfill"]);
|
require(["polyfill"]);
|
||||||
|
require(["classlist-polyfill"]);
|
||||||
|
|
||||||
// Expose jQuery globally
|
// Expose jQuery globally
|
||||||
require(["jQuery"], function(jQuery) {
|
require(["jQuery"], function(jQuery) {
|
||||||
|
@ -767,11 +773,9 @@ var AppInfo = {};
|
||||||
define("emby-textarea", [elementsPath + "/emby-textarea/emby-textarea"], returnFirstDependency);
|
define("emby-textarea", [elementsPath + "/emby-textarea/emby-textarea"], returnFirstDependency);
|
||||||
define("emby-toggle", [elementsPath + "/emby-toggle/emby-toggle"], returnFirstDependency);
|
define("emby-toggle", [elementsPath + "/emby-toggle/emby-toggle"], returnFirstDependency);
|
||||||
|
|
||||||
|
define("webSettings", [scriptsPath + "/settings/webSettings"], returnFirstDependency);
|
||||||
define("appSettings", [scriptsPath + "/settings/appSettings"], returnFirstDependency);
|
define("appSettings", [scriptsPath + "/settings/appSettings"], returnFirstDependency);
|
||||||
define("userSettingsBuilder", [scriptsPath + "/settings/userSettingsBuilder"], returnFirstDependency);
|
define("userSettings", [scriptsPath + "/settings/userSettings"], returnFirstDependency);
|
||||||
define("userSettings", ["userSettingsBuilder"], function(userSettingsBuilder) {
|
|
||||||
return new userSettingsBuilder();
|
|
||||||
});
|
|
||||||
|
|
||||||
define("chromecastHelper", [componentsPath + "/chromecast/chromecasthelpers"], returnFirstDependency);
|
define("chromecastHelper", [componentsPath + "/chromecast/chromecasthelpers"], returnFirstDependency);
|
||||||
define("mediaSession", [componentsPath + "/playback/mediasession"], returnFirstDependency);
|
define("mediaSession", [componentsPath + "/playback/mediasession"], returnFirstDependency);
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
define(["dom", "emby-button"], function (dom) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function onSubmit(e) {
|
|
||||||
if (dom.parentWithClass(this, "page").querySelector(".chkAccept").checked) {
|
|
||||||
Dashboard.navigate("wizardfinish.html");
|
|
||||||
} else {
|
|
||||||
Dashboard.alert({
|
|
||||||
message: Globalize.translate("MessagePleaseAcceptTermsOfServiceBeforeContinuing"),
|
|
||||||
title: ""
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
e.preventDefault();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return function (view, params) {
|
|
||||||
view.querySelector(".wizardAgreementForm").addEventListener("submit", onSubmit);
|
|
||||||
view.addEventListener("viewshow", function () {
|
|
||||||
document.querySelector(".skinHeader").classList.add("noHomeButtonHeader");
|
|
||||||
});
|
|
||||||
view.addEventListener("viewhide", function () {
|
|
||||||
document.querySelector(".skinHeader").classList.remove("noHomeButtonHeader");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -918,8 +918,8 @@
|
||||||
"HeaderFavoriteEpisodes": "الحلقات المفضلة",
|
"HeaderFavoriteEpisodes": "الحلقات المفضلة",
|
||||||
"HeaderFavoriteArtists": "الفنانون المفضلون",
|
"HeaderFavoriteArtists": "الفنانون المفضلون",
|
||||||
"Shows": "الحلقات",
|
"Shows": "الحلقات",
|
||||||
"Books": "كتب",
|
"Books": "الكتب",
|
||||||
"ValueSpecialEpisodeName": "مميز - {0}",
|
"ValueSpecialEpisodeName": "خاص - {0}",
|
||||||
"HeaderFavoriteAlbums": "الألبومات المفضلة",
|
"HeaderFavoriteAlbums": "الألبومات المفضلة",
|
||||||
"HeaderAlbumArtists": "فناني الألبومات",
|
"HeaderAlbumArtists": "فناني الألبومات",
|
||||||
"Genres": "الأنواع",
|
"Genres": "الأنواع",
|
||||||
|
@ -952,7 +952,7 @@
|
||||||
"Artists": "الفنانين",
|
"Artists": "الفنانين",
|
||||||
"Art": "فن",
|
"Art": "فن",
|
||||||
"Anytime": "اي وقت",
|
"Anytime": "اي وقت",
|
||||||
"AnyLanguage": "اي لغة",
|
"AnyLanguage": "أي لغة",
|
||||||
"AlwaysPlaySubtitlesHelp": "الترجمة التي تطابق تفضيلات اللغة سيتم تحميلها بغض النظر عن لغة الصوت.",
|
"AlwaysPlaySubtitlesHelp": "الترجمة التي تطابق تفضيلات اللغة سيتم تحميلها بغض النظر عن لغة الصوت.",
|
||||||
"AlwaysPlaySubtitles": "شغل الترجمة دائماً",
|
"AlwaysPlaySubtitles": "شغل الترجمة دائماً",
|
||||||
"AllowedRemoteAddressesHelp": "قائمة لعناوين IP أو إدخالات IP / قناع الشبكة مفصولة بفاصلة للشبكات التي سيتم السماح لها بالاتصال عن بعد. إذا تركت فارغة ، فسيتم السماح بجميع العناوين البعيدة.",
|
"AllowedRemoteAddressesHelp": "قائمة لعناوين IP أو إدخالات IP / قناع الشبكة مفصولة بفاصلة للشبكات التي سيتم السماح لها بالاتصال عن بعد. إذا تركت فارغة ، فسيتم السماح بجميع العناوين البعيدة.",
|
||||||
|
@ -1040,5 +1040,10 @@
|
||||||
"DatePlayed": "تاريخ التشغيل",
|
"DatePlayed": "تاريخ التشغيل",
|
||||||
"DateAdded": "تاريخ الاضافة",
|
"DateAdded": "تاريخ الاضافة",
|
||||||
"CriticRating": "تقييم النقاد",
|
"CriticRating": "تقييم النقاد",
|
||||||
"ResumeAt": "اكمل من {0}"
|
"ResumeAt": "اكمل من {0}",
|
||||||
|
"AskAdminToCreateLibrary": "أطلب من الأدمن إنشاء مكتبة.",
|
||||||
|
"Artist": "الفنان",
|
||||||
|
"AllowFfmpegThrottling": "إبطاء الترميزات",
|
||||||
|
"AlbumArtist": "المؤدي",
|
||||||
|
"Album": "الألبوم"
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"All": "Всички",
|
"All": "Всички",
|
||||||
"AllLibraries": "Всички библиотеки",
|
"AllLibraries": "Всички библиотеки",
|
||||||
"Art": "Картина",
|
"Art": "Картина",
|
||||||
"Artists": "Изпълнители",
|
"Artists": "Артисти",
|
||||||
"AttributeNew": "Нови",
|
"AttributeNew": "Нови",
|
||||||
"Audio": "Звук",
|
"Audio": "Звук",
|
||||||
"Auto": "Автоматично",
|
"Auto": "Автоматично",
|
||||||
|
@ -101,8 +101,7 @@
|
||||||
"Desktop": "Работен плот",
|
"Desktop": "Работен плот",
|
||||||
"DeviceAccessHelp": "Това се отнася само за устройства, които могат да бъдат различени и няма да попречи на достъп от мрежов четец. Филтрирането на потребителски устройства ще предотврати използването им докато не бъдат одобрени тук.",
|
"DeviceAccessHelp": "Това се отнася само за устройства, които могат да бъдат различени и няма да попречи на достъп от мрежов четец. Филтрирането на потребителски устройства ще предотврати използването им докато не бъдат одобрени тук.",
|
||||||
"Director": "Режисьор",
|
"Director": "Режисьор",
|
||||||
"DirectorValue": "Режисьор: {0}",
|
"Directors": "Режисьори",
|
||||||
"DirectorsValue": "Режисьори: {0}",
|
|
||||||
"Disc": "Диск",
|
"Disc": "Диск",
|
||||||
"Dislike": "Нехаресване",
|
"Dislike": "Нехаресване",
|
||||||
"Display": "Показване",
|
"Display": "Показване",
|
||||||
|
@ -137,9 +136,8 @@
|
||||||
"FormatValue": "Формат: {0}",
|
"FormatValue": "Формат: {0}",
|
||||||
"Friday": "Петък",
|
"Friday": "Петък",
|
||||||
"Fullscreen": "Цял екран",
|
"Fullscreen": "Цял екран",
|
||||||
"GenreValue": "Жанр: {0}",
|
"Genre": "Жанр",
|
||||||
"Genres": "Жанрове",
|
"Genres": "Жанрове",
|
||||||
"GenresValue": "Жанрове: {0}",
|
|
||||||
"GroupVersions": "Групиране на версиите",
|
"GroupVersions": "Групиране на версиите",
|
||||||
"GuestStar": "Гостуваща звезда",
|
"GuestStar": "Гостуваща звезда",
|
||||||
"Guide": "Справочник",
|
"Guide": "Справочник",
|
||||||
|
@ -305,9 +303,9 @@
|
||||||
"LabelCriticRating": "Оценка на критиците:",
|
"LabelCriticRating": "Оценка на критиците:",
|
||||||
"LabelCurrentPassword": "Текуща парола:",
|
"LabelCurrentPassword": "Текуща парола:",
|
||||||
"LabelCustomCertificatePath": "Път към потребителския сертификат:",
|
"LabelCustomCertificatePath": "Път към потребителския сертификат:",
|
||||||
"LabelCustomCertificatePathHelp": "Път до файл с шифровъчен стандарт №12, съдържащ сертификат и частен ключ за поддръжка на протокол TLS на собствен домейн.",
|
"LabelCustomCertificatePathHelp": "Път до файл с шифровъчен стандарт №12 (PKCS #12), съдържащ сертификат и частен ключ за поддръжка на протокол TLS на собствен домейн.",
|
||||||
"LabelCustomCss": "CSS по избор:",
|
"LabelCustomCss": "CSS по избор:",
|
||||||
"LabelCustomCssHelp": "Използвайте собствен CSS към уеб интерфейса.",
|
"LabelCustomCssHelp": "Добавете собствен стил за Уеб-интерфейса.",
|
||||||
"LabelCustomDeviceDisplayName": "Показвано име:",
|
"LabelCustomDeviceDisplayName": "Показвано име:",
|
||||||
"LabelCustomRating": "Оценка по избор:",
|
"LabelCustomRating": "Оценка по избор:",
|
||||||
"LabelDashboardTheme": "Облик на сървърното табло:",
|
"LabelDashboardTheme": "Облик на сървърното табло:",
|
||||||
|
@ -435,7 +433,7 @@
|
||||||
"LabelStatus": "Състояние:",
|
"LabelStatus": "Състояние:",
|
||||||
"LabelStopWhenPossible": "Спирай, когато е възможно:",
|
"LabelStopWhenPossible": "Спирай, когато е възможно:",
|
||||||
"LabelSubtitlePlaybackMode": "Режим на субтитрите:",
|
"LabelSubtitlePlaybackMode": "Режим на субтитрите:",
|
||||||
"LabelSubtitles": "Субтитри:",
|
"LabelSubtitles": "Субтитри",
|
||||||
"LabelSupportedMediaTypes": "Поддържани типове медия:",
|
"LabelSupportedMediaTypes": "Поддържани типове медия:",
|
||||||
"LabelTag": "Етикет:",
|
"LabelTag": "Етикет:",
|
||||||
"LabelTextColor": "Цвят на текста:",
|
"LabelTextColor": "Цвят на текста:",
|
||||||
|
@ -788,7 +786,7 @@
|
||||||
"AllowMediaConversion": "Разрешаване на медийни преобразувания",
|
"AllowMediaConversion": "Разрешаване на медийни преобразувания",
|
||||||
"AllLanguages": "Всички езици",
|
"AllLanguages": "Всички езици",
|
||||||
"AllEpisodes": "Всички епизоди",
|
"AllEpisodes": "Всички епизоди",
|
||||||
"AllComplexFormats": "Всички комплексни формати (ASS, SSA, VOBSUB, PGS, SUB/IDX, и т.н.)",
|
"AllComplexFormats": "Всички общи формати (ASS, SSA, VOBSUB, PGS, SUB/IDX, и др. )",
|
||||||
"AllChannels": "Всички канали",
|
"AllChannels": "Всички канали",
|
||||||
"Alerts": "Известия",
|
"Alerts": "Известия",
|
||||||
"AdditionalNotificationServices": "Разгледайте каталога с добавки за допълнителни услуги за известяване.",
|
"AdditionalNotificationServices": "Разгледайте каталога с добавки за допълнителни услуги за известяване.",
|
||||||
|
@ -812,7 +810,7 @@
|
||||||
"MediaInfoLayout": "Подредба",
|
"MediaInfoLayout": "Подредба",
|
||||||
"MusicVideo": "Музикален клип",
|
"MusicVideo": "Музикален клип",
|
||||||
"MediaInfoStreamTypeVideo": "Видео",
|
"MediaInfoStreamTypeVideo": "Видео",
|
||||||
"LabelVideo": "Видео:",
|
"LabelVideo": "Видео",
|
||||||
"HeaderVideoTypes": "Видове видеа",
|
"HeaderVideoTypes": "Видове видеа",
|
||||||
"HeaderVideoType": "Вид на видеото",
|
"HeaderVideoType": "Вид на видеото",
|
||||||
"EnableExternalVideoPlayers": "Външни възпроизводители",
|
"EnableExternalVideoPlayers": "Външни възпроизводители",
|
||||||
|
@ -836,5 +834,43 @@
|
||||||
"AllowOnTheFlySubtitleExtraction": "Позволява моментално извличане на поднадписи",
|
"AllowOnTheFlySubtitleExtraction": "Позволява моментално извличане на поднадписи",
|
||||||
"AllowHWTranscodingHelp": "Позволява на тунера да прекодира моментално. Това може да помогне за редуциране на прекодирането от сървъра.",
|
"AllowHWTranscodingHelp": "Позволява на тунера да прекодира моментално. Това може да помогне за редуциране на прекодирането от сървъра.",
|
||||||
"AddItemToCollectionHelp": "Добавяне към колекция чрез търсенето им и използване на дясно-щракване с мишката или контекстното меню.",
|
"AddItemToCollectionHelp": "Добавяне към колекция чрез търсенето им и използване на дясно-щракване с мишката или контекстното меню.",
|
||||||
"Absolute": "Aбсолютен"
|
"Absolute": "Aбсолютен",
|
||||||
|
"LabelLanNetworks": "Локални мрежи:",
|
||||||
|
"LabelKodiMetadataSaveImagePathsHelp": "Това е препоръчително ако имате изображения, пътят към които не е съобразен с изискванията на Kodi",
|
||||||
|
"LabelKodiMetadataSaveImagePaths": "Записване на пътеките към изображенията в nfo файловете",
|
||||||
|
"LabelChannels": "Канали:",
|
||||||
|
"DropShadow": "Сянка",
|
||||||
|
"Raised": "Повишено",
|
||||||
|
"OptionResElement": "рес. елемент",
|
||||||
|
"ButtonChangeServer": "Смяна на сървър",
|
||||||
|
"ButtonAddImage": "Добавяне на изображение",
|
||||||
|
"BrowsePluginCatalogMessage": "За да видите наличните добавки, прегледайте каталога с добавките.",
|
||||||
|
"Box": "Кутия",
|
||||||
|
"AlwaysPlaySubtitlesHelp": "Поднадписите, съвпадащи с езика от настройките, ще се зареждат, независимо от езика на аудио то.",
|
||||||
|
"BookLibraryHelp": "Поддържат се звукови и текстови книги. Преглед на инструкция за наименоване {1} на книга {0}.",
|
||||||
|
"Blacklist": "Списък с блокирани",
|
||||||
|
"BirthLocation": "Месторождение",
|
||||||
|
"Banner": "Банер",
|
||||||
|
"AspectRatio": "Съотношение",
|
||||||
|
"AskAdminToCreateLibrary": "Помолете администратора за създаване на библиотека.",
|
||||||
|
"Ascending": "Възходящо",
|
||||||
|
"AsManyAsPossible": "Колкото е възможно повече",
|
||||||
|
"Artist": "Артист",
|
||||||
|
"AroundTime": "Към {0}",
|
||||||
|
"Anytime": "По всяко време",
|
||||||
|
"AnyLanguage": "Който и да е език",
|
||||||
|
"AlwaysPlaySubtitles": "Постоянно изпълнение",
|
||||||
|
"AllowRemoteAccessHelp": "Ако не е маркирано, всеки отдалечен достъп ще бъде блокиран.",
|
||||||
|
"AllowRemoteAccess": "Позволяване на отдалечен достъп до този Jellyfin сървър.",
|
||||||
|
"AllowFfmpegThrottling": "Подтискане на прекодирането",
|
||||||
|
"AllowMediaConversionHelp": "Даване или отнемане на права за функциите за конвертиране на медия.",
|
||||||
|
"AlbumArtist": "Изпълнител",
|
||||||
|
"Album": "Албум",
|
||||||
|
"ClientSettings": "Клиентски настройки",
|
||||||
|
"ChannelNumber": "Номер на канала",
|
||||||
|
"ChannelNameOnly": "Само {0} канал",
|
||||||
|
"CancelSeries": "Откажи сериите",
|
||||||
|
"CancelRecording": "Откажи записа",
|
||||||
|
"ButtonSplit": "Раздели",
|
||||||
|
"ButtonResetEasyPassword": "Нулиране на бързия ПИН код"
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
"AllEpisodes": "Všechny epizody",
|
"AllEpisodes": "Všechny epizody",
|
||||||
"AllLanguages": "Všechny jazyky",
|
"AllLanguages": "Všechny jazyky",
|
||||||
"AllowHWTranscodingHelp": "Povolit tuneru překódování v reálném čase. Může snížit zátěž překódovávání požadované Jellyfin serverem.",
|
"AllowHWTranscodingHelp": "Povolit tuneru překódování v reálném čase. Může snížit zátěž překódovávání požadované Jellyfin serverem.",
|
||||||
"AlwaysPlaySubtitles": "Vždy zobrazit titulky",
|
"AlwaysPlaySubtitles": "Vždy zobrazovat",
|
||||||
"AlwaysPlaySubtitlesHelp": "Titulky odpovídající jazykové předvolbě se načtou bez ohledu na jazyk audia.",
|
"AlwaysPlaySubtitlesHelp": "Titulky odpovídající jazykové předvolbě se načtou bez ohledu na jazyk audia.",
|
||||||
"Anytime": "Kdykoliv",
|
"Anytime": "Kdykoliv",
|
||||||
"AroundTime": "Okolo {0}",
|
"AroundTime": "Okolo {0}",
|
||||||
|
@ -158,7 +158,7 @@
|
||||||
"Display": "Zobrazení",
|
"Display": "Zobrazení",
|
||||||
"DisplayMissingEpisodesWithinSeasons": "Zobrazit chybějící epizody",
|
"DisplayMissingEpisodesWithinSeasons": "Zobrazit chybějící epizody",
|
||||||
"DisplayMissingEpisodesWithinSeasonsHelp": "Toto musí být zapnuto pro knihovny TV v nastavení serveru.",
|
"DisplayMissingEpisodesWithinSeasonsHelp": "Toto musí být zapnuto pro knihovny TV v nastavení serveru.",
|
||||||
"DisplayModeHelp": "Zvolte typ obrazovky, na které používáte Jellyfin.",
|
"DisplayModeHelp": "Vyberte styl rozvržení, který chcete pro rozhraní.",
|
||||||
"DoNotRecord": "Nenahrávat",
|
"DoNotRecord": "Nenahrávat",
|
||||||
"Down": "Dolů",
|
"Down": "Dolů",
|
||||||
"Download": "Stáhnout",
|
"Download": "Stáhnout",
|
||||||
|
@ -426,7 +426,7 @@
|
||||||
"Images": "Obrázky",
|
"Images": "Obrázky",
|
||||||
"ImportFavoriteChannelsHelp": "Pokud je povoleno, jen kanály označené jako oblíbené budou importována na zařízení tuneru.",
|
"ImportFavoriteChannelsHelp": "Pokud je povoleno, jen kanály označené jako oblíbené budou importována na zařízení tuneru.",
|
||||||
"ImportMissingEpisodesHelp": "Pokud je povoleno, budou informace o chybějících epizodách importovány do databáze Jellyfin a zobrazí se v sezónách seriálu. To může způsobit podstatně delší skenování knihovny.",
|
"ImportMissingEpisodesHelp": "Pokud je povoleno, budou informace o chybějících epizodách importovány do databáze Jellyfin a zobrazí se v sezónách seriálu. To může způsobit podstatně delší skenování knihovny.",
|
||||||
"InstallingPackage": "Instalace {0}",
|
"InstallingPackage": "Instalace {0} (Verze {1})",
|
||||||
"InstantMix": "Okamžité míchání",
|
"InstantMix": "Okamžité míchání",
|
||||||
"ItemCount": "{0} položek",
|
"ItemCount": "{0} položek",
|
||||||
"Items": "Položky",
|
"Items": "Položky",
|
||||||
|
@ -461,7 +461,7 @@
|
||||||
"LabelArtist": "Umělec",
|
"LabelArtist": "Umělec",
|
||||||
"LabelArtists": "Umělci:",
|
"LabelArtists": "Umělci:",
|
||||||
"LabelArtistsHelp": "Odděl pomocí ;",
|
"LabelArtistsHelp": "Odděl pomocí ;",
|
||||||
"LabelAudio": "Zvuk:",
|
"LabelAudio": "Zvuk",
|
||||||
"LabelAudioLanguagePreference": "Preferovaný jazyk zvuku:",
|
"LabelAudioLanguagePreference": "Preferovaný jazyk zvuku:",
|
||||||
"LabelBindToLocalNetworkAddress": "Vázat na místní síťovou adresu:",
|
"LabelBindToLocalNetworkAddress": "Vázat na místní síťovou adresu:",
|
||||||
"LabelBindToLocalNetworkAddressHelp": "Volitelné. Přepsat lokální IP adresu vazanou na http server. Pokud je ponecháno prázdné, server se sváže ke všem dostupným adresám (aplikace bude dostupná na všech síťových zařízení, které server nabízí). Změna této hodnoty vyžaduje restartování Jellyfin Serveru.",
|
"LabelBindToLocalNetworkAddressHelp": "Volitelné. Přepsat lokální IP adresu vazanou na http server. Pokud je ponecháno prázdné, server se sváže ke všem dostupným adresám (aplikace bude dostupná na všech síťových zařízení, které server nabízí). Změna této hodnoty vyžaduje restartování Jellyfin Serveru.",
|
||||||
|
@ -709,7 +709,7 @@
|
||||||
"LabelStopping": "Zastavování",
|
"LabelStopping": "Zastavování",
|
||||||
"LabelSubtitleFormatHelp": "Příklad: srt",
|
"LabelSubtitleFormatHelp": "Příklad: srt",
|
||||||
"LabelSubtitlePlaybackMode": "Mód titulků:",
|
"LabelSubtitlePlaybackMode": "Mód titulků:",
|
||||||
"LabelSubtitles": "Titulky:",
|
"LabelSubtitles": "Titulky",
|
||||||
"LabelSupportedMediaTypes": "Podporované typy médií:",
|
"LabelSupportedMediaTypes": "Podporované typy médií:",
|
||||||
"LabelTagline": "Slogan:",
|
"LabelTagline": "Slogan:",
|
||||||
"LabelTextBackgroundColor": "Barva pozadí textu:",
|
"LabelTextBackgroundColor": "Barva pozadí textu:",
|
||||||
|
@ -862,14 +862,14 @@
|
||||||
"NoNextUpItemsMessage": "Nic nenalezeno. Začněte sledovat Vaše oblíbené seriály!",
|
"NoNextUpItemsMessage": "Nic nenalezeno. Začněte sledovat Vaše oblíbené seriály!",
|
||||||
"NoPluginConfigurationMessage": "Tento zásuvný modul nemá žádné nastavení.",
|
"NoPluginConfigurationMessage": "Tento zásuvný modul nemá žádné nastavení.",
|
||||||
"NoSubtitleSearchResultsFound": "Žádné výsledky.",
|
"NoSubtitleSearchResultsFound": "Žádné výsledky.",
|
||||||
"NoSubtitles": "Žádné titulky",
|
"NoSubtitles": "Žádné",
|
||||||
"NoSubtitlesHelp": "Ve výchozím nastavení nebudou titulky načteny. Během přehrávání však mohou být manuálně zapnuty.",
|
"NoSubtitlesHelp": "Ve výchozím nastavení nebudou titulky načteny. Během přehrávání však mohou být manuálně zapnuty.",
|
||||||
"None": "Žádný",
|
"None": "Žádný",
|
||||||
"Normal": "Normální",
|
"Normal": "Normální",
|
||||||
"NumLocationsValue": "{0} složky",
|
"NumLocationsValue": "{0} složky",
|
||||||
"Off": "Vypnuto",
|
"Off": "Vypnuto",
|
||||||
"OneChannel": "Jeden kanál",
|
"OneChannel": "Jeden kanál",
|
||||||
"OnlyForcedSubtitles": "Pouze vynucené titulky",
|
"OnlyForcedSubtitles": "Pouze vynucené",
|
||||||
"OnlyForcedSubtitlesHelp": "Jen vynucené titulky budou nahrány.",
|
"OnlyForcedSubtitlesHelp": "Jen vynucené titulky budou nahrány.",
|
||||||
"OptionAdminUsers": "Administrátoři",
|
"OptionAdminUsers": "Administrátoři",
|
||||||
"OptionAlbumArtist": "Umělec Alba",
|
"OptionAlbumArtist": "Umělec Alba",
|
||||||
|
@ -1248,7 +1248,7 @@
|
||||||
"Blacklist": "Černá listina",
|
"Blacklist": "Černá listina",
|
||||||
"BobAndWeaveWithHelp": "Bob and weave (vyšší kvalita, ale pomalejší)",
|
"BobAndWeaveWithHelp": "Bob and weave (vyšší kvalita, ale pomalejší)",
|
||||||
"Browse": "Procházet",
|
"Browse": "Procházet",
|
||||||
"BurnSubtitlesHelp": "Určuje, zda má server vypalovat titulky při převodu videa v závislosti na formátu titulků. Vynechání vypalování titulků zlepší výkon serveru. Chcete-li vypálit grafické formáty (VOBSUB, PGS, SUB / IDX atd.) a některé titulky ASS / SSA, vyberte možnost Auto.",
|
"BurnSubtitlesHelp": "Určuje, zda má server vypalovat titulky při překódování videa. Vynechání tohoto zlepší výkon serveru. Chcete-li vypálit grafické formáty (VOBSUB, PGS, SUB / IDX atd.) a některé titulky ASS / SSA, vyberte možnost Auto.",
|
||||||
"ButtonInfo": "Info",
|
"ButtonInfo": "Info",
|
||||||
"ButtonMenu": "Menu",
|
"ButtonMenu": "Menu",
|
||||||
"ButtonOk": "Ok",
|
"ButtonOk": "Ok",
|
||||||
|
@ -1276,8 +1276,7 @@
|
||||||
"DetectingDevices": "Hledání zařízení",
|
"DetectingDevices": "Hledání zařízení",
|
||||||
"DirectPlayError": "Chyba přímého přehrávání",
|
"DirectPlayError": "Chyba přímého přehrávání",
|
||||||
"DirectStreamHelp2": "Přímé streamování souboru používá velmi malý výkon bez ztráty kvality videa.",
|
"DirectStreamHelp2": "Přímé streamování souboru používá velmi malý výkon bez ztráty kvality videa.",
|
||||||
"DirectorValue": "Režisér: {0}",
|
"Directors": "Režiséři",
|
||||||
"DirectorsValue": "Režiséři: {0}",
|
|
||||||
"Disabled": "Vypnuto",
|
"Disabled": "Vypnuto",
|
||||||
"DisplayInMyMedia": "Zobrazit na domovské obrazovce",
|
"DisplayInMyMedia": "Zobrazit na domovské obrazovce",
|
||||||
"DisplayInOtherHomeScreenSections": "Zobrazení v sekcích domovské obrazovky, jako jsou nejnovější média, a pokračování ve sledování",
|
"DisplayInOtherHomeScreenSections": "Zobrazení v sekcích domovské obrazovky, jako jsou nejnovější média, a pokračování ve sledování",
|
||||||
|
@ -1297,8 +1296,7 @@
|
||||||
"Filters": "Filtry",
|
"Filters": "Filtry",
|
||||||
"Folders": "Složky",
|
"Folders": "Složky",
|
||||||
"General": "Hlavní",
|
"General": "Hlavní",
|
||||||
"GenreValue": "Žánr: {0}",
|
"Genre": "Žánr",
|
||||||
"GenresValue": "Žánry: {0}",
|
|
||||||
"GroupBySeries": "Seskupit podle série",
|
"GroupBySeries": "Seskupit podle série",
|
||||||
"HandledByProxy": "Zpracováno reverzním proxy",
|
"HandledByProxy": "Zpracováno reverzním proxy",
|
||||||
"HeaderAddLocalUser": "Přidat místního uživatele",
|
"HeaderAddLocalUser": "Přidat místního uživatele",
|
||||||
|
@ -1395,7 +1393,7 @@
|
||||||
"LabelUrl": "URL:",
|
"LabelUrl": "URL:",
|
||||||
"LabelUserAgent": "User agent:",
|
"LabelUserAgent": "User agent:",
|
||||||
"LabelUserRemoteClientBitrateLimitHelp": "Přepíše výchozí globální hodnotu nastavenou v nastavení přehrávání serveru.",
|
"LabelUserRemoteClientBitrateLimitHelp": "Přepíše výchozí globální hodnotu nastavenou v nastavení přehrávání serveru.",
|
||||||
"LabelVideo": "Video:",
|
"LabelVideo": "Video",
|
||||||
"LabelVideoCodec": "Video kodek:",
|
"LabelVideoCodec": "Video kodek:",
|
||||||
"LeaveBlankToNotSetAPassword": "Můžete ponechat prázdné pro nastavení bez hesla.",
|
"LeaveBlankToNotSetAPassword": "Můžete ponechat prázdné pro nastavení bez hesla.",
|
||||||
"LetterButtonAbbreviation": "A",
|
"LetterButtonAbbreviation": "A",
|
||||||
|
@ -1572,5 +1570,29 @@
|
||||||
"NoCreatedLibraries": "Zdá se, že jste dosud nevytvořili žádnou knihovnu. {0}Chtěli byste nějakou vytvořit nyní?{1}",
|
"NoCreatedLibraries": "Zdá se, že jste dosud nevytvořili žádnou knihovnu. {0}Chtěli byste nějakou vytvořit nyní?{1}",
|
||||||
"AskAdminToCreateLibrary": "Požádejte administrátora o vytvoření knihovny.",
|
"AskAdminToCreateLibrary": "Požádejte administrátora o vytvoření knihovny.",
|
||||||
"AllowFfmpegThrottlingHelp": "Když se překódování nebo remux dostane dostatečně daleko dopředu od aktuální pozice přehrávání, pozastaví se proces, aby spotřeboval méně zdrojů. To je nejužitečnější při sledování bez častého vyhledávání. Pokud máte problémy s přehráváním, vypněte tuto funkci.",
|
"AllowFfmpegThrottlingHelp": "Když se překódování nebo remux dostane dostatečně daleko dopředu od aktuální pozice přehrávání, pozastaví se proces, aby spotřeboval méně zdrojů. To je nejužitečnější při sledování bez častého vyhledávání. Pokud máte problémy s přehráváním, vypněte tuto funkci.",
|
||||||
"AllowFfmpegThrottling": "Omezit překódování"
|
"AllowFfmpegThrottling": "Omezit překódování",
|
||||||
|
"BoxSet": "Sbírka",
|
||||||
|
"Track": "Stopa",
|
||||||
|
"Season": "Sezóna",
|
||||||
|
"ReleaseGroup": "Vydavatel",
|
||||||
|
"PreferEmbeddedEpisodeInfosOverFileNames": "Preferovat vložené informace o epizodě před názvy souborů",
|
||||||
|
"PreferEmbeddedEpisodeInfosOverFileNamesHelp": "Používá se informace o epizodě z vložených metadat, pokud jsou k dispozici.",
|
||||||
|
"Person": "Osoba",
|
||||||
|
"OtherArtist": "Ostatní interpreti",
|
||||||
|
"Movie": "Film",
|
||||||
|
"Episode": "Epizoda",
|
||||||
|
"ClientSettings": "Nastavení klienta",
|
||||||
|
"Artist": "Interpret",
|
||||||
|
"AlbumArtist": "Interpret alba",
|
||||||
|
"Album": "Album",
|
||||||
|
"OnApplicationStartup": "Při zapnutí aplikace",
|
||||||
|
"EveryXHours": "Každých {0} hodin",
|
||||||
|
"EveryHour": "Každou hodinu",
|
||||||
|
"EveryXMinutes": "Každých {0} minut",
|
||||||
|
"OnWakeFromSleep": "Při probuzení",
|
||||||
|
"DailyAt": "Denně v {0}",
|
||||||
|
"PersonRole": "jako {0}",
|
||||||
|
"ListPaging": "{0}-{1} ze {2}",
|
||||||
|
"WriteAccessRequired": "Jellyfin Server potřebuje oprávnění pro zápis v této složce. Zkontrolujte oprávnění a zkuste to znovu.",
|
||||||
|
"PathNotFound": "Cesta nebyla nalezena. Zkontrolujte, zda je platná a zkuste to znovu."
|
||||||
}
|
}
|
||||||
|
|
|
@ -1166,8 +1166,7 @@
|
||||||
"DirectStreamHelp1": "Medie filen er kompatibel med enheden i forhold til opløsning og medie type (H.264,AC3, etc.), men er i en ikke kompatibel fil container (mkv, avi, wmv, etc). Videoen vil blive genpakket live før den streames til enheden.",
|
"DirectStreamHelp1": "Medie filen er kompatibel med enheden i forhold til opløsning og medie type (H.264,AC3, etc.), men er i en ikke kompatibel fil container (mkv, avi, wmv, etc). Videoen vil blive genpakket live før den streames til enheden.",
|
||||||
"DirectStreamHelp2": "Direkte Streaming af en fil bruger meget lidt processor kraft uden nogen tab af video kvalitet.",
|
"DirectStreamHelp2": "Direkte Streaming af en fil bruger meget lidt processor kraft uden nogen tab af video kvalitet.",
|
||||||
"DirectStreaming": "Direkte streaming",
|
"DirectStreaming": "Direkte streaming",
|
||||||
"DirectorValue": "Instruktør: {0}",
|
"Directors": "Instruktører",
|
||||||
"DirectorsValue": "Instruktører: {0}",
|
|
||||||
"Disc": "Disk",
|
"Disc": "Disk",
|
||||||
"Dislike": "Kan ikke lide",
|
"Dislike": "Kan ikke lide",
|
||||||
"Display": "Visning",
|
"Display": "Visning",
|
||||||
|
@ -1207,8 +1206,7 @@
|
||||||
"Features": "Funktioner",
|
"Features": "Funktioner",
|
||||||
"Filters": "Filtre",
|
"Filters": "Filtre",
|
||||||
"FormatValue": "Format: {0}",
|
"FormatValue": "Format: {0}",
|
||||||
"GenreValue": "Genre: {0}",
|
"Genre": "Genre",
|
||||||
"GenresValue": "Genrer: {0}",
|
|
||||||
"GroupBySeries": "Gruppér efter serie",
|
"GroupBySeries": "Gruppér efter serie",
|
||||||
"Guide": "Vejledning",
|
"Guide": "Vejledning",
|
||||||
"GuideProviderLogin": "Log Ind",
|
"GuideProviderLogin": "Log Ind",
|
||||||
|
@ -1270,7 +1268,7 @@
|
||||||
"Label3DFormat": "3D format:",
|
"Label3DFormat": "3D format:",
|
||||||
"LabelAlbum": "Album:",
|
"LabelAlbum": "Album:",
|
||||||
"LabelArtist": "Kunstner",
|
"LabelArtist": "Kunstner",
|
||||||
"LabelAudio": "Lyd:",
|
"LabelAudio": "Lyd",
|
||||||
"LabelBitrateMbps": "Bitrate (Mbps):",
|
"LabelBitrateMbps": "Bitrate (Mbps):",
|
||||||
"LabelBlockContentWithTags": "Blokér filer med mærkerne:",
|
"LabelBlockContentWithTags": "Blokér filer med mærkerne:",
|
||||||
"LabelBurnSubtitles": "Brænd undertekster:",
|
"LabelBurnSubtitles": "Brænd undertekster:",
|
||||||
|
@ -1315,7 +1313,7 @@
|
||||||
"LabelSortOrder": "Sorteringsorden:",
|
"LabelSortOrder": "Sorteringsorden:",
|
||||||
"LabelSoundEffects": "Lydeffekter:",
|
"LabelSoundEffects": "Lydeffekter:",
|
||||||
"LabelStatus": "Status:",
|
"LabelStatus": "Status:",
|
||||||
"LabelSubtitles": "Undertekster:",
|
"LabelSubtitles": "Undertekster",
|
||||||
"LabelSyncNoTargetsHelp": "Det ser ud til at du ikke har nogen apps der understøtter offline hentning.",
|
"LabelSyncNoTargetsHelp": "Det ser ud til at du ikke har nogen apps der understøtter offline hentning.",
|
||||||
"LabelTVHomeScreen": "TV modus hjemmeskærm:",
|
"LabelTVHomeScreen": "TV modus hjemmeskærm:",
|
||||||
"LabelTag": "Mærke:",
|
"LabelTag": "Mærke:",
|
||||||
|
@ -1329,7 +1327,7 @@
|
||||||
"LabelUrl": "Link:",
|
"LabelUrl": "Link:",
|
||||||
"LabelVersion": "Version:",
|
"LabelVersion": "Version:",
|
||||||
"LabelVersionNumber": "Version {0}",
|
"LabelVersionNumber": "Version {0}",
|
||||||
"LabelVideo": "Video:",
|
"LabelVideo": "Video",
|
||||||
"LabelVideoCodec": "Video codec:",
|
"LabelVideoCodec": "Video codec:",
|
||||||
"LabelWindowBackgroundColor": "Tekst baggrundsfarve:",
|
"LabelWindowBackgroundColor": "Tekst baggrundsfarve:",
|
||||||
"LabelXDlnaCap": "X-DLNA begrænsning:",
|
"LabelXDlnaCap": "X-DLNA begrænsning:",
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue