mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge remote-tracking branch 'upstream/master' into patch-10
This commit is contained in:
commit
a461f16332
169 changed files with 11856 additions and 5175 deletions
|
@ -7,38 +7,70 @@ trigger:
|
||||||
tags:
|
tags:
|
||||||
include:
|
include:
|
||||||
- '*'
|
- '*'
|
||||||
|
pr:
|
||||||
|
branches:
|
||||||
|
include:
|
||||||
|
- '*'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
- job: main_build
|
- job: build
|
||||||
displayName: 'Main Build'
|
displayName: 'Build'
|
||||||
|
|
||||||
dependsOn: lint
|
|
||||||
condition: succeeded()
|
|
||||||
|
|
||||||
pool:
|
pool:
|
||||||
vmImage: 'ubuntu-latest'
|
vmImage: 'ubuntu-latest'
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
Development:
|
||||||
|
BuildConfiguration: development
|
||||||
|
Production:
|
||||||
|
BuildConfiguration: production
|
||||||
|
Standalone:
|
||||||
|
BuildConfiguration: standalone
|
||||||
|
maxParallel: 3
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
displayName: 'Install Node'
|
displayName: 'Install Node'
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '12.x'
|
||||||
|
|
||||||
- script: 'yarn install'
|
- 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'
|
displayName: 'Install Dependencies'
|
||||||
|
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||||
|
|
||||||
|
- script: 'yarn build:development'
|
||||||
|
displayName: 'Build Development'
|
||||||
|
condition: eq(variables['BuildConfiguration'], 'development')
|
||||||
|
|
||||||
|
- script: 'yarn build:production'
|
||||||
|
displayName: 'Build Bundle'
|
||||||
|
condition: eq(variables['BuildConfiguration'], 'production')
|
||||||
|
|
||||||
|
- script: 'yarn build:standalone'
|
||||||
|
displayName: 'Build Standalone'
|
||||||
|
condition: eq(variables['BuildConfiguration'], 'standalone')
|
||||||
|
|
||||||
- script: 'test -d dist'
|
- script: 'test -d dist'
|
||||||
displayName: 'Check Build'
|
displayName: 'Check Build'
|
||||||
|
|
||||||
- script: 'yarn pack --filename jellyfin-web.tgz'
|
- script: 'mv dist jellyfin-web'
|
||||||
displayName: 'Bundle Release'
|
displayName: 'Rename Directory'
|
||||||
|
condition: succeeded()
|
||||||
|
|
||||||
- task: PublishPipelineArtifact@1
|
- task: PublishPipelineArtifact@1
|
||||||
displayName: 'Publish Release'
|
displayName: 'Publish Release'
|
||||||
condition: succeeded()
|
condition: succeeded()
|
||||||
inputs:
|
inputs:
|
||||||
targetPath: '$(Build.SourcesDirectory)/jellyfin-web.tgz'
|
targetPath: '$(Build.SourcesDirectory)/jellyfin-web'
|
||||||
artifactName: 'jellyfin-web'
|
artifactName: 'jellyfin-web-$(BuildConfiguration)'
|
||||||
|
|
||||||
- job: lint
|
- job: lint
|
||||||
displayName: 'Lint'
|
displayName: 'Lint'
|
||||||
|
@ -50,14 +82,21 @@ jobs:
|
||||||
- task: NodeTool@0
|
- task: NodeTool@0
|
||||||
displayName: 'Install Node'
|
displayName: 'Install Node'
|
||||||
inputs:
|
inputs:
|
||||||
versionSpec: '10.x'
|
versionSpec: '12.x'
|
||||||
|
|
||||||
- script: 'yarn install'
|
- 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'
|
displayName: 'Install Dependencies'
|
||||||
|
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||||
|
|
||||||
- script: 'yarn run lint'
|
- script: 'yarn run lint --quiet'
|
||||||
displayName: 'Run ESLint'
|
displayName: 'Run ESLint'
|
||||||
|
|
||||||
- script: |
|
- script: 'yarn run stylelint'
|
||||||
yarn run stylelint
|
displayName: 'Run Stylelint'
|
||||||
displayName: 'Run stylelint'
|
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
env:
|
env:
|
||||||
es6: false
|
|
||||||
browser: true
|
|
||||||
amd: true
|
amd: true
|
||||||
|
browser: true
|
||||||
|
es6: true
|
||||||
|
es2017: true
|
||||||
|
es2020: true
|
||||||
|
|
||||||
|
parserOptions:
|
||||||
|
ecmaVersion: 2020
|
||||||
|
sourceType: module
|
||||||
|
ecmaFeatures:
|
||||||
|
impliedStrict: true
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- promise
|
||||||
|
- import
|
||||||
|
- eslint-comments
|
||||||
|
|
||||||
|
extends:
|
||||||
|
- eslint:recommended
|
||||||
|
- plugin:promise/recommended
|
||||||
|
- plugin:import/errors
|
||||||
|
- plugin:import/warnings
|
||||||
|
- plugin:eslint-comments/recommended
|
||||||
|
|
||||||
globals:
|
globals:
|
||||||
# New browser globals
|
# Browser globals
|
||||||
DataView: readonly
|
|
||||||
MediaMetadata: readonly
|
MediaMetadata: readonly
|
||||||
Promise: readonly
|
|
||||||
# Deprecated browser globals
|
|
||||||
DocumentTouch: readonly
|
|
||||||
# Tizen globals
|
# Tizen globals
|
||||||
tizen: readonly
|
tizen: readonly
|
||||||
webapis: readonly
|
webapis: readonly
|
||||||
|
@ -18,7 +34,6 @@ globals:
|
||||||
# Dependency globals
|
# Dependency globals
|
||||||
$: readonly
|
$: readonly
|
||||||
jQuery: readonly
|
jQuery: readonly
|
||||||
queryString: readonly
|
|
||||||
requirejs: readonly
|
requirejs: readonly
|
||||||
# Jellyfin globals
|
# Jellyfin globals
|
||||||
ApiClient: writable
|
ApiClient: writable
|
||||||
|
@ -34,8 +49,7 @@ globals:
|
||||||
getWindowLocationSearch: writable
|
getWindowLocationSearch: writable
|
||||||
Globalize: writable
|
Globalize: writable
|
||||||
Hls: writable
|
Hls: writable
|
||||||
humaneDate: writable
|
dfnshelper: writable
|
||||||
humaneElapsed: writable
|
|
||||||
LibraryMenu: writable
|
LibraryMenu: writable
|
||||||
LinkParser: writable
|
LinkParser: writable
|
||||||
LiveTvHelpers: writable
|
LiveTvHelpers: writable
|
||||||
|
@ -46,9 +60,6 @@ globals:
|
||||||
UserParentalControlPage: writable
|
UserParentalControlPage: writable
|
||||||
Windows: readonly
|
Windows: readonly
|
||||||
|
|
||||||
extends:
|
|
||||||
- eslint:recommended
|
|
||||||
|
|
||||||
rules:
|
rules:
|
||||||
block-spacing: ["error"]
|
block-spacing: ["error"]
|
||||||
brace-style: ["error"]
|
brace-style: ["error"]
|
||||||
|
@ -69,3 +80,8 @@ 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"]
|
||||||
|
|
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
|
||||||
|
|
17
README.md
17
README.md
|
@ -45,20 +45,37 @@ Jellyfin Web is the frontend used for most of the clients available for end user
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
- Yarn
|
- Yarn
|
||||||
|
- Gulp-cli
|
||||||
|
|
||||||
### 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.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn build:development
|
||||||
|
```
|
||||||
|
|
||||||
|
You can build a nginx compatible version as well.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
yarn build:standalone
|
||||||
|
```
|
201
gulpfile.js
Normal file
201
gulpfile.js
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { src, dest, series, parallel, watch } = require('gulp');
|
||||||
|
const browserSync = require('browser-sync').create();
|
||||||
|
const del = require('del');
|
||||||
|
const babel = require('gulp-babel');
|
||||||
|
const concat = require('gulp-concat');
|
||||||
|
const terser = require('gulp-terser');
|
||||||
|
const htmlmin = require('gulp-htmlmin');
|
||||||
|
const imagemin = require('gulp-imagemin');
|
||||||
|
const sourcemaps = require('gulp-sourcemaps');
|
||||||
|
const mode = require('gulp-mode')({
|
||||||
|
modes: ["development", "production"],
|
||||||
|
default: "development",
|
||||||
|
verbose: false
|
||||||
|
});
|
||||||
|
const stream = require('webpack-stream');
|
||||||
|
const inject = require('gulp-inject');
|
||||||
|
const postcss = require('gulp-postcss');
|
||||||
|
const sass = require('gulp-sass');
|
||||||
|
const gulpif = require('gulp-if');
|
||||||
|
const lazypipe = require('lazypipe');
|
||||||
|
|
||||||
|
sass.compiler = require('node-sass');
|
||||||
|
|
||||||
|
let config;
|
||||||
|
if (mode.production()) {
|
||||||
|
config = require('./webpack.prod.js');
|
||||||
|
} else {
|
||||||
|
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() {
|
||||||
|
browserSync.init({
|
||||||
|
server: {
|
||||||
|
baseDir: "./dist"
|
||||||
|
},
|
||||||
|
port: 8080
|
||||||
|
});
|
||||||
|
|
||||||
|
let events = ['add', 'change'];
|
||||||
|
|
||||||
|
watch(options.javascript.query).on('all', function (event, path) {
|
||||||
|
if (events.includes(event)) {
|
||||||
|
javascript(path);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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() {
|
||||||
|
return del(['dist/']);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pipelineJavascript = lazypipe()
|
||||||
|
.pipe(function () {
|
||||||
|
return mode.development(sourcemaps.init({ loadMaps: true }));
|
||||||
|
})
|
||||||
|
.pipe(function () {
|
||||||
|
return babel({
|
||||||
|
presets: [
|
||||||
|
['@babel/preset-env']
|
||||||
|
]
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.pipe(function () {
|
||||||
|
return terser({
|
||||||
|
keep_fnames: true,
|
||||||
|
mangle: false
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.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(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() {
|
||||||
|
return stream(config)
|
||||||
|
.pipe(dest('dist/'))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
function css(query) {
|
||||||
|
return src(typeof query !== 'function' ? query : options.css.query, { base: './src/' })
|
||||||
|
.pipe(mode.development(sourcemaps.init({ loadMaps: true })))
|
||||||
|
.pipe(sass().on('error', sass.logError))
|
||||||
|
.pipe(postcss())
|
||||||
|
.pipe(mode.development(sourcemaps.write('.')))
|
||||||
|
.pipe(dest('dist/'))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
function html(query) {
|
||||||
|
return src(typeof query !== 'function' ? query : options.html.query, { base: './src/' })
|
||||||
|
.pipe(mode.production(htmlmin({ collapseWhitespace: true })))
|
||||||
|
.pipe(dest('dist/'))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
function images(query) {
|
||||||
|
return src(typeof query !== 'function' ? query : options.images.query, { base: './src/' })
|
||||||
|
.pipe(mode.production(imagemin()))
|
||||||
|
.pipe(dest('dist/'))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
function copy(query) {
|
||||||
|
return src(typeof query !== 'function' ? query : options.copy.query, { base: './src/' })
|
||||||
|
.pipe(dest('dist/'))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
function injectBundle() {
|
||||||
|
return src(options.injectBundle.query, { base: './src/' })
|
||||||
|
.pipe(inject(
|
||||||
|
src(['src/scripts/apploader.js'], { read: false }, { base: './src/' }), { relative: true }
|
||||||
|
))
|
||||||
|
.pipe(dest('dist/'))
|
||||||
|
.pipe(browserSync.stream());
|
||||||
|
}
|
||||||
|
|
||||||
|
function build(standalone) {
|
||||||
|
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);
|
80
package.json
80
package.json
|
@ -5,12 +5,41 @@
|
||||||
"repository": "https://github.com/jellyfin/jellyfin-web",
|
"repository": "https://github.com/jellyfin/jellyfin-web",
|
||||||
"license": "GPL-2.0-or-later",
|
"license": "GPL-2.0-or-later",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.8.6",
|
||||||
|
"@babel/plugin-transform-modules-amd": "^7.8.3",
|
||||||
|
"@babel/polyfill": "^7.8.7",
|
||||||
|
"@babel/preset-env": "^7.8.6",
|
||||||
|
"autoprefixer": "^9.7.4",
|
||||||
|
"babel-loader": "^8.0.6",
|
||||||
|
"browser-sync": "^2.26.7",
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
"copy-webpack-plugin": "^5.1.1",
|
"copy-webpack-plugin": "^5.1.1",
|
||||||
"css-loader": "^3.4.2",
|
"css-loader": "^3.4.2",
|
||||||
|
"cssnano": "^4.1.10",
|
||||||
|
"del": "^5.1.0",
|
||||||
"eslint": "^6.8.0",
|
"eslint": "^6.8.0",
|
||||||
"file-loader": "^5.0.2",
|
"eslint-plugin-eslint-comments": "^3.1.2",
|
||||||
"html-webpack-plugin": "^3.2.0",
|
"eslint-plugin-import": "^2.20.2",
|
||||||
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
|
"file-loader": "^6.0.0",
|
||||||
|
"gulp": "^4.0.2",
|
||||||
|
"gulp-babel": "^8.0.0",
|
||||||
|
"gulp-cli": "^2.2.0",
|
||||||
|
"gulp-concat": "^2.6.1",
|
||||||
|
"gulp-htmlmin": "^5.0.1",
|
||||||
|
"gulp-if": "^3.0.0",
|
||||||
|
"gulp-imagemin": "^7.1.0",
|
||||||
|
"gulp-inject": "^5.0.5",
|
||||||
|
"gulp-mode": "^1.0.2",
|
||||||
|
"gulp-postcss": "^8.0.0",
|
||||||
|
"gulp-sass": "^4.0.2",
|
||||||
|
"gulp-sourcemaps": "^2.6.5",
|
||||||
|
"gulp-terser": "^1.2.0",
|
||||||
|
"html-webpack-plugin": "^4.0.2",
|
||||||
|
"lazypipe": "^1.0.2",
|
||||||
|
"node-sass": "^4.13.1",
|
||||||
|
"postcss-loader": "^3.0.0",
|
||||||
|
"postcss-preset-env": "^6.7.0",
|
||||||
"style-loader": "^1.1.3",
|
"style-loader": "^1.1.3",
|
||||||
"stylelint": "^13.1.0",
|
"stylelint": "^13.1.0",
|
||||||
"stylelint-config-rational-order": "^0.1.2",
|
"stylelint-config-rational-order": "^0.1.2",
|
||||||
|
@ -20,10 +49,13 @@
|
||||||
"webpack-cli": "^3.3.10",
|
"webpack-cli": "^3.3.10",
|
||||||
"webpack-concat-plugin": "^3.0.0",
|
"webpack-concat-plugin": "^3.0.0",
|
||||||
"webpack-dev-server": "^3.10.3",
|
"webpack-dev-server": "^3.10.3",
|
||||||
"webpack-merge": "^4.2.2"
|
"webpack-merge": "^4.2.2",
|
||||||
|
"webpack-stream": "^5.2.1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"alameda": "^1.4.0",
|
"alameda": "^1.4.0",
|
||||||
|
"core-js": "^3.6.4",
|
||||||
|
"date-fns": "^2.11.1",
|
||||||
"document-register-element": "^1.14.3",
|
"document-register-element": "^1.14.3",
|
||||||
"flv.js": "^1.5.0",
|
"flv.js": "^1.5.0",
|
||||||
"hls.js": "^0.13.1",
|
"hls.js": "^0.13.1",
|
||||||
|
@ -31,17 +63,42 @@
|
||||||
"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": "^2.1.1",
|
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf",
|
||||||
"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",
|
||||||
|
"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",
|
||||||
"whatwg-fetch": "^3.0.0"
|
"whatwg-fetch": "^3.0.0"
|
||||||
},
|
},
|
||||||
|
"babel": {
|
||||||
|
"presets": [
|
||||||
|
"@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/scripts/settings/webSettings.js",
|
||||||
|
"src/components/scrollManager.js",
|
||||||
|
"src/scripts/dfnshelper.js"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"@babel/plugin-transform-modules-amd"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
"browserslist": [
|
"browserslist": [
|
||||||
"last 2 Firefox versions",
|
"last 2 Firefox versions",
|
||||||
"last 2 Chrome versions",
|
"last 2 Chrome versions",
|
||||||
|
@ -49,6 +106,7 @@
|
||||||
"last 2 Safari versions",
|
"last 2 Safari versions",
|
||||||
"last 2 iOS versions",
|
"last 2 iOS versions",
|
||||||
"last 2 Edge versions",
|
"last 2 Edge versions",
|
||||||
|
"Chrome 27",
|
||||||
"Chrome 38",
|
"Chrome 38",
|
||||||
"Chrome 47",
|
"Chrome 47",
|
||||||
"Chrome 53",
|
"Chrome 53",
|
||||||
|
@ -57,10 +115,12 @@
|
||||||
"Firefox ESR"
|
"Firefox ESR"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"serve": "webpack-dev-server --config webpack.dev.js --open",
|
"serve": "gulp serve",
|
||||||
"build": "webpack --config webpack.prod.js",
|
"prepare": "gulp --production",
|
||||||
|
"build:development": "gulp --development",
|
||||||
|
"build:production": "gulp --production",
|
||||||
|
"build:standalone": "gulp standalone --development",
|
||||||
"lint": "eslint \"src\"",
|
"lint": "eslint \"src\"",
|
||||||
"stylelint": "stylelint \"src/**/*.css\"",
|
"stylelint": "stylelint \"src/**/*.css\""
|
||||||
"prepare": "webpack --config webpack.prod.js"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
postcss.config.js
Normal file
11
postcss.config.js
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
const postcssPresetEnv = require('postcss-preset-env');
|
||||||
|
const cssnano = require('cssnano');
|
||||||
|
|
||||||
|
const config = () => ({
|
||||||
|
plugins: [
|
||||||
|
postcssPresetEnv(),
|
||||||
|
cssnano()
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = config
|
|
@ -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 {
|
||||||
|
|
|
@ -29,7 +29,7 @@ h3 {
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout-tv {
|
.layout-tv {
|
||||||
font-size: 2.5vh;
|
font-size: 130%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout-mobile {
|
.layout-mobile {
|
||||||
|
|
|
@ -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;
|
||||||
|
@ -242,7 +246,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
@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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,10 +436,46 @@
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
background-attachment: fixed;
|
||||||
height: 50vh;
|
height: 50vh;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-mobile .itemBackdrop {
|
||||||
|
background-attachment: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-desktop .itemBackdrop::after,
|
||||||
|
.layout-tv .itemBackdrop::after {
|
||||||
|
content: "";
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.65);
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-desktop .noBackdrop .itemBackdrop,
|
||||||
|
.layout-tv .noBackdrop .itemBackdrop {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailPageContent {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding-left: 2%;
|
||||||
|
padding-right: 2%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-desktop .noBackdrop .detailPageContent,
|
||||||
|
.layout-tv .noBackdrop .detailPageContent {
|
||||||
|
margin-top: 2.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-desktop .noBackdrop .detailImageContainer img,
|
||||||
|
.layout-tv .noBackdrop .detailImageContainer img {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.personBackdrop {
|
.personBackdrop {
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
@ -503,14 +536,13 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detailPagePrimaryContainer {
|
.detailPagePrimaryContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
align-content: center;
|
align-content: center;
|
||||||
position: sticky;
|
|
||||||
top: 3.85em;
|
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -520,13 +552,21 @@
|
||||||
top: 0;
|
top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout-tv .detailPagePrimaryContainer {
|
.layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer,
|
||||||
|
.layout-desktop #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 0;
|
top: 0;
|
||||||
|
padding-left: 32.45vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detailSticky {
|
.layout-desktop .detailSticky,
|
||||||
background-color: #101010;
|
.layout-tv .detailSticky {
|
||||||
|
margin-top: -7.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-desktop .noBackdrop .detailSticky,
|
||||||
|
.layout-tv .noBackdrop .detailSticky {
|
||||||
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.infoWrapper {
|
.infoWrapper {
|
||||||
|
@ -548,23 +588,17 @@
|
||||||
margin: 1.25em 0;
|
margin: 1.25em 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detailPageContent {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
padding-left: 2%;
|
|
||||||
padding-right: 2%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.detailImageContainer {
|
.detailImageContainer {
|
||||||
position: sticky;
|
position: relative;
|
||||||
top: 25%;
|
margin-top: -25vh;
|
||||||
float: left;
|
float: left;
|
||||||
width: 22.786458333333332vw;
|
width: 25vw;
|
||||||
|
z-index: 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.layout-mobile .detailImageContainer,
|
.layout-desktop .noBackdrop .detailImageContainer,
|
||||||
.layout-tv .detailImageContainer {
|
.layout-tv .noBackdrop .detailImageContainer {
|
||||||
position: relative;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detailPagePrimaryContent {
|
.detailPagePrimaryContent {
|
||||||
|
@ -572,15 +606,19 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.detailLogo {
|
.detailLogo {
|
||||||
width: 25em;
|
width: 67.25vw;
|
||||||
height: 9.375em;
|
height: 14.5vh;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 14.5%;
|
top: 15vh;
|
||||||
right: 10.5%;
|
right: 0;
|
||||||
-webkit-background-size: contain;
|
-webkit-background-size: contain;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.noBackdrop .detailLogo {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
@media all and (max-width: 87.5em) {
|
@media all and (max-width: 87.5em) {
|
||||||
.detailLogo {
|
.detailLogo {
|
||||||
right: 5%;
|
right: 5%;
|
||||||
|
@ -607,8 +645,8 @@
|
||||||
|
|
||||||
.itemDetailImage {
|
.itemDetailImage {
|
||||||
width: 100% !important;
|
width: 100% !important;
|
||||||
box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37);
|
-webkit-box-shadow: 0 0.1em 0.5em 0 rgba(0, 0, 0, 0.75);
|
||||||
-webkit-box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37);
|
box-shadow: 0 0.1em 0.5em 0 rgba(0, 0, 0, 0.75);
|
||||||
}
|
}
|
||||||
|
|
||||||
div.itemDetailGalleryLink.defaultCardBackground {
|
div.itemDetailGalleryLink.defaultCardBackground {
|
||||||
|
@ -635,6 +673,16 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-desktop .detailPageWrapperContainer,
|
||||||
|
.layout-tv .detailPageWrapperContainer {
|
||||||
|
margin-top: 7.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer,
|
||||||
|
.layout-desktop #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer {
|
||||||
|
padding-left: 3.3%;
|
||||||
|
}
|
||||||
|
|
||||||
.btnPlaySimple {
|
.btnPlaySimple {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
@ -803,7 +851,7 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||||
width: auto;
|
width: auto;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
margin-top: -4.3em;
|
margin-top: -4.2em;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,6 +897,11 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-desktop .noBackdrop .detailPageWrapperContainer,
|
||||||
|
.layout-tv .noBackdrop .detailPageWrapperContainer {
|
||||||
|
margin-top: 3.8em;
|
||||||
|
}
|
||||||
|
|
||||||
.mediaInfoStream {
|
.mediaInfoStream {
|
||||||
margin: 0 3em 0 0;
|
margin: 0 3em 0 0;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
@ -1066,3 +1119,50 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards {
|
||||||
.itemsViewSettingsContainer > .button-flat {
|
.itemsViewSettingsContainer > .button-flat {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.layout-mobile #myPreferencesMenuPage {
|
||||||
|
padding-top: 3.75em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.itemDetailsGroup {
|
||||||
|
margin-bottom: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections {
|
||||||
|
max-width: 44em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailsGroupItem,
|
||||||
|
.trackSelections .selectContainer {
|
||||||
|
display: flex;
|
||||||
|
max-width: 44em;
|
||||||
|
margin: 0 0 0.5em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections .selectContainer {
|
||||||
|
margin: 0 0 0.3em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detailsGroupItem .label,
|
||||||
|
.trackSelections .selectContainer .selectLabel {
|
||||||
|
cursor: default;
|
||||||
|
flex-grow: 0;
|
||||||
|
flex-shrink: 0;
|
||||||
|
flex-basis: 6.25em;
|
||||||
|
margin: 0 0.6em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections .selectContainer .selectLabel {
|
||||||
|
margin: 0 0.2em 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections .selectContainer .detailTrackSelect {
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trackSelections .selectContainer .selectArrowContainer .selectArrow {
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 1.4em;
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,12 @@ _define("fetch", function() {
|
||||||
return fetch
|
return fetch
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// query-string
|
||||||
|
var query = require("query-string");
|
||||||
|
_define("queryString", function() {
|
||||||
|
return query;
|
||||||
|
});
|
||||||
|
|
||||||
// flvjs
|
// flvjs
|
||||||
var flvjs = require("flv.js/dist/flv").default;
|
var flvjs = require("flv.js/dist/flv").default;
|
||||||
_define("flvjs", function() {
|
_define("flvjs", function() {
|
||||||
|
@ -75,14 +81,7 @@ _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
|
||||||
|
@ -97,7 +96,30 @@ _define("material-icons", function() {
|
||||||
return material_icons;
|
return material_icons;
|
||||||
});
|
});
|
||||||
|
|
||||||
var jellyfin_noto = require("jellyfin-noto");
|
// noto font
|
||||||
|
var noto = require("jellyfin-noto");
|
||||||
_define("jellyfin-noto", function () {
|
_define("jellyfin-noto", function () {
|
||||||
return jellyfin_noto;
|
return noto;
|
||||||
|
});
|
||||||
|
|
||||||
|
// page.js
|
||||||
|
var page = require("page");
|
||||||
|
_define("page", function() {
|
||||||
|
return page;
|
||||||
|
});
|
||||||
|
|
||||||
|
var polyfill = require("@babel/polyfill/dist/polyfill");
|
||||||
|
_define("polyfill", function () {
|
||||||
|
return polyfill;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Date-FNS
|
||||||
|
var date_fns = require("date-fns");
|
||||||
|
_define("date-fns", function () {
|
||||||
|
return date_fns;
|
||||||
|
});
|
||||||
|
|
||||||
|
var date_fns_locale = require("date-fns/locale");
|
||||||
|
_define("date-fns/locale", function () {
|
||||||
|
return date_fns_locale;
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["events", "globalize", "dom", "datetime", "userSettings", "serverNotifications", "connectionManager", "emby-button", "listViewStyle"], function (events, globalize, dom, datetime, userSettings, serverNotifications, connectionManager) {
|
define(["events", "globalize", "dom", "date-fns", "dfnshelper", "userSettings", "serverNotifications", "connectionManager", "emby-button", "listViewStyle"], function (events, globalize, dom, datefns, dfnshelper, userSettings, serverNotifications, connectionManager) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function getEntryHtml(entry, apiClient) {
|
function getEntryHtml(entry, apiClient) {
|
||||||
|
@ -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 || "";
|
||||||
|
|
|
@ -370,7 +370,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableNativeHistory() {
|
function enableNativeHistory() {
|
||||||
return page.enableNativeHistory();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function authenticate(ctx, route, callback) {
|
function authenticate(ctx, route, callback) {
|
||||||
|
@ -511,9 +511,16 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
return baseRoute;
|
return baseRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var popstateOccurred = false;
|
||||||
|
window.addEventListener('popstate', function () {
|
||||||
|
popstateOccurred = true;
|
||||||
|
});
|
||||||
|
|
||||||
function getHandler(route) {
|
function getHandler(route) {
|
||||||
return function (ctx, next) {
|
return function (ctx, next) {
|
||||||
|
ctx.isBack = popstateOccurred;
|
||||||
handleRoute(ctx, next, route);
|
handleRoute(ctx, next, route);
|
||||||
|
popstateOccurred = false;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -562,7 +569,10 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
if (!document.querySelector('.dialogContainer') && startPages.indexOf(curr.type) !== -1) {
|
if (!document.querySelector('.dialogContainer') && startPages.indexOf(curr.type) !== -1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return page.canGoBack();
|
if (enableHistory()) {
|
||||||
|
return history.length > 1;
|
||||||
|
}
|
||||||
|
return (page.len || 0) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
function showDirect(path) {
|
function showDirect(path) {
|
||||||
|
@ -666,7 +676,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
|
|
||||||
function pushState(state, title, url) {
|
function pushState(state, title, url) {
|
||||||
state.navigate = false;
|
state.navigate = false;
|
||||||
page.pushState(state, title, url);
|
history.pushState(state, title, url);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setBaseRoute() {
|
function setBaseRoute() {
|
||||||
|
@ -716,7 +727,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinM
|
||||||
appRouter.getRoutes = getRoutes;
|
appRouter.getRoutes = getRoutes;
|
||||||
appRouter.pushState = pushState;
|
appRouter.pushState = pushState;
|
||||||
appRouter.enableNativeHistory = enableNativeHistory;
|
appRouter.enableNativeHistory = enableNativeHistory;
|
||||||
appRouter.handleAnchorClick = page.handleAnchorClick;
|
appRouter.handleAnchorClick = page.clickHandler;
|
||||||
appRouter.TransparencyLevel = {
|
appRouter.TransparencyLevel = {
|
||||||
None: 0,
|
None: 0,
|
||||||
Backdrop: 1,
|
Backdrop: 1,
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 1;
|
z-index: 10;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
transition: transform 180ms linear;
|
transition: transform 180ms linear;
|
||||||
contain: layout style;
|
contain: layout style;
|
||||||
|
|
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
|
@ -429,6 +429,12 @@ button::-moz-focus-inner {
|
||||||
font-size: 1.66956521739130434em !important;
|
font-size: 1.66956521739130434em !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cardOverlayButtonIcon.material-icons {
|
||||||
|
/* material-icons override display, so we need to
|
||||||
|
make a better matching selector to set it to flex */
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
.cardOverlayButton-centered {
|
.cardOverlayButton-centered {
|
||||||
bottom: initial;
|
bottom: initial;
|
||||||
right: initial;
|
right: initial;
|
||||||
|
|
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");
|
||||||
|
|
|
@ -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) {
|
||||||
|
@ -242,9 +251,15 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
|
||||||
|
|
||||||
var onAnimationFinish = function () {
|
var onAnimationFinish = function () {
|
||||||
focusManager.pushScope(dlg);
|
focusManager.pushScope(dlg);
|
||||||
|
|
||||||
if (dlg.getAttribute('data-autofocus') === 'true') {
|
if (dlg.getAttribute('data-autofocus') === 'true') {
|
||||||
focusManager.autoFocus(dlg);
|
focusManager.autoFocus(dlg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (document.activeElement && !dlg.contains(document.activeElement)) {
|
||||||
|
// Blur foreign element to prevent triggering of an action from the previous scope
|
||||||
|
document.activeElement.blur();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (enableAnimation()) {
|
if (enableAnimation()) {
|
||||||
|
|
|
@ -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/>";
|
||||||
|
@ -163,16 +163,15 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
|
|
||||||
<div class="selectContainer fldDateTimeLocale hide">
|
<div class="selectContainer fldDateTimeLocale hide">
|
||||||
<select is="emby-select" class="selectDateTimeLocale" label="${LabelDateTimeLocale}">
|
<select is="emby-select" class="selectDateTimeLocale" label="${LabelDateTimeLocale}">
|
||||||
<option value="">${AutoBasedOnLanguageSetting}</option>
|
<option value="">${Auto}</option>
|
||||||
<option value="ar">Arabic</option>
|
<option value="ar">Arabic</option>
|
||||||
<option value="be-BY">Belarusian (Belarus)</option>
|
<option value="be-BY">Belarusian (Belarus)</option>
|
||||||
<option value="bg-BG">Bulgarian (Bulgaria)</option>
|
<option value="bg-BG">Bulgarian (Bulgaria)</option>
|
||||||
|
|
|
@ -1,8 +1,18 @@
|
||||||
define([], function () {
|
/* eslint-disable indent */
|
||||||
'use strict';
|
|
||||||
|
|
||||||
function parentWithAttribute(elem, name, value) {
|
/**
|
||||||
|
* Useful DOM utilities.
|
||||||
|
* @module components/dom
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns parent of element with specified attribute value.
|
||||||
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
||||||
|
* @param {string} name - Attribute name.
|
||||||
|
* @param {mixed} value - Attribute value.
|
||||||
|
* @returns {HTMLElement} Parent with specified attribute value.
|
||||||
|
*/
|
||||||
|
export function parentWithAttribute(elem, name, value) {
|
||||||
while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) {
|
while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) {
|
||||||
elem = elem.parentNode;
|
elem = elem.parentNode;
|
||||||
|
|
||||||
|
@ -14,8 +24,13 @@ define([], function () {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentWithTag(elem, tagNames) {
|
/**
|
||||||
|
* Returns parent of element with one of specified tag names.
|
||||||
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
||||||
|
* @param {(string|Array)} tagNames - Tag name or array of tag names.
|
||||||
|
* @returns {HTMLElement} Parent with one of specified tag names.
|
||||||
|
*/
|
||||||
|
export function parentWithTag(elem, tagNames) {
|
||||||
// accept both string and array passed in
|
// accept both string and array passed in
|
||||||
if (!Array.isArray(tagNames)) {
|
if (!Array.isArray(tagNames)) {
|
||||||
tagNames = [tagNames];
|
tagNames = [tagNames];
|
||||||
|
@ -32,9 +47,14 @@ define([], function () {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns _true_ if class list contains one of specified names.
|
||||||
|
* @param {DOMTokenList} classList - Class list.
|
||||||
|
* @param {Array} classNames - Array of class names.
|
||||||
|
* @returns {boolean} _true_ if class list contains one of specified names.
|
||||||
|
*/
|
||||||
function containsAnyClass(classList, classNames) {
|
function containsAnyClass(classList, classNames) {
|
||||||
|
for (let i = 0, length = classNames.length; i < length; i++) {
|
||||||
for (var i = 0, length = classNames.length; i < length; i++) {
|
|
||||||
if (classList.contains(classNames[i])) {
|
if (classList.contains(classNames[i])) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -42,8 +62,13 @@ define([], function () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parentWithClass(elem, classNames) {
|
/**
|
||||||
|
* Returns parent of element with one of specified class names.
|
||||||
|
* @param {HTMLElement} elem - Element whose parent need to find.
|
||||||
|
* @param {(string|Array)} classNames - Class name or array of class names.
|
||||||
|
* @returns {HTMLElement} Parent with one of specified class names.
|
||||||
|
*/
|
||||||
|
export function parentWithClass(elem, classNames) {
|
||||||
// accept both string and array passed in
|
// accept both string and array passed in
|
||||||
if (!Array.isArray(classNames)) {
|
if (!Array.isArray(classNames)) {
|
||||||
classNames = [classNames];
|
classNames = [classNames];
|
||||||
|
@ -60,9 +85,9 @@ define([], function () {
|
||||||
return elem;
|
return elem;
|
||||||
}
|
}
|
||||||
|
|
||||||
var supportsCaptureOption = false;
|
let supportsCaptureOption = false;
|
||||||
try {
|
try {
|
||||||
var opts = Object.defineProperty({}, 'capture', {
|
const opts = Object.defineProperty({}, 'capture', {
|
||||||
// eslint-disable-next-line getter-return
|
// eslint-disable-next-line getter-return
|
||||||
get: function () {
|
get: function () {
|
||||||
supportsCaptureOption = true;
|
supportsCaptureOption = true;
|
||||||
|
@ -73,29 +98,58 @@ define([], function () {
|
||||||
console.debug('error checking capture support');
|
console.debug('error checking capture support');
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEventListenerWithOptions(target, type, handler, options) {
|
/**
|
||||||
var optionsOrCapture = options;
|
* Adds event listener to specified target.
|
||||||
|
* @param {EventTarget} target - Event target.
|
||||||
|
* @param {string} type - Event type.
|
||||||
|
* @param {function} handler - Event handler.
|
||||||
|
* @param {Object} [options] - Listener options.
|
||||||
|
*/
|
||||||
|
export function addEventListener(target, type, handler, options) {
|
||||||
|
let optionsOrCapture = options || {};
|
||||||
if (!supportsCaptureOption) {
|
if (!supportsCaptureOption) {
|
||||||
optionsOrCapture = options.capture;
|
optionsOrCapture = optionsOrCapture.capture;
|
||||||
}
|
}
|
||||||
target.addEventListener(type, handler, optionsOrCapture);
|
target.addEventListener(type, handler, optionsOrCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeEventListenerWithOptions(target, type, handler, options) {
|
/**
|
||||||
var optionsOrCapture = options;
|
* Removes event listener from specified target.
|
||||||
|
* @param {EventTarget} target - Event target.
|
||||||
|
* @param {string} type - Event type.
|
||||||
|
* @param {function} handler - Event handler.
|
||||||
|
* @param {Object} [options] - Listener options.
|
||||||
|
*/
|
||||||
|
export function removeEventListener(target, type, handler, options) {
|
||||||
|
let optionsOrCapture = options || {};
|
||||||
if (!supportsCaptureOption) {
|
if (!supportsCaptureOption) {
|
||||||
optionsOrCapture = options.capture;
|
optionsOrCapture = optionsOrCapture.capture;
|
||||||
}
|
}
|
||||||
target.removeEventListener(type, handler, optionsOrCapture);
|
target.removeEventListener(type, handler, optionsOrCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
var windowSize;
|
/**
|
||||||
var windowSizeEventsBound;
|
* Cached window size.
|
||||||
|
*/
|
||||||
|
let windowSize;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag of event listener bound.
|
||||||
|
*/
|
||||||
|
let windowSizeEventsBound;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets cached window size.
|
||||||
|
*/
|
||||||
function clearWindowSize() {
|
function clearWindowSize() {
|
||||||
windowSize = null;
|
windowSize = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWindowSize() {
|
/**
|
||||||
|
* Returns window size.
|
||||||
|
* @returns {Object} Window size.
|
||||||
|
*/
|
||||||
|
export function getWindowSize() {
|
||||||
if (!windowSize) {
|
if (!windowSize) {
|
||||||
windowSize = {
|
windowSize = {
|
||||||
innerHeight: window.innerHeight,
|
innerHeight: window.innerHeight,
|
||||||
|
@ -104,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
|
||||||
|
};
|
||||||
|
|
|
@ -13,11 +13,11 @@
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 1.5em;
|
padding: 1.5em 1.5em;
|
||||||
position: relative;
|
position: relative;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-width: initial;
|
min-width: initial;
|
||||||
line-height: initial;
|
line-height: 1.25;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
|
|
|
@ -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 || "";
|
||||||
|
|
|
@ -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>';
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1047,7 +1046,7 @@ 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 options = {
|
var options = {
|
||||||
video: videoElement,
|
video: videoElement,
|
||||||
|
@ -1056,91 +1055,28 @@ define(['browser', 'require', 'events', 'apphost', 'loading', 'dom', 'playbackMa
|
||||||
return i.DeliveryUrl;
|
return 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",
|
||||||
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
|
||||||
|
@ -1230,7 +1166,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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -251,114 +251,150 @@ require(['apphost'], function (appHost) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var inputLoopTimer;
|
||||||
function runInputLoop() {
|
function runInputLoop() {
|
||||||
// Get the latest gamepad state.
|
// Get the latest gamepad state.
|
||||||
var gamepads;
|
var gamepads = navigator.getGamepads();
|
||||||
if (navigator.getGamepads) {
|
for (var i = 0, len = gamepads.length; i < len; i++) {
|
||||||
gamepads = navigator.getGamepads();
|
|
||||||
} else if (navigator.webkitGetGamepads) {
|
|
||||||
gamepads = navigator.webkitGetGamepads();
|
|
||||||
}
|
|
||||||
gamepads = gamepads || [];
|
|
||||||
var i;
|
|
||||||
var j;
|
|
||||||
var len;
|
|
||||||
for (i = 0, len = gamepads.length; i < len; i++) {
|
|
||||||
var gamepad = gamepads[i];
|
var gamepad = gamepads[i];
|
||||||
if (gamepad) {
|
if (!gamepad) {
|
||||||
// Iterate through the axes
|
continue;
|
||||||
var axes = gamepad.axes;
|
}
|
||||||
var leftStickX = axes[0];
|
// Iterate through the axes
|
||||||
var leftStickY = axes[1];
|
var axes = gamepad.axes;
|
||||||
if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right
|
var leftStickX = axes[0];
|
||||||
_ButtonPressedState.setleftThumbstickRight(true);
|
var leftStickY = axes[1];
|
||||||
} else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left
|
if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right
|
||||||
_ButtonPressedState.setleftThumbstickLeft(true);
|
_ButtonPressedState.setleftThumbstickRight(true);
|
||||||
} else if (leftStickY < -_THUMB_STICK_THRESHOLD) { // Up
|
} else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left
|
||||||
_ButtonPressedState.setleftThumbstickUp(true);
|
_ButtonPressedState.setleftThumbstickLeft(true);
|
||||||
} else if (leftStickY > _THUMB_STICK_THRESHOLD) { // Down
|
} else if (leftStickY < -_THUMB_STICK_THRESHOLD) { // Up
|
||||||
_ButtonPressedState.setleftThumbstickDown(true);
|
_ButtonPressedState.setleftThumbstickUp(true);
|
||||||
} else {
|
} else if (leftStickY > _THUMB_STICK_THRESHOLD) { // Down
|
||||||
_ButtonPressedState.setleftThumbstickLeft(false);
|
_ButtonPressedState.setleftThumbstickDown(true);
|
||||||
_ButtonPressedState.setleftThumbstickRight(false);
|
} else {
|
||||||
_ButtonPressedState.setleftThumbstickUp(false);
|
_ButtonPressedState.setleftThumbstickLeft(false);
|
||||||
_ButtonPressedState.setleftThumbstickDown(false);
|
_ButtonPressedState.setleftThumbstickRight(false);
|
||||||
}
|
_ButtonPressedState.setleftThumbstickUp(false);
|
||||||
// Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed.
|
_ButtonPressedState.setleftThumbstickDown(false);
|
||||||
var buttons = gamepad.buttons;
|
}
|
||||||
for (j = 0, len = buttons.length; j < len; j++) {
|
// Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed.
|
||||||
if (ProcessedButtons.indexOf(j) !== -1) {
|
var buttons = gamepad.buttons;
|
||||||
|
for (var j = 0, len = buttons.length; j < len; j++) {
|
||||||
if (buttons[j].pressed) {
|
if (ProcessedButtons.indexOf(j) !== -1) {
|
||||||
switch (j) {
|
if (buttons[j].pressed) {
|
||||||
case _GAMEPAD_DPAD_UP_BUTTON_INDEX:
|
switch (j) {
|
||||||
_ButtonPressedState.setdPadUp(true);
|
case _GAMEPAD_DPAD_UP_BUTTON_INDEX:
|
||||||
break;
|
_ButtonPressedState.setdPadUp(true);
|
||||||
case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX:
|
break;
|
||||||
_ButtonPressedState.setdPadDown(true);
|
case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX:
|
||||||
break;
|
_ButtonPressedState.setdPadDown(true);
|
||||||
case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX:
|
break;
|
||||||
_ButtonPressedState.setdPadLeft(true);
|
case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX:
|
||||||
break;
|
_ButtonPressedState.setdPadLeft(true);
|
||||||
case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX:
|
break;
|
||||||
_ButtonPressedState.setdPadRight(true);
|
case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX:
|
||||||
break;
|
_ButtonPressedState.setdPadRight(true);
|
||||||
case _GAMEPAD_A_BUTTON_INDEX:
|
break;
|
||||||
_ButtonPressedState.setgamepadA(true);
|
case _GAMEPAD_A_BUTTON_INDEX:
|
||||||
break;
|
_ButtonPressedState.setgamepadA(true);
|
||||||
case _GAMEPAD_B_BUTTON_INDEX:
|
break;
|
||||||
_ButtonPressedState.setgamepadB(true);
|
case _GAMEPAD_B_BUTTON_INDEX:
|
||||||
break;
|
_ButtonPressedState.setgamepadB(true);
|
||||||
default:
|
break;
|
||||||
// No-op
|
default:
|
||||||
break;
|
// No-op
|
||||||
}
|
break;
|
||||||
} else {
|
}
|
||||||
switch (j) {
|
} else {
|
||||||
case _GAMEPAD_DPAD_UP_BUTTON_INDEX:
|
switch (j) {
|
||||||
if (_ButtonPressedState.getdPadUp()) {
|
case _GAMEPAD_DPAD_UP_BUTTON_INDEX:
|
||||||
_ButtonPressedState.setdPadUp(false);
|
if (_ButtonPressedState.getdPadUp()) {
|
||||||
}
|
_ButtonPressedState.setdPadUp(false);
|
||||||
break;
|
}
|
||||||
case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX:
|
break;
|
||||||
if (_ButtonPressedState.getdPadDown()) {
|
case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX:
|
||||||
_ButtonPressedState.setdPadDown(false);
|
if (_ButtonPressedState.getdPadDown()) {
|
||||||
}
|
_ButtonPressedState.setdPadDown(false);
|
||||||
break;
|
}
|
||||||
case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX:
|
break;
|
||||||
if (_ButtonPressedState.getdPadLeft()) {
|
case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX:
|
||||||
_ButtonPressedState.setdPadLeft(false);
|
if (_ButtonPressedState.getdPadLeft()) {
|
||||||
}
|
_ButtonPressedState.setdPadLeft(false);
|
||||||
break;
|
}
|
||||||
case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX:
|
break;
|
||||||
if (_ButtonPressedState.getdPadRight()) {
|
case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX:
|
||||||
_ButtonPressedState.setdPadRight(false);
|
if (_ButtonPressedState.getdPadRight()) {
|
||||||
}
|
_ButtonPressedState.setdPadRight(false);
|
||||||
break;
|
}
|
||||||
case _GAMEPAD_A_BUTTON_INDEX:
|
break;
|
||||||
if (_ButtonPressedState.getgamepadA()) {
|
case _GAMEPAD_A_BUTTON_INDEX:
|
||||||
_ButtonPressedState.setgamepadA(false);
|
if (_ButtonPressedState.getgamepadA()) {
|
||||||
}
|
_ButtonPressedState.setgamepadA(false);
|
||||||
break;
|
}
|
||||||
case _GAMEPAD_B_BUTTON_INDEX:
|
break;
|
||||||
if (_ButtonPressedState.getgamepadB()) {
|
case _GAMEPAD_B_BUTTON_INDEX:
|
||||||
_ButtonPressedState.setgamepadB(false);
|
if (_ButtonPressedState.getgamepadB()) {
|
||||||
}
|
_ButtonPressedState.setgamepadB(false);
|
||||||
break;
|
}
|
||||||
default:
|
break;
|
||||||
// No-op
|
default:
|
||||||
break;
|
// No-op
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Schedule the next one
|
// Schedule the next one
|
||||||
requestAnimationFrame(runInputLoop);
|
inputLoopTimer = requestAnimationFrame(runInputLoop);
|
||||||
}
|
}
|
||||||
|
|
||||||
runInputLoop();
|
function startInputLoop() {
|
||||||
|
if (!inputLoopTimer) {
|
||||||
|
runInputLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function stopInputLoop() {
|
||||||
|
cancelAnimationFrame(inputLoopTimer);
|
||||||
|
inputLoopTimer = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function isGamepadConnected() {
|
||||||
|
var gamepads = navigator.getGamepads();
|
||||||
|
for (var i = 0, len = gamepads.length; i < len; i++) {
|
||||||
|
var gamepad = gamepads[i];
|
||||||
|
if (gamepad && gamepad.connected) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFocusOrGamepadAttach(e) {
|
||||||
|
if (isGamepadConnected() && document.hasFocus()) {
|
||||||
|
console.log("Gamepad connected! Starting input loop");
|
||||||
|
startInputLoop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFocusOrGamepadDetach(e) {
|
||||||
|
if (!isGamepadConnected() || !document.hasFocus()) {
|
||||||
|
console.log("Gamepad disconnected! No other gamepads are connected, stopping input loop");
|
||||||
|
stopInputLoop();
|
||||||
|
} else {
|
||||||
|
console.log("Gamepad disconnected! There are gamepads still connected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event listeners for any change in gamepads' state.
|
||||||
|
window.addEventListener("gamepaddisconnected", onFocusOrGamepadDetach);
|
||||||
|
window.addEventListener("gamepadconnected", onFocusOrGamepadAttach);
|
||||||
|
window.addEventListener("blur", onFocusOrGamepadDetach);
|
||||||
|
window.addEventListener("focus", onFocusOrGamepadAttach);
|
||||||
|
|
||||||
|
onFocusOrGamepadAttach();
|
||||||
|
|
||||||
// The gamepadInputEmulation is a string property that exists in JavaScript UWAs and in WebViews in UWAs.
|
// The gamepadInputEmulation is a string property that exists in JavaScript UWAs and in WebViews in UWAs.
|
||||||
// It won't exist in Win8.1 style apps or browsers.
|
// It won't exist in Win8.1 style apps or browsers.
|
168
src/components/input/keyboardnavigation.js
Normal file
168
src/components/input/keyboardnavigation.js
Normal file
|
@ -0,0 +1,168 @@
|
||||||
|
/**
|
||||||
|
* Module for performing keyboard navigation.
|
||||||
|
* @module components/input/keyboardnavigation
|
||||||
|
*/
|
||||||
|
|
||||||
|
import inputManager from "inputManager";
|
||||||
|
import layoutManager from "layoutManager";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Key name mapping.
|
||||||
|
*/
|
||||||
|
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"
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Keys used for keyboard navigation.
|
||||||
|
*/
|
||||||
|
const NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"];
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns key name from event.
|
||||||
|
*
|
||||||
|
* @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;
|
||||||
|
|
||||||
|
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");
|
||||||
|
break;
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
window.addEventListener("gamepadconnected", attachGamepadScript);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
enable: enable,
|
||||||
|
getKeyName: getKeyName,
|
||||||
|
isNavigationKey: isNavigationKey
|
||||||
|
};
|
|
@ -346,11 +346,7 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter",
|
||||||
break;
|
break;
|
||||||
case "copy-stream":
|
case "copy-stream":
|
||||||
var downloadHref = apiClient.getItemDownloadUrl(itemId);
|
var downloadHref = apiClient.getItemDownloadUrl(itemId);
|
||||||
navigator.clipboard.writeText(downloadHref).then(function () {
|
var textAreaCopy = function () {
|
||||||
require(["toast"], function (toast) {
|
|
||||||
toast(globalize.translate("CopyStreamURLSuccess"));
|
|
||||||
});
|
|
||||||
}, function () {
|
|
||||||
var textArea = document.createElement("textarea");
|
var textArea = document.createElement("textarea");
|
||||||
textArea.value = downloadHref;
|
textArea.value = downloadHref;
|
||||||
document.body.appendChild(textArea);
|
document.body.appendChild(textArea);
|
||||||
|
@ -364,7 +360,16 @@ define(["apphost", "globalize", "connectionManager", "itemHelper", "appRouter",
|
||||||
prompt(globalize.translate("CopyStreamURL"), downloadHref);
|
prompt(globalize.translate("CopyStreamURL"), downloadHref);
|
||||||
}
|
}
|
||||||
document.body.removeChild(textArea);
|
document.body.removeChild(textArea);
|
||||||
});
|
};
|
||||||
|
if (navigator.clipboard === undefined) {
|
||||||
|
textAreaCopy();
|
||||||
|
} else {
|
||||||
|
navigator.clipboard.writeText(downloadHref).then(function () {
|
||||||
|
require(["toast"], function (toast) {
|
||||||
|
toast(globalize.translate("CopyStreamURLSuccess"));
|
||||||
|
});
|
||||||
|
}, textAreaCopy);
|
||||||
|
}
|
||||||
getResolveFunction(resolve, id)();
|
getResolveFunction(resolve, id)();
|
||||||
break;
|
break;
|
||||||
case "editsubtitles":
|
case "editsubtitles":
|
||||||
|
|
|
@ -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 + '"/>';
|
||||||
|
|
||||||
|
|
|
@ -1,154 +0,0 @@
|
||||||
define(["inputManager", "layoutManager"], function (inputManager, layoutManager) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Key name mapping.
|
|
||||||
*/
|
|
||||||
// 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.
|
|
||||||
*/
|
|
||||||
var NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"];
|
|
||||||
|
|
||||||
var hasFieldKey = false;
|
|
||||||
try {
|
|
||||||
hasFieldKey = "key" in new KeyboardEvent("keydown");
|
|
||||||
} catch (e) {
|
|
||||||
console.error("error checking 'key' field");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!hasFieldKey) {
|
|
||||||
// Add [a..z]
|
|
||||||
for (var i = 65; i <= 90; i++) {
|
|
||||||
KeyNames[i] = String.fromCharCode(i).toLowerCase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns key name from event.
|
|
||||||
*
|
|
||||||
* @param {KeyboardEvent} keyboard event
|
|
||||||
* @return {string} key name
|
|
||||||
*/
|
|
||||||
function getKeyName(event) {
|
|
||||||
return KeyNames[event.keyCode] || event.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns _true_ if key is used for navigation.
|
|
||||||
*
|
|
||||||
* @param {string} key name
|
|
||||||
* @return {boolean} _true_ if key is used for navigation
|
|
||||||
*/
|
|
||||||
function isNavigationKey(key) {
|
|
||||||
return NavigationKeys.indexOf(key) != -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
function enable() {
|
|
||||||
document.addEventListener("keydown", function (e) {
|
|
||||||
var key = getKeyName(e);
|
|
||||||
|
|
||||||
// Ignore navigation keys for non-TV
|
|
||||||
if (!layoutManager.tv && isNavigationKey(key)) {
|
|
||||||
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");
|
|
||||||
break;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (capture) {
|
|
||||||
console.debug("disabling default event handling");
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
enable: enable,
|
|
||||||
getKeyName: getKeyName,
|
|
||||||
isNavigationKey: isNavigationKey
|
|
||||||
};
|
|
||||||
});
|
|
|
@ -107,7 +107,7 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
if (!plugins.length) return html;
|
if (!plugins.length) return html;
|
||||||
|
|
||||||
html += '<div class="metadataFetcher" data-type="' + availableTypeOptions.Type + '">';
|
html += '<div class="metadataFetcher" data-type="' + availableTypeOptions.Type + '">';
|
||||||
html += '<h3 class="checkboxListLabel">' + globalize.translate("LabelTypeMetadataDownloaders", availableTypeOptions.Type) + "</h3>";
|
html += '<h3 class="checkboxListLabel">' + globalize.translate("LabelTypeMetadataDownloaders", globalize.translate(availableTypeOptions.Type)) + "</h3>";
|
||||||
html += '<div class="checkboxList paperList checkboxList-paperList">';
|
html += '<div class="checkboxList paperList checkboxList-paperList">';
|
||||||
for (var i = 0; i < plugins.length; i++) {
|
for (var i = 0; i < plugins.length; i++) {
|
||||||
var plugin = plugins[i];
|
var plugin = plugins[i];
|
||||||
|
@ -273,14 +273,19 @@ define(["globalize", "dom", "emby-checkbox", "emby-select", "emby-input"], funct
|
||||||
|
|
||||||
function adjustSortableListElement(elem) {
|
function adjustSortableListElement(elem) {
|
||||||
var btnSortable = elem.querySelector(".btnSortable");
|
var btnSortable = elem.querySelector(".btnSortable");
|
||||||
|
var inner = btnSortable.querySelector("i");
|
||||||
if (elem.previousSibling) {
|
if (elem.previousSibling) {
|
||||||
|
btnSortable.title = globalize.translate("ButtonUp");
|
||||||
btnSortable.classList.add("btnSortableMoveUp");
|
btnSortable.classList.add("btnSortableMoveUp");
|
||||||
btnSortable.classList.remove("btnSortableMoveDown");
|
btnSortable.classList.remove("btnSortableMoveDown");
|
||||||
btnSortable.querySelector("i").innerHTML = "keyboard_arrow_up";
|
inner.classList.remove("keyboard_arrow_down");
|
||||||
|
inner.classList.add("keyboard_arrow_up");
|
||||||
} else {
|
} else {
|
||||||
|
btnSortable.title = globalize.translate("ButtonDown");
|
||||||
btnSortable.classList.remove("btnSortableMoveUp");
|
btnSortable.classList.remove("btnSortableMoveUp");
|
||||||
btnSortable.classList.add("btnSortableMoveDown");
|
btnSortable.classList.add("btnSortableMoveDown");
|
||||||
btnSortable.querySelector("i").innerHTML = "keyboard_arrow_down";
|
inner.classList.remove("keyboard_arrow_up");
|
||||||
|
inner.classList.add("keyboard_arrow_down");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -391,6 +396,12 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -488,6 +499,7 @@ 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,
|
||||||
|
@ -540,6 +552,7 @@ 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;
|
||||||
|
|
|
@ -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"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -465,7 +465,12 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
|
||||||
var id = "txt1" + idInfo.Key;
|
var id = "txt1" + idInfo.Key;
|
||||||
var formatString = idInfo.UrlFormatString || '';
|
var formatString = idInfo.UrlFormatString || '';
|
||||||
|
|
||||||
var labelText = globalize.translate('LabelDynamicExternalId').replace('{0}', idInfo.Name);
|
var fullName = idInfo.Name;
|
||||||
|
if (idInfo.Type) {
|
||||||
|
fullName = idInfo.Name + " " + globalize.translate(idInfo.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
var labelText = globalize.translate('LabelDynamicExternalId', fullName);
|
||||||
|
|
||||||
html += '<div class="inputContainer">';
|
html += '<div class="inputContainer">';
|
||||||
html += '<div class="flex align-items-center">';
|
html += '<div class="flex align-items-center">';
|
||||||
|
|
|
@ -173,15 +173,15 @@ define(['serverNotifications', 'playbackManager', 'events', 'globalize', 'requir
|
||||||
};
|
};
|
||||||
|
|
||||||
if (status === 'completed') {
|
if (status === 'completed') {
|
||||||
notification.title = globalize.translate('PackageInstallCompleted').replace('{0}', installation.Name + ' ' + installation.Version);
|
notification.title = globalize.translate('PackageInstallCompleted', installation.Name, installation.Version);
|
||||||
notification.vibrate = true;
|
notification.vibrate = true;
|
||||||
} else if (status === 'cancelled') {
|
} else if (status === 'cancelled') {
|
||||||
notification.title = globalize.translate('PackageInstallCancelled').replace('{0}', installation.Name + ' ' + installation.Version);
|
notification.title = globalize.translate('PackageInstallCancelled', installation.Name, installation.Version);
|
||||||
} else if (status === 'failed') {
|
} else if (status === 'failed') {
|
||||||
notification.title = globalize.translate('PackageInstallFailed').replace('{0}', installation.Name + ' ' + installation.Version);
|
notification.title = globalize.translate('PackageInstallFailed', installation.Name, installation.Version);
|
||||||
notification.vibrate = true;
|
notification.vibrate = true;
|
||||||
} else if (status === 'progress') {
|
} else if (status === 'progress') {
|
||||||
notification.title = globalize.translate('InstallingPackage').replace('{0}', installation.Name + ' ' + installation.Version);
|
notification.title = globalize.translate('InstallingPackage', installation.Name, installation.Version);
|
||||||
|
|
||||||
notification.actions =
|
notification.actions =
|
||||||
[
|
[
|
||||||
|
|
|
@ -98,15 +98,22 @@ define(['events', 'playbackManager', 'dom', 'browser', 'css!./iconosd', 'materia
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setIcon(iconElement, icon) {
|
||||||
|
iconElement.classList.remove('brightness_high');
|
||||||
|
iconElement.classList.remove('brightness_medium');
|
||||||
|
iconElement.classList.remove('brightness_low');
|
||||||
|
iconElement.classList.add(icon);
|
||||||
|
}
|
||||||
|
|
||||||
function updateElementsFromPlayer(brightness) {
|
function updateElementsFromPlayer(brightness) {
|
||||||
|
|
||||||
if (iconElement) {
|
if (iconElement) {
|
||||||
if (brightness >= 80) {
|
if (brightness >= 80) {
|
||||||
iconElement.innerHTML = '';
|
setIcon(iconElement, 'brightness_high');
|
||||||
} else if (brightness >= 20) {
|
} else if (brightness >= 20) {
|
||||||
iconElement.innerHTML = '';
|
setIcon(iconElement, 'brightness_medium');
|
||||||
} else {
|
} else {
|
||||||
iconElement.innerHTML = '';
|
setIcon(iconElement, 'brightness_low');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (progressElement) {
|
if (progressElement) {
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
};
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -21,16 +21,25 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setButtonIcon(button, icon) {
|
||||||
|
var inner = button.querySelector('i');
|
||||||
|
inner.classList.remove('fiber_smart_record');
|
||||||
|
inner.classList.remove('fiber_manual_record');
|
||||||
|
inner.classList.add(icon);
|
||||||
|
}
|
||||||
|
|
||||||
function RecordingButton(options) {
|
function RecordingButton(options) {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
|
|
||||||
|
var button = options.button;
|
||||||
|
|
||||||
|
setButtonIcon(button, 'fiber_manual_record');
|
||||||
|
|
||||||
if (options.item) {
|
if (options.item) {
|
||||||
this.refreshItem(options.item);
|
this.refreshItem(options.item);
|
||||||
} else if (options.itemId && options.serverId) {
|
} else if (options.itemId && options.serverId) {
|
||||||
this.refresh(options.itemId, options.serverId);
|
this.refresh(options.itemId, options.serverId);
|
||||||
}
|
}
|
||||||
var button = options.button;
|
|
||||||
button.querySelector('i').innerHTML = 'fiber_manual_record';
|
|
||||||
|
|
||||||
var clickFn = onRecordingButtonClick.bind(this);
|
var clickFn = onRecordingButtonClick.bind(this);
|
||||||
this.clickFn = clickFn;
|
this.clickFn = clickFn;
|
||||||
|
@ -80,7 +89,7 @@ define(['globalize', 'connectionManager', 'require', 'loading', 'apphost', 'dom'
|
||||||
var options = this.options;
|
var options = this.options;
|
||||||
var button = options.button;
|
var button = options.button;
|
||||||
this.item = item;
|
this.item = item;
|
||||||
button.querySelector('i').innerHTML = getIndicatorIcon(item);
|
setButtonIcon(button, getIndicatorIcon(item));
|
||||||
|
|
||||||
if (item.TimerId && (item.Status || 'Cancelled') !== 'Cancelled') {
|
if (item.TimerId && (item.Status || 'Cancelled') !== 'Cancelled') {
|
||||||
button.classList.add('recordingIcon-active');
|
button.classList.add('recordingIcon-active');
|
||||||
|
|
|
@ -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
|
||||||
|
};
|
||||||
|
|
|
@ -191,36 +191,14 @@ define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focus
|
||||||
events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]);
|
events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function bindEvents(apiClient) {
|
function bindEvents(apiClient) {
|
||||||
events.off(apiClient, "message", onMessageReceived);
|
events.off(apiClient, "message", onMessageReceived);
|
||||||
events.on(apiClient, "message", onMessageReceived);
|
events.on(apiClient, "message", onMessageReceived);
|
||||||
}
|
}
|
||||||
|
|
||||||
function enableNativeGamepadKeyMapping() {
|
|
||||||
if (window.navigator && "string" == typeof window.navigator.gamepadInputEmulation) {
|
|
||||||
window.navigator.gamepadInputEmulation = "keyboard";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
function isGamepadSupported() {
|
|
||||||
return "ongamepadconnected" in window || navigator.getGamepads || navigator.webkitGetGamepads;
|
|
||||||
}
|
|
||||||
|
|
||||||
connectionManager.getApiClients().forEach(bindEvents);
|
connectionManager.getApiClients().forEach(bindEvents);
|
||||||
|
|
||||||
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
|
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
|
||||||
bindEvents(newApiClient);
|
bindEvents(newApiClient);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!enableNativeGamepadKeyMapping() && isGamepadSupported()) {
|
|
||||||
require(["components/serverNotifications/gamepadtokey"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
require(["components/serverNotifications/mouseManager"]);
|
|
||||||
|
|
||||||
return serverNotifications;
|
return serverNotifications;
|
||||||
});
|
});
|
|
@ -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 () {
|
||||||
|
|
|
@ -70,7 +70,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
|
|
||||||
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>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function setUserScalable(scalable) {
|
function setUserScalable(scalable) {
|
||||||
|
@ -201,14 +201,16 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
function onAutoplayStart() {
|
function onAutoplayStart() {
|
||||||
var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i');
|
var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i');
|
||||||
if (btnSlideshowPause) {
|
if (btnSlideshowPause) {
|
||||||
btnSlideshowPause.innerHTML = "pause";
|
btnSlideshowPause.classList.remove("play_arrow");
|
||||||
|
btnSlideshowPause.classList.add("pause");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onAutoplayStop() {
|
function onAutoplayStop() {
|
||||||
var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i');
|
var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i');
|
||||||
if (btnSlideshowPause) {
|
if (btnSlideshowPause) {
|
||||||
btnSlideshowPause.innerHTML = "";
|
btnSlideshowPause.classList.remove("pause");
|
||||||
|
btnSlideshowPause.classList.add("play_arrow");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,8 +367,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f
|
||||||
}
|
}
|
||||||
|
|
||||||
function playPause() {
|
function playPause() {
|
||||||
|
var paused = !dlg.querySelector('.btnSlideshowPause i').classList.contains("pause");
|
||||||
var paused = dlg.querySelector('.btnSlideshowPause i').innerHTML !== "pause";
|
|
||||||
if (paused) {
|
if (paused) {
|
||||||
play();
|
play();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -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,6 +18,14 @@ 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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
});
|
});
|
||||||
|
|
|
@ -90,10 +90,11 @@ define(["loading", "libraryMenu", "globalize", "emby-checkbox", "emby-select"],
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateHttps(form) {
|
function validateHttps(form) {
|
||||||
|
var remoteAccess = form.querySelector("#chkRemoteAccess").checked;
|
||||||
var certPath = form.querySelector("#txtCertificatePath").value || null;
|
var certPath = form.querySelector("#txtCertificatePath").value || null;
|
||||||
var httpsMode = form.querySelector("#selectHttpsMode").value;
|
var httpsMode = form.querySelector("#selectHttpsMode").value;
|
||||||
|
|
||||||
if ("enabled" !== httpsMode && "required" !== httpsMode || certPath) {
|
if (!remoteAccess || ("enabled" !== httpsMode && "required" !== httpsMode || certPath)) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
||||||
|
@ -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, dfnshelper.localeWithSuffix));
|
||||||
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);
|
||||||
|
|
|
@ -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>";
|
||||||
|
|
|
@ -15,6 +15,7 @@ define(["jQuery", "loading", "globalize", "dom", "libraryMenu"], function ($, lo
|
||||||
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("#chkEnableSubtitleExtraction").checked = config.EnableSubtitleExtraction || false;
|
page.querySelector("#chkEnableSubtitleExtraction").checked = config.EnableSubtitleExtraction || false;
|
||||||
|
page.querySelector("#chkEnableThrottling").checked = config.EnableThrottling || false;
|
||||||
page.querySelector("#selectVideoDecoder").dispatchEvent(new CustomEvent("change", {
|
page.querySelector("#selectVideoDecoder").dispatchEvent(new CustomEvent("change", {
|
||||||
bubbles: true
|
bubbles: true
|
||||||
}));
|
}));
|
||||||
|
@ -58,6 +59,7 @@ define(["jQuery", "loading", "globalize", "dom", "libraryMenu"], function ($, lo
|
||||||
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.EnableSubtitleExtraction = form.querySelector("#chkEnableSubtitleExtraction").checked;
|
config.EnableSubtitleExtraction = form.querySelector("#chkEnableSubtitleExtraction").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) {
|
||||||
return c.checked;
|
return c.checked;
|
||||||
}), function (c) {
|
}), function (c) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuilder", "datetime", "mediaInfo", "backdrop", "listView", "itemContextMenu", "itemHelper", "dom", "indicators", "apphost", "imageLoader", "libraryMenu", "globalize", "browser", "events", "scrollHelper", "playbackManager", "libraryBrowser", "scrollStyles", "emby-itemscontainer", "emby-checkbox", "emby-button", "emby-playstatebutton", "emby-ratingbutton", "emby-scroller", "emby-select"], function (loading, appRouter, layoutManager, connectionManager, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, dom, indicators, appHost, imageLoader, libraryMenu, globalize, browser, events, scrollHelper, playbackManager, libraryBrowser) {
|
define(["loading", "appRouter", "layoutManager", "connectionManager", "userSettings", "cardBuilder", "datetime", "mediaInfo", "backdrop", "listView", "itemContextMenu", "itemHelper", "dom", "indicators", "imageLoader", "libraryMenu", "globalize", "browser", "events", "playbackManager", "scrollStyles", "emby-itemscontainer", "emby-checkbox", "emby-button", "emby-playstatebutton", "emby-ratingbutton", "emby-scroller", "emby-select"], function (loading, appRouter, layoutManager, connectionManager, userSettings, cardBuilder, datetime, mediaInfo, backdrop, listView, itemContextMenu, itemHelper, dom, indicators, imageLoader, libraryMenu, globalize, browser, events, playbackManager) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function getPromise(apiClient, params) {
|
function getPromise(apiClient, params) {
|
||||||
|
@ -60,8 +60,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getProgramScheduleHtml(items, options) {
|
function getProgramScheduleHtml(items) {
|
||||||
options = options || {};
|
|
||||||
var html = "";
|
var html = "";
|
||||||
html += '<div is="emby-itemscontainer" class="itemsContainer vertical-list" data-contextmenu="false">';
|
html += '<div is="emby-itemscontainer" class="itemsContainer vertical-list" data-contextmenu="false">';
|
||||||
html += listView.getListViewHtml({
|
html += listView.getListViewHtml({
|
||||||
|
@ -445,7 +444,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderBackdrop(page, item, apiClient) {
|
function renderBackdrop(item) {
|
||||||
if (dom.getWindowSize().innerWidth >= 1000) {
|
if (dom.getWindowSize().innerWidth >= 1000) {
|
||||||
backdrop.setBackdrops([item]);
|
backdrop.setBackdrops([item]);
|
||||||
} else {
|
} else {
|
||||||
|
@ -455,66 +454,74 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
|
|
||||||
function renderDetailPageBackdrop(page, item, apiClient) {
|
function renderDetailPageBackdrop(page, item, apiClient) {
|
||||||
var imgUrl;
|
var imgUrl;
|
||||||
var screenWidth = screen.availWidth;
|
|
||||||
var hasbackdrop = false;
|
var hasbackdrop = false;
|
||||||
var itemBackdropElement = page.querySelector("#itemBackdrop");
|
var itemBackdropElement = page.querySelector("#itemBackdrop");
|
||||||
var usePrimaryImage = item.MediaType === "Video" && item.Type !== "Movie" && item.Type !== "Trailer" ||
|
var usePrimaryImage = item.MediaType === "Video" && item.Type !== "Movie" && item.Type !== "Trailer" ||
|
||||||
item.MediaType && item.MediaType !== "Video" ||
|
item.MediaType && item.MediaType !== "Video" ||
|
||||||
item.Type === "MusicAlbum" ||
|
item.Type === "MusicAlbum" ||
|
||||||
item.Type === "MusicArtist" ||
|
|
||||||
item.Type === "Person";
|
item.Type === "Person";
|
||||||
|
|
||||||
|
if (!layoutManager.mobile && !userSettings.enableBackdrops()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
});
|
});
|
||||||
itemBackdropElement.classList.remove("noBackdrop");
|
page.classList.remove("noBackdrop");
|
||||||
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
||||||
hasbackdrop = true;
|
hasbackdrop = true;
|
||||||
} 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
|
||||||
});
|
});
|
||||||
itemBackdropElement.classList.remove("noBackdrop");
|
page.classList.remove("noBackdrop");
|
||||||
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
||||||
hasbackdrop = true;
|
hasbackdrop = true;
|
||||||
} 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]
|
||||||
});
|
});
|
||||||
itemBackdropElement.classList.remove("noBackdrop");
|
page.classList.remove("noBackdrop");
|
||||||
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
||||||
hasbackdrop = true;
|
hasbackdrop = true;
|
||||||
} 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]
|
||||||
});
|
});
|
||||||
itemBackdropElement.classList.remove("noBackdrop");
|
page.classList.remove("noBackdrop");
|
||||||
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
||||||
hasbackdrop = true;
|
hasbackdrop = true;
|
||||||
} 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
|
||||||
});
|
});
|
||||||
itemBackdropElement.classList.remove("noBackdrop");
|
page.classList.remove("noBackdrop");
|
||||||
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
|
||||||
hasbackdrop = true;
|
hasbackdrop = true;
|
||||||
} else {
|
} else {
|
||||||
itemBackdropElement.classList.add("noBackdrop");
|
|
||||||
itemBackdropElement.style.backgroundImage = "";
|
itemBackdropElement.style.backgroundImage = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Person" === item.Type) {
|
if ("Person" === item.Type) {
|
||||||
|
// FIXME: This hides the backdrop on all persons to fix a margin issue. Ideally, a proper fix should be made.
|
||||||
|
page.classList.add('noBackdrop');
|
||||||
itemBackdropElement.classList.add("personBackdrop");
|
itemBackdropElement.classList.add("personBackdrop");
|
||||||
} else {
|
} else {
|
||||||
itemBackdropElement.classList.remove("personBackdrop");
|
itemBackdropElement.classList.remove("personBackdrop");
|
||||||
|
@ -526,6 +533,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
function reloadFromItem(instance, page, params, item, user) {
|
function reloadFromItem(instance, page, params, item, user) {
|
||||||
var context = params.context;
|
var context = params.context;
|
||||||
page.querySelector(".detailPagePrimaryContainer").classList.add("detailSticky");
|
page.querySelector(".detailPagePrimaryContainer").classList.add("detailSticky");
|
||||||
|
|
||||||
renderName(item, page.querySelector(".nameContainer"), false, context);
|
renderName(item, page.querySelector(".nameContainer"), false, context);
|
||||||
var apiClient = connectionManager.getApiClient(item.ServerId);
|
var apiClient = connectionManager.getApiClient(item.ServerId);
|
||||||
renderSeriesTimerEditor(page, item, apiClient, user);
|
renderSeriesTimerEditor(page, item, apiClient, user);
|
||||||
|
@ -536,7 +544,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
setInitialCollapsibleState(page, item, apiClient, context, user);
|
setInitialCollapsibleState(page, item, apiClient, context, user);
|
||||||
renderDetails(page, item, apiClient, context);
|
renderDetails(page, item, apiClient, context);
|
||||||
renderTrackSelections(page, instance, item);
|
renderTrackSelections(page, instance, item);
|
||||||
renderBackdrop(page, item, apiClient);
|
renderBackdrop(item);
|
||||||
renderDetailPageBackdrop(page, item, apiClient);
|
renderDetailPageBackdrop(page, item, apiClient);
|
||||||
var canPlay = reloadPlayButtons(page, item);
|
var canPlay = reloadPlayButtons(page, item);
|
||||||
|
|
||||||
|
@ -583,7 +591,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
@ -597,7 +605,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
@ -610,7 +618,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
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");
|
||||||
}
|
}
|
||||||
|
@ -663,7 +671,9 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
});
|
});
|
||||||
var detailLogo = page.querySelector(".detailLogo");
|
var detailLogo = page.querySelector(".detailLogo");
|
||||||
|
|
||||||
if (url) {
|
if (!layoutManager.mobile && !userSettings.enableBackdrops()) {
|
||||||
|
detailLogo.classList.add("hide");
|
||||||
|
} else if (url) {
|
||||||
detailLogo.classList.remove("hide");
|
detailLogo.classList.remove("hide");
|
||||||
detailLogo.classList.add("lazy");
|
detailLogo.classList.add("lazy");
|
||||||
detailLogo.setAttribute("data-src", url);
|
detailLogo.setAttribute("data-src", url);
|
||||||
|
@ -693,26 +703,9 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -726,7 +719,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
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(", ");
|
||||||
|
@ -756,44 +749,54 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -817,7 +820,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
if (editable) {
|
if (editable) {
|
||||||
html += "</a>";
|
html += "</a>";
|
||||||
} else if (!editable && url === undefined) {
|
} else if (!editable && url === undefined) {
|
||||||
html += "</div>"
|
html += "</div>";
|
||||||
}
|
}
|
||||||
|
|
||||||
var progressHtml = item.IsFolder || !item.UserData ? "" : indicators.getProgressBarHtml(item);
|
var progressHtml = item.IsFolder || !item.UserData ? "" : indicators.getProgressBarHtml(item);
|
||||||
|
@ -873,7 +876,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
elem.querySelector(".detailImageProgressContainer").innerHTML = indicators.getProgressBarHtml(item);
|
elem.querySelector(".detailImageProgressContainer").innerHTML = indicators.getProgressBarHtml(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshImage(page, item, user) {
|
function refreshImage(page, item) {
|
||||||
refreshDetailImageUserData(page.querySelector(".detailImageContainer"), item);
|
refreshDetailImageUserData(page.querySelector(".detailImageContainer"), item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -922,10 +925,10 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
|
|
||||||
if ("Playlist" == item.Type) {
|
if ("Playlist" == item.Type) {
|
||||||
page.querySelector("#childrenCollapsible").classList.remove("hide");
|
page.querySelector("#childrenCollapsible").classList.remove("hide");
|
||||||
renderPlaylistItems(page, item, user);
|
renderPlaylistItems(page, item);
|
||||||
} else if ("Studio" == item.Type || "Person" == item.Type || "Genre" == item.Type || "MusicGenre" == item.Type || "MusicArtist" == item.Type) {
|
} else if ("Studio" == item.Type || "Person" == item.Type || "Genre" == item.Type || "MusicGenre" == item.Type || "MusicArtist" == item.Type) {
|
||||||
page.querySelector("#childrenCollapsible").classList.remove("hide");
|
page.querySelector("#childrenCollapsible").classList.remove("hide");
|
||||||
renderItemsByName(page, item, user);
|
renderItemsByName(page, item);
|
||||||
} else if (item.IsFolder) {
|
} else if (item.IsFolder) {
|
||||||
if ("BoxSet" == item.Type) {
|
if ("BoxSet" == item.Type) {
|
||||||
page.querySelector("#childrenCollapsible").classList.add("hide");
|
page.querySelector("#childrenCollapsible").classList.add("hide");
|
||||||
|
@ -937,7 +940,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("Series" == item.Type) {
|
if ("Series" == item.Type) {
|
||||||
renderSeriesSchedule(page, item, user);
|
renderSeriesSchedule(page, item);
|
||||||
renderNextUp(page, item, user);
|
renderNextUp(page, item, user);
|
||||||
} else {
|
} else {
|
||||||
page.querySelector(".nextUpSection").classList.add("hide");
|
page.querySelector(".nextUpSection").classList.add("hide");
|
||||||
|
@ -952,7 +955,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
page.querySelector("#specialsCollapsible").classList.add("hide");
|
page.querySelector("#specialsCollapsible").classList.add("hide");
|
||||||
}
|
}
|
||||||
|
|
||||||
renderCast(page, item, context, enableScrollX() ? null : 12);
|
renderCast(page, item);
|
||||||
|
|
||||||
if (item.PartCount && item.PartCount > 1) {
|
if (item.PartCount && item.PartCount > 1) {
|
||||||
page.querySelector("#additionalPartsCollapsible").classList.remove("hide");
|
page.querySelector("#additionalPartsCollapsible").classList.remove("hide");
|
||||||
|
@ -988,7 +991,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderGenres(page, item, apiClient, context, isStatic) {
|
function renderGenres(page, item, context) {
|
||||||
context = context || inferContext(item);
|
context = context || inferContext(item);
|
||||||
var type;
|
var type;
|
||||||
var genres = item.GenreItems || [];
|
var genres = item.GenreItems || [];
|
||||||
|
@ -1012,17 +1015,21 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderDirector(page, item, apiClient, context, isStatic) {
|
function renderDirector(page, item, context) {
|
||||||
var directors = (item.People || []).filter(function (p) {
|
var directors = (item.People || []).filter(function (p) {
|
||||||
return "Director" === p.Type;
|
return "Director" === p.Type;
|
||||||
});
|
});
|
||||||
|
@ -1036,13 +1043,17 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
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");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1050,8 +1061,8 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
renderSimilarItems(page, item, context);
|
renderSimilarItems(page, item, context);
|
||||||
renderMoreFromSeason(page, item, apiClient);
|
renderMoreFromSeason(page, item, apiClient);
|
||||||
renderMoreFromArtist(page, item, apiClient);
|
renderMoreFromArtist(page, item, apiClient);
|
||||||
renderDirector(page, item, apiClient, context, isStatic);
|
renderDirector(page, item, context);
|
||||||
renderGenres(page, item, apiClient, context, isStatic);
|
renderGenres(page, item, context);
|
||||||
renderChannelGuide(page, apiClient, item);
|
renderChannelGuide(page, apiClient, item);
|
||||||
var taglineElement = page.querySelector(".tagline");
|
var taglineElement = page.querySelector(".tagline");
|
||||||
|
|
||||||
|
@ -1100,7 +1111,6 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
|
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
@ -1125,14 +1135,6 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
return scrollX ? "overflowSquare" : "square";
|
return scrollX ? "overflowSquare" : "square";
|
||||||
}
|
}
|
||||||
|
|
||||||
function getThumbShape(scrollX) {
|
|
||||||
if (null == scrollX) {
|
|
||||||
scrollX = enableScrollX();
|
|
||||||
}
|
|
||||||
|
|
||||||
return scrollX ? "overflowBackdrop" : "backdrop";
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderMoreFromSeason(view, item, apiClient) {
|
function renderMoreFromSeason(view, item, apiClient) {
|
||||||
var section = view.querySelector(".moreFromSeasonSection");
|
var section = view.querySelector(".moreFromSeasonSection");
|
||||||
|
|
||||||
|
@ -1508,13 +1510,13 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderItemsByName(page, item, user) {
|
function renderItemsByName(page, item) {
|
||||||
require("scripts/itembynamedetailpage".split(","), function () {
|
require("scripts/itembynamedetailpage".split(","), function () {
|
||||||
window.ItemsByName.renderItems(page, item);
|
window.ItemsByName.renderItems(page, item);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderPlaylistItems(page, item, user) {
|
function renderPlaylistItems(page, item) {
|
||||||
require("scripts/playlistedit".split(","), function () {
|
require("scripts/playlistedit".split(","), function () {
|
||||||
PlaylistViewer.render(page, item);
|
PlaylistViewer.render(page, item);
|
||||||
});
|
});
|
||||||
|
@ -1594,7 +1596,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderSeriesSchedule(page, item, user) {
|
function renderSeriesSchedule(page, item) {
|
||||||
var apiClient = connectionManager.getApiClient(item.ServerId);
|
var apiClient = connectionManager.getApiClient(item.ServerId);
|
||||||
apiClient.getLiveTvPrograms({
|
apiClient.getLiveTvPrograms({
|
||||||
UserId: apiClient.getCurrentUserId(),
|
UserId: apiClient.getCurrentUserId(),
|
||||||
|
@ -1808,7 +1810,6 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
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"
|
||||||
});
|
});
|
||||||
|
@ -1844,7 +1845,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderCast(page, item, context, limit, isStatic) {
|
function renderCast(page, item) {
|
||||||
var people = (item.People || []).filter(function (p) {
|
var people = (item.People || []).filter(function (p) {
|
||||||
return "Director" !== p.Type;
|
return "Director" !== p.Type;
|
||||||
});
|
});
|
||||||
|
@ -1861,7 +1862,6 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
itemsContainer: castContent,
|
itemsContainer: castContent,
|
||||||
coverImage: true,
|
coverImage: true,
|
||||||
serverId: item.ServerId,
|
serverId: item.ServerId,
|
||||||
width: 160,
|
|
||||||
shape: "overflowPortrait"
|
shape: "overflowPortrait"
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1934,7 +1934,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
playbackManager.play(playOptions);
|
playbackManager.play(playOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
function playTrailer(page) {
|
function playTrailer() {
|
||||||
playbackManager.playTrailers(currentItem);
|
playbackManager.playTrailers(currentItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1991,11 +1991,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
}
|
}
|
||||||
|
|
||||||
function onPlayTrailerClick() {
|
function onPlayTrailerClick() {
|
||||||
playTrailer(view);
|
playTrailer();
|
||||||
}
|
|
||||||
|
|
||||||
function onDownloadChange() {
|
|
||||||
reload(self, view, params);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function onDownloadClick() {
|
function onDownloadClick() {
|
||||||
|
@ -2050,9 +2046,7 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
if (userData) {
|
if (userData) {
|
||||||
currentItem.UserData = userData;
|
currentItem.UserData = userData;
|
||||||
reloadPlayButtons(view, currentItem);
|
reloadPlayButtons(view, currentItem);
|
||||||
apiClient.getCurrentUser().then(function (user) {
|
refreshImage(view, currentItem);
|
||||||
refreshImage(view, currentItem, user);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2083,11 +2077,9 @@ define(["loading", "appRouter", "layoutManager", "connectionManager", "cardBuild
|
||||||
});
|
});
|
||||||
view.addEventListener("click", function (e) {
|
view.addEventListener("click", function (e) {
|
||||||
if (dom.parentWithClass(e.target, "moreScenes")) {
|
if (dom.parentWithClass(e.target, "moreScenes")) {
|
||||||
apiClient.getCurrentUser().then(function (user) {
|
renderScenes(view, currentItem);
|
||||||
renderScenes(view, currentItem);
|
|
||||||
});
|
|
||||||
} else if (dom.parentWithClass(e.target, "morePeople")) {
|
} else if (dom.parentWithClass(e.target, "morePeople")) {
|
||||||
renderCast(view, currentItem, params.context);
|
renderCast(view, currentItem);
|
||||||
} else if (dom.parentWithClass(e.target, "moreSpecials")) {
|
} else if (dom.parentWithClass(e.target, "moreSpecials")) {
|
||||||
apiClient.getCurrentUser().then(function (user) {
|
apiClient.getCurrentUser().then(function (user) {
|
||||||
renderSpecials(view, currentItem, user);
|
renderSpecials(view, currentItem, user);
|
||||||
|
|
|
@ -157,6 +157,12 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager"
|
||||||
return query;
|
return query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setSortButtonIcon(btnSortIcon, icon) {
|
||||||
|
btnSortIcon.classList.remove("arrow_downward");
|
||||||
|
btnSortIcon.classList.remove("arrow_upward");
|
||||||
|
btnSortIcon.classList.add(icon);
|
||||||
|
}
|
||||||
|
|
||||||
function updateSortText(instance) {
|
function updateSortText(instance) {
|
||||||
var btnSortText = instance.btnSortText;
|
var btnSortText = instance.btnSortText;
|
||||||
|
|
||||||
|
@ -175,7 +181,7 @@ define(["globalize", "listView", "layoutManager", "userSettings", "focusManager"
|
||||||
var btnSortIcon = instance.btnSortIcon;
|
var btnSortIcon = instance.btnSortIcon;
|
||||||
|
|
||||||
if (btnSortIcon) {
|
if (btnSortIcon) {
|
||||||
btnSortIcon.innerHTML = "Descending" === values.sortOrder ? "" : "";
|
setSortButtonIcon(btnSortIcon, "Descending" === values.sortOrder ? "arrow_downward" : "arrow_upward");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,10 @@ define(["layoutManager", "loading", "cardBuilder", "apphost", "imageLoader", "sc
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function renderRecordings(elem, recordings, cardOptions, scrollX) {
|
function renderRecordings(elem, recordings, cardOptions, scrollX) {
|
||||||
|
if (!elem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (recordings.length) {
|
if (recordings.length) {
|
||||||
elem.classList.remove("hide");
|
elem.classList.remove("hide");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -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"
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 + '" />');
|
||||||
|
@ -376,7 +382,7 @@ define(["playbackManager", "dom", "inputManager", "datetime", "itemHelper", "med
|
||||||
|
|
||||||
function startOsdHideTimer() {
|
function startOsdHideTimer() {
|
||||||
stopOsdHideTimer();
|
stopOsdHideTimer();
|
||||||
osdHideTimeout = setTimeout(hideOsd, 5e3);
|
osdHideTimeout = setTimeout(hideOsd, 3e3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function stopOsdHideTimer() {
|
function stopOsdHideTimer() {
|
||||||
|
@ -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)) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["displaySettings", "userSettings", "autoFocuser"], function (DisplaySettings, currentUserSettings, autoFocuser) {
|
define(["displaySettings", "userSettingsBuilder", "userSettings", "autoFocuser"], function (DisplaySettings, userSettingsBuilder, currentUserSettings, autoFocuser) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -11,7 +11,7 @@ define(["displaySettings", "userSettings", "autoFocuser"], function (DisplaySett
|
||||||
var settingsInstance;
|
var settingsInstance;
|
||||||
var hasChanges;
|
var hasChanges;
|
||||||
var userId = params.userId || ApiClient.getCurrentUserId();
|
var userId = params.userId || ApiClient.getCurrentUserId();
|
||||||
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettings();
|
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettingsBuilder();
|
||||||
view.addEventListener("viewshow", function () {
|
view.addEventListener("viewshow", function () {
|
||||||
window.addEventListener("beforeunload", onBeforeUnload);
|
window.addEventListener("beforeunload", onBeforeUnload);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["homescreenSettings", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (HomescreenSettings, dom, globalize, loading, currentUserSettings, autoFocuser) {
|
define(["homescreenSettings", "userSettingsBuilder", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (HomescreenSettings, userSettingsBuilder, dom, globalize, loading, currentUserSettings, autoFocuser) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -11,7 +11,7 @@ define(["homescreenSettings", "dom", "globalize", "loading", "userSettings", "au
|
||||||
var homescreenSettingsInstance;
|
var homescreenSettingsInstance;
|
||||||
var hasChanges;
|
var hasChanges;
|
||||||
var userId = params.userId || ApiClient.getCurrentUserId();
|
var userId = params.userId || ApiClient.getCurrentUserId();
|
||||||
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettings();
|
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettingsBuilder();
|
||||||
view.addEventListener("viewshow", function () {
|
view.addEventListener("viewshow", function () {
|
||||||
window.addEventListener("beforeunload", onBeforeUnload);
|
window.addEventListener("beforeunload", onBeforeUnload);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["apphost", "connectionManager", "listViewStyle", "emby-button"], function(appHost, connectionManager) {
|
define(["apphost", "connectionManager", "layoutManager", "listViewStyle", "emby-button"], function(appHost, connectionManager, layoutManager) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function(view, params) {
|
return function(view, params) {
|
||||||
|
@ -10,6 +10,10 @@ define(["apphost", "connectionManager", "listViewStyle", "emby-button"], functio
|
||||||
Dashboard.selectServer();
|
Dashboard.selectServer();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
view.querySelector(".clientSettings").addEventListener("click", function () {
|
||||||
|
window.NativeShell.openClientSettings();
|
||||||
|
});
|
||||||
|
|
||||||
view.addEventListener("viewshow", function() {
|
view.addEventListener("viewshow", function() {
|
||||||
// this page can also be used by admins to change user preferences from the user edit page
|
// this page can also be used by admins to change user preferences from the user edit page
|
||||||
var userId = params.userId || Dashboard.getCurrentUserId();
|
var userId = params.userId || Dashboard.getCurrentUserId();
|
||||||
|
@ -21,6 +25,12 @@ define(["apphost", "connectionManager", "listViewStyle", "emby-button"], functio
|
||||||
page.querySelector(".lnkPlaybackPreferences").setAttribute("href", "mypreferencesplayback.html?userId=" + userId);
|
page.querySelector(".lnkPlaybackPreferences").setAttribute("href", "mypreferencesplayback.html?userId=" + userId);
|
||||||
page.querySelector(".lnkSubtitlePreferences").setAttribute("href", "mypreferencessubtitles.html?userId=" + userId);
|
page.querySelector(".lnkSubtitlePreferences").setAttribute("href", "mypreferencessubtitles.html?userId=" + userId);
|
||||||
|
|
||||||
|
if (window.NativeShell && window.NativeShell.AppHost.supports("clientsettings")) {
|
||||||
|
page.querySelector(".clientSettings").classList.remove("hide");
|
||||||
|
} else {
|
||||||
|
page.querySelector(".clientSettings").classList.add("hide");
|
||||||
|
}
|
||||||
|
|
||||||
if (appHost.supports("multiserver")) {
|
if (appHost.supports("multiserver")) {
|
||||||
page.querySelector(".selectServer").classList.remove("hide");
|
page.querySelector(".selectServer").classList.remove("hide");
|
||||||
} else {
|
} else {
|
||||||
|
@ -33,6 +43,12 @@ define(["apphost", "connectionManager", "listViewStyle", "emby-button"], functio
|
||||||
page.querySelector(".adminSection").classList.add("hide");
|
page.querySelector(".adminSection").classList.add("hide");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (layoutManager.mobile) {
|
||||||
|
page.querySelector(".headerUsername").classList.add("hide");
|
||||||
|
page.querySelector(".adminSection").classList.add("hide");
|
||||||
|
page.querySelector(".userSection").classList.add("hide");
|
||||||
|
}
|
||||||
|
|
||||||
ApiClient.getUser(userId).then(function(user) {
|
ApiClient.getUser(userId).then(function(user) {
|
||||||
page.querySelector(".headerUsername").innerHTML = user.Name;
|
page.querySelector(".headerUsername").innerHTML = user.Name;
|
||||||
if (!user.Policy.IsAdministrator) {
|
if (!user.Policy.IsAdministrator) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["playbackSettings", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (PlaybackSettings, dom, globalize, loading, currentUserSettings, autoFocuser) {
|
define(["playbackSettings", "userSettingsBuilder", "dom", "globalize", "loading", "userSettings", "autoFocuser", "listViewStyle"], function (PlaybackSettings, userSettingsBuilder, dom, globalize, loading, currentUserSettings, autoFocuser) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -11,7 +11,7 @@ define(["playbackSettings", "dom", "globalize", "loading", "userSettings", "auto
|
||||||
var settingsInstance;
|
var settingsInstance;
|
||||||
var hasChanges;
|
var hasChanges;
|
||||||
var userId = params.userId || ApiClient.getCurrentUserId();
|
var userId = params.userId || ApiClient.getCurrentUserId();
|
||||||
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettings();
|
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettingsBuilder();
|
||||||
view.addEventListener("viewshow", function () {
|
view.addEventListener("viewshow", function () {
|
||||||
window.addEventListener("beforeunload", onBeforeUnload);
|
window.addEventListener("beforeunload", onBeforeUnload);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSettings, currentUserSettings, autoFocuser) {
|
define(["subtitleSettings", "userSettingsBuilder", "userSettings", "autoFocuser"], function (SubtitleSettings, userSettingsBuilder, currentUserSettings, autoFocuser) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
return function (view, params) {
|
return function (view, params) {
|
||||||
|
@ -11,7 +11,7 @@ define(["subtitleSettings", "userSettings", "autoFocuser"], function (SubtitleSe
|
||||||
var subtitleSettingsInstance;
|
var subtitleSettingsInstance;
|
||||||
var hasChanges;
|
var hasChanges;
|
||||||
var userId = params.userId || ApiClient.getCurrentUserId();
|
var userId = params.userId || ApiClient.getCurrentUserId();
|
||||||
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettings();
|
var userSettings = userId === ApiClient.getCurrentUserId() ? currentUserSettings : new userSettingsBuilder();
|
||||||
view.addEventListener("viewshow", function () {
|
view.addEventListener("viewshow", function () {
|
||||||
window.addEventListener("beforeunload", onBeforeUnload);
|
window.addEventListener("beforeunload", onBeforeUnload);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(["loading", "dom", "globalize", "humanedate", "paper-icon-button-light", "cardStyle", "emby-button", "indicators", "flexStyles"], function (loading, dom, globalize) {
|
define(["loading", "dom", "globalize", "date-fns", "dfnshelper", "paper-icon-button-light", "cardStyle", "emby-button", "indicators", "flexStyles"], function (loading, dom, globalize, datefns, dfnshelper) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function deleteUser(page, id) {
|
function deleteUser(page, id) {
|
||||||
|
@ -125,10 +125,11 @@ define(["loading", "dom", "globalize", "humanedate", "paper-icon-button-light",
|
||||||
html += "</div>";
|
html += "</div>";
|
||||||
return html + "</div>";
|
return html + "</div>";
|
||||||
}
|
}
|
||||||
|
// FIXME: It seems that, sometimes, server sends date in the future, so date-fns displays messages like 'in less than a minute'. We should fix
|
||||||
|
// how dates are returned by the server when the session is active and show something like 'Active now', instead of past/future sentences
|
||||||
function getLastSeenText(lastActivityDate) {
|
function getLastSeenText(lastActivityDate) {
|
||||||
if (lastActivityDate) {
|
if (lastActivityDate) {
|
||||||
return "Last seen " + humaneDate(lastActivityDate);
|
return globalize.translate("LastSeen", datefns.formatDistanceToNow(Date.parse(lastActivityDate), dfnshelper.localeWithSuffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="verticalSection">
|
<div class="verticalSection">
|
||||||
<div class="sectionTitleContainer flex align-items-center">
|
<div class="sectionTitleContainer flex align-items-center">
|
||||||
<h2 class="sectionTitle">${TabSettings}</h2>
|
<h2 class="sectionTitle">${TabSettings}</h2>
|
||||||
<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/administration/connectivity.html#DLNA">${Help}</a>
|
<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/networking/dlna.html">${Help}</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
height: 1.83em;
|
height: 1.83em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 2px solid currentcolor;
|
border: 0.14em solid currentcolor;
|
||||||
border-radius: 0.14em;
|
border-radius: 0.14em;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -68,7 +68,8 @@
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkboxIcon-checked {
|
.checkboxIcon-checked,
|
||||||
|
.emby-checkbox-label .checkboxIcon-checked {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ define(['browser', 'dom', 'css!./emby-checkbox', 'registerElement'], function (b
|
||||||
|
|
||||||
function onKeyDown(e) {
|
function onKeyDown(e) {
|
||||||
// Don't submit form on enter
|
// Don't submit form on enter
|
||||||
if (e.keyCode === 13) {
|
// Real (non-emulator) Tizen does nothing on Space
|
||||||
|
if (e.keyCode === 13 || e.keyCode === 32) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
this.checked = !this.checked;
|
this.checked = !this.checked;
|
||||||
|
|
|
@ -80,7 +80,7 @@ define(['browser', 'css!./emby-collapse', 'registerElement', 'emby-button'], fun
|
||||||
|
|
||||||
var title = this.getAttribute('title');
|
var title = this.getAttribute('title');
|
||||||
|
|
||||||
var html = '<button is="emby-button" type="button" on-click="toggleExpand" id="expandButton" class="emby-collapsible-button iconRight"><h3 class="emby-collapsible-title" title="' + title + '">' + title + '</h3><i class="material-icons emby-collapse-expandIcon">expand_more</i></button>';
|
var html = '<button is="emby-button" type="button" on-click="toggleExpand" id="expandButton" class="emby-collapsible-button iconRight"><h3 class="emby-collapsible-title" title="' + title + '">' + title + '</h3><i class="material-icons emby-collapse-expandIcon expand_more"></i></button>';
|
||||||
|
|
||||||
this.insertAdjacentHTML('afterbegin', html);
|
this.insertAdjacentHTML('afterbegin', html);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding-left: 24px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio-label-block {
|
.radio-label-block {
|
||||||
|
@ -31,67 +30,82 @@
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__outer-circle {
|
.mdl-radio__circles {
|
||||||
position: absolute;
|
position: relative;
|
||||||
top: 4px;
|
margin-right: 0.54em;
|
||||||
left: 0;
|
width: 1.08em;
|
||||||
display: inline-block;
|
height: 1.08em;
|
||||||
box-sizing: border-box;
|
|
||||||
width: 16px;
|
|
||||||
height: 16px;
|
|
||||||
margin: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
border: 2px solid currentcolor;
|
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
z-index: 2;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__button:checked + .mdl-radio__label + .mdl-radio__outer-circle {
|
.mdl-radio__circles svg {
|
||||||
border: 2px solid #00a4dc;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__button:disabled + .mdl-radio__label + .mdl-radio__outer-circle {
|
.mdl-radio__button:disabled + .mdl-radio__circles {
|
||||||
border: 2px solid rgba(0, 0, 0, 0.26);
|
|
||||||
cursor: auto;
|
cursor: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mdl-radio__button:disabled + .mdl-radio__circles .mdl-radio__outer-circle {
|
||||||
|
color: rgba(0, 0, 0, 0.26);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio.show-focus .mdl-radio__button:focus + .mdl-radio__circles .mdl-radio__outer-circle {
|
||||||
|
color: #00a4dc;
|
||||||
|
}
|
||||||
|
|
||||||
.mdl-radio__inner-circle {
|
.mdl-radio__inner-circle {
|
||||||
position: absolute;
|
transition-duration: 0.2s;
|
||||||
z-index: 1;
|
|
||||||
margin: 0;
|
|
||||||
top: 8px;
|
|
||||||
left: 4px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
cursor: pointer;
|
|
||||||
transition-duration: 0.28s;
|
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
transition-property: -webkit-transform;
|
transition-property: -webkit-transform;
|
||||||
transition-property: transform;
|
transition-property: transform;
|
||||||
transition-property: transform, -webkit-transform;
|
transition-property: transform, -webkit-transform;
|
||||||
-webkit-transform: scale3d(0, 0, 0);
|
-webkit-transform: scale(0);
|
||||||
transform: scale3d(0, 0, 0);
|
transform: scale(0);
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio__button:checked + .mdl-radio__circles .mdl-radio__inner-circle {
|
||||||
|
-webkit-transform: scale(1);
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio__button:disabled + .mdl-radio__circles .mdl-radio__inner-circle {
|
||||||
|
color: rgba(0, 0, 0, 0.26);
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio.show-focus .mdl-radio__button:focus + .mdl-radio__circles .mdl-radio__inner-circle {
|
||||||
|
color: #00a4dc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdl-radio__focus-circle {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background: #00a4dc;
|
background: #00a4dc;
|
||||||
|
opacity: 0.26;
|
||||||
|
transition-duration: 0.2s;
|
||||||
|
transition-property: -webkit-transform;
|
||||||
|
transition-property: transform;
|
||||||
|
transition-property: transform, -webkit-transform;
|
||||||
|
-webkit-transform: scale(0);
|
||||||
|
transform: scale(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__button:checked + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle {
|
.mdl-radio.show-focus .mdl-radio__button:focus + .mdl-radio__circles .mdl-radio__focus-circle {
|
||||||
-webkit-transform: scale3d(1, 1, 1);
|
-webkit-transform: scale(1.75);
|
||||||
transform: scale3d(1, 1, 1);
|
transform: scale(1.75);
|
||||||
}
|
|
||||||
|
|
||||||
.mdl-radio__button:disabled + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle {
|
|
||||||
background: rgba(0, 0, 0, 0.26);
|
|
||||||
cursor: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdl-radio__button:focus + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle {
|
|
||||||
box-shadow: 0 0 0 10px rgba(255, 255, 255, 0.76);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mdl-radio__button:checked:focus + .mdl-radio__label + .mdl-radio__outer-circle + .mdl-radio__inner-circle {
|
|
||||||
box-shadow: 0 0 0 10px rgba(0, 164, 220, 0.26);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdl-radio__label {
|
.mdl-radio__label {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
define(['css!./emby-radio', 'registerElement'], function () {
|
define(['layoutManager', 'css!./emby-radio', 'registerElement'], function (layoutManager) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var EmbyRadioPrototype = Object.create(HTMLInputElement.prototype);
|
var EmbyRadioPrototype = Object.create(HTMLInputElement.prototype);
|
||||||
|
@ -6,16 +6,24 @@ define(['css!./emby-radio', 'registerElement'], function () {
|
||||||
function onKeyDown(e) {
|
function onKeyDown(e) {
|
||||||
|
|
||||||
// Don't submit form on enter
|
// Don't submit form on enter
|
||||||
if (e.keyCode === 13) {
|
// Real (non-emulator) Tizen does nothing on Space
|
||||||
|
if (e.keyCode === 13 || e.keyCode === 32) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
this.checked = true;
|
if (!this.checked) {
|
||||||
|
this.checked = true;
|
||||||
|
|
||||||
|
this.dispatchEvent(new CustomEvent('change', {
|
||||||
|
bubbles: true
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EmbyRadioPrototype.attachedCallback = function () {
|
EmbyRadioPrototype.attachedCallback = function () {
|
||||||
|
var showFocus = !layoutManager.mobile;
|
||||||
|
|
||||||
if (this.getAttribute('data-radio') === 'true') {
|
if (this.getAttribute('data-radio') === 'true') {
|
||||||
return;
|
return;
|
||||||
|
@ -30,13 +38,36 @@ define(['css!./emby-radio', 'registerElement'], function () {
|
||||||
labelElement.classList.add('mdl-radio');
|
labelElement.classList.add('mdl-radio');
|
||||||
labelElement.classList.add('mdl-js-radio');
|
labelElement.classList.add('mdl-js-radio');
|
||||||
labelElement.classList.add('mdl-js-ripple-effect');
|
labelElement.classList.add('mdl-js-ripple-effect');
|
||||||
|
if (showFocus) {
|
||||||
|
labelElement.classList.add('show-focus');
|
||||||
|
}
|
||||||
|
|
||||||
var labelTextElement = labelElement.querySelector('span');
|
var labelTextElement = labelElement.querySelector('span');
|
||||||
|
|
||||||
labelTextElement.classList.add('radioButtonLabel');
|
labelTextElement.classList.add('radioButtonLabel');
|
||||||
labelTextElement.classList.add('mdl-radio__label');
|
labelTextElement.classList.add('mdl-radio__label');
|
||||||
|
|
||||||
labelElement.insertAdjacentHTML('beforeend', '<span class="mdl-radio__outer-circle"></span><span class="mdl-radio__inner-circle"></span>');
|
var html = '';
|
||||||
|
|
||||||
|
html += '<div class="mdl-radio__circles">';
|
||||||
|
|
||||||
|
html += '<svg>';
|
||||||
|
html += '<defs>';
|
||||||
|
html += '<clipPath id="cutoff">';
|
||||||
|
html += '<circle cx="50%" cy="50%" r="50%" />';
|
||||||
|
html += '</clipPath>';
|
||||||
|
html += '</defs>';
|
||||||
|
html += '<circle class="mdl-radio__outer-circle" cx="50%" cy="50%" r="50%" fill="none" stroke="currentcolor" stroke-width="0.26em" clip-path="url(#cutoff)" />';
|
||||||
|
html += '<circle class="mdl-radio__inner-circle" cx="50%" cy="50%" r="25%" fill="currentcolor" />';
|
||||||
|
html += '</svg>';
|
||||||
|
|
||||||
|
if (showFocus) {
|
||||||
|
html += '<div class="mdl-radio__focus-circle"></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
this.insertAdjacentHTML('afterend', html);
|
||||||
|
|
||||||
this.addEventListener('keydown', onKeyDown);
|
this.addEventListener('keydown', onKeyDown);
|
||||||
};
|
};
|
||||||
|
|
|
@ -80,6 +80,10 @@
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.trackSelections > .selectContainer {
|
||||||
|
margin: 0.4em 0;
|
||||||
|
}
|
||||||
|
|
||||||
.emby-select-withcolor {
|
.emby-select-withcolor {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
appearance: none;
|
appearance: none;
|
||||||
|
@ -105,7 +109,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.selectArrow {
|
.selectArrow {
|
||||||
margin-top: 0.35em;
|
margin-top: 1.2em;
|
||||||
font-size: 1.7em;
|
font-size: 1.7em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -144,7 +144,7 @@ define(['layoutManager', 'browser', 'actionsheet', 'css!./emby-select', 'registe
|
||||||
this.parentNode.insertBefore(label, this);
|
this.parentNode.insertBefore(label, this);
|
||||||
|
|
||||||
if (this.classList.contains('emby-select-withcolor')) {
|
if (this.classList.contains('emby-select-withcolor')) {
|
||||||
this.parentNode.insertAdjacentHTML('beforeend', '<div class="selectArrowContainer"><div style="visibility:hidden;">0</div><i class="selectArrow material-icons keyboard_arrow_down"></i></div>');
|
this.parentNode.insertAdjacentHTML('beforeend', '<div class="selectArrowContainer"><div style="visibility:hidden;display:none;">0</div><i class="selectArrow material-icons keyboard_arrow_down"></i></div>');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -144,6 +144,14 @@
|
||||||
<div class="fieldDescription checkboxFieldDescription">${AllowOnTheFlySubtitleExtractionHelp}</div>
|
<div class="fieldDescription checkboxFieldDescription">${AllowOnTheFlySubtitleExtractionHelp}</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||||
|
<label>
|
||||||
|
<input is="emby-checkbox" type="checkbox" id="chkEnableThrottling" />
|
||||||
|
<span>${AllowFfmpegThrottling}</span>
|
||||||
|
</label>
|
||||||
|
<div class="fieldDescription checkboxFieldDescription">${AllowFfmpegThrottlingHelp}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button is="emby-button" type="submit" class="raised button-submit block">
|
<button is="emby-button" type="submit" class="raised button-submit block">
|
||||||
<span>${ButtonSave}</span>
|
<span>${ButtonSave}</span>
|
||||||
|
|
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