mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge remote-tracking branch 'upstream/master' into fix-logo-size
This commit is contained in:
commit
d2cd47d3e3
194 changed files with 5770 additions and 4213 deletions
|
@ -2,8 +2,7 @@ trigger:
|
||||||
batch: true
|
batch: true
|
||||||
branches:
|
branches:
|
||||||
include:
|
include:
|
||||||
- master
|
- '*'
|
||||||
- release-*
|
|
||||||
tags:
|
tags:
|
||||||
include:
|
include:
|
||||||
- '*'
|
- '*'
|
||||||
|
@ -13,65 +12,94 @@ pr:
|
||||||
- '*'
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: build
|
- job: Build
|
||||||
displayName: 'Build'
|
displayName: 'Build'
|
||||||
|
|
||||||
dependsOn: lint
|
strategy:
|
||||||
condition: succeeded()
|
matrix:
|
||||||
|
Development:
|
||||||
|
BuildConfiguration: development
|
||||||
|
Production:
|
||||||
|
BuildConfiguration: production
|
||||||
|
Standalone:
|
||||||
|
BuildConfiguration: standalone
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-latest'
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
strategy:
|
steps:
|
||||||
matrix:
|
- task: NodeTool@0
|
||||||
bundle:
|
displayName: 'Install Node'
|
||||||
BuildConfiguration: Bundle
|
inputs:
|
||||||
standalone:
|
versionSpec: '12.x'
|
||||||
BuildConfiguration: Standalone
|
|
||||||
maxParallel: 2
|
|
||||||
|
|
||||||
steps:
|
- task: Cache@2
|
||||||
- task: NodeTool@0
|
displayName: 'Check Cache'
|
||||||
displayName: 'Install Node'
|
inputs:
|
||||||
inputs:
|
key: 'yarn | yarn.lock'
|
||||||
versionSpec: '10.x'
|
path: 'node_modules'
|
||||||
|
cacheHitVar: CACHE_RESTORED
|
||||||
|
|
||||||
- script: 'yarn install'
|
- script: 'yarn install --frozen-lockfile'
|
||||||
displayName: 'Install Dependencies'
|
displayName: 'Install Dependencies'
|
||||||
|
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||||
|
|
||||||
- script: 'yarn build'
|
- script: 'yarn build:development'
|
||||||
displayName: 'Build'
|
displayName: 'Build Development'
|
||||||
|
condition: eq(variables['BuildConfiguration'], 'development')
|
||||||
|
|
||||||
- script: 'test -d dist'
|
- script: 'yarn build:production'
|
||||||
displayName: 'Check Build'
|
displayName: 'Build Bundle'
|
||||||
|
condition: eq(variables['BuildConfiguration'], 'production')
|
||||||
|
|
||||||
- script: 'yarn pack --filename jellyfin-web.tgz'
|
- script: 'yarn build:standalone'
|
||||||
displayName: 'Bundle Release'
|
displayName: 'Build Standalone'
|
||||||
|
condition: eq(variables['BuildConfiguration'], 'standalone')
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@1
|
- script: 'test -d dist'
|
||||||
displayName: 'Publish Release'
|
displayName: 'Check Build'
|
||||||
condition: succeeded()
|
|
||||||
inputs:
|
|
||||||
targetPath: '$(Build.SourcesDirectory)/jellyfin-web.tgz'
|
|
||||||
artifactName: 'jellyfin-web'
|
|
||||||
|
|
||||||
- job: lint
|
- script: 'mv dist jellyfin-web'
|
||||||
displayName: 'Lint'
|
displayName: 'Rename Directory'
|
||||||
|
|
||||||
pool:
|
- task: ArchiveFiles@2
|
||||||
vmImage: 'ubuntu-latest'
|
displayName: 'Archive Directory'
|
||||||
|
inputs:
|
||||||
|
rootFolderOrFile: 'jellyfin-web'
|
||||||
|
includeRootFolder: true
|
||||||
|
archiveFile: 'jellyfin-web-$(BuildConfiguration)'
|
||||||
|
|
||||||
steps:
|
- task: PublishPipelineArtifact@1
|
||||||
- task: NodeTool@0
|
displayName: 'Publish Release'
|
||||||
displayName: 'Install Node'
|
inputs:
|
||||||
inputs:
|
targetPath: '$(Build.SourcesDirectory)/jellyfin-web-$(BuildConfiguration).zip'
|
||||||
versionSpec: '10.x'
|
artifactName: 'jellyfin-web-$(BuildConfiguration)'
|
||||||
|
|
||||||
- script: 'yarn install'
|
- job: Lint
|
||||||
displayName: 'Install Dependencies'
|
displayName: 'Lint'
|
||||||
|
|
||||||
- script: 'yarn run lint'
|
pool:
|
||||||
displayName: 'Run ESLint'
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
- script: 'yarn run stylelint'
|
steps:
|
||||||
displayName: 'Run Stylelint'
|
- task: NodeTool@0
|
||||||
|
displayName: 'Install Node'
|
||||||
|
inputs:
|
||||||
|
versionSpec: '12.x'
|
||||||
|
|
||||||
|
- task: Cache@2
|
||||||
|
displayName: 'Check Cache'
|
||||||
|
inputs:
|
||||||
|
key: 'yarn | yarn.lock'
|
||||||
|
path: 'node_modules'
|
||||||
|
cacheHitVar: CACHE_RESTORED
|
||||||
|
|
||||||
|
- script: 'yarn install --frozen-lockfile'
|
||||||
|
displayName: 'Install Dependencies'
|
||||||
|
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||||
|
|
||||||
|
- script: 'yarn run lint --quiet'
|
||||||
|
displayName: 'Run ESLint'
|
||||||
|
|
||||||
|
- script: 'yarn run stylelint'
|
||||||
|
displayName: 'Run Stylelint'
|
||||||
|
|
128
.eslintrc.yml
128
.eslintrc.yml
|
@ -1,15 +1,32 @@
|
||||||
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
|
||||||
|
- plugin:compat/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 +35,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 +50,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 +61,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 +75,97 @@ 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"]
|
||||||
|
|
||||||
|
settings:
|
||||||
|
polyfills:
|
||||||
|
# Native Promises Only
|
||||||
|
- Promise
|
||||||
|
# whatwg-fetch
|
||||||
|
- fetch
|
||||||
|
# document-register-element
|
||||||
|
- document.registerElement
|
||||||
|
# resize-observer-polyfill
|
||||||
|
- ResizeObserver
|
||||||
|
# fast-text-encoding
|
||||||
|
- TextEncoder
|
||||||
|
# intersection-observer
|
||||||
|
- IntersectionObserver
|
||||||
|
# Core-js
|
||||||
|
- Object.assign
|
||||||
|
- Object.is
|
||||||
|
- Object.setPrototypeOf
|
||||||
|
- Object.toString
|
||||||
|
- Object.freeze
|
||||||
|
- Object.seal
|
||||||
|
- Object.preventExtensions
|
||||||
|
- Object.isFrozen
|
||||||
|
- Object.isSealed
|
||||||
|
- Object.isExtensible
|
||||||
|
- Object.getOwnPropertyDescriptor
|
||||||
|
- Object.getPrototypeOf
|
||||||
|
- Object.keys
|
||||||
|
- Object.getOwnPropertyNames
|
||||||
|
- Function.name
|
||||||
|
- Function.hasInstance
|
||||||
|
- Array.from
|
||||||
|
- Array.arrayOf
|
||||||
|
- Array.copyWithin
|
||||||
|
- Array.fill
|
||||||
|
- Array.find
|
||||||
|
- Array.findIndex
|
||||||
|
- Array.iterator
|
||||||
|
- String.fromCodePoint
|
||||||
|
- String.raw
|
||||||
|
- String.iterator
|
||||||
|
- String.codePointAt
|
||||||
|
- String.endsWith
|
||||||
|
- String.includes
|
||||||
|
- String.repeat
|
||||||
|
- String.startsWith
|
||||||
|
- String.trim
|
||||||
|
- String.anchor
|
||||||
|
- String.big
|
||||||
|
- String.blink
|
||||||
|
- String.bold
|
||||||
|
- String.fixed
|
||||||
|
- String.fontcolor
|
||||||
|
- String.fontsize
|
||||||
|
- String.italics
|
||||||
|
- String.link
|
||||||
|
- String.small
|
||||||
|
- String.strike
|
||||||
|
- String.sub
|
||||||
|
- String.sup
|
||||||
|
- RegExp
|
||||||
|
- Number
|
||||||
|
- Math
|
||||||
|
- Date
|
||||||
|
- async
|
||||||
|
- Symbol
|
||||||
|
- Map
|
||||||
|
- Set
|
||||||
|
- WeakMap
|
||||||
|
- WeakSet
|
||||||
|
- ArrayBuffer
|
||||||
|
- DataView
|
||||||
|
- Int8Array
|
||||||
|
- Uint8Array
|
||||||
|
- Uint8ClampedArray
|
||||||
|
- Int16Array
|
||||||
|
- Uint16Array
|
||||||
|
- Int32Array
|
||||||
|
- Uint32Array
|
||||||
|
- Float32Array
|
||||||
|
- Float64Array
|
||||||
|
- Reflect
|
||||||
|
|
584
.gitignore
vendored
584
.gitignore
vendored
|
@ -1,578 +1,10 @@
|
||||||
|
# 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
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# ide
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
|
21
README.md
21
README.md
|
@ -50,25 +50,32 @@ Jellyfin Web is the frontend used for most of the clients available for end user
|
||||||
### Getting Started
|
### Getting Started
|
||||||
|
|
||||||
1. Clone or download this repository.
|
1. Clone or download this repository.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/jellyfin/jellyfin-web.git
|
git clone https://github.com/jellyfin/jellyfin-web.git
|
||||||
cd jellyfin-web
|
cd jellyfin-web
|
||||||
```
|
```
|
||||||
|
|
||||||
2. Install build dependencies in the project directory.
|
2. Install build dependencies in the project directory.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn install
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Run the web client with webpack for local development.
|
3. Run the web client with webpack for local development.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
yarn serve
|
yarn serve
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Build the client with sourcemaps.
|
4. Build the client with sourcemaps.
|
||||||
'''sh
|
|
||||||
yarn
|
```sh
|
||||||
'''
|
yarn build:development
|
||||||
Or without sourcemaps
|
```
|
||||||
'''sh
|
|
||||||
yarn --production
|
You can build a nginx compatible version as well.
|
||||||
'''
|
|
||||||
|
```sh
|
||||||
|
yarn build:standalone
|
||||||
|
```
|
3
babel.config.json
Normal file
3
babel.config.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"presets": ["@babel/preset-env"]
|
||||||
|
}
|
160
gulpfile.js
160
gulpfile.js
|
@ -10,7 +10,7 @@ const htmlmin = require('gulp-htmlmin');
|
||||||
const imagemin = require('gulp-imagemin');
|
const imagemin = require('gulp-imagemin');
|
||||||
const sourcemaps = require('gulp-sourcemaps');
|
const sourcemaps = require('gulp-sourcemaps');
|
||||||
const mode = require('gulp-mode')({
|
const mode = require('gulp-mode')({
|
||||||
modes: ["development", "bundle", "standalone"],
|
modes: ["development", "production"],
|
||||||
default: "development",
|
default: "development",
|
||||||
verbose: false
|
verbose: false
|
||||||
});
|
});
|
||||||
|
@ -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');
|
||||||
sass.compiler = require('node-sass')
|
const lazypipe = require('lazypipe');
|
||||||
|
|
||||||
|
sass.compiler = require('node-sass');
|
||||||
|
|
||||||
if (mode.bundle() || mode.standalone()) {
|
let config;
|
||||||
var config = require('./webpack.prod.js');
|
if (mode.production()) {
|
||||||
|
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);
|
const 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() {
|
const 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,29 +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.bundle(htmlmin({ collapseWhitespace: true })))
|
.pipe(mode.production(htmlmin({ collapseWhitespace: true })))
|
||||||
.pipe(mode.standalone(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(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 }
|
||||||
))
|
))
|
||||||
|
@ -119,5 +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.serve = series(exports.default, standalone, serve)
|
return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy), injectBundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.default = build(false);
|
||||||
|
exports.standalone = build(true);
|
||||||
|
exports.serve = series(exports.standalone, serve);
|
||||||
|
|
53
package.json
53
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,17 @@
|
||||||
"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-compat": "^3.5.1",
|
||||||
|
"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 +36,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,21 +55,25 @@
|
||||||
},
|
},
|
||||||
"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",
|
||||||
|
"fast-text-encoding": "^1.0.1",
|
||||||
"flv.js": "^1.5.0",
|
"flv.js": "^1.5.0",
|
||||||
"hls.js": "^0.13.1",
|
"hls.js": "^0.13.1",
|
||||||
"howler": "^2.1.3",
|
"howler": "^2.1.3",
|
||||||
|
"intersection-observer": "^0.7.0",
|
||||||
"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",
|
||||||
"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",
|
||||||
|
@ -71,6 +82,29 @@
|
||||||
"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/sanatizefilename.js",
|
||||||
|
"src/components/scrollManager.js",
|
||||||
|
"src/scripts/settings/appSettings.js",
|
||||||
|
"src/scripts/settings/userSettings.js",
|
||||||
|
"src/scripts/settings/webSettings.js",
|
||||||
|
"src/scripts/dfnshelper.js",
|
||||||
|
"src/scripts/imagehelper.js",
|
||||||
|
"src/scripts/inputManager.js"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-transform-modules-amd"
|
||||||
|
]
|
||||||
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
|
@ -89,10 +123,11 @@
|
||||||
"Firefox ESR"
|
"Firefox ESR"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "gulp serve",
|
"serve": "gulp serve --development",
|
||||||
"build": "gulp --bundle",
|
"prepare": "gulp --production",
|
||||||
"build standalone": "gulp --standalone",
|
"build:development": "gulp --development",
|
||||||
"build development": "gulp",
|
"build:production": "gulp --production",
|
||||||
|
"build:standalone": "gulp standalone --development",
|
||||||
"lint": "eslint \"src\"",
|
"lint": "eslint \"src\"",
|
||||||
"stylelint": "stylelint \"src/**/*.css\""
|
"stylelint": "stylelint \"src/**/*.css\""
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
@ -115,7 +115,7 @@
|
||||||
display: -webkit-inline-box;
|
display: -webkit-inline-box;
|
||||||
display: -webkit-inline-flex;
|
display: -webkit-inline-flex;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
margin: 0.3em 0 0 0.5em;
|
margin: 0 0 0 0.5em;
|
||||||
height: 1.7em;
|
height: 1.7em;
|
||||||
-webkit-box-align: center;
|
-webkit-box-align: center;
|
||||||
-webkit-align-items: center;
|
-webkit-align-items: center;
|
||||||
|
@ -128,6 +128,10 @@
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-mobile .pageTitleWithDefaultLogo {
|
||||||
|
background-image: url(../img/icon-transparent.png);
|
||||||
|
}
|
||||||
|
|
||||||
.headerLeft,
|
.headerLeft,
|
||||||
.skinHeader {
|
.skinHeader {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
|
@ -238,11 +242,10 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.mainDrawer-scrollContainer {
|
.mainDrawer-scrollContainer {
|
||||||
padding-bottom: 10vh;
|
margin-bottom: 10vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (min-width: 40em) {
|
@media all and (min-width: 40em) {
|
||||||
.dashboardDocument .adminDrawerLogo,
|
|
||||||
.dashboardDocument .mainDrawerButton {
|
.dashboardDocument .mainDrawerButton {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
@ -268,12 +271,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media all and (max-width: 60em) {
|
|
||||||
.libraryDocument .mainDrawerButton {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media all and (max-width: 84em) {
|
@media all and (max-width: 84em) {
|
||||||
.withSectionTabs .headerTop {
|
.withSectionTabs .headerTop {
|
||||||
padding-bottom: 0.55em;
|
padding-bottom: 0.55em;
|
||||||
|
@ -316,7 +313,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboardDocument .mainDrawer-scrollContainer {
|
.dashboardDocument .mainDrawer-scrollContainer {
|
||||||
margin-top: 6em !important;
|
margin-top: 4.6em !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1098,3 +1095,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,13 @@ _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
|
||||||
|
var query = require("query-string");
|
||||||
|
_define("queryString", function() {
|
||||||
|
return query;
|
||||||
});
|
});
|
||||||
|
|
||||||
// flvjs
|
// flvjs
|
||||||
|
@ -75,33 +81,25 @@ _define("sortable", function() {
|
||||||
// webcomponents
|
// webcomponents
|
||||||
var webcomponents = require("webcomponents.js/webcomponents-lite");
|
var webcomponents = require("webcomponents.js/webcomponents-lite");
|
||||||
_define("webcomponents", function() {
|
_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 libassWasm = require("libass-wasm");
|
||||||
_define("JavascriptSubtitlesOctopus", function() {
|
_define("JavascriptSubtitlesOctopus", function() {
|
||||||
return libass_wasm;
|
return libassWasm;
|
||||||
});
|
});
|
||||||
|
|
||||||
// material-icons
|
// material-icons
|
||||||
var material_icons = require("material-design-icons-iconfont/dist/material-design-icons.css");
|
var materialIcons = require("material-design-icons-iconfont/dist/material-design-icons.css");
|
||||||
_define("material-icons", function() {
|
_define("material-icons", function() {
|
||||||
return material_icons;
|
return materialIcons;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Noto Sans
|
// noto font
|
||||||
|
var noto = require("jellyfin-noto");
|
||||||
var jellyfin_noto = require("jellyfin-noto");
|
|
||||||
_define("jellyfin-noto", function () {
|
_define("jellyfin-noto", function () {
|
||||||
return jellyfin_noto;
|
return noto;
|
||||||
});
|
});
|
||||||
|
|
||||||
// page.js
|
// page.js
|
||||||
|
@ -114,3 +112,30 @@ 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 dateFns = require("date-fns");
|
||||||
|
_define("date-fns", function () {
|
||||||
|
return dateFns;
|
||||||
|
});
|
||||||
|
|
||||||
|
var dateFnsLocale = require("date-fns/locale");
|
||||||
|
_define("date-fns/locale", function () {
|
||||||
|
return dateFnsLocale;
|
||||||
|
});
|
||||||
|
|
||||||
|
var fast_text_encoding = require("fast-text-encoding");
|
||||||
|
_define("fast-text-encoding", function () {
|
||||||
|
return fast_text_encoding;
|
||||||
|
});
|
||||||
|
|
||||||
|
var intersection_observer = require("intersection-observer");
|
||||||
|
_define("intersection-observer", function () {
|
||||||
|
return intersection_observer;
|
||||||
|
});
|
||||||
|
|
|
@ -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 || "";
|
||||||
|
|
|
@ -268,6 +268,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMaxBandwidth() {
|
function getMaxBandwidth() {
|
||||||
|
/* eslint-disable compat/compat */
|
||||||
if (navigator.connection) {
|
if (navigator.connection) {
|
||||||
var max = navigator.connection.downlinkMax;
|
var max = navigator.connection.downlinkMax;
|
||||||
if (max && max > 0 && max < Number.POSITIVE_INFINITY) {
|
if (max && max > 0 && max < Number.POSITIVE_INFINITY) {
|
||||||
|
@ -279,6 +280,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* eslint-enable compat/compat */
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -511,9 +513,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 +579,9 @@ 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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,8 +351,6 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSet
|
||||||
var deviceName;
|
var deviceName;
|
||||||
var appName = "Jellyfin Web";
|
var appName = "Jellyfin Web";
|
||||||
var appVersion = "10.5.0";
|
var appVersion = "10.5.0";
|
||||||
var visibilityChange;
|
|
||||||
var visibilityState;
|
|
||||||
|
|
||||||
var appHost = {
|
var appHost = {
|
||||||
getWindowState: function () {
|
getWindowState: function () {
|
||||||
|
@ -381,7 +381,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 () {
|
||||||
|
@ -424,40 +424,26 @@ define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSet
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var doc = self.document;
|
|
||||||
var isHidden = false;
|
var isHidden = false;
|
||||||
|
var hidden;
|
||||||
|
var visibilityChange;
|
||||||
|
|
||||||
if (doc) {
|
if (typeof document.hidden !== "undefined") { /* eslint-disable-line compat/compat */
|
||||||
if (void 0 !== doc.visibilityState) {
|
hidden = "hidden";
|
||||||
visibilityChange = "visibilitychange";
|
visibilityChange = "visibilitychange";
|
||||||
visibilityState = "hidden";
|
} else if (typeof document.webkitHidden !== "undefined") {
|
||||||
|
hidden = "webkitHidden";
|
||||||
|
visibilityChange = "webkitvisibilitychange";
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener(visibilityChange, function () {
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
|
if (document[hidden]) {
|
||||||
|
onAppHidden();
|
||||||
} else {
|
} else {
|
||||||
if (void 0 !== doc.mozHidden) {
|
onAppVisible();
|
||||||
visibilityChange = "mozvisibilitychange";
|
|
||||||
visibilityState = "mozVisibilityState";
|
|
||||||
} else {
|
|
||||||
if (void 0 !== doc.msHidden) {
|
|
||||||
visibilityChange = "msvisibilitychange";
|
|
||||||
visibilityState = "msVisibilityState";
|
|
||||||
} else {
|
|
||||||
if (void 0 !== doc.webkitHidden) {
|
|
||||||
visibilityChange = "webkitvisibilitychange";
|
|
||||||
visibilityState = "webkitVisibilityState";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}, false);
|
||||||
|
|
||||||
if (doc) {
|
|
||||||
doc.addEventListener(visibilityChange, function () {
|
|
||||||
if (document[visibilityState]) {
|
|
||||||
onAppHidden();
|
|
||||||
} else {
|
|
||||||
onAppVisible();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.addEventListener) {
|
if (self.addEventListener) {
|
||||||
self.addEventListener("focus", onAppVisible);
|
self.addEventListener("focus", onAppVisible);
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
||||||
|
|
|
@ -182,6 +182,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', "userSettings"
|
||||||
return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, {
|
return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, {
|
||||||
type: "Backdrop",
|
type: "Backdrop",
|
||||||
tag: imgTag,
|
tag: imgTag,
|
||||||
|
maxWidth: dom.getScreenWidth(),
|
||||||
index: index
|
index: index
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
@ -192,6 +193,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', "userSettings"
|
||||||
return apiClient.getScaledImageUrl(item.ParentBackdropItemId, Object.assign(imageOptions, {
|
return apiClient.getScaledImageUrl(item.ParentBackdropItemId, Object.assign(imageOptions, {
|
||||||
type: "Backdrop",
|
type: "Backdrop",
|
||||||
tag: imgTag,
|
tag: imgTag,
|
||||||
|
maxWidth: dom.getScreenWidth(),
|
||||||
index: index
|
index: index
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
|
@ -52,5 +52,5 @@ define(["connectionManager"], function (connectionManager) {
|
||||||
currentSlideshow = null;
|
currentSlideshow = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -68,7 +68,7 @@ define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browse
|
||||||
|
|
||||||
return apiClient.getScaledImageUrl(item.Id, {
|
return apiClient.getScaledImageUrl(item.Id, {
|
||||||
|
|
||||||
maxWidth: maxWidth,
|
maxWidth: maxWidth * 2,
|
||||||
tag: chapter.ImageTag,
|
tag: chapter.ImageTag,
|
||||||
type: "Chapter",
|
type: "Chapter",
|
||||||
index: index
|
index: index
|
||||||
|
|
34
src/components/castSenderApi.js
Normal file
34
src/components/castSenderApi.js
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
define([], function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (window.appMode === "cordova" || window.appMode === "android") {
|
||||||
|
return {
|
||||||
|
load: function () {
|
||||||
|
window.chrome = window.chrome || {};
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
var ccLoaded = false;
|
||||||
|
return {
|
||||||
|
load: function () {
|
||||||
|
if (ccLoaded) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
var fileref = document.createElement("script");
|
||||||
|
fileref.setAttribute("type", "text/javascript");
|
||||||
|
|
||||||
|
fileref.onload = function () {
|
||||||
|
ccLoaded = true;
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
fileref.setAttribute("src", "https://www.gstatic.com/cv/js/sender/v1/cast_sender.js");
|
||||||
|
document.querySelector("head").appendChild(fileref);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -1,40 +1,65 @@
|
||||||
define(['dialog', 'globalize'], function (dialog, globalize) {
|
define(["browser", "dialog", "globalize"], function(browser, dialog, globalize) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
return function (text, title) {
|
function replaceAll(str, find, replace) {
|
||||||
|
return str.split(find).join(replace);
|
||||||
|
}
|
||||||
|
|
||||||
var options;
|
if (browser.tv && window.confirm) {
|
||||||
if (typeof text === 'string') {
|
// Use the native confirm dialog
|
||||||
options = {
|
return function (options) {
|
||||||
title: title,
|
if (typeof options === 'string') {
|
||||||
text: text
|
options = {
|
||||||
};
|
title: '',
|
||||||
} else {
|
text: options
|
||||||
options = text;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
var items = [];
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
name: options.cancelText || globalize.translate('ButtonCancel'),
|
|
||||||
id: 'cancel',
|
|
||||||
type: 'cancel'
|
|
||||||
});
|
|
||||||
|
|
||||||
items.push({
|
|
||||||
name: options.confirmText || globalize.translate('ButtonOk'),
|
|
||||||
id: 'ok',
|
|
||||||
type: options.primary === 'delete' ? 'delete' : 'submit'
|
|
||||||
});
|
|
||||||
|
|
||||||
options.buttons = items;
|
|
||||||
|
|
||||||
return dialog(options).then(function (result) {
|
|
||||||
if (result === 'ok') {
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Promise.reject();
|
var text = replaceAll(options.text || '', '<br/>', '\n');
|
||||||
});
|
var result = confirm(text);
|
||||||
};
|
|
||||||
|
if (result) {
|
||||||
|
return Promise.resolve();
|
||||||
|
} else {
|
||||||
|
return Promise.reject();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
// Use our own dialog
|
||||||
|
return function (text, title) {
|
||||||
|
var options;
|
||||||
|
if (typeof text === 'string') {
|
||||||
|
options = {
|
||||||
|
title: title,
|
||||||
|
text: text
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
options = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
var items = [];
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
name: options.cancelText || globalize.translate('ButtonCancel'),
|
||||||
|
id: 'cancel',
|
||||||
|
type: 'cancel'
|
||||||
|
});
|
||||||
|
|
||||||
|
items.push({
|
||||||
|
name: options.confirmText || globalize.translate('ButtonOk'),
|
||||||
|
id: 'ok',
|
||||||
|
type: options.primary === 'delete' ? 'delete' : 'submit'
|
||||||
|
});
|
||||||
|
|
||||||
|
options.buttons = items;
|
||||||
|
|
||||||
|
return dialog(options).then(function (result) {
|
||||||
|
if (result === 'ok') {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,27 +0,0 @@
|
||||||
define([], function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function replaceAll(str, find, replace) {
|
|
||||||
|
|
||||||
return str.split(find).join(replace);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function (options) {
|
|
||||||
|
|
||||||
if (typeof options === 'string') {
|
|
||||||
options = {
|
|
||||||
title: '',
|
|
||||||
text: options
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = replaceAll(options.text || '', '<br/>', '\n');
|
|
||||||
var result = confirm(text);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
return Promise.resolve();
|
|
||||||
} else {
|
|
||||||
return Promise.reject();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -169,6 +169,15 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
|
||||||
}, {
|
}, {
|
||||||
passive: true
|
passive: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
dom.addEventListener((dlg.dialogContainer || backdrop), 'contextmenu', function (e) {
|
||||||
|
if (e.target === dlg.dialogContainer) {
|
||||||
|
// Close the application dialog menu
|
||||||
|
close(dlg);
|
||||||
|
// Prevent the default browser context menu from appearing
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function isHistoryEnabled(dlg) {
|
function isHistoryEnabled(dlg) {
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -186,6 +186,8 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
|
||||||
context.querySelector('#selectLanguage').value = userSettings.language() || '';
|
context.querySelector('#selectLanguage').value = userSettings.language() || '';
|
||||||
context.querySelector('.selectDateTimeLocale').value = userSettings.dateTimeLocale() || '';
|
context.querySelector('.selectDateTimeLocale').value = userSettings.dateTimeLocale() || '';
|
||||||
|
|
||||||
|
context.querySelector('#txtLibraryPageSize').value = userSettings.libraryPageSize();
|
||||||
|
|
||||||
selectDashboardTheme.value = userSettings.dashboardTheme() || '';
|
selectDashboardTheme.value = userSettings.dashboardTheme() || '';
|
||||||
selectTheme.value = userSettings.theme() || '';
|
selectTheme.value = userSettings.theme() || '';
|
||||||
|
|
||||||
|
@ -215,6 +217,8 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
|
||||||
userSettingsInstance.soundEffects(context.querySelector('.selectSoundEffects').value);
|
userSettingsInstance.soundEffects(context.querySelector('.selectSoundEffects').value);
|
||||||
userSettingsInstance.screensaver(context.querySelector('.selectScreensaver').value);
|
userSettingsInstance.screensaver(context.querySelector('.selectScreensaver').value);
|
||||||
|
|
||||||
|
userSettingsInstance.libraryPageSize(context.querySelector('#txtLibraryPageSize').value);
|
||||||
|
|
||||||
userSettingsInstance.skin(context.querySelector('.selectSkin').value);
|
userSettingsInstance.skin(context.querySelector('.selectSkin').value);
|
||||||
|
|
||||||
userSettingsInstance.enableFastFadein(context.querySelector('#chkFadein').checked);
|
userSettingsInstance.enableFastFadein(context.querySelector('#chkFadein').checked);
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -143,6 +143,11 @@
|
||||||
<select is="emby-select" class="selectSoundEffects" label="${LabelSoundEffects}"></select>
|
<select is="emby-select" class="selectSoundEffects" label="${LabelSoundEffects}"></select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="inputContainer inputContainer-withDescription fldFadein">
|
||||||
|
<input is="emby-input" type="number" id="txtLibraryPageSize" pattern="[0-9]*" required="required" min="0" max="1000" step="1" label="${LabelLibraryPageSize}" />
|
||||||
|
<div class="fieldDescription">${LabelLibraryPageSizeHelp}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription fldFadein">
|
<div class="checkboxContainer checkboxContainer-withDescription fldFadein">
|
||||||
<label>
|
<label>
|
||||||
<input type="checkbox" is="emby-checkbox" id="chkFadein" />
|
<input type="checkbox" is="emby-checkbox" id="chkFadein" />
|
||||||
|
|
|
@ -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,30 +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 _animationEvent;
|
/**
|
||||||
function whichAnimationEvent() {
|
* Standard screen widths.
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
width = height * (16.0 / 9.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const closest = standardWidths.sort(function (a, b) {
|
||||||
|
return Math.abs(width - a) - Math.abs(width - b);
|
||||||
|
})[0];
|
||||||
|
|
||||||
|
return closest;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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];
|
||||||
|
@ -138,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];
|
||||||
|
@ -168,15 +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,
|
||||||
whichTransitionEvent: whichTransitionEvent,
|
removeEventListener: removeEventListener,
|
||||||
whichAnimationEvent: whichAnimationEvent,
|
getWindowSize: getWindowSize,
|
||||||
whichAnimationCancelEvent: whichAnimationCancelEvent
|
getScreenWidth: getScreenWidth,
|
||||||
};
|
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");
|
||||||
|
@ -131,7 +131,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
|
||||||
} else if (section === 'librarytiles' || section === 'smalllibrarytiles' || section === 'smalllibrarytiles-automobile' || section === 'librarytiles-automobile') {
|
} else if (section === 'librarytiles' || section === 'smalllibrarytiles' || section === 'smalllibrarytiles-automobile' || section === 'librarytiles-automobile') {
|
||||||
loadLibraryTiles(elem, apiClient, user, userSettings, 'smallBackdrop', userViews, allSections);
|
loadLibraryTiles(elem, apiClient, user, userSettings, 'smallBackdrop', userViews, allSections);
|
||||||
} else if (section === 'librarybuttons') {
|
} else if (section === 'librarybuttons') {
|
||||||
loadlibraryButtons(elem, apiClient, user, userSettings, userViews, allSections);
|
loadlibraryButtons(elem, apiClient, user, userSettings, userViews);
|
||||||
} else if (section === 'resume') {
|
} else if (section === 'resume') {
|
||||||
loadResumeVideo(elem, apiClient, userId);
|
loadResumeVideo(elem, apiClient, userId);
|
||||||
} else if (section === 'resumeaudio') {
|
} else if (section === 'resumeaudio') {
|
||||||
|
@ -183,7 +183,7 @@ define(['connectionManager', 'cardBuilder', 'appSettings', 'dom', 'apphost', 'la
|
||||||
for (var i = 0, length = items.length; i < length; i++) {
|
for (var i = 0, length = items.length; i < length; i++) {
|
||||||
var item = items[i];
|
var item = items[i];
|
||||||
var icon = imageHelper.getLibraryIcon(item.CollectionType);
|
var icon = imageHelper.getLibraryIcon(item.CollectionType);
|
||||||
html += '<a is="emby-linkbutton" href="' + appRouter.getRouteUrl(item) + '" class="raised homeLibraryButton"><i class="material-icons homeLibraryIcon">' + icon + '</i><span class="homeLibraryText">' + item.Name + '</span></a>';
|
html += '<a is="emby-linkbutton" href="' + appRouter.getRouteUrl(item) + '" class="raised homeLibraryButton"><i class="material-icons homeLibraryIcon ' + icon + '"></i><span class="homeLibraryText">' + item.Name + '</span></a>';
|
||||||
}
|
}
|
||||||
|
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
@ -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">';
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,7 +31,7 @@ define(['appSettings', 'browser', 'events'], function (appSettings, browser, eve
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableHlsShakaPlayer(item, mediaSource, mediaType) {
|
function enableHlsShakaPlayer(item, mediaSource, mediaType) {
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
if (!!window.MediaSource && !!MediaSource.isTypeSupported) {
|
if (!!window.MediaSource && !!MediaSource.isTypeSupported) {
|
||||||
|
|
||||||
if (canPlayNativeHls()) {
|
if (canPlayNativeHls()) {
|
||||||
|
@ -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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,7 +101,7 @@ define(['events', 'browser', 'require', 'apphost', 'appSettings', 'htmlMediaHelp
|
||||||
self._timeUpdated = false;
|
self._timeUpdated = false;
|
||||||
self._currentTime = null;
|
self._currentTime = null;
|
||||||
|
|
||||||
var elem = createMediaElement(options);
|
var elem = createMediaElement();
|
||||||
return setCurrentSrc(elem, options);
|
return setCurrentSrc(elem, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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,10 +1,6 @@
|
||||||
define(['lazyLoader', 'imageFetcher', 'layoutManager', 'browser', 'appSettings', 'userSettings', 'require', 'css!./style'], function (lazyLoader, imageFetcher, layoutManager, browser, appSettings, userSettings, require) {
|
define(['lazyLoader', 'imageFetcher', 'layoutManager', 'browser', 'appSettings', 'userSettings', 'require', 'css!./style'], function (lazyLoader, imageFetcher, layoutManager, browser, appSettings, userSettings, require) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var requestIdleCallback = window.requestIdleCallback || function (fn) {
|
|
||||||
fn();
|
|
||||||
};
|
|
||||||
|
|
||||||
var self = {};
|
var self = {};
|
||||||
|
|
||||||
function fillImage(elem, source, enableEffects) {
|
function fillImage(elem, source, enableEffects) {
|
||||||
|
|
|
@ -184,7 +184,7 @@ require(['apphost'], function (appHost) {
|
||||||
function allowInput() {
|
function allowInput() {
|
||||||
|
|
||||||
// This would be nice but always seems to return true with electron
|
// This would be nice but always seems to return true with electron
|
||||||
if (!isElectron && document.hidden) {
|
if (!isElectron && document.hidden) { /* eslint-disable-line compat/compat */
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,7 +254,7 @@ require(['apphost'], function (appHost) {
|
||||||
var inputLoopTimer;
|
var inputLoopTimer;
|
||||||
function runInputLoop() {
|
function runInputLoop() {
|
||||||
// Get the latest gamepad state.
|
// Get the latest gamepad state.
|
||||||
var gamepads = navigator.getGamepads();
|
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
|
||||||
for (var i = 0, len = gamepads.length; i < len; i++) {
|
for (var i = 0, len = gamepads.length; i < len; i++) {
|
||||||
var gamepad = gamepads[i];
|
var gamepad = gamepads[i];
|
||||||
if (!gamepad) {
|
if (!gamepad) {
|
||||||
|
@ -362,7 +362,7 @@ require(['apphost'], function (appHost) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function isGamepadConnected() {
|
function isGamepadConnected() {
|
||||||
var gamepads = navigator.getGamepads();
|
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
|
||||||
for (var i = 0, len = gamepads.length; i < len; i++) {
|
for (var i = 0, len = gamepads.length; i < len; i++) {
|
||||||
var gamepad = gamepads[i];
|
var gamepad = gamepads[i];
|
||||||
if (gamepad && gamepad.connected) {
|
if (gamepad && gamepad.connected) {
|
||||||
|
@ -373,6 +373,7 @@ require(['apphost'], function (appHost) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFocusOrGamepadAttach(e) {
|
function onFocusOrGamepadAttach(e) {
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
if (isGamepadConnected() && document.hasFocus()) {
|
if (isGamepadConnected() && document.hasFocus()) {
|
||||||
console.log("Gamepad connected! Starting input loop");
|
console.log("Gamepad connected! Starting input loop");
|
||||||
startInputLoop();
|
startInputLoop();
|
||||||
|
@ -380,6 +381,7 @@ require(['apphost'], function (appHost) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFocusOrGamepadDetach(e) {
|
function onFocusOrGamepadDetach(e) {
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
if (!isGamepadConnected() || !document.hasFocus()) {
|
if (!isGamepadConnected() || !document.hasFocus()) {
|
||||||
console.log("Gamepad disconnected! No other gamepads are connected, stopping input loop");
|
console.log("Gamepad disconnected! No other gamepads are connected, stopping input loop");
|
||||||
stopInputLoop();
|
stopInputLoop();
|
||||||
|
|
|
@ -1,165 +1,170 @@
|
||||||
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) {
|
||||||
|
console.debug("disabling default event handling");
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gamepad initialisation. No script is required if no gamepads are present at init time, saving a bit of resources.
|
||||||
|
// Whenever the gamepad is connected, we hand all the control of the gamepad to gamepadtokey.js by removing the event handler
|
||||||
|
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 (navigator.getGamepads) { /* eslint-disable-line compat/compat */
|
||||||
window.addEventListener("gamepadconnected", attachGamepadScript);
|
window.addEventListener("gamepadconnected", attachGamepadScript);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
export default {
|
||||||
enable: enable,
|
enable: enable,
|
||||||
getKeyName: getKeyName,
|
getKeyName: getKeyName,
|
||||||
isNavigationKey: isNavigationKey
|
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) {
|
||||||
|
|
|
@ -352,6 +352,7 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter",
|
||||||
document.body.appendChild(textArea);
|
document.body.appendChild(textArea);
|
||||||
textArea.focus();
|
textArea.focus();
|
||||||
textArea.select();
|
textArea.select();
|
||||||
|
|
||||||
if (document.execCommand("copy")) {
|
if (document.execCommand("copy")) {
|
||||||
require(["toast"], function (toast) {
|
require(["toast"], function (toast) {
|
||||||
toast(globalize.translate("CopyStreamURLSuccess"));
|
toast(globalize.translate("CopyStreamURLSuccess"));
|
||||||
|
@ -361,14 +362,19 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter",
|
||||||
}
|
}
|
||||||
document.body.removeChild(textArea);
|
document.body.removeChild(textArea);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
if (navigator.clipboard === undefined) {
|
if (navigator.clipboard === undefined) {
|
||||||
textAreaCopy();
|
textAreaCopy();
|
||||||
} else {
|
} else {
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
navigator.clipboard.writeText(downloadHref).then(function () {
|
navigator.clipboard.writeText(downloadHref).then(function () {
|
||||||
require(["toast"], function (toast) {
|
require(["toast"], function (toast) {
|
||||||
toast(globalize.translate("CopyStreamURLSuccess"));
|
toast(globalize.translate("CopyStreamURLSuccess"));
|
||||||
});
|
});
|
||||||
}, textAreaCopy);
|
}).catch(function () {
|
||||||
|
textAreaCopy();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
getResolveFunction(resolve, id)();
|
getResolveFunction(resolve, id)();
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -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 + '"/>';
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,6 @@ define(['visibleinviewport', 'dom', 'browser'], function (visibleinviewport, dom
|
||||||
var thresholdX;
|
var thresholdX;
|
||||||
var thresholdY;
|
var thresholdY;
|
||||||
|
|
||||||
var requestIdleCallback = window.requestIdleCallback || function (fn) {
|
|
||||||
fn();
|
|
||||||
};
|
|
||||||
|
|
||||||
function resetThresholds() {
|
function resetThresholds() {
|
||||||
|
|
||||||
var threshold = 0.3;
|
var threshold = 0.3;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -396,18 +396,24 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
parent.querySelector(".chkEnableEmbeddedTitlesContainer").classList.remove("hide");
|
parent.querySelector(".chkEnableEmbeddedTitlesContainer").classList.remove("hide");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (contentType === "tvshows") {
|
||||||
|
parent.querySelector(".chkEnableEmbeddedEpisodeInfosContainer").classList.remove("hide");
|
||||||
|
} else {
|
||||||
|
parent.querySelector(".chkEnableEmbeddedEpisodeInfosContainer").classList.add("hide");
|
||||||
|
}
|
||||||
|
|
||||||
return populateMetadataSettings(parent, contentType);
|
return populateMetadataSettings(parent, contentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -449,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");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -493,25 +499,26 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
SeasonZeroDisplayName: parent.querySelector("#txtSeasonZeroName").value,
|
SeasonZeroDisplayName: parent.querySelector("#txtSeasonZeroName").value,
|
||||||
AutomaticRefreshIntervalDays: parseInt(parent.querySelector("#selectAutoRefreshInterval").value),
|
AutomaticRefreshIntervalDays: parseInt(parent.querySelector("#selectAutoRefreshInterval").value),
|
||||||
EnableEmbeddedTitles: parent.querySelector("#chkEnableEmbeddedTitles").checked,
|
EnableEmbeddedTitles: parent.querySelector("#chkEnableEmbeddedTitles").checked,
|
||||||
|
EnableEmbeddedEpisodeInfos: parent.querySelector("#chkEnableEmbeddedEpisodeInfos").checked,
|
||||||
SkipSubtitlesIfEmbeddedSubtitlesPresent: parent.querySelector("#chkSkipIfGraphicalSubsPresent").checked,
|
SkipSubtitlesIfEmbeddedSubtitlesPresent: parent.querySelector("#chkSkipIfGraphicalSubsPresent").checked,
|
||||||
SkipSubtitlesIfAudioTrackMatches: parent.querySelector("#chkSkipIfAudioTrackPresent").checked,
|
SkipSubtitlesIfAudioTrackMatches: parent.querySelector("#chkSkipIfAudioTrackPresent").checked,
|
||||||
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);
|
||||||
|
@ -524,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;
|
||||||
}
|
}
|
||||||
|
@ -545,15 +552,16 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
parent.querySelector("#chkImportMissingEpisodes").checked = options.ImportMissingEpisodes;
|
parent.querySelector("#chkImportMissingEpisodes").checked = options.ImportMissingEpisodes;
|
||||||
parent.querySelector(".chkAutomaticallyGroupSeries").checked = options.EnableAutomaticSeriesGrouping;
|
parent.querySelector(".chkAutomaticallyGroupSeries").checked = options.EnableAutomaticSeriesGrouping;
|
||||||
parent.querySelector("#chkEnableEmbeddedTitles").checked = options.EnableEmbeddedTitles;
|
parent.querySelector("#chkEnableEmbeddedTitles").checked = options.EnableEmbeddedTitles;
|
||||||
|
parent.querySelector("#chkEnableEmbeddedEpisodeInfos").checked = options.EnableEmbeddedEpisodeInfos;
|
||||||
parent.querySelector("#chkSkipIfGraphicalSubsPresent").checked = options.SkipSubtitlesIfEmbeddedSubtitlesPresent;
|
parent.querySelector("#chkSkipIfGraphicalSubsPresent").checked = options.SkipSubtitlesIfEmbeddedSubtitlesPresent;
|
||||||
parent.querySelector("#chkSaveSubtitlesLocally").checked = options.SaveSubtitlesWithMedia;
|
parent.querySelector("#chkSaveSubtitlesLocally").checked = options.SaveSubtitlesWithMedia;
|
||||||
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);
|
||||||
|
@ -570,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
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -28,6 +28,13 @@
|
||||||
</label>
|
</label>
|
||||||
<div class="fieldDescription checkboxFieldDescription">${PreferEmbeddedTitlesOverFileNamesHelp}</div>
|
<div class="fieldDescription checkboxFieldDescription">${PreferEmbeddedTitlesOverFileNamesHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkboxContainer checkboxContainer-withDescription chkEnableEmbeddedEpisodeInfosContainer hide advanced">
|
||||||
|
<label>
|
||||||
|
<input is="emby-checkbox" type="checkbox" id="chkEnableEmbeddedEpisodeInfos" />
|
||||||
|
<span>${PreferEmbeddedEpisodeInfosOverFileNames}</span>
|
||||||
|
</label>
|
||||||
|
<div class="fieldDescription checkboxFieldDescription">${PreferEmbeddedEpisodeInfosOverFileNamesHelp}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="checkboxContainer checkboxContainer-withDescription advanced">
|
<div class="checkboxContainer checkboxContainer-withDescription advanced">
|
||||||
<label>
|
<label>
|
||||||
|
|
|
@ -238,13 +238,6 @@
|
||||||
background-color: transparent !important;
|
background-color: transparent !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.listItemMediaInfo {
|
|
||||||
/* Don't display if flex not supported */
|
|
||||||
display: none;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 1em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.listGroupHeader-first {
|
.listGroupHeader-first {
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
|
||||||
var apiClient = connectionManager.getApiClient(item.ServerId);
|
var apiClient = connectionManager.getApiClient(item.ServerId);
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
width: width,
|
maxWidth: width * 2,
|
||||||
type: "Primary"
|
type: "Primary"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ define(['itemHelper', 'mediaInfo', 'indicators', 'connectionManager', 'layoutMan
|
||||||
var apiClient = connectionManager.getApiClient(item.ServerId);
|
var apiClient = connectionManager.getApiClient(item.ServerId);
|
||||||
|
|
||||||
var options = {
|
var options = {
|
||||||
width: width,
|
maxWidth: width * 2,
|
||||||
type: "Primary"
|
type: "Primary"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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">';
|
||||||
|
|
|
@ -6,6 +6,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir
|
||||||
document.removeEventListener('keydown', onOneDocumentClick);
|
document.removeEventListener('keydown', onOneDocumentClick);
|
||||||
|
|
||||||
if (window.Notification) {
|
if (window.Notification) {
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
Notification.requestPermission();
|
Notification.requestPermission();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +27,7 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetRegistration() {
|
function resetRegistration() {
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
var serviceWorker = navigator.serviceWorker;
|
var serviceWorker = navigator.serviceWorker;
|
||||||
if (serviceWorker) {
|
if (serviceWorker) {
|
||||||
serviceWorker.ready.then(function (registration) {
|
serviceWorker.ready.then(function (registration) {
|
||||||
|
@ -173,15 +175,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 =
|
||||||
[
|
[
|
||||||
|
|
|
@ -241,7 +241,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader',
|
||||||
elem.addEventListener('click', function (e) {
|
elem.addEventListener('click', function (e) {
|
||||||
|
|
||||||
if (!dom.parentWithTag(e.target, ['BUTTON', 'INPUT', 'A'])) {
|
if (!dom.parentWithTag(e.target, ['BUTTON', 'INPUT', 'A'])) {
|
||||||
showRemoteControl(0);
|
showRemoteControl();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
||||||
|
@ -3140,7 +3140,7 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
||||||
AllowVideoStreamCopy: false,
|
AllowVideoStreamCopy: false,
|
||||||
AllowAudioStreamCopy: currentlyPreventsAudioStreamCopy || currentlyPreventsVideoStreamCopy ? false : null
|
AllowAudioStreamCopy: currentlyPreventsAudioStreamCopy || currentlyPreventsVideoStreamCopy ? false : null
|
||||||
|
|
||||||
}, true);
|
});
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -3162,7 +3162,8 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
||||||
|
|
||||||
// User clicked stop or content ended
|
// User clicked stop or content ended
|
||||||
var state = self.getPlayerState(player);
|
var state = self.getPlayerState(player);
|
||||||
var streamInfo = getPlayerData(player).streamInfo;
|
var data = getPlayerData(player);
|
||||||
|
var streamInfo = data.streamInfo;
|
||||||
|
|
||||||
var nextItem = self._playNextAfterEnded ? self._playQueueManager.getNextItemInfo() : null;
|
var nextItem = self._playNextAfterEnded ? self._playQueueManager.getNextItemInfo() : null;
|
||||||
|
|
||||||
|
@ -3210,6 +3211,9 @@ define(['events', 'datetime', 'appSettings', 'itemHelper', 'pluginManager', 'pla
|
||||||
showPlaybackInfoErrorMessage(self, displayErrorCode, nextItem);
|
showPlaybackInfoErrorMessage(self, displayErrorCode, nextItem);
|
||||||
} else if (nextItem) {
|
} else if (nextItem) {
|
||||||
self.nextTrack();
|
self.nextTrack();
|
||||||
|
} else {
|
||||||
|
// Nothing more to play - clear data
|
||||||
|
data.streamInfo = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ define(['playbackManager', 'layoutManager', 'events'], function (playbackManager
|
||||||
var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player);
|
var isLocalVideo = player.isLocalPlayer && !player.isExternalPlayer && playbackManager.isPlayingVideo(player);
|
||||||
|
|
||||||
if (isLocalVideo && layoutManager.mobile) {
|
if (isLocalVideo && layoutManager.mobile) {
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock);
|
var lockOrientation = screen.lockOrientation || screen.mozLockOrientation || screen.msLockOrientation || (screen.orientation && screen.orientation.lock);
|
||||||
|
|
||||||
if (lockOrientation) {
|
if (lockOrientation) {
|
||||||
|
@ -40,6 +41,7 @@ define(['playbackManager', 'layoutManager', 'events'], function (playbackManager
|
||||||
|
|
||||||
if (orientationLocked && !playbackStopInfo.nextMediaType) {
|
if (orientationLocked && !playbackStopInfo.nextMediaType) {
|
||||||
|
|
||||||
|
/* eslint-disable-next-line compat/compat */
|
||||||
var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock);
|
var unlockOrientation = screen.unlockOrientation || screen.mozUnlockOrientation || screen.msUnlockOrientation || (screen.orientation && screen.orientation.unlock);
|
||||||
|
|
||||||
if (unlockOrientation) {
|
if (unlockOrientation) {
|
||||||
|
|
|
@ -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,28 +0,0 @@
|
||||||
define([], function () {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function replaceAll(str, find, replace) {
|
|
||||||
|
|
||||||
return str.split(find).join(replace);
|
|
||||||
}
|
|
||||||
|
|
||||||
return function (options) {
|
|
||||||
|
|
||||||
if (typeof options === 'string') {
|
|
||||||
options = {
|
|
||||||
label: '',
|
|
||||||
text: options
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var label = replaceAll(options.label || '', '<br/>', '\n');
|
|
||||||
|
|
||||||
var result = prompt(label, options.text || '');
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
return Promise.resolve(result);
|
|
||||||
} else {
|
|
||||||
return Promise.reject(result);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -1,6 +1,10 @@
|
||||||
define(['dialogHelper', 'layoutManager', 'scrollHelper', 'globalize', 'dom', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle'], function (dialogHelper, layoutManager, scrollHelper, globalize, dom, require) {
|
define(["browser", 'dialogHelper', 'layoutManager', 'scrollHelper', 'globalize', 'dom', 'require', 'material-icons', 'emby-button', 'paper-icon-button-light', 'emby-input', 'formDialogStyle'], function(browser, dialogHelper, layoutManager, scrollHelper, globalize, dom, require) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
function replaceAll(str, find, replace) {
|
||||||
|
return str.split(find).join(replace);
|
||||||
|
}
|
||||||
|
|
||||||
function setInputProperties(dlg, options) {
|
function setInputProperties(dlg, options) {
|
||||||
var txtInput = dlg.querySelector('#txtInput');
|
var txtInput = dlg.querySelector('#txtInput');
|
||||||
|
|
||||||
|
@ -13,7 +17,6 @@ define(['dialogHelper', 'layoutManager', 'scrollHelper', 'globalize', 'dom', 're
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDialog(options, template) {
|
function showDialog(options, template) {
|
||||||
|
|
||||||
var dialogOptions = {
|
var dialogOptions = {
|
||||||
removeOnClose: true,
|
removeOnClose: true,
|
||||||
scrollY: false
|
scrollY: false
|
||||||
|
@ -71,34 +74,49 @@ define(['dialogHelper', 'layoutManager', 'scrollHelper', 'globalize', 'dom', 're
|
||||||
dlg.style.minWidth = (Math.min(400, dom.getWindowSize().innerWidth - 50)) + 'px';
|
dlg.style.minWidth = (Math.min(400, dom.getWindowSize().innerWidth - 50)) + 'px';
|
||||||
|
|
||||||
return dialogHelper.open(dlg).then(function () {
|
return dialogHelper.open(dlg).then(function () {
|
||||||
|
|
||||||
if (layoutManager.tv) {
|
if (layoutManager.tv) {
|
||||||
scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false);
|
scrollHelper.centerFocus.off(dlg.querySelector('.formDialogContent'), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
var value = submitValue;
|
if (submitValue) {
|
||||||
|
return submitValue;
|
||||||
if (value) {
|
|
||||||
return value;
|
|
||||||
} else {
|
} else {
|
||||||
return Promise.reject();
|
return Promise.reject();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return function (options) {
|
if ((browser.tv || browser.xboxOne) && window.confirm) {
|
||||||
|
return function (options) {
|
||||||
|
if (typeof options === 'string') {
|
||||||
|
options = {
|
||||||
|
label: '',
|
||||||
|
text: options
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise(function (resolve, reject) {
|
var label = replaceAll(options.label || '', '<br/>', '\n');
|
||||||
require(['text!./prompt.template.html'], function (template) {
|
var result = prompt(label, options.text || '');
|
||||||
|
|
||||||
if (typeof options === 'string') {
|
if (result) {
|
||||||
options = {
|
return Promise.resolve(result);
|
||||||
title: '',
|
} else {
|
||||||
text: options
|
return Promise.reject(result);
|
||||||
};
|
}
|
||||||
}
|
};
|
||||||
showDialog(options, template).then(resolve, reject);
|
} else {
|
||||||
|
return function (options) {
|
||||||
|
return new Promise(function (resolve, reject) {
|
||||||
|
require(['text!./prompt.template.html'], function (template) {
|
||||||
|
if (typeof options === 'string') {
|
||||||
|
options = {
|
||||||
|
title: '',
|
||||||
|
text: options
|
||||||
|
};
|
||||||
|
}
|
||||||
|
showDialog(options, template).then(resolve, reject);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -122,9 +122,9 @@ define(["browser", "datetime", "backdrop", "libraryBrowser", "listView", "imageL
|
||||||
}
|
}
|
||||||
|
|
||||||
var url = item ? seriesImageUrl(item, {
|
var url = item ? seriesImageUrl(item, {
|
||||||
maxHeight: 300
|
maxHeight: 300 * 2
|
||||||
}) || imageUrl(item, {
|
}) || imageUrl(item, {
|
||||||
maxHeight: 300
|
maxHeight: 300 * 2
|
||||||
}) : null;
|
}) : null;
|
||||||
|
|
||||||
console.debug("updateNowPlayingInfo");
|
console.debug("updateNowPlayingInfo");
|
||||||
|
|
|
@ -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,79 +208,106 @@ 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.innerHTML = "pause";
|
btnSlideshowPause.classList.replace("play_arrow", "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.innerHTML = "";
|
btnSlideshowPause.classList.replace("pause", "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),
|
originalImage: getImgUrl(item),
|
||||||
originalImage: getImgUrl(item, true),
|
|
||||||
//title: item.Name,
|
//title: item.Name,
|
||||||
//description: item.Overview
|
//description: item.Overview
|
||||||
Id: item.Id,
|
Id: item.Id,
|
||||||
|
@ -262,11 +315,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">';
|
||||||
|
@ -288,42 +347,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')
|
||||||
};
|
};
|
||||||
|
@ -334,8 +369,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) {
|
||||||
|
@ -343,8 +380,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({
|
||||||
|
@ -352,21 +391,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 = !dialog.querySelector('.btnSlideshowPause i').classList.contains("pause");
|
||||||
var paused = dlg.querySelector('.btnSlideshowPause i').innerHTML !== "pause";
|
|
||||||
if (paused) {
|
if (paused) {
|
||||||
play();
|
play();
|
||||||
} else {
|
} else {
|
||||||
|
@ -374,8 +421,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);
|
||||||
|
@ -386,53 +435,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);
|
||||||
|
@ -440,71 +474,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) {
|
||||||
|
@ -527,125 +566,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;
|
||||||
});
|
});
|
||||||
|
|
|
@ -264,17 +264,13 @@ define(["jQuery", "loading", "emby-checkbox", "listViewStyle", "emby-input", "em
|
||||||
self.init = function () {
|
self.init = function () {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
if (options.showCancelButton) {
|
// Only hide the buttons if explicitly set to false; default to showing if undefined or null
|
||||||
page.querySelector(".btnCancel").classList.remove("hide");
|
// FIXME: rename this option to clarify logic
|
||||||
} else {
|
var hideCancelButton = options.showCancelButton === false;
|
||||||
page.querySelector(".btnCancel").classList.add("hide");
|
page.querySelector(".btnCancel").classList.toggle("hide", hideCancelButton);
|
||||||
}
|
|
||||||
|
|
||||||
if (options.showSubmitButton) {
|
var hideSubmitButton = options.showSubmitButton === false;
|
||||||
page.querySelector(".btnSubmitListings").classList.remove("hide");
|
page.querySelector(".btnSubmitListings").classList.toggle("hide", hideSubmitButton);
|
||||||
} else {
|
|
||||||
page.querySelector(".btnSubmitListings").classList.add("hide");
|
|
||||||
}
|
|
||||||
|
|
||||||
$(".formLogin", page).on("submit", function () {
|
$(".formLogin", page).on("submit", function () {
|
||||||
submitLoginForm();
|
submitLoginForm();
|
||||||
|
|
|
@ -163,17 +163,13 @@ define(["jQuery", "loading", "emby-checkbox", "emby-input", "listViewStyle", "pa
|
||||||
self.init = function () {
|
self.init = function () {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
|
|
||||||
if (false !== options.showCancelButton) {
|
// Only hide the buttons if explicitly set to false; default to showing if undefined or null
|
||||||
page.querySelector(".btnCancel").classList.remove("hide");
|
// FIXME: rename this option to clarify logic
|
||||||
} else {
|
var hideCancelButton = options.showCancelButton === false;
|
||||||
page.querySelector(".btnCancel").classList.add("hide");
|
page.querySelector(".btnCancel").classList.toggle("hide", hideCancelButton);
|
||||||
}
|
|
||||||
|
|
||||||
if (false !== options.showSubmitButton) {
|
var hideSubmitButton = options.showSubmitButton === false;
|
||||||
page.querySelector(".btnSubmitListings").classList.remove("hide");
|
page.querySelector(".btnSubmitListings").classList.toggle("hide", hideSubmitButton);
|
||||||
} else {
|
|
||||||
page.querySelector(".btnSubmitListings").classList.add("hide");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("form", page).on("submit", function () {
|
$("form", page).on("submit", function () {
|
||||||
submitListingsForm();
|
submitListingsForm();
|
||||||
|
|
3
src/config.example.json
Normal file
3
src/config.example.json
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"multiserver": true
|
||||||
|
}
|
|
@ -95,7 +95,7 @@ define(["loading", "appRouter", "layoutManager", "appSettings", "apphost", "focu
|
||||||
}
|
}
|
||||||
|
|
||||||
function showServerConnectionFailure() {
|
function showServerConnectionFailure() {
|
||||||
alertText(globalize.translate("MessageUnableToConnectToServer"), globalize.translate("HeaderConnectionFailure"));
|
alertText(globalize.translate("MessageUnableToConnectToServer"));
|
||||||
}
|
}
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -634,8 +635,11 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
return "<img src='" + iconUrl + "' />";
|
return "<img src='" + iconUrl + "' />";
|
||||||
},
|
},
|
||||||
getNowPlayingImageUrl: function (item) {
|
getNowPlayingImageUrl: function (item) {
|
||||||
|
/* Screen width is multiplied by 0.2, as the there is currently no way to get the width of
|
||||||
|
elements that aren't created yet. */
|
||||||
if (item && item.BackdropImageTags && item.BackdropImageTags.length) {
|
if (item && item.BackdropImageTags && item.BackdropImageTags.length) {
|
||||||
return ApiClient.getScaledImageUrl(item.Id, {
|
return ApiClient.getScaledImageUrl(item.Id, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Backdrop",
|
type: "Backdrop",
|
||||||
tag: item.BackdropImageTags[0]
|
tag: item.BackdropImageTags[0]
|
||||||
});
|
});
|
||||||
|
@ -643,6 +647,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
|
|
||||||
if (item && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
|
if (item && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
|
||||||
return ApiClient.getScaledImageUrl(item.ParentBackdropItemId, {
|
return ApiClient.getScaledImageUrl(item.ParentBackdropItemId, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Backdrop",
|
type: "Backdrop",
|
||||||
tag: item.ParentBackdropImageTags[0]
|
tag: item.ParentBackdropImageTags[0]
|
||||||
});
|
});
|
||||||
|
@ -650,6 +655,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
|
|
||||||
if (item && item.BackdropImageTag) {
|
if (item && item.BackdropImageTag) {
|
||||||
return ApiClient.getScaledImageUrl(item.BackdropItemId, {
|
return ApiClient.getScaledImageUrl(item.BackdropItemId, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Backdrop",
|
type: "Backdrop",
|
||||||
tag: item.BackdropImageTag
|
tag: item.BackdropImageTag
|
||||||
});
|
});
|
||||||
|
@ -659,6 +665,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
|
|
||||||
if (item && imageTags.Thumb) {
|
if (item && imageTags.Thumb) {
|
||||||
return ApiClient.getScaledImageUrl(item.Id, {
|
return ApiClient.getScaledImageUrl(item.Id, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Thumb",
|
type: "Thumb",
|
||||||
tag: imageTags.Thumb
|
tag: imageTags.Thumb
|
||||||
});
|
});
|
||||||
|
@ -666,6 +673,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
|
|
||||||
if (item && item.ParentThumbImageTag) {
|
if (item && item.ParentThumbImageTag) {
|
||||||
return ApiClient.getScaledImageUrl(item.ParentThumbItemId, {
|
return ApiClient.getScaledImageUrl(item.ParentThumbItemId, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Thumb",
|
type: "Thumb",
|
||||||
tag: item.ParentThumbImageTag
|
tag: item.ParentThumbImageTag
|
||||||
});
|
});
|
||||||
|
@ -673,6 +681,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
|
|
||||||
if (item && item.ThumbImageTag) {
|
if (item && item.ThumbImageTag) {
|
||||||
return ApiClient.getScaledImageUrl(item.ThumbItemId, {
|
return ApiClient.getScaledImageUrl(item.ThumbItemId, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Thumb",
|
type: "Thumb",
|
||||||
tag: item.ThumbImageTag
|
tag: item.ThumbImageTag
|
||||||
});
|
});
|
||||||
|
@ -680,6 +689,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
|
|
||||||
if (item && imageTags.Primary) {
|
if (item && imageTags.Primary) {
|
||||||
return ApiClient.getScaledImageUrl(item.Id, {
|
return ApiClient.getScaledImageUrl(item.Id, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
tag: imageTags.Primary
|
tag: imageTags.Primary
|
||||||
});
|
});
|
||||||
|
@ -687,6 +697,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
|
|
||||||
if (item && item.PrimaryImageTag) {
|
if (item && item.PrimaryImageTag) {
|
||||||
return ApiClient.getScaledImageUrl(item.PrimaryImageItemId, {
|
return ApiClient.getScaledImageUrl(item.PrimaryImageItemId, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
tag: item.PrimaryImageTag
|
tag: item.PrimaryImageTag
|
||||||
});
|
});
|
||||||
|
@ -694,6 +705,7 @@ define(["datetime", "events", "itemHelper", "serverNotifications", "dom", "globa
|
||||||
|
|
||||||
if (item && item.AlbumPrimaryImageTag) {
|
if (item && item.AlbumPrimaryImageTag) {
|
||||||
return ApiClient.getScaledImageUrl(item.AlbumId, {
|
return ApiClient.getScaledImageUrl(item.AlbumId, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.20),
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
tag: item.AlbumPrimaryImageTag
|
tag: item.AlbumPrimaryImageTag
|
||||||
});
|
});
|
||||||
|
|
|
@ -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) {
|
||||||
|
@ -49,7 +49,7 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "huma
|
||||||
html += "</a>";
|
html += "</a>";
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
if (task.State === "Running") {
|
if (task.State === "Running") {
|
||||||
html += '<button type="button" is="paper-icon-button-light" id="btnTask' + task.Id + '" class="btnStopTask" data-taskid="' + task.Id + '" title="' + globalize.translate("ButtonStop") + '"><i class="material-icons">stop</i></button>';
|
html += '<button type="button" is="paper-icon-button-light" id="btnTask' + task.Id + '" class="btnStopTask" data-taskid="' + task.Id + '" title="' + globalize.translate("ButtonStop") + '"><i class="material-icons stop"></i></button>';
|
||||||
} else if (task.State === "Idle") {
|
} else if (task.State === "Idle") {
|
||||||
html += '<button type="button" is="paper-icon-button-light" id="btnTask' + task.Id + '" class="btnStartTask" data-taskid="' + task.Id + '" title="' + globalize.translate("ButtonStart") + '"><i class="material-icons play_arrow"></i></button>';
|
html += '<button type="button" is="paper-icon-button-light" id="btnTask' + task.Id + '" class="btnStartTask" data-taskid="' + task.Id + '" title="' + globalize.translate("ButtonStart") + '"><i class="material-icons play_arrow"></i></button>';
|
||||||
}
|
}
|
||||||
|
@ -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") {
|
||||||
|
@ -90,16 +93,22 @@ define(["jQuery", "loading", "events", "globalize", "serverNotifications", "huma
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setTaskButtonIcon(button, icon) {
|
||||||
|
var inner = button.querySelector("i");
|
||||||
|
inner.classList.remove("stop", "play_arrow");
|
||||||
|
inner.classList.add(icon);
|
||||||
|
}
|
||||||
|
|
||||||
function updateTaskButton(elem, state) {
|
function updateTaskButton(elem, state) {
|
||||||
if (state === "Running") {
|
if (state === "Running") {
|
||||||
elem.classList.remove("btnStartTask");
|
elem.classList.remove("btnStartTask");
|
||||||
elem.classList.add("btnStopTask");
|
elem.classList.add("btnStopTask");
|
||||||
elem.querySelector("i").innerHTML = "stop";
|
setTaskButtonIcon(elem, "stop");
|
||||||
elem.title = globalize.translate("ButtonStop");
|
elem.title = globalize.translate("ButtonStop");
|
||||||
} else if (state === "Idle") {
|
} else if (state === "Idle") {
|
||||||
elem.classList.add("btnStartTask");
|
elem.classList.add("btnStartTask");
|
||||||
elem.classList.remove("btnStopTask");
|
elem.classList.remove("btnStopTask");
|
||||||
elem.querySelector("i").innerHTML = "";
|
setTaskButtonIcon(elem, "play_arrow");
|
||||||
elem.title = globalize.translate("ButtonStart");
|
elem.title = globalize.translate("ButtonStart");
|
||||||
}
|
}
|
||||||
$(elem).parents(".listItem")[0].setAttribute("data-status", state);
|
$(elem).parents(".listItem")[0].setAttribute("data-status", state);
|
||||||
|
@ -146,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() {
|
||||||
|
@ -155,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() {
|
||||||
|
@ -169,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) {
|
||||||
|
|
|
@ -468,6 +468,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
if ("Program" === item.Type && item.ImageTags && item.ImageTags.Thumb) {
|
if ("Program" === item.Type && item.ImageTags && item.ImageTags.Thumb) {
|
||||||
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
||||||
type: "Thumb",
|
type: "Thumb",
|
||||||
|
maxWidth: dom.getScreenWidth(),
|
||||||
index: 0,
|
index: 0,
|
||||||
tag: item.ImageTags.Thumb
|
tag: item.ImageTags.Thumb
|
||||||
});
|
});
|
||||||
|
@ -477,6 +478,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
} else if (usePrimaryImage && item.ImageTags && item.ImageTags.Primary) {
|
} else if (usePrimaryImage && item.ImageTags && item.ImageTags.Primary) {
|
||||||
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
|
maxWidth: dom.getScreenWidth(),
|
||||||
index: 0,
|
index: 0,
|
||||||
tag: item.ImageTags.Primary
|
tag: item.ImageTags.Primary
|
||||||
});
|
});
|
||||||
|
@ -486,6 +488,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
} else if (item.BackdropImageTags && item.BackdropImageTags.length) {
|
} else if (item.BackdropImageTags && item.BackdropImageTags.length) {
|
||||||
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
||||||
type: "Backdrop",
|
type: "Backdrop",
|
||||||
|
maxWidth: dom.getScreenWidth(),
|
||||||
index: 0,
|
index: 0,
|
||||||
tag: item.BackdropImageTags[0]
|
tag: item.BackdropImageTags[0]
|
||||||
});
|
});
|
||||||
|
@ -495,6 +498,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
} else if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
|
} else if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
|
||||||
imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, {
|
imgUrl = apiClient.getScaledImageUrl(item.ParentBackdropItemId, {
|
||||||
type: "Backdrop",
|
type: "Backdrop",
|
||||||
|
maxWidth: dom.getScreenWidth(),
|
||||||
index: 0,
|
index: 0,
|
||||||
tag: item.ParentBackdropImageTags[0]
|
tag: item.ParentBackdropImageTags[0]
|
||||||
});
|
});
|
||||||
|
@ -504,6 +508,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
} else if (item.ImageTags && item.ImageTags.Thumb) {
|
} else if (item.ImageTags && item.ImageTags.Thumb) {
|
||||||
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
imgUrl = apiClient.getScaledImageUrl(item.Id, {
|
||||||
type: "Thumb",
|
type: "Thumb",
|
||||||
|
maxWidth: dom.getScreenWidth(),
|
||||||
index: 0,
|
index: 0,
|
||||||
tag: item.ImageTags.Thumb
|
tag: item.ImageTags.Thumb
|
||||||
});
|
});
|
||||||
|
@ -586,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");
|
||||||
}
|
}
|
||||||
|
@ -600,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");
|
||||||
}
|
}
|
||||||
|
@ -613,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");
|
||||||
}
|
}
|
||||||
|
@ -698,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) {
|
||||||
|
@ -731,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(", ");
|
||||||
|
@ -761,44 +749,54 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
var shape = "portrait";
|
var shape = "portrait";
|
||||||
var detectRatio = false;
|
var detectRatio = false;
|
||||||
|
|
||||||
|
/* In the following section, getScreenWidth() is multiplied by 0.5 as the posters
|
||||||
|
are 25vw and we need double the resolution to counter Skia's scaling. */
|
||||||
|
// TODO: Find a reliable way to get the poster width
|
||||||
if (imageTags.Primary) {
|
if (imageTags.Primary) {
|
||||||
url = apiClient.getScaledImageUrl(item.Id, {
|
url = apiClient.getScaledImageUrl(item.Id, {
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.5),
|
||||||
tag: item.ImageTags.Primary
|
tag: item.ImageTags.Primary
|
||||||
});
|
});
|
||||||
detectRatio = true;
|
detectRatio = true;
|
||||||
} else if (item.BackdropImageTags && item.BackdropImageTags.length) {
|
} else if (item.BackdropImageTags && item.BackdropImageTags.length) {
|
||||||
url = apiClient.getScaledImageUrl(item.Id, {
|
url = apiClient.getScaledImageUrl(item.Id, {
|
||||||
type: "Backdrop",
|
type: "Backdrop",
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.5),
|
||||||
tag: item.BackdropImageTags[0]
|
tag: item.BackdropImageTags[0]
|
||||||
});
|
});
|
||||||
shape = "thumb";
|
shape = "thumb";
|
||||||
} else if (imageTags.Thumb) {
|
} else if (imageTags.Thumb) {
|
||||||
url = apiClient.getScaledImageUrl(item.Id, {
|
url = apiClient.getScaledImageUrl(item.Id, {
|
||||||
type: "Thumb",
|
type: "Thumb",
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.5),
|
||||||
tag: item.ImageTags.Thumb
|
tag: item.ImageTags.Thumb
|
||||||
});
|
});
|
||||||
shape = "thumb";
|
shape = "thumb";
|
||||||
} else if (imageTags.Disc) {
|
} else if (imageTags.Disc) {
|
||||||
url = apiClient.getScaledImageUrl(item.Id, {
|
url = apiClient.getScaledImageUrl(item.Id, {
|
||||||
type: "Disc",
|
type: "Disc",
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.5),
|
||||||
tag: item.ImageTags.Disc
|
tag: item.ImageTags.Disc
|
||||||
});
|
});
|
||||||
shape = "square";
|
shape = "square";
|
||||||
} else if (item.AlbumId && item.AlbumPrimaryImageTag) {
|
} else if (item.AlbumId && item.AlbumPrimaryImageTag) {
|
||||||
url = apiClient.getScaledImageUrl(item.AlbumId, {
|
url = apiClient.getScaledImageUrl(item.AlbumId, {
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.5),
|
||||||
tag: item.AlbumPrimaryImageTag
|
tag: item.AlbumPrimaryImageTag
|
||||||
});
|
});
|
||||||
shape = "square";
|
shape = "square";
|
||||||
} else if (item.SeriesId && item.SeriesPrimaryImageTag) {
|
} else if (item.SeriesId && item.SeriesPrimaryImageTag) {
|
||||||
url = apiClient.getScaledImageUrl(item.SeriesId, {
|
url = apiClient.getScaledImageUrl(item.SeriesId, {
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.5),
|
||||||
tag: item.SeriesPrimaryImageTag
|
tag: item.SeriesPrimaryImageTag
|
||||||
});
|
});
|
||||||
} else if (item.ParentPrimaryImageItemId && item.ParentPrimaryImageTag) {
|
} else if (item.ParentPrimaryImageItemId && item.ParentPrimaryImageTag) {
|
||||||
url = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, {
|
url = apiClient.getScaledImageUrl(item.ParentPrimaryImageItemId, {
|
||||||
type: "Primary",
|
type: "Primary",
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.5),
|
||||||
tag: item.ParentPrimaryImageTag
|
tag: item.ParentPrimaryImageTag
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1017,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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1041,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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1105,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);
|
||||||
}
|
}
|
||||||
|
@ -1805,7 +1810,6 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
require(["chaptercardbuilder"], function (chaptercardbuilder) {
|
require(["chaptercardbuilder"], function (chaptercardbuilder) {
|
||||||
chaptercardbuilder.buildChapterCards(item, chapters, {
|
chaptercardbuilder.buildChapterCards(item, chapters, {
|
||||||
itemsContainer: scenesContent,
|
itemsContainer: scenesContent,
|
||||||
width: 400,
|
|
||||||
backdropShape: "overflowBackdrop",
|
backdropShape: "overflowBackdrop",
|
||||||
squareShape: "overflowSquare"
|
squareShape: "overflowSquare"
|
||||||
});
|
});
|
||||||
|
@ -1858,7 +1862,6 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "userSetti
|
||||||
itemsContainer: castContent,
|
itemsContainer: castContent,
|
||||||
coverImage: true,
|
coverImage: true,
|
||||||
serverId: item.ServerId,
|
serverId: item.ServerId,
|
||||||
width: 160,
|
|
||||||
shape: "overflowPortrait"
|
shape: "overflowPortrait"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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", "
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "emby-itemscontainer"], function (cardBuilder, imageLoader, libraryBrowser, loading, events) {
|
define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "userSettings", "emby-itemscontainer"], function (cardBuilder, imageLoader, libraryBrowser, loading, events, userSettings) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params, tabContent) {
|
return function (view, params, tabContent) {
|
||||||
|
@ -7,12 +7,15 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em
|
||||||
pageData = {
|
pageData = {
|
||||||
query: {
|
query: {
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Limit: 100,
|
|
||||||
Fields: "PrimaryImageAspectRatio"
|
Fields: "PrimaryImageAspectRatio"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
pageData.query['Limit'] = userSettings.libraryPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
return pageData;
|
return pageData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +42,9 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex += query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex += query.Limit;
|
||||||
|
}
|
||||||
reloadItems(context);
|
reloadItems(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,7 +53,9 @@ define(["cardBuilder", "imageLoader", "libraryBrowser", "loading", "events", "em
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex -= query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex = Math.max(0, query.StartIndex - query.Limit);
|
||||||
|
}
|
||||||
reloadItems(context);
|
reloadItems(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -256,6 +256,7 @@ define(["jQuery", "apphost", "scripts/taskbutton", "loading", "libraryMenu", "gl
|
||||||
|
|
||||||
if (virtualFolder.PrimaryImageItemId) {
|
if (virtualFolder.PrimaryImageItemId) {
|
||||||
imgUrl = ApiClient.getScaledImageUrl(virtualFolder.PrimaryImageItemId, {
|
imgUrl = ApiClient.getScaledImageUrl(virtualFolder.PrimaryImageItemId, {
|
||||||
|
maxWidth: Math.round(dom.getScreenWidth() * 0.40),
|
||||||
type: "Primary"
|
type: "Primary"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
})
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardBuilder", "apphost", "emby-itemscontainer"], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder, appHost) {
|
define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardBuilder", "userSettings", "emby-itemscontainer"], function (loading, events, libraryBrowser, imageLoader, listView, cardBuilder, userSettings) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params, tabContent) {
|
return function (view, params, tabContent) {
|
||||||
|
@ -16,11 +16,15 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB
|
||||||
Fields: "PrimaryImageAspectRatio,SortName",
|
Fields: "PrimaryImageAspectRatio,SortName",
|
||||||
ImageTypeLimit: 1,
|
ImageTypeLimit: 1,
|
||||||
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
||||||
StartIndex: 0,
|
StartIndex: 0
|
||||||
Limit: pageSize
|
|
||||||
},
|
},
|
||||||
view: libraryBrowser.getSavedView(key) || "Poster"
|
view: libraryBrowser.getSavedView(key) || "Poster"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
pageData.query['Limit'] = userSettings.libraryPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
pageData.query.ParentId = params.topParentId;
|
pageData.query.ParentId = params.topParentId;
|
||||||
libraryBrowser.loadSavedQueryValues(key, pageData.query);
|
libraryBrowser.loadSavedQueryValues(key, pageData.query);
|
||||||
}
|
}
|
||||||
|
@ -65,7 +69,9 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex += query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex += query.Limit;
|
||||||
|
}
|
||||||
reloadItems(tabContent);
|
reloadItems(tabContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +80,9 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex -= query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex = Math.max(0, query.StartIndex - query.Limit);
|
||||||
|
}
|
||||||
reloadItems(tabContent);
|
reloadItems(tabContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +188,6 @@ define(["loading", "events", "libraryBrowser", "imageLoader", "listView", "cardB
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var pageSize = 100;
|
|
||||||
var data = {};
|
var data = {};
|
||||||
var isLoading = false;
|
var isLoading = false;
|
||||||
|
|
||||||
|
|
|
@ -184,12 +184,12 @@ define(["layoutManager", "loading", "libraryBrowser", "cardBuilder", "lazyLoader
|
||||||
};
|
};
|
||||||
|
|
||||||
self.getCurrentViewStyle = function () {
|
self.getCurrentViewStyle = function () {
|
||||||
return getPageData(tabContent).view;
|
return getPageData().view;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.setCurrentViewStyle = function (viewStyle) {
|
self.setCurrentViewStyle = function (viewStyle) {
|
||||||
getPageData(tabContent).view = viewStyle;
|
getPageData().view = viewStyle;
|
||||||
libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle);
|
libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle);
|
||||||
fullyReload();
|
fullyReload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,9 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser",
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex += query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex += query.Limit;
|
||||||
|
}
|
||||||
itemsContainer.refreshItems();
|
itemsContainer.refreshItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +43,9 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser",
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex -= query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex = Math.max(0, query.StartIndex - query.Limit);
|
||||||
|
}
|
||||||
itemsContainer.refreshItems();
|
itemsContainer.refreshItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,9 +254,13 @@ define(["loading", "layoutManager", "userSettings", "events", "libraryBrowser",
|
||||||
ImageTypeLimit: 1,
|
ImageTypeLimit: 1,
|
||||||
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
Limit: 100,
|
|
||||||
ParentId: params.topParentId
|
ParentId: params.topParentId
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query['Limit'] = userSettings.libraryPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
var isLoading = false;
|
var isLoading = false;
|
||||||
|
|
||||||
if (options.mode === "favorites") {
|
if (options.mode === "favorites") {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "apphost", "emby-itemscontainer"], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost) {
|
define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "userSettings", "emby-itemscontainer"], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, userSettings) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params, tabContent) {
|
return function (view, params, tabContent) {
|
||||||
|
@ -16,11 +16,15 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "
|
||||||
Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo",
|
Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo",
|
||||||
ImageTypeLimit: 1,
|
ImageTypeLimit: 1,
|
||||||
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
||||||
StartIndex: 0,
|
StartIndex: 0
|
||||||
Limit: pageSize
|
|
||||||
},
|
},
|
||||||
view: libraryBrowser.getSavedView(key) || "Poster"
|
view: libraryBrowser.getSavedView(key) || "Poster"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
pageData.query['Limit'] = userSettings.libraryPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
libraryBrowser.loadSavedQueryValues(key, pageData.query);
|
libraryBrowser.loadSavedQueryValues(key, pageData.query);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +53,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex += query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex += query.Limit;
|
||||||
|
}
|
||||||
reloadItems();
|
reloadItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +64,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex -= query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex = Math.max(0, query.StartIndex - query.Limit);
|
||||||
|
}
|
||||||
reloadItems();
|
reloadItems();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -168,7 +176,6 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
var pageSize = 100;
|
|
||||||
var data = {};
|
var data = {};
|
||||||
var isLoading = false;
|
var isLoading = false;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "apphost", "emby-itemscontainer"], function (layoutManager, playbackManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost) {
|
define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "userSettings", "emby-itemscontainer"], function (layoutManager, playbackManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, userSettings) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params, tabContent) {
|
return function (view, params, tabContent) {
|
||||||
|
@ -30,11 +30,15 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser
|
||||||
Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo",
|
Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo",
|
||||||
ImageTypeLimit: 1,
|
ImageTypeLimit: 1,
|
||||||
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
||||||
StartIndex: 0,
|
StartIndex: 0
|
||||||
Limit: pageSize
|
|
||||||
},
|
},
|
||||||
view: libraryBrowser.getSavedView(key) || "Poster"
|
view: libraryBrowser.getSavedView(key) || "Poster"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
pageData.query['Limit'] = userSettings.libraryPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
pageData.query.ParentId = params.topParentId;
|
pageData.query.ParentId = params.topParentId;
|
||||||
libraryBrowser.loadSavedQueryValues(key, pageData.query);
|
libraryBrowser.loadSavedQueryValues(key, pageData.query);
|
||||||
}
|
}
|
||||||
|
@ -79,7 +83,9 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex += query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex += query.Limit;
|
||||||
|
}
|
||||||
reloadItems(tabContent);
|
reloadItems(tabContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +94,9 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex -= query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex = Math.max(0, query.StartIndex - query.Limit);
|
||||||
|
}
|
||||||
reloadItems(tabContent);
|
reloadItems(tabContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +183,6 @@ define(["layoutManager", "playbackManager", "loading", "events", "libraryBrowser
|
||||||
var savedQueryKey;
|
var savedQueryKey;
|
||||||
var pageData;
|
var pageData;
|
||||||
var self = this;
|
var self = this;
|
||||||
var pageSize = 100;
|
|
||||||
var isLoading = false;
|
var isLoading = false;
|
||||||
|
|
||||||
self.showFilterMenu = function () {
|
self.showFilterMenu = function () {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "apphost", "emby-itemscontainer"], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost) {
|
define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "alphaPicker", "listView", "cardBuilder", "apphost", "userSettings", "emby-itemscontainer"], function (layoutManager, loading, events, libraryBrowser, imageLoader, alphaPicker, listView, cardBuilder, appHost, userSettings) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params, tabContent) {
|
return function (view, params, tabContent) {
|
||||||
|
@ -7,17 +7,22 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "
|
||||||
var pageData = data[key];
|
var pageData = data[key];
|
||||||
|
|
||||||
if (!pageData) {
|
if (!pageData) {
|
||||||
|
var queryValues = {
|
||||||
|
SortBy: "SortName",
|
||||||
|
SortOrder: "Ascending",
|
||||||
|
Recursive: true,
|
||||||
|
Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo",
|
||||||
|
StartIndex: 0,
|
||||||
|
ImageTypeLimit: 1,
|
||||||
|
EnableImageTypes: "Primary,Backdrop,Banner,Thumb"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
queryValues['Limit'] = userSettings.libraryPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
pageData = data[key] = {
|
pageData = data[key] = {
|
||||||
query: {
|
query: queryValues,
|
||||||
SortBy: "SortName",
|
|
||||||
SortOrder: "Ascending",
|
|
||||||
Recursive: true,
|
|
||||||
Fields: "PrimaryImageAspectRatio,SortName,BasicSyncInfo",
|
|
||||||
StartIndex: 0,
|
|
||||||
ImageTypeLimit: 1,
|
|
||||||
EnableImageTypes: "Primary,Backdrop,Banner,Thumb",
|
|
||||||
Limit: 100
|
|
||||||
},
|
|
||||||
view: libraryBrowser.getSavedView(key) || "Poster"
|
view: libraryBrowser.getSavedView(key) || "Poster"
|
||||||
};
|
};
|
||||||
pageData.query.ParentId = params.topParentId;
|
pageData.query.ParentId = params.topParentId;
|
||||||
|
@ -67,7 +72,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex += query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex += query.Limit;
|
||||||
|
}
|
||||||
reloadItems(tabContent);
|
reloadItems(tabContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +83,9 @@ define(["layoutManager", "loading", "events", "libraryBrowser", "imageLoader", "
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex -= query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex = Math.max(0, query.StartIndex - query.Limit);
|
||||||
|
}
|
||||||
reloadItems(tabContent);
|
reloadItems(tabContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,12 +107,12 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f
|
||||||
};
|
};
|
||||||
|
|
||||||
self.getCurrentViewStyle = function () {
|
self.getCurrentViewStyle = function () {
|
||||||
return getPageData(tabContent).view;
|
return getPageData().view;
|
||||||
};
|
};
|
||||||
|
|
||||||
self.setCurrentViewStyle = function (viewStyle) {
|
self.setCurrentViewStyle = function (viewStyle) {
|
||||||
getPageData(tabContent).view = viewStyle;
|
getPageData().view = viewStyle;
|
||||||
libraryBrowser.saveViewSetting(getSavedQueryKey(tabContent), viewStyle);
|
libraryBrowser.saveViewSetting(getSavedQueryKey(), viewStyle);
|
||||||
fullyReload();
|
fullyReload();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -69,7 +69,7 @@ define(["libraryBrowser", "cardBuilder", "apphost", "imageLoader", "loading"], f
|
||||||
var data = {};
|
var data = {};
|
||||||
|
|
||||||
self.getCurrentViewStyle = function () {
|
self.getCurrentViewStyle = function () {
|
||||||
return getPageData(tabContent).view;
|
return getPageData().view;
|
||||||
};
|
};
|
||||||
|
|
||||||
var promise;
|
var promise;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby-itemscontainer"], function (events, libraryBrowser, imageLoader, listView, loading) {
|
define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "userSettings", "emby-itemscontainer"], function (events, libraryBrowser, imageLoader, listView, loading, userSettings) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params, tabContent) {
|
return function (view, params, tabContent) {
|
||||||
|
@ -14,12 +14,16 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby-
|
||||||
IncludeItemTypes: "Audio",
|
IncludeItemTypes: "Audio",
|
||||||
Recursive: true,
|
Recursive: true,
|
||||||
Fields: "AudioInfo,ParentId",
|
Fields: "AudioInfo,ParentId",
|
||||||
Limit: 100,
|
|
||||||
StartIndex: 0,
|
StartIndex: 0,
|
||||||
ImageTypeLimit: 1,
|
ImageTypeLimit: 1,
|
||||||
EnableImageTypes: "Primary"
|
EnableImageTypes: "Primary"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
pageData.query['Limit'] = userSettings.libraryPageSize();
|
||||||
|
}
|
||||||
|
|
||||||
pageData.query.ParentId = params.topParentId;
|
pageData.query.ParentId = params.topParentId;
|
||||||
libraryBrowser.loadSavedQueryValues(key, pageData.query);
|
libraryBrowser.loadSavedQueryValues(key, pageData.query);
|
||||||
}
|
}
|
||||||
|
@ -49,7 +53,9 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby-
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex += query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex += query.Limit;
|
||||||
|
}
|
||||||
reloadItems(tabContent);
|
reloadItems(tabContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -58,7 +64,9 @@ define(["events", "libraryBrowser", "imageLoader", "listView", "loading", "emby-
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
query.StartIndex -= query.Limit;
|
if (userSettings.libraryPageSize() > 0) {
|
||||||
|
query.StartIndex = Math.max(0, query.StartIndex - query.Limit);
|
||||||
|
}
|
||||||
reloadItems(tabContent);
|
reloadItems(tabContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -334,18 +334,24 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
var imgUrl = seriesImageUrl(item, {
|
var imgUrl = seriesImageUrl(item, {
|
||||||
|
maxWidth: osdPoster.clientWidth * 2,
|
||||||
type: "Primary"
|
type: "Primary"
|
||||||
}) || seriesImageUrl(item, {
|
}) || seriesImageUrl(item, {
|
||||||
|
maxWidth: osdPoster.clientWidth * 2,
|
||||||
type: "Thumb"
|
type: "Thumb"
|
||||||
}) || imageUrl(item, {
|
}) || imageUrl(item, {
|
||||||
|
maxWidth: osdPoster.clientWidth * 2,
|
||||||
type: "Primary"
|
type: "Primary"
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!imgUrl && secondaryItem && (imgUrl = seriesImageUrl(secondaryItem, {
|
if (!imgUrl && secondaryItem && (imgUrl = seriesImageUrl(secondaryItem, {
|
||||||
|
maxWidth: osdPoster.clientWidth * 2,
|
||||||
type: "Primary"
|
type: "Primary"
|
||||||
}) || seriesImageUrl(secondaryItem, {
|
}) || seriesImageUrl(secondaryItem, {
|
||||||
|
maxWidth: osdPoster.clientWidth * 2,
|
||||||
type: "Thumb"
|
type: "Thumb"
|
||||||
}) || imageUrl(secondaryItem, {
|
}) || imageUrl(secondaryItem, {
|
||||||
|
maxWidth: osdPoster.clientWidth * 2,
|
||||||
type: "Primary"
|
type: "Primary"
|
||||||
})), imgUrl) {
|
})), imgUrl) {
|
||||||
return void (osdPoster.innerHTML = '<img src="' + imgUrl + '" />');
|
return void (osdPoster.innerHTML = '<img src="' + imgUrl + '" />');
|
||||||
|
@ -665,7 +671,8 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
|
||||||
}
|
}
|
||||||
|
|
||||||
function onTimeUpdate(e) {
|
function onTimeUpdate(e) {
|
||||||
if (isEnabled) {
|
// Test for 'currentItem' is required for Firefox since its player spams 'timeupdate' events even being at breakpoint
|
||||||
|
if (isEnabled && currentItem) {
|
||||||
var now = new Date().getTime();
|
var now = new Date().getTime();
|
||||||
|
|
||||||
if (!(now - lastUpdateTime < 700)) {
|
if (!(now - lastUpdateTime < 700)) {
|
||||||
|
@ -1145,7 +1152,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
|
||||||
case "GamepadDPadLeft":
|
case "GamepadDPadLeft":
|
||||||
case "GamepadLeftThumbstickLeft":
|
case "GamepadLeftThumbstickLeft":
|
||||||
// Ignores gamepad events that are always triggered, even when not focused.
|
// Ignores gamepad events that are always triggered, even when not focused.
|
||||||
if (document.hasFocus()) {
|
if (document.hasFocus()) { /* eslint-disable-line compat/compat */
|
||||||
playbackManager.rewind(currentPlayer);
|
playbackManager.rewind(currentPlayer);
|
||||||
showOsd();
|
showOsd();
|
||||||
}
|
}
|
||||||
|
@ -1154,7 +1161,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
|
||||||
case "GamepadDPadRight":
|
case "GamepadDPadRight":
|
||||||
case "GamepadLeftThumbstickRight":
|
case "GamepadLeftThumbstickRight":
|
||||||
// Ignores gamepad events that are always triggered, even when not focused.
|
// Ignores gamepad events that are always triggered, even when not focused.
|
||||||
if (document.hasFocus()) {
|
if (document.hasFocus()) { /* eslint-disable-line compat/compat */
|
||||||
playbackManager.fastForward(currentPlayer);
|
playbackManager.fastForward(currentPlayer);
|
||||||
showOsd();
|
showOsd();
|
||||||
}
|
}
|
||||||
|
|
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