Compare commits
49 Commits
release-10
...
release-10
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c25db80cbd | ||
|
|
e33ffbe9aa | ||
|
|
f68bdaa276 | ||
|
|
e804227d6d | ||
|
|
9092d7fc06 | ||
|
|
148920f2f9 | ||
|
|
482fa20364 | ||
|
|
b102201607 | ||
|
|
7909c0874b | ||
|
|
d691f4c38f | ||
|
|
b4adc677fd | ||
|
|
fd66c5a3fb | ||
|
|
364bbc988d | ||
|
|
51722fd225 | ||
|
|
a158b1f85a | ||
|
|
0bb732c60a | ||
|
|
2782127ba8 | ||
|
|
6a80b4caeb | ||
|
|
b2e95b0ee6 | ||
|
|
b262b98e83 | ||
|
|
b52cb34319 | ||
|
|
914ef1e566 | ||
|
|
f832206145 | ||
|
|
46fcdf91b8 | ||
|
|
3c953d5ffd | ||
|
|
6c28570e82 | ||
|
|
acf632e77e | ||
|
|
44c273f531 | ||
|
|
ef09b24f0c | ||
|
|
87bcf40e81 | ||
|
|
3cae48be23 | ||
|
|
ecc65dfa3b | ||
|
|
9be3f2e731 | ||
|
|
44c7b75dbb | ||
|
|
253b0d96d6 | ||
|
|
7fb75c6d82 | ||
|
|
98816bcce0 | ||
|
|
067e43c0d3 | ||
|
|
a956d602f9 | ||
|
|
9cf6ccc73c | ||
|
|
b3b9f355c3 | ||
|
|
b2d2b1360c | ||
|
|
af7f626a43 | ||
|
|
5568d945a7 | ||
|
|
4dc5535a02 | ||
|
|
b6f5435750 | ||
|
|
8db63ab520 | ||
|
|
527c5fb43e | ||
|
|
9ef4e95467 |
@@ -1,63 +0,0 @@
|
||||
jobs:
|
||||
- job: Build
|
||||
displayName: 'Build'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
Development:
|
||||
BuildConfiguration: development
|
||||
Production:
|
||||
BuildConfiguration: production
|
||||
Standalone:
|
||||
BuildConfiguration: standalone
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node'
|
||||
inputs:
|
||||
versionSpec: '12.x'
|
||||
|
||||
- task: Cache@2
|
||||
displayName: 'Check Cache'
|
||||
inputs:
|
||||
key: 'yarn | yarn.lock'
|
||||
path: 'node_modules'
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
|
||||
- script: 'yarn install --frozen-lockfile'
|
||||
displayName: 'Install Dependencies'
|
||||
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||
|
||||
- script: 'yarn build:development'
|
||||
displayName: 'Build Development'
|
||||
condition: eq(variables['BuildConfiguration'], 'development')
|
||||
|
||||
- script: 'yarn build:production'
|
||||
displayName: 'Build Production'
|
||||
condition: eq(variables['BuildConfiguration'], 'production')
|
||||
|
||||
- script: 'yarn build:standalone'
|
||||
displayName: 'Build Standalone'
|
||||
condition: eq(variables['BuildConfiguration'], 'standalone')
|
||||
|
||||
- script: 'test -d dist'
|
||||
displayName: 'Check Build'
|
||||
|
||||
- script: 'mv dist jellyfin-web'
|
||||
displayName: 'Rename Directory'
|
||||
|
||||
- task: ArchiveFiles@2
|
||||
displayName: 'Archive Directory'
|
||||
inputs:
|
||||
rootFolderOrFile: 'jellyfin-web'
|
||||
includeRootFolder: true
|
||||
archiveFile: 'jellyfin-web-$(BuildConfiguration)'
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Publish Release'
|
||||
inputs:
|
||||
targetPath: '$(Build.SourcesDirectory)/jellyfin-web-$(BuildConfiguration).zip'
|
||||
artifactName: 'jellyfin-web-$(BuildConfiguration)'
|
||||
@@ -1,29 +0,0 @@
|
||||
jobs:
|
||||
- job: Lint
|
||||
displayName: 'Lint'
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node'
|
||||
inputs:
|
||||
versionSpec: '12.x'
|
||||
|
||||
- task: Cache@2
|
||||
displayName: 'Check Cache'
|
||||
inputs:
|
||||
key: 'yarn | yarn.lock'
|
||||
path: 'node_modules'
|
||||
cacheHitVar: CACHE_RESTORED
|
||||
|
||||
- script: 'yarn install --frozen-lockfile'
|
||||
displayName: 'Install Dependencies'
|
||||
condition: ne(variables.CACHE_RESTORED, 'true')
|
||||
|
||||
- script: 'yarn run lint --quiet'
|
||||
displayName: 'Run ESLint'
|
||||
|
||||
- script: 'yarn run stylelint'
|
||||
displayName: 'Run Stylelint'
|
||||
@@ -1,122 +0,0 @@
|
||||
jobs:
|
||||
- job: BuildPackage
|
||||
displayName: 'Build Packages'
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
CentOS:
|
||||
BuildConfiguration: centos
|
||||
Debian:
|
||||
BuildConfiguration: debian
|
||||
Fedora:
|
||||
BuildConfiguration: fedora
|
||||
Portable:
|
||||
BuildConfiguration: portable
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- script: 'docker build -f deployment/Dockerfile.$(BuildConfiguration) -t jellyfin-web-$(BuildConfiguration) deployment'
|
||||
displayName: 'Build Dockerfile'
|
||||
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
|
||||
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="yes" -e BUILD_ID=$(Build.BuildNumber) jellyfin-web-$(BuildConfiguration)'
|
||||
displayName: 'Run Dockerfile (unstable)'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
|
||||
- script: 'docker image ls -a && docker run -v $(pwd)/deployment/dist:/dist -v $(pwd):/jellyfin -e IS_UNSTABLE="no" -e BUILD_ID=$(Build.BuildNumber) jellyfin-web-$(BuildConfiguration)'
|
||||
displayName: 'Run Dockerfile (stable)'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Publish Release'
|
||||
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
inputs:
|
||||
targetPath: '$(Build.SourcesDirectory)/deployment/dist'
|
||||
artifactName: 'jellyfin-web-$(BuildConfiguration)'
|
||||
|
||||
- task: SSH@0
|
||||
displayName: 'Create target directory on repository server'
|
||||
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: 'mkdir -p /srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||
|
||||
- task: CopyFilesOverSSH@0
|
||||
displayName: 'Upload artifacts to repository server'
|
||||
condition: or(startsWith(variables['Build.SourceBranch'], 'refs/tags'), startsWith(variables['Build.SourceBranch'], 'refs/heads/master'))
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
sourceFolder: '$(Build.SourcesDirectory)/deployment/dist'
|
||||
contents: '**'
|
||||
targetFolder: '/srv/repository/incoming/azure/$(Build.BuildNumber)/$(BuildConfiguration)'
|
||||
|
||||
- job: BuildDocker
|
||||
displayName: 'Build Docker'
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
variables:
|
||||
- name: JellyfinVersion
|
||||
value: 0.0.0
|
||||
|
||||
steps:
|
||||
- script: echo "##vso[task.setvariable variable=JellyfinVersion]$( awk -F '/' '{ print $NF }' <<<'$(Build.SourceBranch)' | sed 's/^v//' )"
|
||||
displayName: Set release version (stable)
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
|
||||
- task: Docker@2
|
||||
displayName: 'Push Unstable Image'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
repository: 'jellyfin/jellyfin-web'
|
||||
command: buildAndPush
|
||||
buildContext: '.'
|
||||
Dockerfile: 'deployment/Dockerfile.docker'
|
||||
containerRegistry: Docker Hub
|
||||
tags: |
|
||||
unstable-$(Build.BuildNumber)
|
||||
unstable
|
||||
|
||||
- task: Docker@2
|
||||
displayName: 'Push Stable Image'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
inputs:
|
||||
repository: 'jellyfin/jellyfin-web'
|
||||
command: buildAndPush
|
||||
buildContext: '.'
|
||||
Dockerfile: 'deployment/Dockerfile.docker'
|
||||
containerRegistry: Docker Hub
|
||||
tags: |
|
||||
stable-$(Build.BuildNumber)
|
||||
$(JellyfinVersion)
|
||||
|
||||
- job: CollectArtifacts
|
||||
displayName: 'Collect Artifacts'
|
||||
dependsOn:
|
||||
- BuildPackage
|
||||
- BuildDocker
|
||||
condition: and(succeeded('BuildPackage'), succeeded('BuildDocker'))
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: SSH@0
|
||||
displayName: 'Update Unstable Repository'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/heads/master')
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: 'sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber) unstable'
|
||||
|
||||
- task: SSH@0
|
||||
displayName: 'Update Stable Repository'
|
||||
condition: startsWith(variables['Build.SourceBranch'], 'refs/tags')
|
||||
inputs:
|
||||
sshEndpoint: repository
|
||||
runOptions: 'inline'
|
||||
inline: 'sudo /srv/repository/collect-server.azure.sh /srv/repository/incoming/azure $(Build.BuildNumber)'
|
||||
@@ -2,16 +2,62 @@ trigger:
|
||||
batch: true
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
- master
|
||||
- release-*
|
||||
tags:
|
||||
include:
|
||||
- '*'
|
||||
pr:
|
||||
branches:
|
||||
include:
|
||||
- '*'
|
||||
|
||||
jobs:
|
||||
- template: azure-pipelines-build.yml
|
||||
- template: azure-pipelines-lint.yml
|
||||
- template: azure-pipelines-package.yml
|
||||
- job: main_build
|
||||
displayName: 'Main Build'
|
||||
|
||||
dependsOn: lint
|
||||
condition: succeeded()
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node'
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
|
||||
- script: 'yarn install'
|
||||
displayName: 'Install Dependencies'
|
||||
|
||||
- script: 'test -d dist'
|
||||
displayName: 'Check Build'
|
||||
|
||||
- script: 'yarn pack --filename jellyfin-web.tgz'
|
||||
displayName: 'Bundle Release'
|
||||
|
||||
- task: PublishPipelineArtifact@1
|
||||
displayName: 'Publish Release'
|
||||
condition: succeeded()
|
||||
inputs:
|
||||
targetPath: '$(Build.SourcesDirectory)/jellyfin-web.tgz'
|
||||
artifactName: 'jellyfin-web'
|
||||
|
||||
- job: lint
|
||||
displayName: 'Lint'
|
||||
|
||||
pool:
|
||||
vmImage: 'ubuntu-latest'
|
||||
|
||||
steps:
|
||||
- task: NodeTool@0
|
||||
displayName: 'Install Node'
|
||||
inputs:
|
||||
versionSpec: '10.x'
|
||||
|
||||
- script: 'yarn install'
|
||||
displayName: 'Install Dependencies'
|
||||
|
||||
- script: 'yarn run lint'
|
||||
displayName: 'Run ESLint'
|
||||
|
||||
- script: |
|
||||
yarn run stylelint
|
||||
displayName: 'Run stylelint'
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../fedora/Makefile
|
||||
@@ -1,5 +0,0 @@
|
||||
version: 1
|
||||
update_configs:
|
||||
- package_manager: "javascript"
|
||||
directory: "/"
|
||||
update_schedule: "weekly"
|
||||
@@ -7,6 +7,3 @@ charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
end_of_line = lf
|
||||
|
||||
[json]
|
||||
indent_size = 2
|
||||
|
||||
@@ -1,5 +1 @@
|
||||
node_modules
|
||||
dist
|
||||
.idea
|
||||
.vscode
|
||||
src/libraries
|
||||
libraries/
|
||||
|
||||
196
.eslintrc.js
196
.eslintrc.js
@@ -1,196 +0,0 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
plugins: [
|
||||
'promise',
|
||||
'import',
|
||||
'eslint-comments'
|
||||
],
|
||||
env: {
|
||||
node: true,
|
||||
es6: true,
|
||||
es2017: true,
|
||||
es2020: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2020,
|
||||
sourceType: 'module',
|
||||
ecmaFeatures: {
|
||||
impliedStrict: true
|
||||
}
|
||||
},
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
// 'plugin:promise/recommended',
|
||||
'plugin:import/errors',
|
||||
'plugin:import/warnings',
|
||||
'plugin:eslint-comments/recommended',
|
||||
'plugin:compat/recommended'
|
||||
],
|
||||
rules: {
|
||||
'block-spacing': ['error'],
|
||||
'brace-style': ['error'],
|
||||
'comma-dangle': ['error', 'never'],
|
||||
'comma-spacing': ['error'],
|
||||
'eol-last': ['error'],
|
||||
'indent': ['error', 4, { 'SwitchCase': 1 }],
|
||||
'keyword-spacing': ['error'],
|
||||
'max-statements-per-line': ['error'],
|
||||
'no-floating-decimal': ['error'],
|
||||
'no-multi-spaces': ['error'],
|
||||
'no-multiple-empty-lines': ['error', { 'max': 1 }],
|
||||
'no-trailing-spaces': ['error'],
|
||||
'one-var': ['error', 'never'],
|
||||
'quotes': ['error', 'single', { 'avoidEscape': true, 'allowTemplateLiterals': false }],
|
||||
'semi': ['error'],
|
||||
'space-before-blocks': ['error'],
|
||||
'space-infix-ops': 'error'
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: [
|
||||
'./src/**/*.js'
|
||||
],
|
||||
parser: 'babel-eslint',
|
||||
env: {
|
||||
node: false,
|
||||
amd: true,
|
||||
browser: true,
|
||||
es6: true,
|
||||
es2017: true,
|
||||
es2020: true
|
||||
},
|
||||
globals: {
|
||||
// Browser globals
|
||||
'MediaMetadata': 'readonly',
|
||||
// Tizen globals
|
||||
'tizen': 'readonly',
|
||||
'webapis': 'readonly',
|
||||
// WebOS globals
|
||||
'webOS': 'readonly',
|
||||
// Dependency globals
|
||||
'$': 'readonly',
|
||||
'jQuery': 'readonly',
|
||||
'requirejs': 'readonly',
|
||||
// Jellyfin globals
|
||||
'ApiClient': 'writable',
|
||||
'AppInfo': 'writable',
|
||||
'chrome': 'writable',
|
||||
'ConnectionManager': 'writable',
|
||||
'DlnaProfilePage': 'writable',
|
||||
'Dashboard': 'writable',
|
||||
'DashboardPage': 'writable',
|
||||
'Emby': 'readonly',
|
||||
'Events': 'writable',
|
||||
'getParameterByName': 'writable',
|
||||
'getWindowLocationSearch': 'writable',
|
||||
'Globalize': 'writable',
|
||||
'Hls': 'writable',
|
||||
'dfnshelper': 'writable',
|
||||
'LibraryMenu': 'writable',
|
||||
'LinkParser': 'writable',
|
||||
'LiveTvHelpers': 'writable',
|
||||
'MetadataEditor': 'writable',
|
||||
'pageClassOn': 'writable',
|
||||
'pageIdOn': 'writable',
|
||||
'PlaylistViewer': 'writable',
|
||||
'UserParentalControlPage': 'writable',
|
||||
'Windows': 'readonly'
|
||||
},
|
||||
rules: {
|
||||
// TODO: Fix warnings and remove these rules
|
||||
'no-redeclare': ['warn'],
|
||||
'no-unused-vars': ['warn'],
|
||||
'no-useless-escape': ['warn'],
|
||||
// TODO: Remove after ES6 migration is complete
|
||||
'import/no-unresolved': ['off']
|
||||
},
|
||||
settings: {
|
||||
polyfills: [
|
||||
// Native Promises Only
|
||||
'Promise',
|
||||
// whatwg-fetch
|
||||
'fetch',
|
||||
// document-register-element
|
||||
'document.registerElement',
|
||||
// resize-observer-polyfill
|
||||
'ResizeObserver',
|
||||
// fast-text-encoding
|
||||
'TextEncoder',
|
||||
// intersection-observer
|
||||
'IntersectionObserver',
|
||||
// Core-js
|
||||
'Object.assign',
|
||||
'Object.is',
|
||||
'Object.setPrototypeOf',
|
||||
'Object.toString',
|
||||
'Object.freeze',
|
||||
'Object.seal',
|
||||
'Object.preventExtensions',
|
||||
'Object.isFrozen',
|
||||
'Object.isSealed',
|
||||
'Object.isExtensible',
|
||||
'Object.getOwnPropertyDescriptor',
|
||||
'Object.getPrototypeOf',
|
||||
'Object.keys',
|
||||
'Object.entries',
|
||||
'Object.getOwnPropertyNames',
|
||||
'Function.name',
|
||||
'Function.hasInstance',
|
||||
'Array.from',
|
||||
'Array.arrayOf',
|
||||
'Array.copyWithin',
|
||||
'Array.fill',
|
||||
'Array.find',
|
||||
'Array.findIndex',
|
||||
'Array.iterator',
|
||||
'String.fromCodePoint',
|
||||
'String.raw',
|
||||
'String.iterator',
|
||||
'String.codePointAt',
|
||||
'String.endsWith',
|
||||
'String.includes',
|
||||
'String.repeat',
|
||||
'String.startsWith',
|
||||
'String.trim',
|
||||
'String.anchor',
|
||||
'String.big',
|
||||
'String.blink',
|
||||
'String.bold',
|
||||
'String.fixed',
|
||||
'String.fontcolor',
|
||||
'String.fontsize',
|
||||
'String.italics',
|
||||
'String.link',
|
||||
'String.small',
|
||||
'String.strike',
|
||||
'String.sub',
|
||||
'String.sup',
|
||||
'RegExp',
|
||||
'Number',
|
||||
'Math',
|
||||
'Date',
|
||||
'async',
|
||||
'Symbol',
|
||||
'Map',
|
||||
'Set',
|
||||
'WeakMap',
|
||||
'WeakSet',
|
||||
'ArrayBuffer',
|
||||
'DataView',
|
||||
'Int8Array',
|
||||
'Uint8Array',
|
||||
'Uint8ClampedArray',
|
||||
'Int16Array',
|
||||
'Uint16Array',
|
||||
'Int32Array',
|
||||
'Uint32Array',
|
||||
'Float32Array',
|
||||
'Float64Array',
|
||||
'Reflect',
|
||||
// Temporary while eslint-compat-plugin is buggy
|
||||
'document.querySelector'
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
71
.eslintrc.yml
Normal file
71
.eslintrc.yml
Normal file
@@ -0,0 +1,71 @@
|
||||
env:
|
||||
es6: false
|
||||
browser: true
|
||||
amd: true
|
||||
|
||||
globals:
|
||||
# New browser globals
|
||||
DataView: readonly
|
||||
MediaMetadata: readonly
|
||||
Promise: readonly
|
||||
# Deprecated browser globals
|
||||
DocumentTouch: readonly
|
||||
# Tizen globals
|
||||
tizen: readonly
|
||||
webapis: readonly
|
||||
# WebOS globals
|
||||
webOS: readonly
|
||||
# Dependency globals
|
||||
$: readonly
|
||||
jQuery: readonly
|
||||
queryString: readonly
|
||||
requirejs: readonly
|
||||
# Jellyfin globals
|
||||
ApiClient: writable
|
||||
AppInfo: writable
|
||||
chrome: writable
|
||||
ConnectionManager: writable
|
||||
DlnaProfilePage: writable
|
||||
Dashboard: writable
|
||||
DashboardPage: writable
|
||||
Emby: readonly
|
||||
Events: writable
|
||||
getParameterByName: writable
|
||||
getWindowLocationSearch: writable
|
||||
Globalize: writable
|
||||
Hls: writable
|
||||
humaneDate: writable
|
||||
humaneElapsed: writable
|
||||
LibraryMenu: writable
|
||||
LinkParser: writable
|
||||
LiveTvHelpers: writable
|
||||
MetadataEditor: writable
|
||||
pageClassOn: writable
|
||||
pageIdOn: writable
|
||||
PlaylistViewer: writable
|
||||
UserParentalControlPage: writable
|
||||
Windows: readonly
|
||||
|
||||
extends:
|
||||
- eslint:recommended
|
||||
|
||||
rules:
|
||||
block-spacing: ["error"]
|
||||
brace-style: ["error"]
|
||||
comma-dangle: ["error", "never"]
|
||||
comma-spacing: ["error"]
|
||||
eol-last: ["error"]
|
||||
indent: ["error", 4, { "SwitchCase": 1 }]
|
||||
keyword-spacing: ["error"]
|
||||
max-statements-per-line: ["error"]
|
||||
no-floating-decimal: ["error"]
|
||||
no-multi-spaces: ["error"]
|
||||
no-multiple-empty-lines: ["error", { "max": 1 }]
|
||||
no-trailing-spaces: ["error"]
|
||||
one-var: ["error", "never"]
|
||||
semi: ["warn"]
|
||||
space-before-blocks: ["error"]
|
||||
# TODO: Fix warnings and remove these rules
|
||||
no-redeclare: ["warn"]
|
||||
no-unused-vars: ["warn"]
|
||||
no-useless-escape: ["warn"]
|
||||
36
.gitattributes
vendored
36
.gitattributes
vendored
@@ -1,35 +1 @@
|
||||
* text=auto
|
||||
|
||||
CONTRIBUTORS.md merge=union
|
||||
README.md text
|
||||
LICENSE text
|
||||
|
||||
*.css text
|
||||
*.eot binary
|
||||
*.gif binary
|
||||
*.html text diff=html
|
||||
*.ico binary
|
||||
*.*ignore text
|
||||
*.jpg binary
|
||||
*.js text
|
||||
*.json text
|
||||
*.lock text -diff
|
||||
*.map text -diff
|
||||
*.md text
|
||||
*.otf binary
|
||||
*.png binary
|
||||
*.py text diff=python
|
||||
*.svg binary
|
||||
*.ts text
|
||||
*.ttf binary
|
||||
*.sass text
|
||||
*.vue text
|
||||
*.webp binary
|
||||
*.woff binary
|
||||
*.woff2 binary
|
||||
|
||||
.editorconfig text
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
|
||||
*.gitattributes linguist-language=gitattributes
|
||||
/CONTRIBUTORS.md merge=union
|
||||
|
||||
4
.github/CODEOWNERS
vendored
4
.github/CODEOWNERS
vendored
@@ -1,4 +0,0 @@
|
||||
.ci @dkanada @EraYaN
|
||||
.github @jellyfin/core
|
||||
build.sh @joshuaboniface
|
||||
deployment @joshuaboniface
|
||||
587
.gitignore
vendored
587
.gitignore
vendored
@@ -1,11 +1,578 @@
|
||||
# npm
|
||||
|
||||
# Created by https://www.gitignore.io/api/node,rider,macos,linux,windows,visualstudio,visualstudiocode
|
||||
# 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
|
||||
web
|
||||
node_modules
|
||||
|
||||
# ide
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
# log
|
||||
yarn-error.log
|
||||
|
||||
@@ -34,8 +34,6 @@
|
||||
- [Ryan Hartzell](https://github.com/ryan-hartzell)
|
||||
- [Thibault Nocchi](https://github.com/ThibaultNocchi)
|
||||
- [MrTimscampi](https://github.com/MrTimscampi)
|
||||
- [Sarab Singh](https://github.com/sarab97)
|
||||
- [Andrei Oanca](https://github.com/OancaAndrei)
|
||||
|
||||
# Emby Contributors
|
||||
|
||||
|
||||
19
README.md
19
README.md
@@ -44,38 +44,21 @@ Jellyfin Web is the frontend used for most of the clients available for end user
|
||||
|
||||
### Dependencies
|
||||
|
||||
- [Yarn 1.22.4](https://classic.yarnpkg.com/en/docs/install)
|
||||
- Gulp-cli
|
||||
- Yarn
|
||||
|
||||
### Getting Started
|
||||
|
||||
1. Clone or download this repository.
|
||||
|
||||
```sh
|
||||
git clone https://github.com/jellyfin/jellyfin-web.git
|
||||
cd jellyfin-web
|
||||
```
|
||||
|
||||
2. Install build dependencies in the project directory.
|
||||
|
||||
```sh
|
||||
yarn install
|
||||
```
|
||||
|
||||
3. Run the web client with webpack for local development.
|
||||
|
||||
```sh
|
||||
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
|
||||
```
|
||||
|
||||
110
build.sh
110
build.sh
@@ -1,110 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# build.sh - Build Jellyfin binary packages
|
||||
# Part of the Jellyfin Project
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
|
||||
usage() {
|
||||
echo -e "build.sh - Build Jellyfin binary packages"
|
||||
echo -e "Usage:"
|
||||
echo -e " $0 -t/--type <BUILD_TYPE> -p/--platform <PLATFORM> [-k/--keep-artifacts] [-l/--list-platforms]"
|
||||
echo -e "Notes:"
|
||||
echo -e " * BUILD_TYPE can be one of: [native, docker] and must be specified"
|
||||
echo -e " * native: Build using the build script in the host OS"
|
||||
echo -e " * docker: Build using the build script in a standardized Docker container"
|
||||
echo -e " * PLATFORM can be any platform shown by -l/--list-platforms and must be specified"
|
||||
echo -e " * If -k/--keep-artifacts is specified, transient artifacts (e.g. Docker containers) will be"
|
||||
echo -e " retained after the build is finished; the source directory will still be cleaned"
|
||||
echo -e " * If -l/--list-platforms is specified, all other arguments are ignored; the script will print"
|
||||
echo -e " the list of supported platforms and exit"
|
||||
}
|
||||
|
||||
list_platforms() {
|
||||
declare -a platforms
|
||||
platforms=(
|
||||
$( find deployment -maxdepth 1 -mindepth 1 -name "build.*" | awk -F'.' '{ $1=""; printf $2; if ($3 != ""){ printf "." $3; }; if ($4 != ""){ printf "." $4; }; print ""; }' | sort )
|
||||
)
|
||||
echo -e "Valid platforms:"
|
||||
echo
|
||||
for platform in ${platforms[@]}; do
|
||||
echo -e "* ${platform} : $( grep '^#=' deployment/build.${platform} | sed 's/^#= //' )"
|
||||
done
|
||||
}
|
||||
|
||||
do_build_native() {
|
||||
export IS_DOCKER=NO
|
||||
deployment/build.${PLATFORM}
|
||||
}
|
||||
|
||||
do_build_docker() {
|
||||
if ! dpkg --print-architecture | grep -q 'amd64'; then
|
||||
echo "Docker-based builds only support amd64-based cross-building; use a 'native' build instead."
|
||||
exit 1
|
||||
fi
|
||||
if [[ ! -f deployment/Dockerfile.${PLATFORM} ]]; then
|
||||
echo "Missing Dockerfile for platform ${PLATFORM}"
|
||||
exit 1
|
||||
fi
|
||||
if [[ ${KEEP_ARTIFACTS} == YES ]]; then
|
||||
docker_args=""
|
||||
else
|
||||
docker_args="--rm"
|
||||
fi
|
||||
|
||||
docker build . -t "jellyfin-builder.${PLATFORM}" -f deployment/Dockerfile.${PLATFORM}
|
||||
mkdir -p ${ARTIFACT_DIR}
|
||||
docker run $docker_args -v "${SOURCE_DIR}:/jellyfin" -v "${ARTIFACT_DIR}:/dist" "jellyfin-builder.${PLATFORM}"
|
||||
}
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
key="$1"
|
||||
case $key in
|
||||
-t|--type)
|
||||
BUILD_TYPE="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-p|--platform)
|
||||
PLATFORM="$2"
|
||||
shift
|
||||
shift
|
||||
;;
|
||||
-k|--keep-artifacts)
|
||||
KEEP_ARTIFACTS=YES
|
||||
shift
|
||||
;;
|
||||
-l|--list-platforms)
|
||||
list_platforms
|
||||
exit 0
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Unknown option $1"
|
||||
usage
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [[ -z ${BUILD_TYPE} || -z ${PLATFORM} ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export SOURCE_DIR="$( pwd )"
|
||||
export ARTIFACT_DIR="${SOURCE_DIR}/../bin/${PLATFORM}"
|
||||
|
||||
# Determine build type
|
||||
case ${BUILD_TYPE} in
|
||||
native)
|
||||
do_build_native
|
||||
;;
|
||||
docker)
|
||||
do_build_docker
|
||||
;;
|
||||
esac
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
# We just wrap `build` so this is really it
|
||||
name: "jellyfin-web"
|
||||
version: "10.6.4"
|
||||
packages:
|
||||
- debian.all
|
||||
- fedora.all
|
||||
- centos.all
|
||||
- portable
|
||||
91
bump_version
91
bump_version
@@ -1,91 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# bump_version - increase the shared version and generate changelogs
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
set -o xtrace
|
||||
|
||||
usage() {
|
||||
echo -e "bump_version - increase the shared version and generate changelogs"
|
||||
echo -e ""
|
||||
echo -e "Usage:"
|
||||
echo -e " $ bump_version <new_version>"
|
||||
}
|
||||
|
||||
if [[ -z $1 ]]; then
|
||||
usage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
shared_version_file="src/components/apphost.js"
|
||||
build_file="./build.yaml"
|
||||
|
||||
new_version="$1"
|
||||
|
||||
# Parse the version from shared version file
|
||||
old_version="$( grep "appVersion" ${shared_version_file} | head -1 | sed -E "s/var appVersion = '([0-9\.]+)';/\1/" | tr -d '[:space:]' )"
|
||||
echo "Old version in appHost is: $old_version"
|
||||
|
||||
# Set the shared version to the specified new_version
|
||||
old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
|
||||
new_version_sed="$( cut -f1 -d'-' <<<"${new_version}" )"
|
||||
sed -i "s/${old_version_sed}/${new_version_sed}/g" ${shared_version_file}
|
||||
|
||||
old_version="$( grep "version:" ${build_file} | sed -E 's/version: "([0-9\.]+[-a-z0-9]*)"/\1/' )"
|
||||
echo "Old version in ${build_file}: ${old_version}"
|
||||
|
||||
# Set the build.yaml version to the specified new_version
|
||||
old_version_sed="$( sed 's/\./\\./g' <<<"${old_version}" )" # Escape the '.' chars
|
||||
sed -i "s/${old_version_sed}/${new_version}/g" ${build_file}
|
||||
|
||||
if [[ ${new_version} == *"-"* ]]; then
|
||||
new_version_deb="$( sed 's/-/~/g' <<<"${new_version}" )"
|
||||
else
|
||||
new_version_deb="${new_version}-1"
|
||||
fi
|
||||
|
||||
# Write out a temporary Debian changelog with our new stuff appended and some templated formatting
|
||||
debian_changelog_file="debian/changelog"
|
||||
debian_changelog_temp="$( mktemp )"
|
||||
# Create new temp file with our changelog
|
||||
echo -e "jellyfin-web (${new_version_deb}) unstable; urgency=medium
|
||||
|
||||
* New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v${new_version}
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
|
||||
" >> ${debian_changelog_temp}
|
||||
cat ${debian_changelog_file} >> ${debian_changelog_temp}
|
||||
# Move into place
|
||||
mv ${debian_changelog_temp} ${debian_changelog_file}
|
||||
|
||||
# Write out a temporary Yum changelog with our new stuff prepended and some templated formatting
|
||||
fedora_spec_file="fedora/jellyfin-web.spec"
|
||||
fedora_changelog_temp="$( mktemp )"
|
||||
fedora_spec_temp_dir="$( mktemp -d )"
|
||||
fedora_spec_temp="${fedora_spec_temp_dir}/jellyfin-web.spec.tmp"
|
||||
# Make a copy of our spec file for hacking
|
||||
cp ${fedora_spec_file} ${fedora_spec_temp_dir}/
|
||||
pushd ${fedora_spec_temp_dir}
|
||||
# Split out the stuff before and after changelog
|
||||
csplit jellyfin-web.spec "/^%changelog/" # produces xx00 xx01
|
||||
# Update the version in xx00
|
||||
sed -i "s/${old_version_sed}/${new_version_sed}/g" xx00
|
||||
# Remove the header from xx01
|
||||
sed -i '/^%changelog/d' xx01
|
||||
# Create new temp file with our changelog
|
||||
echo -e "%changelog
|
||||
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version ${new_version}; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v${new_version}" >> ${fedora_changelog_temp}
|
||||
cat xx01 >> ${fedora_changelog_temp}
|
||||
# Reassembble
|
||||
cat xx00 ${fedora_changelog_temp} > ${fedora_spec_temp}
|
||||
popd
|
||||
# Move into place
|
||||
mv ${fedora_spec_temp} ${fedora_spec_file}
|
||||
# Clean up
|
||||
rm -rf ${fedora_changelog_temp} ${fedora_spec_temp_dir}
|
||||
|
||||
# Stage the changed files for commit
|
||||
git add ${shared_version_file} ${build_file} ${debian_changelog_file} ${fedora_spec_file}
|
||||
git status
|
||||
29
debian/changelog
vendored
29
debian/changelog
vendored
@@ -1,29 +0,0 @@
|
||||
jellyfin-web (10.6.4-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.6.4; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.4
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 30 Aug 2020 16:45:08 -0400
|
||||
|
||||
jellyfin-web (10.6.3-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.6.3; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.3
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 16 Aug 2020 19:48:03 -0400
|
||||
|
||||
jellyfin-web (10.6.2-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.6.2; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.2
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Sun, 02 Aug 2020 20:25:58 -0400
|
||||
|
||||
jellyfin-web (10.6.1-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.6.1; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.1
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Mon, 27 Jul 2020 18:29:54 -0400
|
||||
|
||||
jellyfin-web (10.6.0-1) unstable; urgency=medium
|
||||
|
||||
* New upstream version 10.6.0; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.0
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> Mon, 16 Mar 2020 11:15:00 -0400
|
||||
1
debian/compat
vendored
1
debian/compat
vendored
@@ -1 +0,0 @@
|
||||
8
|
||||
16
debian/control
vendored
16
debian/control
vendored
@@ -1,16 +0,0 @@
|
||||
Source: jellyfin-web
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Maintainer: Jellyfin Team <team@jellyfin.org>
|
||||
Build-Depends: debhelper (>= 9),
|
||||
npm | nodejs
|
||||
Standards-Version: 3.9.4
|
||||
Homepage: https://jellyfin.org/
|
||||
Vcs-Git: https://github.org/jellyfin/jellyfin-web.git
|
||||
Vcs-Browser: https://github.org/jellyfin/jellyfin-web
|
||||
|
||||
Package: jellyfin-web
|
||||
Recommends: jellyfin-server
|
||||
Architecture: all
|
||||
Description: Jellyfin is the Free Software Media System.
|
||||
This package provides the Jellyfin web client.
|
||||
28
debian/copyright
vendored
28
debian/copyright
vendored
@@ -1,28 +0,0 @@
|
||||
Format: http://dep.debian.net/deps/dep5
|
||||
Upstream-Name: jellyfin-web
|
||||
Source: https://github.com/jellyfin/jellyfin-web
|
||||
|
||||
Files: *
|
||||
Copyright: 2018-2020 Jellyfin Team
|
||||
License: GPL-3.0
|
||||
|
||||
Files: debian/*
|
||||
Copyright: 2020 Joshua Boniface <joshua@boniface.me>
|
||||
License: GPL-3.0
|
||||
|
||||
License: GPL-3.0
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
.
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
.
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2".
|
||||
6
debian/gbp.conf
vendored
6
debian/gbp.conf
vendored
@@ -1,6 +0,0 @@
|
||||
[DEFAULT]
|
||||
pristine-tar = False
|
||||
cleaner = fakeroot debian/rules clean
|
||||
|
||||
[import-orig]
|
||||
filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ]
|
||||
1
debian/install
vendored
1
debian/install
vendored
@@ -1 +0,0 @@
|
||||
web usr/share/jellyfin/
|
||||
1
debian/po/POTFILES.in
vendored
1
debian/po/POTFILES.in
vendored
@@ -1 +0,0 @@
|
||||
[type: gettext/rfc822deb] templates
|
||||
57
debian/po/templates.pot
vendored
57
debian/po/templates.pot
vendored
@@ -1,57 +0,0 @@
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: jellyfin-server\n"
|
||||
"Report-Msgid-Bugs-To: jellyfin-server@packages.debian.org\n"
|
||||
"POT-Creation-Date: 2015-06-12 20:51-0600\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=CHARSET\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../templates:1001
|
||||
msgid "Jellyfin permission info:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../templates:1001
|
||||
msgid ""
|
||||
"Jellyfin by default runs under a user named \"jellyfin\". Please ensure that the "
|
||||
"user jellyfin has read and write access to any folders you wish to add to your "
|
||||
"library. Otherwise please run jellyfin under a different user."
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../templates:2001
|
||||
msgid "Username to run Jellyfin as:"
|
||||
msgstr ""
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
#: ../templates:2001
|
||||
msgid "The user that jellyfin will run as."
|
||||
msgstr ""
|
||||
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../templates:3001
|
||||
msgid "Jellyfin still running"
|
||||
msgstr ""
|
||||
|
||||
#. Type: note
|
||||
#. Description
|
||||
#: ../templates:3001
|
||||
msgid "Jellyfin is currently running. Please close it and try again."
|
||||
msgstr ""
|
||||
20
debian/rules
vendored
20
debian/rules
vendored
@@ -1,20 +0,0 @@
|
||||
#! /usr/bin/make -f
|
||||
export DH_VERBOSE=1
|
||||
|
||||
%:
|
||||
dh $@
|
||||
|
||||
# disable "make check"
|
||||
override_dh_auto_test:
|
||||
|
||||
# disable stripping debugging symbols
|
||||
override_dh_clistrip:
|
||||
|
||||
override_dh_auto_build:
|
||||
npx yarn install
|
||||
mv $(CURDIR)/dist $(CURDIR)/web
|
||||
|
||||
override_dh_auto_clean:
|
||||
test -d $(CURDIR)/dist && rm -rf '$(CURDIR)/dist' || true
|
||||
test -d $(CURDIR)/web && rm -rf '$(CURDIR)/web' || true
|
||||
test -d $(CURDIR)/node_modules && rm -rf '$(CURDIR)/node_modules' || true
|
||||
1
debian/source/format
vendored
1
debian/source/format
vendored
@@ -1 +0,0 @@
|
||||
1.0
|
||||
7
debian/source/options
vendored
7
debian/source/options
vendored
@@ -1,7 +0,0 @@
|
||||
tar-ignore='.git*'
|
||||
tar-ignore='**/.git'
|
||||
tar-ignore='**/.hg'
|
||||
tar-ignore='**/.vs'
|
||||
tar-ignore='**/.vscode'
|
||||
tar-ignore='deployment'
|
||||
tar-ignore='*.deb'
|
||||
@@ -1,29 +0,0 @@
|
||||
FROM centos:7
|
||||
|
||||
# Docker build arguments
|
||||
ARG SOURCE_DIR=/jellyfin
|
||||
ARG ARTIFACT_DIR=/dist
|
||||
|
||||
# Docker run environment
|
||||
ENV SOURCE_DIR=/jellyfin
|
||||
ENV ARTIFACT_DIR=/dist
|
||||
ENV IS_DOCKER=YES
|
||||
|
||||
# Prepare CentOS environment
|
||||
RUN yum update -y \
|
||||
&& yum install -y epel-release \
|
||||
&& yum install -y @buildsys-build rpmdevtools git yum-plugins-core nodejs-yarn autoconf automake glibc-devel
|
||||
|
||||
# Install recent NodeJS and Yarn
|
||||
RUN curl -fSsLo /etc/yum.repos.d/yarn.repo https://dl.yarnpkg.com/rpm/yarn.repo \
|
||||
&& rpm -i https://rpm.nodesource.com/pub_10.x/el/7/x86_64/nodesource-release-el7-1.noarch.rpm \
|
||||
&& yum install -y yarn
|
||||
|
||||
# Link to build script
|
||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.centos /build.sh
|
||||
|
||||
VOLUME ${SOURCE_DIR}
|
||||
|
||||
VOLUME ${ARTIFACT_DIR}
|
||||
|
||||
ENTRYPOINT ["/build.sh"]
|
||||
@@ -1,27 +0,0 @@
|
||||
FROM debian:10
|
||||
|
||||
# Docker build arguments
|
||||
ARG SOURCE_DIR=/jellyfin
|
||||
ARG ARTIFACT_DIR=/dist
|
||||
|
||||
# Docker run environment
|
||||
ENV SOURCE_DIR=/jellyfin
|
||||
ENV ARTIFACT_DIR=/dist
|
||||
ENV DEB_BUILD_OPTIONS=noddebs
|
||||
ENV IS_DOCKER=YES
|
||||
|
||||
# Prepare Debian build environment
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y debhelper mmv npm git
|
||||
|
||||
# Prepare Yarn
|
||||
RUN npm install -g yarn
|
||||
|
||||
# Link to build script
|
||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.debian /build.sh
|
||||
|
||||
VOLUME ${SOURCE_DIR}
|
||||
|
||||
VOLUME ${ARTIFACT_DIR}
|
||||
|
||||
ENTRYPOINT ["/build.sh"]
|
||||
@@ -1,11 +0,0 @@
|
||||
FROM node:alpine
|
||||
|
||||
ARG SOURCE_DIR=/src
|
||||
ARG ARTIFACT_DIR=/jellyfin-web
|
||||
|
||||
RUN apk add autoconf g++ make libpng-dev gifsicle alpine-sdk automake libtool make gcc musl-dev nasm python
|
||||
|
||||
WORKDIR ${SOURCE_DIR}
|
||||
COPY . .
|
||||
|
||||
RUN yarn install && mv dist ${ARTIFACT_DIR}
|
||||
@@ -1,23 +0,0 @@
|
||||
FROM fedora:31
|
||||
|
||||
# Docker build arguments
|
||||
ARG SOURCE_DIR=/jellyfin
|
||||
ARG ARTIFACT_DIR=/dist
|
||||
|
||||
# Docker run environment
|
||||
ENV SOURCE_DIR=/jellyfin
|
||||
ENV ARTIFACT_DIR=/dist
|
||||
ENV IS_DOCKER=YES
|
||||
|
||||
# Prepare Fedora environment
|
||||
RUN dnf update -y \
|
||||
&& dnf install -y @buildsys-build rpmdevtools git dnf-plugins-core nodejs-yarn autoconf automake glibc-devel
|
||||
|
||||
# Link to build script
|
||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.fedora /build.sh
|
||||
|
||||
VOLUME ${SOURCE_DIR}
|
||||
|
||||
VOLUME ${ARTIFACT_DIR}
|
||||
|
||||
ENTRYPOINT ["/build.sh"]
|
||||
@@ -1,26 +0,0 @@
|
||||
FROM debian:10
|
||||
|
||||
# Docker build arguments
|
||||
ARG SOURCE_DIR=/jellyfin
|
||||
ARG ARTIFACT_DIR=/dist
|
||||
|
||||
# Docker run environment
|
||||
ENV SOURCE_DIR=/jellyfin
|
||||
ENV ARTIFACT_DIR=/dist
|
||||
ENV IS_DOCKER=YES
|
||||
|
||||
# Prepare Debian build environment
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y mmv npm git
|
||||
|
||||
# Prepare Yarn
|
||||
RUN npm install -g yarn
|
||||
|
||||
# Link to build script
|
||||
RUN ln -sf ${SOURCE_DIR}/deployment/build.portable /build.sh
|
||||
|
||||
VOLUME ${SOURCE_DIR}
|
||||
|
||||
VOLUME ${ARTIFACT_DIR}
|
||||
|
||||
ENTRYPOINT ["/build.sh"]
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
|
||||
# move to source directory
|
||||
pushd ${SOURCE_DIR}
|
||||
|
||||
cp -a yarn.lock /tmp/yarn.lock
|
||||
|
||||
# modify changelog to unstable configuration if IS_UNSTABLE
|
||||
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||
pushd fedora
|
||||
|
||||
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||
|
||||
sed -i "s/Version:.*/Version: ${BUILD_ID}/" jellyfin-web.spec
|
||||
sed -i "/%changelog/q" jellyfin-web.spec
|
||||
|
||||
cat <<EOF >>jellyfin-web.spec
|
||||
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||
EOF
|
||||
popd
|
||||
fi
|
||||
|
||||
# build rpm
|
||||
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
|
||||
rpmbuild --rebuild -bb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
|
||||
|
||||
# move the artifacts
|
||||
mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}/
|
||||
|
||||
if [[ ${IS_DOCKER} == YES ]]; then
|
||||
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||
fi
|
||||
|
||||
rm -f fedora/jellyfin*.tar.gz
|
||||
cp -a /tmp/yarn.lock yarn.lock
|
||||
|
||||
popd
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
|
||||
# move to source directory
|
||||
pushd ${SOURCE_DIR}
|
||||
|
||||
cp -a yarn.lock /tmp/yarn.lock
|
||||
|
||||
# modify changelog to unstable configuration if IS_UNSTABLE
|
||||
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||
pushd debian
|
||||
|
||||
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||
|
||||
cat <<EOF >changelog
|
||||
jellyfin-web (${BUILD_ID}-unstable) unstable; urgency=medium
|
||||
|
||||
* Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||
|
||||
-- Jellyfin Packaging Team <packaging@jellyfin.org> $( date --rfc-2822 )
|
||||
EOF
|
||||
popd
|
||||
fi
|
||||
|
||||
# build deb
|
||||
dpkg-buildpackage -us -uc --pre-clean --post-clean
|
||||
|
||||
mkdir -p ${ARTIFACT_DIR}
|
||||
mv ../jellyfin*.{deb,dsc,tar.gz,buildinfo,changes} ${ARTIFACT_DIR}
|
||||
|
||||
cp -a /tmp/yarn.lock yarn.lock
|
||||
|
||||
if [[ ${IS_DOCKER} == YES ]]; then
|
||||
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||
fi
|
||||
|
||||
popd
|
||||
@@ -1,41 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
|
||||
# move to source directory
|
||||
pushd ${SOURCE_DIR}
|
||||
|
||||
cp -a yarn.lock /tmp/yarn.lock
|
||||
|
||||
# modify changelog to unstable configuration if IS_UNSTABLE
|
||||
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||
pushd fedora
|
||||
|
||||
PR_ID=$( git log --grep 'Merge pull request' --oneline --single-worktree --first-parent | head -1 | grep --color=none -Eo '#[0-9]+' | tr -d '#' )
|
||||
|
||||
sed -i "s/Version:.*/Version: ${BUILD_ID}/" jellyfin-web.spec
|
||||
sed -i "/%changelog/q" jellyfin-web.spec
|
||||
|
||||
cat <<EOF >>jellyfin-web.spec
|
||||
* $( LANG=C date '+%a %b %d %Y' ) Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- Jellyfin Web unstable build ${BUILD_ID} for merged PR #${PR_ID}
|
||||
EOF
|
||||
popd
|
||||
fi
|
||||
|
||||
# build rpm
|
||||
make -f fedora/Makefile srpm outdir=/root/rpmbuild/SRPMS
|
||||
rpmbuild -rb /root/rpmbuild/SRPMS/jellyfin-*.src.rpm
|
||||
|
||||
# move the artifacts
|
||||
mv /root/rpmbuild/RPMS/noarch/jellyfin-*.rpm /root/rpmbuild/SRPMS/jellyfin-*.src.rpm ${ARTIFACT_DIR}
|
||||
|
||||
if [[ ${IS_DOCKER} == YES ]]; then
|
||||
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||
fi
|
||||
|
||||
rm -f fedora/jellyfin*.tar.gz
|
||||
cp -a /tmp/yarn.lock yarn.lock
|
||||
|
||||
popd
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
set -o xtrace
|
||||
|
||||
# move to source directory
|
||||
pushd ${SOURCE_DIR}
|
||||
|
||||
# get version
|
||||
if [[ ${IS_UNSTABLE} == 'yes' ]]; then
|
||||
version="${BUILD_ID}"
|
||||
else
|
||||
version="$( grep "version:" ./build.yaml | sed -E 's/version: "([0-9\.]+.*)"/\1/' )"
|
||||
fi
|
||||
|
||||
# build archives
|
||||
npx yarn install
|
||||
mv dist jellyfin-web_${version}
|
||||
tar -czf jellyfin-web_${version}_portable.tar.gz jellyfin-web_${version}
|
||||
rm -rf dist
|
||||
|
||||
# move the artifacts
|
||||
mkdir -p ${ARTIFACT_DIR}
|
||||
mv jellyfin[-_]*.tar.gz ${ARTIFACT_DIR}
|
||||
|
||||
if [[ ${IS_DOCKER} == YES ]]; then
|
||||
chown -Rc $(stat -c %u:%g ${ARTIFACT_DIR}) ${ARTIFACT_DIR}
|
||||
fi
|
||||
|
||||
popd
|
||||
@@ -1,21 +0,0 @@
|
||||
VERSION := $(shell sed -ne '/^Version:/s/.* *//p' fedora/jellyfin-web.spec)
|
||||
|
||||
srpm:
|
||||
cd fedora/; \
|
||||
SOURCE_DIR=.. \
|
||||
WORKDIR="$${PWD}"; \
|
||||
tar \
|
||||
--transform "s,^\.,jellyfin-web-$(VERSION)," \
|
||||
--exclude='.git*' \
|
||||
--exclude='**/.git' \
|
||||
--exclude='**/.hg' \
|
||||
--exclude='deployment' \
|
||||
--exclude='*.deb' \
|
||||
--exclude='*.rpm' \
|
||||
--exclude='jellyfin-web-$(VERSION).tar.gz' \
|
||||
-czf "jellyfin-web-$(VERSION).tar.gz" \
|
||||
-C $${SOURCE_DIR} ./
|
||||
cd fedora/; \
|
||||
rpmbuild -bs jellyfin-web.spec \
|
||||
--define "_sourcedir $$PWD/" \
|
||||
--define "_srcrpmdir $(outdir)"
|
||||
@@ -1,53 +0,0 @@
|
||||
%global debug_package %{nil}
|
||||
|
||||
Name: jellyfin-web
|
||||
Version: 10.6.4
|
||||
Release: 1%{?dist}
|
||||
Summary: The Free Software Media System web client
|
||||
License: GPLv3
|
||||
URL: https://jellyfin.org
|
||||
# Jellyfin Server tarball created by `make -f .copr/Makefile srpm`, real URL ends with `v%{version}.tar.gz`
|
||||
Source0: jellyfin-web-%{version}.tar.gz
|
||||
|
||||
%if 0%{?centos}
|
||||
BuildRequires: yarn
|
||||
# sadly the yarn RPM at https://dl.yarnpkg.com/rpm/ uses git but doesn't Requires: it
|
||||
BuildRequires: git
|
||||
%else
|
||||
BuildRequires: nodejs-yarn
|
||||
%endif
|
||||
BuildArch: noarch
|
||||
|
||||
# Disable Automatic Dependency Processing
|
||||
AutoReqProv: no
|
||||
|
||||
%description
|
||||
Jellyfin is a free software media system that puts you in control of managing and streaming your media.
|
||||
|
||||
|
||||
%prep
|
||||
%autosetup -n jellyfin-web-%{version} -b 0
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
yarn install
|
||||
%{__mkdir} -p %{buildroot}%{_datadir}
|
||||
mv dist %{buildroot}%{_datadir}/jellyfin-web
|
||||
%{__install} -D -m 0644 LICENSE %{buildroot}%{_datadir}/licenses/jellyfin/LICENSE
|
||||
|
||||
%files
|
||||
%attr(755,root,root) %{_datadir}/jellyfin-web
|
||||
%{_datadir}/licenses/jellyfin/LICENSE
|
||||
|
||||
%changelog
|
||||
* Sun Aug 30 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.6.4; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.4
|
||||
* Sun Aug 16 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.6.3; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.3
|
||||
* Sun Aug 02 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.6.2; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.2
|
||||
* Mon Jul 27 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- New upstream version 10.6.1; release changelog at https://github.com/jellyfin/jellyfin-web/releases/tag/v10.6.1
|
||||
* Mon Mar 23 2020 Jellyfin Packaging Team <packaging@jellyfin.org>
|
||||
- Forthcoming stable release
|
||||
204
gulpfile.js
204
gulpfile.js
@@ -1,204 +0,0 @@
|
||||
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', 'src/**/*.mp3']
|
||||
},
|
||||
injectBundle: {
|
||||
query: 'src/index.html'
|
||||
}
|
||||
};
|
||||
|
||||
function serve() {
|
||||
browserSync.init({
|
||||
server: {
|
||||
baseDir: './dist'
|
||||
},
|
||||
port: 8080
|
||||
});
|
||||
|
||||
const 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/']);
|
||||
}
|
||||
|
||||
const 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,
|
||||
transform: function (filepath) {
|
||||
return `<script src="${filepath}" defer></script>`;
|
||||
}
|
||||
}
|
||||
))
|
||||
.pipe(dest('dist/'))
|
||||
.pipe(browserSync.stream());
|
||||
}
|
||||
|
||||
function build(standalone) {
|
||||
return series(clean, parallel(javascript, apploader(standalone), webpack, css, html, images, copy));
|
||||
}
|
||||
|
||||
exports.default = series(build(false), injectBundle);
|
||||
exports.standalone = series(build(true), injectBundle);
|
||||
exports.serve = series(exports.standalone, serve);
|
||||
160
package.json
160
package.json
@@ -5,170 +5,62 @@
|
||||
"repository": "https://github.com/jellyfin/jellyfin-web",
|
||||
"license": "GPL-2.0-or-later",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.10.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.10.1",
|
||||
"@babel/plugin-proposal-private-methods": "^7.10.1",
|
||||
"@babel/plugin-transform-modules-amd": "^7.9.6",
|
||||
"@babel/polyfill": "^7.8.7",
|
||||
"@babel/preset-env": "^7.10.3",
|
||||
"autoprefixer": "^9.8.5",
|
||||
"babel-eslint": "^11.0.0-beta.2",
|
||||
"babel-loader": "^8.0.6",
|
||||
"browser-sync": "^2.26.7",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"copy-webpack-plugin": "^5.1.1",
|
||||
"css-loader": "^3.6.0",
|
||||
"cssnano": "^4.1.10",
|
||||
"del": "^5.1.0",
|
||||
"css-loader": "^3.4.2",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-plugin-compat": "^3.5.1",
|
||||
"eslint-plugin-eslint-comments": "^3.2.0",
|
||||
"eslint-plugin-import": "^2.21.2",
|
||||
"eslint-plugin-promise": "^4.2.1",
|
||||
"file-loader": "^6.0.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-babel": "^8.0.0",
|
||||
"gulp-cli": "^2.3.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.3.0",
|
||||
"lazypipe": "^1.0.2",
|
||||
"node-sass": "^4.13.1",
|
||||
"postcss-loader": "^3.0.0",
|
||||
"postcss-preset-env": "^6.7.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"style-loader": "^1.1.3",
|
||||
"stylelint": "^13.6.1",
|
||||
"stylelint": "^13.1.0",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
"stylelint-no-browser-hacks": "^1.2.1",
|
||||
"stylelint-order": "^4.1.0",
|
||||
"stylelint-order": "^4.0.0",
|
||||
"webpack": "^4.41.5",
|
||||
"webpack-merge": "^4.2.2",
|
||||
"webpack-stream": "^5.2.1"
|
||||
"webpack-cli": "^3.3.10",
|
||||
"webpack-concat-plugin": "^3.0.0",
|
||||
"webpack-dev-server": "^3.10.3",
|
||||
"webpack-merge": "^4.2.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"alameda": "^1.4.0",
|
||||
"blurhash": "^1.1.3",
|
||||
"classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz",
|
||||
"core-js": "^3.6.5",
|
||||
"date-fns": "^2.14.0",
|
||||
"epubjs": "^0.3.85",
|
||||
"fast-text-encoding": "^1.0.3",
|
||||
"document-register-element": "^1.14.3",
|
||||
"flv.js": "^1.5.0",
|
||||
"headroom.js": "^0.11.0",
|
||||
"hls.js": "^0.14.0",
|
||||
"howler": "^2.2.0",
|
||||
"intersection-observer": "^0.11.0",
|
||||
"jellyfin-apiclient": "^1.4.1",
|
||||
"hls.js": "^0.13.1",
|
||||
"howler": "^2.1.3",
|
||||
"jellyfin-noto": "https://github.com/jellyfin/jellyfin-noto",
|
||||
"jquery": "^3.5.1",
|
||||
"jstree": "^3.3.10",
|
||||
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-smarttv",
|
||||
"jquery": "^3.4.1",
|
||||
"jstree": "^3.3.7",
|
||||
"libass-wasm": "https://github.com/jellyfin/JavascriptSubtitlesOctopus#4.0.0-jf-cordova",
|
||||
"libjass": "^0.11.0",
|
||||
"material-design-icons-iconfont": "^5.0.1",
|
||||
"native-promise-only": "^0.8.0-a",
|
||||
"page": "^1.11.6",
|
||||
"query-string": "^6.13.1",
|
||||
"resize-observer-polyfill": "^1.5.1",
|
||||
"screenfull": "^5.0.2",
|
||||
"shaka-player": "^2.5.13",
|
||||
"shaka-player": "^2.5.9",
|
||||
"sortablejs": "^1.10.2",
|
||||
"swiper": "^5.4.5",
|
||||
"swiper": "^5.3.1",
|
||||
"webcomponents.js": "^0.7.24",
|
||||
"whatwg-fetch": "^3.2.0"
|
||||
},
|
||||
"babel": {
|
||||
"presets": [
|
||||
"@babel/preset-env"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"test": [
|
||||
"src/components/accessSchedule/accessSchedule.js",
|
||||
"src/components/actionSheet/actionSheet.js",
|
||||
"src/components/autoFocuser.js",
|
||||
"src/components/cardbuilder/cardBuilder.js",
|
||||
"src/components/cardbuilder/chaptercardbuilder.js",
|
||||
"src/components/cardbuilder/peoplecardbuilder.js",
|
||||
"src/components/images/imageLoader.js",
|
||||
"src/components/indicators/indicators.js",
|
||||
"src/components/lazyLoader/lazyLoaderIntersectionObserver.js",
|
||||
"src/components/playback/brightnessosd.js",
|
||||
"src/components/playback/mediasession.js",
|
||||
"src/components/playback/nowplayinghelper.js",
|
||||
"src/components/playback/playbackorientation.js",
|
||||
"src/components/playback/playerSelectionMenu.js",
|
||||
"src/components/playback/playersettingsmenu.js",
|
||||
"src/components/playback/playmethodhelper.js",
|
||||
"src/components/playback/remotecontrolautoplay.js",
|
||||
"src/components/playback/volumeosd.js",
|
||||
"src/components/playmenu.js",
|
||||
"src/components/sanatizefilename.js",
|
||||
"src/components/scrollManager.js",
|
||||
"src/components/syncPlay/groupSelectionMenu.js",
|
||||
"src/components/syncPlay/playbackPermissionManager.js",
|
||||
"src/components/syncPlay/syncPlayManager.js",
|
||||
"src/components/syncPlay/timeSyncManager.js",
|
||||
"src/controllers/dashboard/logs.js",
|
||||
"src/controllers/dashboard/plugins/repositories.js",
|
||||
"src/controllers/user/display.js",
|
||||
"src/controllers/user/home.js",
|
||||
"src/controllers/user/playback.js",
|
||||
"src/controllers/user/subtitles.js",
|
||||
"src/plugins/bookPlayer/plugin.js",
|
||||
"src/plugins/bookPlayer/tableOfContents.js",
|
||||
"src/plugins/photoPlayer/plugin.js",
|
||||
"src/scripts/deleteHelper.js",
|
||||
"src/scripts/dfnshelper.js",
|
||||
"src/scripts/dom.js",
|
||||
"src/scripts/fileDownloader.js",
|
||||
"src/scripts/filesystem.js",
|
||||
"src/scripts/imagehelper.js",
|
||||
"src/scripts/inputManager.js",
|
||||
"src/plugins/backdropScreensaver/plugin.js",
|
||||
"src/components/filterdialog/filterdialog.js",
|
||||
"src/components/fetchhelper.js",
|
||||
"src/scripts/keyboardNavigation.js",
|
||||
"src/scripts/settings/appSettings.js",
|
||||
"src/scripts/settings/userSettings.js",
|
||||
"src/scripts/settings/webSettings.js"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-transform-modules-amd",
|
||||
"@babel/plugin-proposal-class-properties",
|
||||
"@babel/plugin-proposal-private-methods"
|
||||
]
|
||||
}
|
||||
]
|
||||
"whatwg-fetch": "^3.0.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"last 2 Firefox versions",
|
||||
"last 2 Chrome versions",
|
||||
"last 2 ChromeAndroid versions",
|
||||
"last 2 Safari versions",
|
||||
"iOS > 10",
|
||||
"last 2 iOS versions",
|
||||
"last 2 Edge versions",
|
||||
"Chrome 27",
|
||||
"Chrome 38",
|
||||
"Chrome 47",
|
||||
"Chrome 53",
|
||||
"Chrome 56",
|
||||
"Chrome 63",
|
||||
"Edge 18",
|
||||
"Firefox ESR"
|
||||
],
|
||||
"scripts": {
|
||||
"serve": "gulp serve --development",
|
||||
"prepare": "gulp --production",
|
||||
"build:development": "gulp --development",
|
||||
"build:production": "gulp --production",
|
||||
"build:standalone": "gulp standalone --development",
|
||||
"lint": "eslint \".\"",
|
||||
"stylelint": "stylelint \"src/**/*.css\""
|
||||
"serve": "webpack-dev-server --config webpack.dev.js --open",
|
||||
"build": "webpack --config webpack.prod.js",
|
||||
"lint": "eslint \"src\"",
|
||||
"stylelint": "stylelint \"src/**/*.css\"",
|
||||
"prepare": "webpack --config webpack.prod.js"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
const packageConfig = require('./package.json');
|
||||
const postcssPresetEnv = require('postcss-preset-env');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
const cssnano = require('cssnano');
|
||||
|
||||
const config = () => ({
|
||||
plugins: [
|
||||
// Explicitly specify browserslist to override ones from node_modules
|
||||
// For example, Swiper has it in its package.json
|
||||
postcssPresetEnv({browsers: packageConfig.browserslist}),
|
||||
autoprefixer({overrideBrowserslist: packageConfig.browserslist}),
|
||||
cssnano()
|
||||
]
|
||||
});
|
||||
|
||||
module.exports = config;
|
||||
@@ -15,8 +15,6 @@ print(langlst)
|
||||
input('press enter to continue')
|
||||
|
||||
keysus = []
|
||||
missing = []
|
||||
|
||||
with open(langdir + '/' + 'en-us.json') as en:
|
||||
langus = json.load(en)
|
||||
for key in langus:
|
||||
@@ -34,19 +32,10 @@ for lang in langlst:
|
||||
for key in langjson:
|
||||
if key in keysus:
|
||||
langjnew[key] = langjson[key]
|
||||
elif key not in missing:
|
||||
missing.append(key)
|
||||
f.seek(0)
|
||||
f.write(json.dumps(langjnew, indent=inde, sort_keys=False, ensure_ascii=False))
|
||||
f.write('\n')
|
||||
f.truncate()
|
||||
f.close()
|
||||
|
||||
print(missing)
|
||||
print('LENGTH: ' + str(len(missing)))
|
||||
with open('missing.txt', 'w') as out:
|
||||
for item in missing:
|
||||
out.write(item + '\n')
|
||||
out.close()
|
||||
|
||||
print('DONE')
|
||||
|
||||
@@ -34,7 +34,7 @@ for lang in langlst:
|
||||
|
||||
print(dep)
|
||||
print('LENGTH: ' + str(len(dep)))
|
||||
with open('unused.txt', 'w') as out:
|
||||
with open('scout.txt', 'w') as out:
|
||||
for item in dep:
|
||||
out.write(item + '\n')
|
||||
out.close()
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
<div class="verticalSection">
|
||||
<div class="sectionTitleContainer flex align-items-center">
|
||||
<h1 class="sectionTitle pluginName"></h1>
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/plugins/index.html">${Help}</a>
|
||||
<a is="emby-linkbutton" class="raised button-alt headerHelpButton" target="_blank" href="https://docs.jellyfin.org/general/server/plugins/index.html">${Help}</a>
|
||||
</div>
|
||||
|
||||
<p id="overview" style="font-style: italic;"></p>
|
||||
<p id="description"></p>
|
||||
<p id="tagline" style="font-style: italic;"></p>
|
||||
<p id="pPreviewImage"></p>
|
||||
<p id="overview"></p>
|
||||
</div>
|
||||
|
||||
<div class="verticalSection">
|
||||
@@ -27,6 +28,7 @@
|
||||
</button>
|
||||
<div class="fieldDescription">${ServerRestartNeededAfterPluginInstall}</div>
|
||||
</div>
|
||||
<p id="nonServerMsg"></p>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@@ -35,6 +37,9 @@
|
||||
<div is="emby-collapse" title="${HeaderDeveloperInfo}">
|
||||
<div class="collapseContent">
|
||||
<p id="developer"></p>
|
||||
<p id="pViewWebsite" style="display: none;">
|
||||
<a is="emby-linkbutton" class="button-link" href="#" target="_blank">${ButtonViewWebsite}</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<div data-role="page" class="page standalonePage">
|
||||
<div class="padded-left padded-right padded-bottom-page">
|
||||
<form class="addServerForm" style="margin: 0 auto;" novalidate>
|
||||
<form class="addServerForm" style="margin: 0 auto;">
|
||||
<h1>${HeaderConnectToServer}</h1>
|
||||
<div class="inputContainer">
|
||||
<input is="emby-input" type="url" id="txtServerHost" required="required" label="${LabelServerHost}"/>
|
||||
<input is="emby-input" type="text" id="txtServerHost" required="required" label="${LabelServerHost}" autocomplete="off" spellcheck="false" autocapitalize="none" autocorrect="off" />
|
||||
<div class="fieldDescription">${LabelServerHostHelp}</div>
|
||||
</div>
|
||||
<br />
|
||||
|
||||
@@ -4,19 +4,18 @@
|
||||
<div class="detailSectionHeader">
|
||||
<h2 style="margin:.6em 0;vertical-align:middle;display:inline-block;">${HeaderApiKeys}</h2>
|
||||
<button is="emby-button" type="button" class="fab btnNewKey submit" style="margin-left:1em;" title="${ButtonAdd}">
|
||||
<span class="material-icons add" aria-hidden="true"></span>
|
||||
<i class="material-icons">add</i>
|
||||
</button>
|
||||
</div>
|
||||
<p>${HeaderApiKeysHelp}</p>
|
||||
<br />
|
||||
<table class="tblApiKeys detailTable">
|
||||
<caption class="clipForScreenReader">${ApiKeysCaption}</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="detailTableHeaderCell"></th>
|
||||
<th scope="col" class="detailTableHeaderCell">${HeaderApiKey}</th>
|
||||
<th scope="col" class="detailTableHeaderCell">${HeaderApp}</th>
|
||||
<th scope="col" class="detailTableHeaderCell">${HeaderDateIssued}</th>
|
||||
<th class="detailTableHeaderCell"></th>
|
||||
<th class="detailTableHeaderCell">${HeaderApiKey}</th>
|
||||
<th class="detailTableHeaderCell">${HeaderApp}</th>
|
||||
<th class="detailTableHeaderCell">${HeaderDateIssued}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="resultBody"></tbody>
|
||||
|
||||
Binary file not shown.
@@ -235,15 +235,6 @@ div[data-role=controlgroup] a.ui-btn-active {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.localUsers .cardText-secondary {
|
||||
white-space: pre-wrap;
|
||||
height: 3em;
|
||||
}
|
||||
|
||||
.customCssContainer textarea {
|
||||
resize: none;
|
||||
}
|
||||
|
||||
@media all and (min-width: 70em) {
|
||||
.dashboardSections {
|
||||
-webkit-flex-wrap: wrap;
|
||||
|
||||
@@ -30,10 +30,6 @@
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.align-items-flex-end {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.justify-content-center {
|
||||
justify-content: center;
|
||||
}
|
||||
@@ -42,10 +38,6 @@
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.justify-content-space-between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.flex-wrap-wrap {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
html {
|
||||
font-size: 82% !important;
|
||||
}
|
||||
|
||||
.formDialogFooter {
|
||||
position: static !important;
|
||||
margin: 0 -1em !important;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,10 @@
|
||||
padding-top: 7em !important;
|
||||
}
|
||||
|
||||
.layout-mobile .libraryPage {
|
||||
padding-top: 4em !important;
|
||||
}
|
||||
|
||||
.itemDetailPage {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
@@ -160,7 +164,6 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
contain: layout style paint;
|
||||
transition: background ease-in-out 0.5s;
|
||||
}
|
||||
|
||||
.hiddenViewMenuBar .skinHeader {
|
||||
@@ -175,10 +178,6 @@
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.layout-tv .sectionTabs {
|
||||
width: 55%;
|
||||
}
|
||||
|
||||
.selectedMediaFolder {
|
||||
background-color: #f2f2f2 !important;
|
||||
}
|
||||
@@ -273,7 +272,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (max-width: 100em) {
|
||||
@media all and (max-width: 84em) {
|
||||
.withSectionTabs .headerTop {
|
||||
padding-bottom: 0.55em;
|
||||
}
|
||||
@@ -281,13 +280,9 @@
|
||||
.sectionTabs {
|
||||
font-size: 83.5%;
|
||||
}
|
||||
|
||||
.layout-tv .sectionTabs {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 100em) {
|
||||
@media all and (min-width: 84em) {
|
||||
.headerTop {
|
||||
padding: 0.8em 0.8em;
|
||||
}
|
||||
@@ -443,14 +438,12 @@
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
background-attachment: fixed;
|
||||
height: 40vh;
|
||||
height: 50vh;
|
||||
position: relative;
|
||||
animation: backdrop-fadein 800ms ease-in normal both;
|
||||
}
|
||||
|
||||
.layout-mobile .itemBackdrop {
|
||||
background-attachment: scroll;
|
||||
height: 26.5vh;
|
||||
}
|
||||
|
||||
.layout-desktop .itemBackdrop::after,
|
||||
@@ -470,20 +463,10 @@
|
||||
.detailPageContent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-left: 32.45vw;
|
||||
padding-left: 2%;
|
||||
padding-right: 2%;
|
||||
}
|
||||
|
||||
.layout-mobile .detailPageContent {
|
||||
padding-left: 5%;
|
||||
padding-right: 5%;
|
||||
}
|
||||
|
||||
.layout-desktop .detailPageContent .emby-scroller,
|
||||
.layout-tv .detailPageContent .emby-scroller {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.layout-desktop .noBackdrop .detailPageContent,
|
||||
.layout-tv .noBackdrop .detailPageContent {
|
||||
margin-top: 2.5em;
|
||||
@@ -494,10 +477,6 @@
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.detailSectionContent a {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.personBackdrop {
|
||||
background-size: contain;
|
||||
}
|
||||
@@ -516,23 +495,7 @@
|
||||
|
||||
.parentName {
|
||||
display: block;
|
||||
margin: 0 0 0;
|
||||
}
|
||||
|
||||
.layout-mobile .parentName {
|
||||
margin: 0.6em 0 0;
|
||||
}
|
||||
|
||||
.musicParentName {
|
||||
margin: 0.15em 0 0.2em;
|
||||
}
|
||||
|
||||
.layout-mobile .musicParentName {
|
||||
margin: -0.25em 0 0.25em;
|
||||
}
|
||||
|
||||
.layout-mobile .itemExternalLinks {
|
||||
display: none;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.mainDetailButtons {
|
||||
@@ -540,6 +503,8 @@
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
align-items: center;
|
||||
-webkit-flex-wrap: wrap;
|
||||
flex-wrap: wrap;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
@@ -555,35 +520,6 @@
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.itemName.originalTitle {
|
||||
margin: 0.2em 0 0.2em;
|
||||
}
|
||||
|
||||
.itemName.parentNameLast {
|
||||
margin: 0 0 0;
|
||||
}
|
||||
|
||||
.layout-mobile .itemName.parentNameLast {
|
||||
margin: 0.4em 0 0.4em;
|
||||
}
|
||||
|
||||
.layout-mobile h1.itemName,
|
||||
.layout-mobile h1.parentName {
|
||||
font-size: 1.6em;
|
||||
}
|
||||
|
||||
.itemName.parentNameLast.withOriginalTitle {
|
||||
margin: 0 0 0;
|
||||
}
|
||||
|
||||
.layout-mobile .itemName.parentNameLast.withOriginalTitle {
|
||||
margin: 0.6em 0 0;
|
||||
}
|
||||
|
||||
.layout-mobile .itemName.originalTitle {
|
||||
margin: 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
.nameContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -610,19 +546,6 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.layout-mobile .mainDetailButtons {
|
||||
margin-top: 1em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin: 0.15em 0 0.2em;
|
||||
}
|
||||
|
||||
.layout-mobile .subtitle {
|
||||
margin: 0.2em 0 0.2em;
|
||||
}
|
||||
|
||||
.detailPagePrimaryContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -633,7 +556,7 @@
|
||||
.layout-mobile .detailPagePrimaryContainer {
|
||||
display: block;
|
||||
position: relative;
|
||||
padding: 0.5em 3.3% 0.5em;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer,
|
||||
@@ -643,14 +566,13 @@
|
||||
padding-left: 32.45vw;
|
||||
}
|
||||
|
||||
.layout-desktop .detailRibbon,
|
||||
.layout-tv .detailRibbon {
|
||||
.layout-desktop .detailSticky,
|
||||
.layout-tv .detailSticky {
|
||||
margin-top: -7.2em;
|
||||
height: 7.2em;
|
||||
}
|
||||
|
||||
.layout-desktop .noBackdrop .detailRibbon,
|
||||
.layout-tv .noBackdrop .detailRibbon {
|
||||
.layout-desktop .noBackdrop .detailSticky,
|
||||
.layout-tv .noBackdrop .detailSticky {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
@@ -662,9 +584,6 @@
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
text-align: left;
|
||||
min-width: 0;
|
||||
max-width: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.layout-mobile .infoText {
|
||||
@@ -675,29 +594,12 @@
|
||||
margin: 1.25em 0;
|
||||
}
|
||||
|
||||
.layout-mobile .detailPageSecondaryContainer {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.layout-mobile .detailImageContainer {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.detailImageContainer .card {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
.detailImageContainer {
|
||||
position: relative;
|
||||
margin-top: -25vh;
|
||||
float: left;
|
||||
width: 25vw;
|
||||
z-index: 3;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.detailImageContainer .card.backdropCard {
|
||||
top: 35%;
|
||||
}
|
||||
|
||||
.detailImageContainer .card.squareCard {
|
||||
top: 40%;
|
||||
}
|
||||
|
||||
.layout-desktop .noBackdrop .detailImageContainer,
|
||||
@@ -710,11 +612,11 @@
|
||||
}
|
||||
|
||||
.detailLogo {
|
||||
width: 25vw;
|
||||
height: 16vh;
|
||||
width: 30vw;
|
||||
height: 25vh;
|
||||
position: absolute;
|
||||
top: 10vh;
|
||||
right: 25vw;
|
||||
right: 20vw;
|
||||
background-size: contain;
|
||||
}
|
||||
|
||||
@@ -739,11 +641,15 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||
}
|
||||
|
||||
.itemDetailGalleryLink.defaultCardBackground {
|
||||
/* Dirty hack to get it to look somewhat square. Less than ideal. */
|
||||
height: 23vw;
|
||||
height: 23vw; /* Dirty hack to get it to look somewhat square. Less than ideal. */
|
||||
}
|
||||
|
||||
.itemDetailGalleryLink.defaultCardBackground > .material-icons {
|
||||
.btnSyncComplete i {
|
||||
-webkit-border-radius: 100em;
|
||||
border-radius: 100em;
|
||||
}
|
||||
|
||||
.itemDetailGalleryLink.defaultCardBackground > i {
|
||||
font-size: 15vw;
|
||||
margin-top: 50%;
|
||||
transform: translateY(-50%);
|
||||
@@ -754,19 +660,14 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.layout-desktop .itemBackdrop,
|
||||
.layout-tv .itemBackdrop {
|
||||
height: 40vh;
|
||||
}
|
||||
|
||||
.layout-desktop .detailPageWrapperContainer,
|
||||
.layout-tv .detailPageWrapperContainer {
|
||||
margin-top: 0.1em;
|
||||
margin-top: 7.2em;
|
||||
}
|
||||
|
||||
.layout-desktop .detailImageContainer .card,
|
||||
.layout-tv .detailImageContainer .card {
|
||||
top: 10%;
|
||||
.layout-tv #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer,
|
||||
.layout-desktop #itemDetailPage:not(.noBackdrop) .detailPagePrimaryContainer {
|
||||
padding-left: 3.3%;
|
||||
}
|
||||
|
||||
.btnPlaySimple {
|
||||
@@ -780,29 +681,37 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||
}
|
||||
}
|
||||
|
||||
.btnSyncComplete {
|
||||
background: #673ab7 !important;
|
||||
}
|
||||
|
||||
.emby-button.detailFloatingButton {
|
||||
position: absolute;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 3;
|
||||
top: 100%;
|
||||
left: 90%;
|
||||
background-color: rgba(0, 0, 0, 0.5) !important;
|
||||
z-index: 1;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
margin: -2.2em 0 0 -2.2em;
|
||||
padding: 0.4em;
|
||||
padding: 0.4em !important;
|
||||
color: rgba(255, 255, 255, 0.76);
|
||||
}
|
||||
|
||||
.emby-button.detailFloatingButton .material-icons {
|
||||
.emby-button.detailFloatingButton i {
|
||||
font-size: 3.5em;
|
||||
}
|
||||
|
||||
@media all and (max-width: 62.5em) {
|
||||
.parentName {
|
||||
margin-bottom: 0;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.itemDetailPage {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
.detailimg-hidemobile {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 31.25em) {
|
||||
@@ -897,9 +806,9 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||
}
|
||||
|
||||
.detailImageProgressContainer {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
margin-top: -0.4vw;
|
||||
width: 100%;
|
||||
width: 22.786458333333332vw;
|
||||
}
|
||||
|
||||
.detailButton-text {
|
||||
@@ -918,7 +827,21 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||
}
|
||||
}
|
||||
|
||||
@media all and (min-width: 100em) {
|
||||
@media all and (min-width: 62.5em) {
|
||||
.headerTop {
|
||||
padding-left: 0.8em;
|
||||
padding-right: 0.8em;
|
||||
}
|
||||
|
||||
.headerTabs {
|
||||
align-self: center;
|
||||
width: auto;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-top: -4.2em;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.detailFloatingButton {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -952,10 +875,6 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||
}
|
||||
}
|
||||
|
||||
.detailVerticalSection .emby-scrollbuttons {
|
||||
padding-top: 0.4em;
|
||||
}
|
||||
|
||||
.layout-tv .detailVerticalSection {
|
||||
margin-bottom: 3.4em !important;
|
||||
}
|
||||
@@ -1044,10 +963,6 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||
margin-bottom: 2.7em;
|
||||
}
|
||||
|
||||
.layout-mobile .verticalSection-extrabottompadding {
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.sectionTitleButton,
|
||||
.sectionTitleIconButton {
|
||||
margin-right: 0 !important;
|
||||
@@ -1073,13 +988,7 @@ div.itemDetailGalleryLink.defaultCardBackground {
|
||||
|
||||
div:not(.sectionTitleContainer-cards) > .sectionTitle-cards {
|
||||
margin: 0;
|
||||
padding-top: 0.5em;
|
||||
padding-bottom: 0.2em;
|
||||
}
|
||||
|
||||
.layout-mobile :not(.sectionTitleContainer-cards) > .sectionTitle-cards {
|
||||
margin: 0;
|
||||
padding-top: 0.5em;
|
||||
padding-top: 1.25em;
|
||||
}
|
||||
|
||||
.sectionTitleButton {
|
||||
@@ -1197,74 +1106,3 @@ div:not(.sectionTitleContainer-cards) > .sectionTitle-cards {
|
||||
.itemsViewSettingsContainer > .button-flat {
|
||||
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;
|
||||
line-height: 1.75;
|
||||
}
|
||||
|
||||
.layout-mobile .detailsGroupItem .label,
|
||||
.layout-mobile .trackSelections .selectContainer .selectLabel {
|
||||
flex-basis: 4.5em;
|
||||
}
|
||||
|
||||
.trackSelections .selectContainer .detailTrackSelect {
|
||||
font-size: inherit;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.trackSelections .selectContainer .selectArrowContainer .selectArrow {
|
||||
margin-top: 0;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
.overview-controls {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.detail-clamp-text {
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 12;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
@media all and (min-width: 40em) {
|
||||
.detail-clamp-text {
|
||||
-webkit-line-clamp: 6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
.hiddenScrollX,
|
||||
.layout-tv .scrollX {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.hiddenScrollX-forced {
|
||||
@@ -41,7 +40,6 @@
|
||||
.hiddenScrollY,
|
||||
.layout-tv .smoothScrollY {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
/* Can't do this because it not only hides the scrollbar, but also prevents scrolling */
|
||||
|
||||
|
||||
@@ -5,26 +5,9 @@ html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.layout-mobile,
|
||||
.layout-tv {
|
||||
-webkit-touch-callout: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.clipForScreenReader {
|
||||
clip: rect(1px, 1px, 1px, 1px);
|
||||
clip-path: inset(50%);
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
/* Fix font ligatures on older WebOS versions */
|
||||
font-feature-settings: "liga";
|
||||
-webkit-font-feature-settings: "liga";
|
||||
}
|
||||
|
||||
.backgroundContainer {
|
||||
@@ -40,6 +23,16 @@ html {
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.layout-mobile,
|
||||
.layout-tv {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
body {
|
||||
overflow-x: hidden;
|
||||
background-color: transparent !important;
|
||||
@@ -103,29 +96,3 @@ div[data-role=page] {
|
||||
margin-right: auto;
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
.headroom {
|
||||
will-change: transform;
|
||||
transition: transform 200ms linear;
|
||||
}
|
||||
|
||||
.headroom--pinned {
|
||||
transform: translateY(0%);
|
||||
}
|
||||
|
||||
.headroom--unpinned {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
.drawerContent {
|
||||
/* make sure the bottom of the drawer is visible when music is playing */
|
||||
padding-bottom: 4em;
|
||||
}
|
||||
|
||||
.force-scroll {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.hide-scroll {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
}
|
||||
|
||||
.osdPoster img,
|
||||
.pageContainer,
|
||||
.videoOsdBottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
@@ -29,7 +30,7 @@
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.osdHeader .headerButton:not(.headerBackButton):not(.headerCastButton):not(.headerSyncButton) {
|
||||
.osdHeader .headerButton:not(.headerBackButton):not(.headerCastButton) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -247,6 +248,11 @@
|
||||
animation: spin 4s linear infinite;
|
||||
}
|
||||
|
||||
.pageContainer {
|
||||
top: 0;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
@media all and (max-width: 30em) {
|
||||
.btnFastForward,
|
||||
.btnRewind,
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" role="img" viewBox="0 0 24 24"><title>Microsoft Edge icon</title><path d="M21.86 17.86q.14 0 .25.12.1.13.1.25t-.11.33l-.32.46-.43.53-.44.5q-.21.25-.38.42l-.22.23q-.58.53-1.34 1.04-.76.51-1.6.91-.86.4-1.74.64t-1.67.24q-.9 0-1.69-.28-.8-.28-1.48-.78-.68-.5-1.22-1.17-.53-.66-.92-1.44-.38-.77-.58-1.6-.2-.83-.2-1.67 0-1 .32-1.96.33-.97.87-1.8.14.95.55 1.77.41.82 1.02 1.5.6.68 1.38 1.21.78.54 1.64.9.86.36 1.77.56.92.2 1.8.2 1.12 0 2.18-.24 1.06-.23 2.06-.72l.2-.1.2-.05zm-15.5-1.27q0 1.1.27 2.15.27 1.06.78 2.03.51.96 1.24 1.77.74.82 1.66 1.4-1.47-.2-2.8-.74-1.33-.55-2.48-1.37-1.15-.83-2.08-1.9-.92-1.07-1.58-2.33T.36 14.94Q0 13.54 0 12.06q0-.81.32-1.49.31-.68.83-1.23.53-.55 1.2-.96.66-.4 1.35-.66.74-.27 1.5-.39.78-.12 1.55-.12.7 0 1.42.1.72.12 1.4.35.68.23 1.32.57.63.35 1.16.83-.35 0-.7.07-.33.07-.65.23v-.02q-.63.28-1.2.74-.57.46-1.05 1.04-.48.58-.87 1.26-.38.67-.65 1.39-.27.71-.42 1.44-.15.72-.15 1.38zM11.96.06q1.7 0 3.33.39 1.63.38 3.07 1.15 1.43.77 2.62 1.93 1.18 1.16 1.98 2.7.49.94.76 1.96.28 1 .28 2.08 0 .89-.23 1.7-.24.8-.69 1.48-.45.68-1.1 1.22-.64.53-1.45.88-.54.24-1.11.36-.58.13-1.16.13-.42 0-.97-.03-.54-.03-1.1-.12-.55-.1-1.05-.28-.5-.19-.84-.5-.12-.09-.23-.24-.1-.16-.1-.33 0-.15.16-.35.16-.2.35-.5.2-.28.36-.68.16-.4.16-.95 0-1.06-.4-1.96-.4-.91-1.06-1.64-.66-.74-1.52-1.28-.86-.55-1.79-.89-.84-.3-1.72-.44-.87-.14-1.76-.14-1.55 0-3.06.45T.94 7.55q.71-1.74 1.81-3.13 1.1-1.38 2.52-2.35Q6.68 1.1 8.37.58q1.7-.52 3.58-.52Z" fill="#fff"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
180
src/bundle.js
180
src/bundle.js
@@ -4,180 +4,100 @@
|
||||
// Use define from require.js not webpack's define
|
||||
var _define = window.define;
|
||||
|
||||
// document-register-element
|
||||
var docRegister = require("document-register-element");
|
||||
_define("document-register-element", function() {
|
||||
return docRegister;
|
||||
});
|
||||
|
||||
// fetch
|
||||
var fetch = require('whatwg-fetch');
|
||||
_define('fetch', function() {
|
||||
return fetch;
|
||||
});
|
||||
|
||||
// Blurhash
|
||||
var blurhash = require('blurhash');
|
||||
_define('blurhash', function() {
|
||||
return blurhash;
|
||||
});
|
||||
|
||||
// query-string
|
||||
var query = require('query-string');
|
||||
_define('queryString', function() {
|
||||
return query;
|
||||
var fetch = require("whatwg-fetch");
|
||||
_define("fetch", function() {
|
||||
return fetch
|
||||
});
|
||||
|
||||
// flvjs
|
||||
var flvjs = require('flv.js/dist/flv').default;
|
||||
_define('flvjs', function() {
|
||||
var flvjs = require("flv.js/dist/flv").default;
|
||||
_define("flvjs", function() {
|
||||
return flvjs;
|
||||
});
|
||||
|
||||
// jstree
|
||||
var jstree = require('jstree');
|
||||
require('jstree/dist/themes/default/style.css');
|
||||
_define('jstree', function() {
|
||||
var jstree = require("jstree");
|
||||
require("jstree/dist/themes/default/style.css");
|
||||
_define("jstree", function() {
|
||||
return jstree;
|
||||
});
|
||||
|
||||
// jquery
|
||||
var jquery = require('jquery');
|
||||
_define('jQuery', function() {
|
||||
var jquery = require("jquery");
|
||||
_define("jQuery", function() {
|
||||
return jquery;
|
||||
});
|
||||
|
||||
// hlsjs
|
||||
var hlsjs = require('hls.js');
|
||||
_define('hlsjs', function() {
|
||||
var hlsjs = require("hls.js");
|
||||
_define("hlsjs", function() {
|
||||
return hlsjs;
|
||||
});
|
||||
|
||||
// howler
|
||||
var howler = require('howler');
|
||||
_define('howler', function() {
|
||||
var howler = require("howler");
|
||||
_define("howler", function() {
|
||||
return howler;
|
||||
});
|
||||
|
||||
// resize-observer-polyfill
|
||||
var resize = require('resize-observer-polyfill').default;
|
||||
_define('resize-observer-polyfill', function() {
|
||||
var resize = require("resize-observer-polyfill").default;
|
||||
_define("resize-observer-polyfill", function() {
|
||||
return resize;
|
||||
});
|
||||
|
||||
// shaka
|
||||
var shaka = require("shaka-player");
|
||||
_define("shaka", function() {
|
||||
return shaka;
|
||||
});
|
||||
|
||||
// swiper
|
||||
var swiper = require('swiper/js/swiper');
|
||||
require('swiper/css/swiper.min.css');
|
||||
_define('swiper', function() {
|
||||
var swiper = require("swiper/js/swiper");
|
||||
require("swiper/css/swiper.min.css");
|
||||
_define("swiper", function() {
|
||||
return swiper;
|
||||
});
|
||||
|
||||
// sortable
|
||||
var sortable = require('sortablejs').default;
|
||||
_define('sortable', function() {
|
||||
var sortable = require("sortablejs").default;
|
||||
_define("sortable", function() {
|
||||
return sortable;
|
||||
});
|
||||
|
||||
// webcomponents
|
||||
var webcomponents = require('webcomponents.js/webcomponents-lite');
|
||||
_define('webcomponents', function() {
|
||||
return webcomponents;
|
||||
var webcomponents = require("webcomponents.js/webcomponents-lite");
|
||||
_define("webcomponents", function() {
|
||||
return webcomponents
|
||||
});
|
||||
|
||||
// shaka
|
||||
var shaka = require('shaka-player');
|
||||
_define('shaka', function() {
|
||||
return shaka;
|
||||
// libjass
|
||||
var libjass = require("libjass");
|
||||
require("libjass/libjass.css");
|
||||
_define("libjass", function() {
|
||||
return libjass;
|
||||
});
|
||||
|
||||
// libass-wasm
|
||||
var libassWasm = require('libass-wasm');
|
||||
_define('JavascriptSubtitlesOctopus', function() {
|
||||
return libassWasm;
|
||||
var libass_wasm = require("libass-wasm");
|
||||
_define("JavascriptSubtitlesOctopus", function() {
|
||||
return libass_wasm;
|
||||
});
|
||||
|
||||
// material-icons
|
||||
var materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css');
|
||||
_define('material-icons', function() {
|
||||
return materialIcons;
|
||||
var material_icons = require("material-design-icons-iconfont/dist/material-design-icons.css");
|
||||
_define("material-icons", function() {
|
||||
return material_icons;
|
||||
});
|
||||
|
||||
// noto font
|
||||
var noto = require('jellyfin-noto');
|
||||
_define('jellyfin-noto', function () {
|
||||
return noto;
|
||||
});
|
||||
|
||||
var epubjs = require('epubjs');
|
||||
_define('epubjs', function () {
|
||||
return epubjs;
|
||||
});
|
||||
|
||||
// page.js
|
||||
var page = require('page');
|
||||
_define('page', function() {
|
||||
return page;
|
||||
});
|
||||
|
||||
// core-js
|
||||
var polyfill = require('@babel/polyfill/dist/polyfill');
|
||||
_define('polyfill', function () {
|
||||
return polyfill;
|
||||
});
|
||||
|
||||
// domtokenlist-shim
|
||||
var classlist = require('classlist.js');
|
||||
_define('classlist-polyfill', function () {
|
||||
return classlist;
|
||||
});
|
||||
|
||||
// Date-FNS
|
||||
var dateFns = require('date-fns');
|
||||
_define('date-fns', function () {
|
||||
return dateFns;
|
||||
});
|
||||
|
||||
var dateFnsLocale = require('date-fns/locale');
|
||||
_define('date-fns/locale', function () {
|
||||
return dateFnsLocale;
|
||||
});
|
||||
|
||||
var fast_text_encoding = require('fast-text-encoding');
|
||||
_define('fast-text-encoding', function () {
|
||||
return fast_text_encoding;
|
||||
});
|
||||
|
||||
// intersection-observer
|
||||
var intersection_observer = require('intersection-observer');
|
||||
_define('intersection-observer', function () {
|
||||
return intersection_observer;
|
||||
});
|
||||
|
||||
// screenfull
|
||||
var screenfull = require('screenfull');
|
||||
_define('screenfull', function () {
|
||||
return screenfull;
|
||||
});
|
||||
|
||||
// headroom.js
|
||||
var headroom = require('headroom.js/dist/headroom');
|
||||
_define('headroom', function () {
|
||||
return headroom;
|
||||
});
|
||||
|
||||
// apiclient
|
||||
var apiclient = require('jellyfin-apiclient');
|
||||
|
||||
_define('apiclient', function () {
|
||||
return apiclient.ApiClient;
|
||||
});
|
||||
|
||||
_define('events', function () {
|
||||
return apiclient.Events;
|
||||
});
|
||||
|
||||
_define('credentialprovider', function () {
|
||||
return apiclient.Credentials;
|
||||
});
|
||||
|
||||
_define('connectionManagerFactory', function () {
|
||||
return apiclient.ConnectionManager;
|
||||
});
|
||||
|
||||
_define('appStorage', function () {
|
||||
return apiclient.AppStorage;
|
||||
var jellyfin_noto = require("jellyfin-noto");
|
||||
_define("jellyfin-noto", function () {
|
||||
return jellyfin_noto;
|
||||
});
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
/* eslint-disable indent */
|
||||
|
||||
/**
|
||||
* Module for controlling user parental control from.
|
||||
* @module components/accessSchedule/accessSchedule
|
||||
*/
|
||||
|
||||
import dialogHelper from 'dialogHelper';
|
||||
import datetime from 'datetime';
|
||||
import globalize from 'globalize';
|
||||
import 'emby-select';
|
||||
import 'paper-icon-button-light';
|
||||
import 'formDialogStyle';
|
||||
|
||||
function getDisplayTime(hours) {
|
||||
let minutes = 0;
|
||||
const pct = hours % 1;
|
||||
|
||||
if (pct) {
|
||||
minutes = parseInt(60 * pct);
|
||||
}
|
||||
|
||||
return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0));
|
||||
}
|
||||
|
||||
function populateHours(context) {
|
||||
let html = '';
|
||||
|
||||
for (let i = 0; i < 24; i++) {
|
||||
html += `<option value="${i}">${getDisplayTime(i)}</option>`;
|
||||
}
|
||||
|
||||
html += `<option value="24">${getDisplayTime(0)}</option>`;
|
||||
context.querySelector('#selectStart').innerHTML = html;
|
||||
context.querySelector('#selectEnd').innerHTML = html;
|
||||
}
|
||||
|
||||
function loadSchedule(context, {DayOfWeek, StartHour, EndHour}) {
|
||||
context.querySelector('#selectDay').value = DayOfWeek || 'Sunday';
|
||||
context.querySelector('#selectStart').value = StartHour || 0;
|
||||
context.querySelector('#selectEnd').value = EndHour || 0;
|
||||
}
|
||||
|
||||
function submitSchedule(context, options) {
|
||||
const updatedSchedule = {
|
||||
DayOfWeek: context.querySelector('#selectDay').value,
|
||||
StartHour: context.querySelector('#selectStart').value,
|
||||
EndHour: context.querySelector('#selectEnd').value
|
||||
};
|
||||
|
||||
if (parseFloat(updatedSchedule.StartHour) >= parseFloat(updatedSchedule.EndHour)) {
|
||||
return void alert(globalize.translate('ErrorMessageStartHourGreaterThanEnd'));
|
||||
}
|
||||
|
||||
context.submitted = true;
|
||||
options.schedule = Object.assign(options.schedule, updatedSchedule);
|
||||
dialogHelper.close(context);
|
||||
}
|
||||
|
||||
export function show(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// TODO: remove require
|
||||
require(['text!./components/accessSchedule/accessSchedule.template.html'], template => {
|
||||
const dlg = dialogHelper.createDialog({
|
||||
removeOnClose: true,
|
||||
size: 'small'
|
||||
});
|
||||
dlg.classList.add('formDialog');
|
||||
let html = '';
|
||||
html += globalize.translateDocument(template);
|
||||
dlg.innerHTML = html;
|
||||
populateHours(dlg);
|
||||
loadSchedule(dlg, options.schedule);
|
||||
dialogHelper.open(dlg);
|
||||
dlg.addEventListener('close', () => {
|
||||
if (dlg.submitted) {
|
||||
resolve(options.schedule);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
dlg.querySelector('.btnCancel').addEventListener('click', () => {
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
dlg.querySelector('form').addEventListener('submit', event => {
|
||||
submitSchedule(dlg, options);
|
||||
event.preventDefault();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
|
||||
export default {
|
||||
show: show
|
||||
};
|
||||
89
src/components/accessschedule/accessschedule.js
Normal file
89
src/components/accessschedule/accessschedule.js
Normal file
@@ -0,0 +1,89 @@
|
||||
define(["dialogHelper", "datetime", "emby-select", "paper-icon-button-light", "formDialogStyle"], function (dialogHelper, datetime) {
|
||||
"use strict";
|
||||
|
||||
function getDisplayTime(hours) {
|
||||
var minutes = 0;
|
||||
var pct = hours % 1;
|
||||
|
||||
if (pct) {
|
||||
minutes = parseInt(60 * pct);
|
||||
}
|
||||
|
||||
return datetime.getDisplayTime(new Date(2000, 1, 1, hours, minutes, 0, 0));
|
||||
}
|
||||
|
||||
function populateHours(context) {
|
||||
var html = "";
|
||||
|
||||
for (var i = 0; i < 24; i++) {
|
||||
html += '<option value="' + i + '">' + getDisplayTime(i) + "</option>";
|
||||
}
|
||||
|
||||
html += '<option value="24">' + getDisplayTime(0) + "</option>";
|
||||
context.querySelector("#selectStart").innerHTML = html;
|
||||
context.querySelector("#selectEnd").innerHTML = html;
|
||||
}
|
||||
|
||||
function loadSchedule(context, schedule) {
|
||||
context.querySelector("#selectDay").value = schedule.DayOfWeek || "Sunday";
|
||||
context.querySelector("#selectStart").value = schedule.StartHour || 0;
|
||||
context.querySelector("#selectEnd").value = schedule.EndHour || 0;
|
||||
}
|
||||
|
||||
function submitSchedule(context, options) {
|
||||
var updatedSchedule = {
|
||||
DayOfWeek: context.querySelector("#selectDay").value,
|
||||
StartHour: context.querySelector("#selectStart").value,
|
||||
EndHour: context.querySelector("#selectEnd").value
|
||||
};
|
||||
|
||||
if (parseFloat(updatedSchedule.StartHour) >= parseFloat(updatedSchedule.EndHour)) {
|
||||
return void alert(Globalize.translate("ErrorMessageStartHourGreaterThanEnd"));
|
||||
}
|
||||
|
||||
context.submitted = true;
|
||||
options.schedule = Object.assign(options.schedule, updatedSchedule);
|
||||
dialogHelper.close(context);
|
||||
}
|
||||
|
||||
return {
|
||||
show: function (options) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", "components/accessschedule/accessschedule.template.html", true);
|
||||
|
||||
xhr.onload = function (e) {
|
||||
var template = this.response;
|
||||
var dlg = dialogHelper.createDialog({
|
||||
removeOnClose: true,
|
||||
size: "small"
|
||||
});
|
||||
dlg.classList.add("formDialog");
|
||||
var html = "";
|
||||
html += Globalize.translateDocument(template);
|
||||
dlg.innerHTML = html;
|
||||
populateHours(dlg);
|
||||
loadSchedule(dlg, options.schedule);
|
||||
dialogHelper.open(dlg);
|
||||
dlg.addEventListener("close", function () {
|
||||
if (dlg.submitted) {
|
||||
resolve(options.schedule);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
});
|
||||
dlg.querySelector(".btnCancel").addEventListener("click", function (e) {
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
dlg.querySelector("form").addEventListener("submit", function (e) {
|
||||
submitSchedule(dlg, options);
|
||||
e.preventDefault();
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="formDialogHeader">
|
||||
<button is="paper-icon-button-light" class="btnCancel autoSize" title="${LabelPrevious}" tabindex="-1">
|
||||
<span class="material-icons arrow_back" aria-hidden="true"></span>
|
||||
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1">
|
||||
<i class="material-icons arrow_back"></i>
|
||||
</button>
|
||||
<h3 class="formDialogHeaderTitle">
|
||||
${HeaderAccessSchedule}
|
||||
@@ -1,340 +0,0 @@
|
||||
import dialogHelper from 'dialogHelper';
|
||||
import layoutManager from 'layoutManager';
|
||||
import globalize from 'globalize';
|
||||
import dom from 'dom';
|
||||
import 'emby-button';
|
||||
import 'css!./actionSheet';
|
||||
import 'material-icons';
|
||||
import 'scrollStyles';
|
||||
import 'listViewStyle';
|
||||
|
||||
function getOffsets(elems) {
|
||||
|
||||
let results = [];
|
||||
|
||||
if (!document) {
|
||||
return results;
|
||||
}
|
||||
|
||||
for (const elem of elems) {
|
||||
let box = elem.getBoundingClientRect();
|
||||
|
||||
results.push({
|
||||
top: box.top,
|
||||
left: box.left,
|
||||
width: box.width,
|
||||
height: box.height
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function getPosition(options, dlg) {
|
||||
|
||||
const windowSize = dom.getWindowSize();
|
||||
const windowHeight = windowSize.innerHeight;
|
||||
const windowWidth = windowSize.innerWidth;
|
||||
|
||||
let pos = getOffsets([options.positionTo])[0];
|
||||
|
||||
if (options.positionY !== 'top') {
|
||||
pos.top += (pos.height || 0) / 2;
|
||||
}
|
||||
|
||||
pos.left += (pos.width || 0) / 2;
|
||||
|
||||
const height = dlg.offsetHeight || 300;
|
||||
const width = dlg.offsetWidth || 160;
|
||||
|
||||
// Account for popup size
|
||||
pos.top -= height / 2;
|
||||
pos.left -= width / 2;
|
||||
|
||||
// Avoid showing too close to the bottom
|
||||
const overflowX = pos.left + width - windowWidth;
|
||||
const overflowY = pos.top + height - windowHeight;
|
||||
|
||||
if (overflowX > 0) {
|
||||
pos.left -= (overflowX + 20);
|
||||
}
|
||||
if (overflowY > 0) {
|
||||
pos.top -= (overflowY + 20);
|
||||
}
|
||||
|
||||
pos.top += (options.offsetTop || 0);
|
||||
pos.left += (options.offsetLeft || 0);
|
||||
|
||||
// Do some boundary checking
|
||||
pos.top = Math.max(pos.top, 10);
|
||||
pos.left = Math.max(pos.left, 10);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
require(['scrollHelper'], function (scrollHelper) {
|
||||
const fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
}
|
||||
|
||||
export function show(options) {
|
||||
|
||||
// items
|
||||
// positionTo
|
||||
// showCancel
|
||||
// title
|
||||
let dialogOptions = {
|
||||
removeOnClose: true,
|
||||
enableHistory: options.enableHistory,
|
||||
scrollY: false
|
||||
};
|
||||
|
||||
let isFullscreen;
|
||||
|
||||
if (layoutManager.tv) {
|
||||
dialogOptions.size = 'fullscreen';
|
||||
isFullscreen = true;
|
||||
dialogOptions.autoFocus = true;
|
||||
} else {
|
||||
|
||||
dialogOptions.modal = false;
|
||||
dialogOptions.entryAnimation = options.entryAnimation;
|
||||
dialogOptions.exitAnimation = options.exitAnimation;
|
||||
dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140;
|
||||
dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100;
|
||||
dialogOptions.autoFocus = false;
|
||||
}
|
||||
|
||||
let dlg = dialogHelper.createDialog(dialogOptions);
|
||||
|
||||
if (isFullscreen) {
|
||||
dlg.classList.add('actionsheet-fullscreen');
|
||||
} else {
|
||||
dlg.classList.add('actionsheet-not-fullscreen');
|
||||
}
|
||||
|
||||
dlg.classList.add('actionSheet');
|
||||
|
||||
if (options.dialogClass) {
|
||||
dlg.classList.add(options.dialogClass);
|
||||
}
|
||||
|
||||
let html = '';
|
||||
|
||||
const scrollClassName = layoutManager.tv ? 'scrollY smoothScrollY hiddenScrollY' : 'scrollY';
|
||||
let style = '';
|
||||
|
||||
// Admittedly a hack but right now the scrollbar is being factored into the width which is causing truncation
|
||||
if (options.items.length > 20) {
|
||||
const minWidth = dom.getWindowSize().innerWidth >= 300 ? 240 : 200;
|
||||
style += 'min-width:' + minWidth + 'px;';
|
||||
}
|
||||
|
||||
let renderIcon = false;
|
||||
let icons = [];
|
||||
let itemIcon;
|
||||
for (const item of options.items) {
|
||||
|
||||
itemIcon = item.icon || (item.selected ? 'check' : null);
|
||||
|
||||
if (itemIcon) {
|
||||
renderIcon = true;
|
||||
}
|
||||
icons.push(itemIcon || '');
|
||||
}
|
||||
|
||||
if (layoutManager.tv) {
|
||||
html += `<button is="paper-icon-button-light" class="btnCloseActionSheet hide-mouse-idle-tv" tabindex="-1">
|
||||
<span class="material-icons arrow_back"></span>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
// If any items have an icon, give them all an icon just to make sure they're all lined up evenly
|
||||
const center = options.title && (!renderIcon /*|| itemsWithIcons.length != options.items.length*/);
|
||||
|
||||
if (center || layoutManager.tv) {
|
||||
html += '<div class="actionSheetContent actionSheetContent-centered">';
|
||||
} else {
|
||||
html += '<div class="actionSheetContent">';
|
||||
}
|
||||
|
||||
if (options.title) {
|
||||
|
||||
html += '<h1 class="actionSheetTitle">' + options.title + '</h1>';
|
||||
}
|
||||
if (options.text) {
|
||||
html += '<p class="actionSheetText">' + options.text + '</p>';
|
||||
}
|
||||
|
||||
let scrollerClassName = 'actionSheetScroller';
|
||||
if (layoutManager.tv) {
|
||||
scrollerClassName += ' actionSheetScroller-tv focuscontainer-x focuscontainer-y';
|
||||
}
|
||||
html += '<div class="' + scrollerClassName + ' ' + scrollClassName + '" style="' + style + '">';
|
||||
|
||||
let menuItemClass = 'listItem listItem-button actionSheetMenuItem';
|
||||
|
||||
if (options.border || options.shaded) {
|
||||
menuItemClass += ' listItem-border';
|
||||
}
|
||||
|
||||
if (options.menuItemClass) {
|
||||
menuItemClass += ' ' + options.menuItemClass;
|
||||
}
|
||||
|
||||
if (layoutManager.tv) {
|
||||
menuItemClass += ' listItem-focusscale';
|
||||
}
|
||||
|
||||
if (layoutManager.mobile) {
|
||||
menuItemClass += ' actionsheet-xlargeFont';
|
||||
}
|
||||
|
||||
// 'options.items' is HTMLOptionsCollection, so no fancy loops
|
||||
for (let i = 0; i < options.items.length; i++) {
|
||||
const item = options.items[i];
|
||||
|
||||
if (item.divider) {
|
||||
|
||||
html += '<div class="actionsheetDivider"></div>';
|
||||
continue;
|
||||
}
|
||||
|
||||
const autoFocus = item.selected && layoutManager.tv ? ' autoFocus' : '';
|
||||
|
||||
// Check for null in case int 0 was passed in
|
||||
const optionId = item.id == null || item.id === '' ? item.value : item.id;
|
||||
html += '<button' + autoFocus + ' is="emby-button" type="button" class="' + menuItemClass + '" data-id="' + optionId + '">';
|
||||
|
||||
itemIcon = icons[i];
|
||||
|
||||
if (itemIcon) {
|
||||
html += `<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons ${itemIcon}"></span>`;
|
||||
} else if (renderIcon && !center) {
|
||||
html += '<span class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons check" style="visibility:hidden;"></span>';
|
||||
}
|
||||
|
||||
html += '<div class="listItemBody actionsheetListItemBody">';
|
||||
|
||||
html += '<div class="listItemBodyText actionSheetItemText">';
|
||||
html += (item.name || item.textContent || item.innerText);
|
||||
html += '</div>';
|
||||
|
||||
if (item.secondaryText) {
|
||||
html += `<div class="listItemBodyText secondary">${item.secondaryText}</div>`;
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
if (item.asideText) {
|
||||
html += `<div class="listItemAside actionSheetItemAsideText">${item.asideText}</div>`;
|
||||
}
|
||||
|
||||
html += '</button>';
|
||||
}
|
||||
|
||||
if (options.showCancel) {
|
||||
html += '<div class="buttons">';
|
||||
html += `<button is="emby-button" type="button" class="btnCloseActionSheet">${globalize.translate('ButtonCancel')}</button>`;
|
||||
html += '</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
dlg.innerHTML = html;
|
||||
|
||||
if (layoutManager.tv) {
|
||||
centerFocus(dlg.querySelector('.actionSheetScroller'), false, true);
|
||||
}
|
||||
|
||||
let btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet');
|
||||
if (btnCloseActionSheet) {
|
||||
btnCloseActionSheet.addEventListener('click', function () {
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
}
|
||||
|
||||
// Seeing an issue in some non-chrome browsers where this is requiring a double click
|
||||
//var eventName = browser.firefox ? 'mousedown' : 'click';
|
||||
let selectedId;
|
||||
|
||||
let timeout;
|
||||
if (options.timeout) {
|
||||
timeout = setTimeout(function () {
|
||||
dialogHelper.close(dlg);
|
||||
}, options.timeout);
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
let isResolved;
|
||||
|
||||
dlg.addEventListener('click', function (e) {
|
||||
|
||||
const actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem');
|
||||
|
||||
if (actionSheetMenuItem) {
|
||||
selectedId = actionSheetMenuItem.getAttribute('data-id');
|
||||
|
||||
if (options.resolveOnClick) {
|
||||
|
||||
if (options.resolveOnClick.indexOf) {
|
||||
|
||||
if (options.resolveOnClick.indexOf(selectedId) !== -1) {
|
||||
|
||||
resolve(selectedId);
|
||||
isResolved = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
resolve(selectedId);
|
||||
isResolved = true;
|
||||
}
|
||||
}
|
||||
|
||||
dialogHelper.close(dlg);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
dlg.addEventListener('close', function () {
|
||||
|
||||
if (layoutManager.tv) {
|
||||
centerFocus(dlg.querySelector('.actionSheetScroller'), false, false);
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
|
||||
if (!isResolved) {
|
||||
if (selectedId != null) {
|
||||
if (options.callback) {
|
||||
options.callback(selectedId);
|
||||
}
|
||||
|
||||
resolve(selectedId);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialogHelper.open(dlg);
|
||||
|
||||
const pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options, dlg) : null;
|
||||
|
||||
if (pos) {
|
||||
dlg.style.position = 'fixed';
|
||||
dlg.style.margin = 0;
|
||||
dlg.style.left = pos.left + 'px';
|
||||
dlg.style.top = pos.top + 'px';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
show: show
|
||||
};
|
||||
360
src/components/actionsheet/actionsheet.js
Normal file
360
src/components/actionsheet/actionsheet.js
Normal file
@@ -0,0 +1,360 @@
|
||||
define(['dialogHelper', 'layoutManager', 'globalize', 'browser', 'dom', 'emby-button', 'css!./actionsheet', 'material-icons', 'scrollStyles', 'listViewStyle'], function (dialogHelper, layoutManager, globalize, browser, dom) {
|
||||
'use strict';
|
||||
|
||||
function getOffsets(elems) {
|
||||
|
||||
var doc = document;
|
||||
var results = [];
|
||||
|
||||
if (!doc) {
|
||||
return results;
|
||||
}
|
||||
|
||||
var box;
|
||||
var elem;
|
||||
|
||||
for (var i = 0, length = elems.length; i < length; i++) {
|
||||
|
||||
elem = elems[i];
|
||||
// Support: BlackBerry 5, iOS 3 (original iPhone)
|
||||
// If we don't have gBCR, just use 0,0 rather than error
|
||||
if (elem.getBoundingClientRect) {
|
||||
box = elem.getBoundingClientRect();
|
||||
} else {
|
||||
box = { top: 0, left: 0 };
|
||||
}
|
||||
|
||||
results[i] = {
|
||||
top: box.top,
|
||||
left: box.left,
|
||||
width: box.width,
|
||||
height: box.height
|
||||
};
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
function getPosition(options, dlg) {
|
||||
|
||||
var windowSize = dom.getWindowSize();
|
||||
var windowHeight = windowSize.innerHeight;
|
||||
var windowWidth = windowSize.innerWidth;
|
||||
|
||||
var pos = getOffsets([options.positionTo])[0];
|
||||
|
||||
if (options.positionY !== 'top') {
|
||||
pos.top += (pos.height || 0) / 2;
|
||||
}
|
||||
|
||||
pos.left += (pos.width || 0) / 2;
|
||||
|
||||
var height = dlg.offsetHeight || 300;
|
||||
var width = dlg.offsetWidth || 160;
|
||||
|
||||
// Account for popup size
|
||||
pos.top -= height / 2;
|
||||
pos.left -= width / 2;
|
||||
|
||||
// Avoid showing too close to the bottom
|
||||
var overflowX = pos.left + width - windowWidth;
|
||||
var overflowY = pos.top + height - windowHeight;
|
||||
|
||||
if (overflowX > 0) {
|
||||
pos.left -= (overflowX + 20);
|
||||
}
|
||||
if (overflowY > 0) {
|
||||
pos.top -= (overflowY + 20);
|
||||
}
|
||||
|
||||
pos.top += (options.offsetTop || 0);
|
||||
pos.left += (options.offsetLeft || 0);
|
||||
|
||||
// Do some boundary checking
|
||||
pos.top = Math.max(pos.top, 10);
|
||||
pos.left = Math.max(pos.left, 10);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
function centerFocus(elem, horiz, on) {
|
||||
require(['scrollHelper'], function (scrollHelper) {
|
||||
var fn = on ? 'on' : 'off';
|
||||
scrollHelper.centerFocus[fn](elem, horiz);
|
||||
});
|
||||
}
|
||||
|
||||
function show(options) {
|
||||
|
||||
// items
|
||||
// positionTo
|
||||
// showCancel
|
||||
// title
|
||||
var dialogOptions = {
|
||||
removeOnClose: true,
|
||||
enableHistory: options.enableHistory,
|
||||
scrollY: false
|
||||
};
|
||||
|
||||
var backButton = false;
|
||||
var isFullscreen;
|
||||
|
||||
if (layoutManager.tv) {
|
||||
dialogOptions.size = 'fullscreen';
|
||||
isFullscreen = true;
|
||||
backButton = true;
|
||||
dialogOptions.autoFocus = true;
|
||||
} else {
|
||||
|
||||
dialogOptions.modal = false;
|
||||
dialogOptions.entryAnimation = options.entryAnimation;
|
||||
dialogOptions.exitAnimation = options.exitAnimation;
|
||||
dialogOptions.entryAnimationDuration = options.entryAnimationDuration || 140;
|
||||
dialogOptions.exitAnimationDuration = options.exitAnimationDuration || 100;
|
||||
dialogOptions.autoFocus = false;
|
||||
}
|
||||
|
||||
var dlg = dialogHelper.createDialog(dialogOptions);
|
||||
|
||||
if (isFullscreen) {
|
||||
dlg.classList.add('actionsheet-fullscreen');
|
||||
} else {
|
||||
dlg.classList.add('actionsheet-not-fullscreen');
|
||||
}
|
||||
|
||||
dlg.classList.add('actionSheet');
|
||||
|
||||
if (options.dialogClass) {
|
||||
dlg.classList.add(options.dialogClass);
|
||||
}
|
||||
|
||||
var html = '';
|
||||
|
||||
var scrollClassName = layoutManager.tv ? 'scrollY smoothScrollY hiddenScrollY' : 'scrollY';
|
||||
var style = '';
|
||||
|
||||
// Admittedly a hack but right now the scrollbar is being factored into the width which is causing truncation
|
||||
if (options.items.length > 20) {
|
||||
var minWidth = dom.getWindowSize().innerWidth >= 300 ? 240 : 200;
|
||||
style += "min-width:" + minWidth + "px;";
|
||||
}
|
||||
|
||||
var i;
|
||||
var length;
|
||||
var option;
|
||||
var renderIcon = false;
|
||||
var icons = [];
|
||||
var itemIcon;
|
||||
for (i = 0, length = options.items.length; i < length; i++) {
|
||||
|
||||
option = options.items[i];
|
||||
|
||||
itemIcon = option.icon || (option.selected ? 'check' : null);
|
||||
|
||||
if (itemIcon) {
|
||||
renderIcon = true;
|
||||
}
|
||||
icons.push(itemIcon || '');
|
||||
}
|
||||
|
||||
if (layoutManager.tv) {
|
||||
html += '<button is="paper-icon-button-light" class="btnCloseActionSheet hide-mouse-idle-tv" tabindex="-1"><i class="material-icons arrow_back"></i></button>';
|
||||
}
|
||||
|
||||
// If any items have an icon, give them all an icon just to make sure they're all lined up evenly
|
||||
var center = options.title && (!renderIcon /*|| itemsWithIcons.length != options.items.length*/);
|
||||
|
||||
if (center || layoutManager.tv) {
|
||||
html += '<div class="actionSheetContent actionSheetContent-centered">';
|
||||
} else {
|
||||
html += '<div class="actionSheetContent">';
|
||||
}
|
||||
|
||||
if (options.title) {
|
||||
|
||||
html += '<h1 class="actionSheetTitle">';
|
||||
html += options.title;
|
||||
html += '</h1>';
|
||||
}
|
||||
if (options.text) {
|
||||
html += '<p class="actionSheetText">';
|
||||
html += options.text;
|
||||
html += '</p>';
|
||||
}
|
||||
|
||||
var scrollerClassName = 'actionSheetScroller';
|
||||
if (layoutManager.tv) {
|
||||
scrollerClassName += ' actionSheetScroller-tv focuscontainer-x focuscontainer-y';
|
||||
}
|
||||
html += '<div class="' + scrollerClassName + ' ' + scrollClassName + '" style="' + style + '">';
|
||||
|
||||
var menuItemClass = 'listItem listItem-button actionSheetMenuItem';
|
||||
|
||||
if (options.border || options.shaded) {
|
||||
menuItemClass += ' listItem-border';
|
||||
}
|
||||
|
||||
if (options.menuItemClass) {
|
||||
menuItemClass += ' ' + options.menuItemClass;
|
||||
}
|
||||
|
||||
if (layoutManager.tv) {
|
||||
menuItemClass += ' listItem-focusscale';
|
||||
}
|
||||
|
||||
if (layoutManager.mobile) {
|
||||
menuItemClass += ' actionsheet-xlargeFont';
|
||||
}
|
||||
|
||||
for (i = 0, length = options.items.length; i < length; i++) {
|
||||
|
||||
option = options.items[i];
|
||||
|
||||
if (option.divider) {
|
||||
|
||||
html += '<div class="actionsheetDivider"></div>';
|
||||
continue;
|
||||
}
|
||||
|
||||
var autoFocus = option.selected && layoutManager.tv ? ' autoFocus' : '';
|
||||
|
||||
// Check for null in case int 0 was passed in
|
||||
var optionId = option.id == null || option.id === '' ? option.value : option.id;
|
||||
html += '<button' + autoFocus + ' is="emby-button" type="button" class="' + menuItemClass + '" data-id="' + optionId + '">';
|
||||
|
||||
itemIcon = icons[i];
|
||||
|
||||
if (itemIcon) {
|
||||
|
||||
html += '<i class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons">' + itemIcon + '</i>';
|
||||
} else if (renderIcon && !center) {
|
||||
html += '<i class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent material-icons" style="visibility:hidden;">check</i>';
|
||||
}
|
||||
|
||||
html += '<div class="listItemBody actionsheetListItemBody">';
|
||||
|
||||
html += '<div class="listItemBodyText actionSheetItemText">';
|
||||
html += (option.name || option.textContent || option.innerText);
|
||||
html += '</div>';
|
||||
|
||||
if (option.secondaryText) {
|
||||
html += '<div class="listItemBodyText secondary">';
|
||||
html += option.secondaryText;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
if (option.asideText) {
|
||||
html += '<div class="listItemAside actionSheetItemAsideText">';
|
||||
html += option.asideText;
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
html += '</button>';
|
||||
}
|
||||
|
||||
if (options.showCancel) {
|
||||
html += '<div class="buttons">';
|
||||
html += '<button is="emby-button" type="button" class="btnCloseActionSheet">' + globalize.translate('ButtonCancel') + '</button>';
|
||||
html += '</div>';
|
||||
}
|
||||
html += '</div>';
|
||||
|
||||
dlg.innerHTML = html;
|
||||
|
||||
if (layoutManager.tv) {
|
||||
centerFocus(dlg.querySelector('.actionSheetScroller'), false, true);
|
||||
}
|
||||
|
||||
var btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet');
|
||||
if (btnCloseActionSheet) {
|
||||
dlg.querySelector('.btnCloseActionSheet').addEventListener('click', function () {
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
}
|
||||
|
||||
// Seeing an issue in some non-chrome browsers where this is requiring a double click
|
||||
//var eventName = browser.firefox ? 'mousedown' : 'click';
|
||||
var selectedId;
|
||||
|
||||
var timeout;
|
||||
if (options.timeout) {
|
||||
timeout = setTimeout(function () {
|
||||
dialogHelper.close(dlg);
|
||||
}, options.timeout);
|
||||
}
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
var isResolved;
|
||||
|
||||
dlg.addEventListener('click', function (e) {
|
||||
|
||||
var actionSheetMenuItem = dom.parentWithClass(e.target, 'actionSheetMenuItem');
|
||||
|
||||
if (actionSheetMenuItem) {
|
||||
selectedId = actionSheetMenuItem.getAttribute('data-id');
|
||||
|
||||
if (options.resolveOnClick) {
|
||||
|
||||
if (options.resolveOnClick.indexOf) {
|
||||
|
||||
if (options.resolveOnClick.indexOf(selectedId) !== -1) {
|
||||
|
||||
resolve(selectedId);
|
||||
isResolved = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
resolve(selectedId);
|
||||
isResolved = true;
|
||||
}
|
||||
}
|
||||
|
||||
dialogHelper.close(dlg);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
dlg.addEventListener('close', function () {
|
||||
|
||||
if (layoutManager.tv) {
|
||||
centerFocus(dlg.querySelector('.actionSheetScroller'), false, false);
|
||||
}
|
||||
|
||||
if (timeout) {
|
||||
clearTimeout(timeout);
|
||||
timeout = null;
|
||||
}
|
||||
|
||||
if (!isResolved) {
|
||||
if (selectedId != null) {
|
||||
if (options.callback) {
|
||||
options.callback(selectedId);
|
||||
}
|
||||
|
||||
resolve(selectedId);
|
||||
} else {
|
||||
reject();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialogHelper.open(dlg);
|
||||
|
||||
var pos = options.positionTo && dialogOptions.size !== 'fullscreen' ? getPosition(options, dlg) : null;
|
||||
|
||||
if (pos) {
|
||||
dlg.style.position = 'fixed';
|
||||
dlg.style.margin = 0;
|
||||
dlg.style.left = pos.left + 'px';
|
||||
dlg.style.top = pos.top + 'px';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
show: show
|
||||
};
|
||||
});
|
||||
@@ -1,63 +1,60 @@
|
||||
define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings', 'serverNotifications', 'connectionManager', 'emby-button', 'listViewStyle'], function (events, globalize, dom, datefns, dfnshelper, userSettings, serverNotifications, connectionManager) {
|
||||
'use strict';
|
||||
define(["events", "globalize", "dom", "datetime", "userSettings", "serverNotifications", "connectionManager", "emby-button", "listViewStyle"], function (events, globalize, dom, datetime, userSettings, serverNotifications, connectionManager) {
|
||||
"use strict";
|
||||
|
||||
function getEntryHtml(entry, apiClient) {
|
||||
var html = '';
|
||||
var html = "";
|
||||
html += '<div class="listItem listItem-border">';
|
||||
var color = '#00a4dc';
|
||||
var icon = 'notifications';
|
||||
var color = "#00a4dc";
|
||||
var icon = "notifications";
|
||||
|
||||
if ('Error' == entry.Severity || 'Fatal' == entry.Severity || 'Warn' == entry.Severity) {
|
||||
color = '#cc0000';
|
||||
icon = 'notification_important';
|
||||
if ("Error" == entry.Severity || "Fatal" == entry.Severity || "Warn" == entry.Severity) {
|
||||
color = "#cc0000";
|
||||
icon = "notification_important";
|
||||
}
|
||||
|
||||
if (entry.UserId && entry.UserPrimaryImageTag) {
|
||||
html += '<span class="listItemIcon material-icons dvr" style="width:2em!important;height:2em!important;padding:0;color:transparent;background-color:' + color + ";background-image:url('" + apiClient.getUserImageUrl(entry.UserId, {
|
||||
type: 'Primary',
|
||||
html += '<i class="listItemIcon material-icons" style="width:2em!important;height:2em!important;padding:0;color:transparent;background-color:' + color + ";background-image:url('" + apiClient.getUserImageUrl(entry.UserId, {
|
||||
type: "Primary",
|
||||
tag: entry.UserPrimaryImageTag
|
||||
}) + "');background-repeat:no-repeat;background-position:center center;background-size: cover;\"></span>";
|
||||
}) + "');background-repeat:no-repeat;background-position:center center;background-size: cover;\">dvr</i>"
|
||||
} else {
|
||||
html += '<span class="listItemIcon material-icons ' + icon + '" style="background-color:' + color + '"></span>';
|
||||
html += '<i class="listItemIcon material-icons" style="background-color:' + color + '">' + icon + '</i>';
|
||||
}
|
||||
|
||||
html += '<div class="listItemBody three-line">';
|
||||
html += '<div class="listItemBodyText">';
|
||||
html += entry.Name;
|
||||
html += '</div>';
|
||||
html += "</div>";
|
||||
html += '<div class="listItemBodyText secondary">';
|
||||
html += datefns.formatRelative(Date.parse(entry.Date), Date.parse(new Date()), { locale: dfnshelper.getLocale() });
|
||||
html += '</div>';
|
||||
var date = datetime.parseISO8601Date(entry.Date, true);
|
||||
html += datetime.toLocaleString(date).toLowerCase();
|
||||
html += "</div>";
|
||||
html += '<div class="listItemBodyText secondary listItemBodyText-nowrap">';
|
||||
html += entry.ShortOverview || '';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += entry.ShortOverview || "";
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
|
||||
if (entry.Overview) {
|
||||
html += `<button type="button" is="paper-icon-button-light" class="btnEntryInfo" data-id="${entry.Id}" title="${globalize.translate('Info')}">
|
||||
<span class="material-icons info"></span>
|
||||
</button>`;
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnEntryInfo" data-id="' + entry.Id + '" title="' + globalize.translate("Info") + '"><i class="material-icons">info</i></button>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
return html += "</div>";
|
||||
}
|
||||
|
||||
function renderList(elem, apiClient, result, startIndex, limit) {
|
||||
elem.innerHTML = result.Items.map(function (i) {
|
||||
return getEntryHtml(i, apiClient);
|
||||
}).join('');
|
||||
}).join("");
|
||||
}
|
||||
|
||||
function reloadData(instance, elem, apiClient, startIndex, limit) {
|
||||
if (null == startIndex) {
|
||||
startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0');
|
||||
startIndex = parseInt(elem.getAttribute("data-activitystartindex") || "0");
|
||||
}
|
||||
|
||||
limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7');
|
||||
limit = limit || parseInt(elem.getAttribute("data-activitylimit") || "7");
|
||||
var minDate = new Date();
|
||||
var hasUserId = 'false' !== elem.getAttribute('data-useractivity');
|
||||
var hasUserId = "false" !== elem.getAttribute("data-useractivity");
|
||||
|
||||
if (hasUserId) {
|
||||
minDate.setTime(minDate.getTime() - 24 * 60 * 60 * 1000); // one day back
|
||||
@@ -65,22 +62,22 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
|
||||
minDate.setTime(minDate.getTime() - 7 * 24 * 60 * 60 * 1000); // one week back
|
||||
}
|
||||
|
||||
ApiClient.getJSON(ApiClient.getUrl('System/ActivityLog/Entries', {
|
||||
ApiClient.getJSON(ApiClient.getUrl("System/ActivityLog/Entries", {
|
||||
startIndex: startIndex,
|
||||
limit: limit,
|
||||
minDate: minDate.toISOString(),
|
||||
hasUserId: hasUserId
|
||||
})).then(function (result) {
|
||||
elem.setAttribute('data-activitystartindex', startIndex);
|
||||
elem.setAttribute('data-activitylimit', limit);
|
||||
elem.setAttribute("data-activitystartindex", startIndex);
|
||||
elem.setAttribute("data-activitylimit", limit);
|
||||
if (!startIndex) {
|
||||
var activityContainer = dom.parentWithClass(elem, 'activityContainer');
|
||||
var activityContainer = dom.parentWithClass(elem, "activityContainer");
|
||||
|
||||
if (activityContainer) {
|
||||
if (result.Items.length) {
|
||||
activityContainer.classList.remove('hide');
|
||||
activityContainer.classList.remove("hide");
|
||||
} else {
|
||||
activityContainer.classList.add('hide');
|
||||
activityContainer.classList.add("hide");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -99,10 +96,10 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
|
||||
}
|
||||
|
||||
function onListClick(e) {
|
||||
var btnEntryInfo = dom.parentWithClass(e.target, 'btnEntryInfo');
|
||||
var btnEntryInfo = dom.parentWithClass(e.target, "btnEntryInfo");
|
||||
|
||||
if (btnEntryInfo) {
|
||||
var id = btnEntryInfo.getAttribute('data-id');
|
||||
var id = btnEntryInfo.getAttribute("data-id");
|
||||
var items = this.items;
|
||||
|
||||
if (items) {
|
||||
@@ -118,7 +115,7 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
|
||||
}
|
||||
|
||||
function showItemOverview(item) {
|
||||
require(['alert'], function (alert) {
|
||||
require(["alert"], function (alert) {
|
||||
alert({
|
||||
text: item.Overview
|
||||
});
|
||||
@@ -128,28 +125,28 @@ define(['events', 'globalize', 'dom', 'date-fns', 'dfnshelper', 'userSettings',
|
||||
function ActivityLog(options) {
|
||||
this.options = options;
|
||||
var element = options.element;
|
||||
element.classList.add('activityLogListWidget');
|
||||
element.addEventListener('click', onListClick.bind(this));
|
||||
element.classList.add("activityLogListWidget");
|
||||
element.addEventListener("click", onListClick.bind(this));
|
||||
var apiClient = connectionManager.getApiClient(options.serverId);
|
||||
reloadData(this, element, apiClient);
|
||||
var onUpdate = onActivityLogUpdate.bind(this);
|
||||
this.updateFn = onUpdate;
|
||||
events.on(serverNotifications, 'ActivityLogEntry', onUpdate);
|
||||
apiClient.sendMessage('ActivityLogEntryStart', '0,1500');
|
||||
events.on(serverNotifications, "ActivityLogEntry", onUpdate);
|
||||
apiClient.sendMessage("ActivityLogEntryStart", "0,1500");
|
||||
}
|
||||
|
||||
ActivityLog.prototype.destroy = function () {
|
||||
var options = this.options;
|
||||
|
||||
if (options) {
|
||||
options.element.classList.remove('activityLogListWidget');
|
||||
connectionManager.getApiClient(options.serverId).sendMessage('ActivityLogEntryStop', '0,1500');
|
||||
options.element.classList.remove("activityLogListWidget");
|
||||
connectionManager.getApiClient(options.serverId).sendMessage("ActivityLogEntryStop", "0,1500");
|
||||
}
|
||||
|
||||
var onUpdate = this.updateFn;
|
||||
|
||||
if (onUpdate) {
|
||||
events.off(serverNotifications, 'ActivityLogEntry', onUpdate);
|
||||
events.off(serverNotifications, "ActivityLogEntry", onUpdate);
|
||||
}
|
||||
|
||||
this.items = null;
|
||||
|
||||
@@ -67,7 +67,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
|
||||
|
||||
html += '<div class="' + rowClassName + '">';
|
||||
if (options.mode === 'keyboard') {
|
||||
html += '<button data-value=" " is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><span class="material-icons alphaPickerButtonIcon space_bar"></span></button>';
|
||||
html += '<button data-value=" " is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><i class="material-icons alphaPickerButtonIcon space_bar"></i></button>';
|
||||
} else {
|
||||
letters = ['#'];
|
||||
html += mapLetters(letters, vertical).join('');
|
||||
@@ -77,7 +77,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
|
||||
html += mapLetters(letters, vertical).join('');
|
||||
|
||||
if (options.mode === 'keyboard') {
|
||||
html += '<button data-value="backspace" is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><span class="material-icons alphaPickerButtonIcon backspace"></span></button>';
|
||||
html += '<button data-value="backspace" is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><i class="material-icons alphaPickerButtonIcon">backspace</i></button>';
|
||||
html += '</div>';
|
||||
|
||||
letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
|
||||
@@ -132,7 +132,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
|
||||
if (alphaPickerButton) {
|
||||
var value = alphaPickerButton.getAttribute('data-value');
|
||||
|
||||
element.dispatchEvent(new CustomEvent('alphavalueclicked', {
|
||||
element.dispatchEvent(new CustomEvent("alphavalueclicked", {
|
||||
cancelable: false,
|
||||
detail: {
|
||||
value: value
|
||||
@@ -262,7 +262,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
|
||||
}
|
||||
|
||||
if (applyValue) {
|
||||
element.dispatchEvent(new CustomEvent('alphavaluechanged', {
|
||||
element.dispatchEvent(new CustomEvent("alphavaluechanged", {
|
||||
cancelable: false,
|
||||
detail: {
|
||||
value: value
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, skinManager, backdrop, browser, page, appSettings, appHost, connectionManager) {
|
||||
define(['loading', 'globalize', 'events', 'viewManager', 'layoutManager', 'skinManager', 'pluginManager', 'backdrop', 'browser', 'page', 'appSettings', 'apphost', 'connectionManager'], function (loading, globalize, events, viewManager, layoutManager, skinManager, pluginManager, backdrop, browser, page, appSettings, appHost, connectionManager) {
|
||||
'use strict';
|
||||
|
||||
var appRouter = {
|
||||
@@ -16,7 +16,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
show('/settings/settings.html');
|
||||
},
|
||||
showNowPlaying: function () {
|
||||
show('/nowplaying.html');
|
||||
show("/nowplaying.html");
|
||||
}
|
||||
};
|
||||
|
||||
@@ -26,11 +26,11 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
connectionManager.connect({
|
||||
enableAutoLogin: appSettings.enableAutoLogin()
|
||||
}).then(function (result) {
|
||||
handleConnectionResult(result);
|
||||
handleConnectionResult(result, loading);
|
||||
});
|
||||
}
|
||||
|
||||
function handleConnectionResult(result) {
|
||||
function handleConnectionResult(result, loading) {
|
||||
switch (result.State) {
|
||||
case 'SignedIn':
|
||||
loading.hide();
|
||||
@@ -200,8 +200,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
|
||||
var apiClient = this;
|
||||
|
||||
if (data.status === 403) {
|
||||
if (data.errorCode === 'ParentalControl') {
|
||||
if (data.status === 401) {
|
||||
if (data.errorCode === "ParentalControl") {
|
||||
|
||||
var isCurrentAllowed = currentRouteInfo ? (currentRouteInfo.route.anonymous || currentRouteInfo.route.startup) : true;
|
||||
|
||||
@@ -222,18 +222,52 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
}
|
||||
|
||||
function normalizeImageOptions(options) {
|
||||
var scaleFactor = browser.tv ? 0.8 : 1;
|
||||
|
||||
var setQuality;
|
||||
if (options.maxWidth || options.width || options.maxHeight || options.height) {
|
||||
if (options.maxWidth) {
|
||||
options.maxWidth = Math.round(options.maxWidth * scaleFactor);
|
||||
setQuality = true;
|
||||
}
|
||||
|
||||
if (setQuality && !options.quality) {
|
||||
options.quality = 90;
|
||||
if (options.width) {
|
||||
options.width = Math.round(options.width * scaleFactor);
|
||||
setQuality = true;
|
||||
}
|
||||
|
||||
if (options.maxHeight) {
|
||||
options.maxHeight = Math.round(options.maxHeight * scaleFactor);
|
||||
setQuality = true;
|
||||
}
|
||||
|
||||
if (options.height) {
|
||||
options.height = Math.round(options.height * scaleFactor);
|
||||
setQuality = true;
|
||||
}
|
||||
|
||||
if (setQuality) {
|
||||
|
||||
var quality = 100;
|
||||
|
||||
var type = options.type || 'Primary';
|
||||
|
||||
if (browser.tv || browser.slow) {
|
||||
|
||||
if (browser.chrome) {
|
||||
// webp support
|
||||
quality = type === 'Primary' ? 40 : 50;
|
||||
} else {
|
||||
quality = type === 'Backdrop' ? 60 : 50;
|
||||
}
|
||||
} else {
|
||||
quality = type === 'Backdrop' ? 70 : 90;
|
||||
}
|
||||
|
||||
options.quality = quality;
|
||||
}
|
||||
}
|
||||
|
||||
function getMaxBandwidth() {
|
||||
/* eslint-disable compat/compat */
|
||||
if (navigator.connection) {
|
||||
var max = navigator.connection.downlinkMax;
|
||||
if (max && max > 0 && max < Number.POSITIVE_INFINITY) {
|
||||
@@ -245,7 +279,6 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
return max;
|
||||
}
|
||||
}
|
||||
/* eslint-enable compat/compat */
|
||||
|
||||
return null;
|
||||
}
|
||||
@@ -337,7 +370,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
}
|
||||
|
||||
function enableNativeHistory() {
|
||||
return false;
|
||||
return page.enableNativeHistory();
|
||||
}
|
||||
|
||||
function authenticate(ctx, route, callback) {
|
||||
@@ -349,7 +382,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
|
||||
if (firstResult.State !== 'SignedIn' && !route.anonymous) {
|
||||
|
||||
handleConnectionResult(firstResult);
|
||||
handleConnectionResult(firstResult, loading);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -428,6 +461,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
var isHandlingBackToDefault;
|
||||
var isDummyBackToHome;
|
||||
|
||||
function loadContent(ctx, route, html, request) {
|
||||
@@ -477,16 +511,9 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
return baseRoute;
|
||||
}
|
||||
|
||||
var popstateOccurred = false;
|
||||
window.addEventListener('popstate', function () {
|
||||
popstateOccurred = true;
|
||||
});
|
||||
|
||||
function getHandler(route) {
|
||||
return function (ctx, next) {
|
||||
ctx.isBack = popstateOccurred;
|
||||
handleRoute(ctx, next, route);
|
||||
popstateOccurred = false;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -505,15 +532,15 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
}
|
||||
|
||||
function param(name, url) {
|
||||
name = name.replace(/[\[]/, '\\\[').replace(/[\]]/, '\\\]');
|
||||
var regexS = '[\\?&]' + name + '=([^&#]*)';
|
||||
var regex = new RegExp(regexS, 'i');
|
||||
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
|
||||
var regexS = "[\\?&]" + name + "=([^&#]*)";
|
||||
var regex = new RegExp(regexS, "i");
|
||||
|
||||
var results = regex.exec(url || getWindowLocationSearch());
|
||||
if (results == null) {
|
||||
return '';
|
||||
return "";
|
||||
} else {
|
||||
return decodeURIComponent(results[1].replace(/\+/g, ' '));
|
||||
return decodeURIComponent(results[1].replace(/\+/g, " "));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -535,17 +562,13 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
if (!document.querySelector('.dialogContainer') && startPages.indexOf(curr.type) !== -1) {
|
||||
return false;
|
||||
}
|
||||
if (enableHistory()) {
|
||||
return history.length > 1;
|
||||
}
|
||||
return (page.len || 0) > 0;
|
||||
return page.canGoBack();
|
||||
}
|
||||
|
||||
function showDirect(path) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
resolveOnNextShow = resolve;
|
||||
page.show(baseUrl() + path);
|
||||
});
|
||||
resolveOnNextShow = resolve, page.show(baseUrl()+path)
|
||||
})
|
||||
}
|
||||
|
||||
function show(path, options) {
|
||||
@@ -553,7 +576,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
path = '/' + path;
|
||||
}
|
||||
|
||||
path = path.replace(baseUrl(), '');
|
||||
var baseRoute = baseUrl();
|
||||
path = path.replace(baseRoute, '');
|
||||
|
||||
if (currentRouteInfo && currentRouteInfo.path === path) {
|
||||
// can't use this with home right now due to the back menu
|
||||
@@ -584,11 +608,10 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
}
|
||||
|
||||
function showItem(item, serverId, options) {
|
||||
// TODO: Refactor this so it only gets items, not strings.
|
||||
if (typeof (item) === 'string') {
|
||||
var apiClient = serverId ? connectionManager.getApiClient(serverId) : connectionManager.currentApiClient();
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (itemObject) {
|
||||
appRouter.showItem(itemObject, options);
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), item).then(function (item) {
|
||||
appRouter.showItem(item, options);
|
||||
});
|
||||
} else {
|
||||
if (arguments.length === 2) {
|
||||
@@ -643,8 +666,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
|
||||
function pushState(state, title, url) {
|
||||
state.navigate = false;
|
||||
history.pushState(state, title, url);
|
||||
|
||||
page.pushState(state, title, url);
|
||||
}
|
||||
|
||||
function setBaseRoute() {
|
||||
@@ -694,7 +716,7 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro
|
||||
appRouter.getRoutes = getRoutes;
|
||||
appRouter.pushState = pushState;
|
||||
appRouter.enableNativeHistory = enableNativeHistory;
|
||||
appRouter.handleAnchorClick = page.clickHandler;
|
||||
appRouter.handleAnchorClick = page.handleAnchorClick;
|
||||
appRouter.TransparencyLevel = {
|
||||
None: 0,
|
||||
Backdrop: 1,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['browser', 'css!./appFooter'], function (browser) {
|
||||
define(['browser', 'css!./appfooter'], function (browser) {
|
||||
'use strict';
|
||||
|
||||
function render(options) {
|
||||
@@ -1,17 +1,17 @@
|
||||
define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'globalize'], function (appSettings, browser, events, htmlMediaHelper, webSettings, globalize) {
|
||||
'use strict';
|
||||
define(["appSettings", "browser", "events", "htmlMediaHelper"], function (appSettings, browser, events, htmlMediaHelper) {
|
||||
"use strict";
|
||||
|
||||
function getBaseProfileOptions(item) {
|
||||
var disableHlsVideoAudioCodecs = [];
|
||||
|
||||
if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) {
|
||||
if (browser.edge) {
|
||||
disableHlsVideoAudioCodecs.push('mp3');
|
||||
if (browser.edge || browser.msie) {
|
||||
disableHlsVideoAudioCodecs.push("mp3");
|
||||
}
|
||||
|
||||
disableHlsVideoAudioCodecs.push('ac3');
|
||||
disableHlsVideoAudioCodecs.push('eac3');
|
||||
disableHlsVideoAudioCodecs.push('opus');
|
||||
disableHlsVideoAudioCodecs.push("ac3");
|
||||
disableHlsVideoAudioCodecs.push("eac3");
|
||||
disableHlsVideoAudioCodecs.push("opus");
|
||||
}
|
||||
|
||||
return {
|
||||
@@ -22,7 +22,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
|
||||
function getDeviceProfileForWindowsUwp(item) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
require(['browserdeviceprofile', 'environments/windows-uwp/mediacaps'], function (profileBuilder, uwpMediaCaps) {
|
||||
require(["browserdeviceprofile", "environments/windows-uwp/mediacaps"], function (profileBuilder, uwpMediaCaps) {
|
||||
var profileOptions = getBaseProfileOptions(item);
|
||||
profileOptions.supportsDts = uwpMediaCaps.supportsDTS();
|
||||
profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby();
|
||||
@@ -40,15 +40,26 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
}
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
require(['browserdeviceprofile'], function (profileBuilder) {
|
||||
require(["browserdeviceprofile"], function (profileBuilder) {
|
||||
var profile;
|
||||
|
||||
if (window.NativeShell) {
|
||||
profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder);
|
||||
} else {
|
||||
var builderOpts = getBaseProfileOptions(item);
|
||||
builderOpts.enableSsaRender = (item && !options.isRetry && 'allcomplexformats' !== appSettings.get('subtitleburnin'));
|
||||
profile = profileBuilder(builderOpts);
|
||||
profile = profileBuilder(getBaseProfileOptions(item));
|
||||
|
||||
if (item && !options.isRetry && "allcomplexformats" !== appSettings.get("subtitleburnin")) {
|
||||
if (!browser.orsay && !browser.tizen) {
|
||||
profile.SubtitleProfiles.push({
|
||||
Format: "ass",
|
||||
Method: "External"
|
||||
});
|
||||
profile.SubtitleProfiles.push({
|
||||
Format: "ssa",
|
||||
Method: "External"
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resolve(profile);
|
||||
@@ -57,12 +68,12 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
}
|
||||
|
||||
function escapeRegExp(str) {
|
||||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1');
|
||||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
|
||||
}
|
||||
|
||||
function replaceAll(originalString, strReplace, strWith) {
|
||||
var strReplace2 = escapeRegExp(strReplace);
|
||||
var reg = new RegExp(strReplace2, 'ig');
|
||||
var reg = new RegExp(strReplace2, "ig");
|
||||
return originalString.replace(reg, strWith);
|
||||
}
|
||||
|
||||
@@ -70,7 +81,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
var keys = [];
|
||||
|
||||
if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), self.btoa) {
|
||||
var result = replaceAll(btoa(keys.join('|')), '=', '1');
|
||||
var result = replaceAll(btoa(keys.join("|")), "=", "1");
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
@@ -78,7 +89,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
}
|
||||
|
||||
function getDeviceId() {
|
||||
var key = '_deviceId2';
|
||||
var key = "_deviceId2";
|
||||
var deviceId = appSettings.get(key);
|
||||
|
||||
if (deviceId) {
|
||||
@@ -93,38 +104,18 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
|
||||
function getDeviceName() {
|
||||
var deviceName;
|
||||
if (browser.tizen) {
|
||||
deviceName = 'Samsung Smart TV';
|
||||
} else if (browser.web0s) {
|
||||
deviceName = 'LG Smart TV';
|
||||
} else if (browser.operaTv) {
|
||||
deviceName = 'Opera TV';
|
||||
} else if (browser.xboxOne) {
|
||||
deviceName = 'Xbox One';
|
||||
} else if (browser.ps4) {
|
||||
deviceName = 'Sony PS4';
|
||||
} else if (browser.chrome) {
|
||||
deviceName = 'Chrome';
|
||||
} else if (browser.edgeChromium) {
|
||||
deviceName = 'Edge Chromium';
|
||||
} else if (browser.edge) {
|
||||
deviceName = 'Edge';
|
||||
} else if (browser.firefox) {
|
||||
deviceName = 'Firefox';
|
||||
} else if (browser.opera) {
|
||||
deviceName = 'Opera';
|
||||
} else if (browser.safari) {
|
||||
deviceName = 'Safari';
|
||||
} else {
|
||||
deviceName = 'Web Browser';
|
||||
}
|
||||
deviceName = browser.tizen ? "Samsung Smart TV" : browser.web0s ? "LG Smart TV" : browser.operaTv ? "Opera TV" : browser.xboxOne ? "Xbox One" : browser.ps4 ? "Sony PS4" : browser.chrome ? "Chrome" : browser.edge ? "Edge" : browser.firefox ? "Firefox" : browser.msie ? "Internet Explorer" : browser.opera ? "Opera" : browser.safari ? "Safari" : "Web Browser";
|
||||
|
||||
if (browser.ipad) {
|
||||
deviceName += ' iPad';
|
||||
} else if (browser.iphone) {
|
||||
deviceName += ' iPhone';
|
||||
} else if (browser.android) {
|
||||
deviceName += ' Android';
|
||||
deviceName += " iPad";
|
||||
} else {
|
||||
if (browser.iphone) {
|
||||
deviceName += " iPhone";
|
||||
} else {
|
||||
if (browser.android) {
|
||||
deviceName += " Android";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deviceName;
|
||||
@@ -144,12 +135,12 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
}
|
||||
|
||||
var element = document.documentElement;
|
||||
return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement('video').webkitEnterFullscreen;
|
||||
return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement("video").webkitEnterFullscreen;
|
||||
}
|
||||
|
||||
function getSyncProfile() {
|
||||
return new Promise(function (resolve) {
|
||||
require(['browserdeviceprofile', 'appSettings'], function (profileBuilder, appSettings) {
|
||||
require(["browserdeviceprofile", "appSettings"], function (profileBuilder, appSettings) {
|
||||
var profile;
|
||||
|
||||
if (window.NativeShell) {
|
||||
@@ -165,7 +156,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
}
|
||||
|
||||
function getDefaultLayout() {
|
||||
return 'desktop';
|
||||
return "desktop";
|
||||
}
|
||||
|
||||
function supportsHtmlMediaAutoplay() {
|
||||
@@ -182,20 +173,20 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
|
||||
function supportsCue() {
|
||||
try {
|
||||
var video = document.createElement('video');
|
||||
var style = document.createElement('style');
|
||||
var video = document.createElement("video");
|
||||
var style = document.createElement("style");
|
||||
|
||||
style.textContent = 'video::cue {background: inherit}';
|
||||
style.textContent = "video::cue {background: inherit}";
|
||||
document.body.appendChild(style);
|
||||
document.body.appendChild(video);
|
||||
|
||||
var cue = window.getComputedStyle(video, '::cue').background;
|
||||
var cue = window.getComputedStyle(video, "::cue").background;
|
||||
document.body.removeChild(style);
|
||||
document.body.removeChild(video);
|
||||
|
||||
return !!cue.length;
|
||||
} catch (err) {
|
||||
console.error('error detecting cue support: ' + err);
|
||||
console.error("error detecting cue support: " + err);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -203,15 +194,15 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
function onAppVisible() {
|
||||
if (isHidden) {
|
||||
isHidden = false;
|
||||
console.debug('triggering app resume event');
|
||||
events.trigger(appHost, 'resume');
|
||||
console.debug("triggering app resume event");
|
||||
events.trigger(appHost, "resume");
|
||||
}
|
||||
}
|
||||
|
||||
function onAppHidden() {
|
||||
if (!isHidden) {
|
||||
isHidden = true;
|
||||
console.debug('app is hidden');
|
||||
console.debug("app is hidden");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,88 +210,90 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
var features = [];
|
||||
|
||||
if (navigator.share) {
|
||||
features.push('sharing');
|
||||
features.push("sharing");
|
||||
}
|
||||
|
||||
if (!browser.edgeUwp && !browser.tv && !browser.xboxOne && !browser.ps4) {
|
||||
features.push('filedownload');
|
||||
features.push("filedownload");
|
||||
}
|
||||
|
||||
if (browser.operaTv || browser.tizen || browser.orsay || browser.web0s) {
|
||||
features.push('exit');
|
||||
features.push("exit");
|
||||
} else {
|
||||
features.push('exitmenu');
|
||||
features.push('plugins');
|
||||
features.push("exitmenu");
|
||||
features.push("plugins");
|
||||
}
|
||||
|
||||
if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.ps4) {
|
||||
features.push('externallinks');
|
||||
features.push('externalpremium');
|
||||
features.push("externallinks");
|
||||
features.push("externalpremium");
|
||||
}
|
||||
|
||||
if (!browser.operaTv) {
|
||||
features.push('externallinkdisplay');
|
||||
features.push("externallinkdisplay");
|
||||
}
|
||||
|
||||
if (supportsVoiceInput()) {
|
||||
features.push('voiceinput');
|
||||
features.push("voiceinput");
|
||||
}
|
||||
|
||||
if (!browser.tv && !browser.xboxOne) {
|
||||
browser.ps4;
|
||||
}
|
||||
|
||||
if (supportsHtmlMediaAutoplay()) {
|
||||
features.push('htmlaudioautoplay');
|
||||
features.push('htmlvideoautoplay');
|
||||
features.push("htmlaudioautoplay");
|
||||
features.push("htmlvideoautoplay");
|
||||
}
|
||||
|
||||
if (browser.edgeUwp) {
|
||||
features.push('sync');
|
||||
features.push("sync");
|
||||
}
|
||||
|
||||
if (supportsFullscreen()) {
|
||||
features.push('fullscreenchange');
|
||||
features.push("fullscreenchange");
|
||||
}
|
||||
|
||||
if (browser.chrome || browser.edge && !browser.slow) {
|
||||
if (!browser.noAnimation && !browser.edgeUwp && !browser.xboxOne) {
|
||||
features.push('imageanalysis');
|
||||
features.push("imageanalysis");
|
||||
}
|
||||
}
|
||||
|
||||
if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) {
|
||||
features.push('physicalvolumecontrol');
|
||||
features.push("physicalvolumecontrol");
|
||||
}
|
||||
|
||||
if (!browser.tv && !browser.xboxOne && !browser.ps4) {
|
||||
features.push('remotecontrol');
|
||||
features.push("remotecontrol");
|
||||
}
|
||||
|
||||
if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.edgeUwp) {
|
||||
features.push('remotevideo');
|
||||
features.push("remotevideo");
|
||||
}
|
||||
|
||||
features.push('displaylanguage');
|
||||
features.push('otherapppromotions');
|
||||
features.push('displaymode');
|
||||
features.push('targetblank');
|
||||
features.push('screensaver');
|
||||
features.push("displaylanguage");
|
||||
features.push("otherapppromotions");
|
||||
features.push("displaymode");
|
||||
features.push("targetblank");
|
||||
// allows users to connect to more than one server
|
||||
//features.push("multiserver");
|
||||
features.push("screensaver");
|
||||
|
||||
webSettings.enableMultiServer().then(enabled => {
|
||||
if (enabled) features.push('multiserver');
|
||||
});
|
||||
|
||||
if (!browser.orsay && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) {
|
||||
features.push('subtitleappearancesettings');
|
||||
if (!browser.orsay && !browser.tizen && !browser.msie && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) {
|
||||
features.push("subtitleappearancesettings");
|
||||
}
|
||||
|
||||
if (!browser.orsay) {
|
||||
features.push('subtitleburnsettings');
|
||||
if (!browser.orsay && !browser.tizen) {
|
||||
features.push("subtitleburnsettings");
|
||||
}
|
||||
|
||||
if (!browser.tv && !browser.ps4 && !browser.xboxOne) {
|
||||
features.push('fileinput');
|
||||
features.push("fileinput");
|
||||
}
|
||||
|
||||
if (browser.chrome || browser.edgeChromium) {
|
||||
features.push('chromecast');
|
||||
if (browser.chrome) {
|
||||
features.push("chromecast");
|
||||
}
|
||||
|
||||
return features;
|
||||
@@ -321,7 +314,7 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
window.close();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('error closing application: ' + err);
|
||||
console.error("error closing application: " + err);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -335,15 +328,15 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
return;
|
||||
}
|
||||
|
||||
require(['actionsheet'], function (actionsheet) {
|
||||
require(["actionsheet"], function (actionsheet) {
|
||||
exitPromise = actionsheet.show({
|
||||
title: globalize.translate('MessageConfirmAppExit'),
|
||||
title: Globalize.translate("MessageConfirmAppExit"),
|
||||
items: [
|
||||
{id: 'yes', name: globalize.translate('Yes')},
|
||||
{id: 'no', name: globalize.translate('No')}
|
||||
{id: "yes", name: Globalize.translate("Yes")},
|
||||
{id: "no", name: Globalize.translate("No")}
|
||||
]
|
||||
}).then(function (value) {
|
||||
if (value === 'yes') {
|
||||
if (value === "yes") {
|
||||
doExit();
|
||||
}
|
||||
}).finally(function () {
|
||||
@@ -354,15 +347,17 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
|
||||
var deviceId;
|
||||
var deviceName;
|
||||
var appName = 'Jellyfin Web';
|
||||
var appVersion = '10.6.4';
|
||||
var appName = "Jellyfin Web";
|
||||
var appVersion = "10.5.5";
|
||||
var visibilityChange;
|
||||
var visibilityState;
|
||||
|
||||
var appHost = {
|
||||
getWindowState: function () {
|
||||
return document.windowState || 'Normal';
|
||||
return document.windowState || "Normal";
|
||||
},
|
||||
setWindowState: function (state) {
|
||||
alert('setWindowState is not supported and should not be called');
|
||||
alert("setWindowState is not supported and should not be called");
|
||||
},
|
||||
exit: function () {
|
||||
if (!!window.appMode && browser.tizen) {
|
||||
@@ -379,13 +374,14 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
return -1 !== supportedFeatures.indexOf(command.toLowerCase());
|
||||
},
|
||||
preferVisualCards: browser.android || browser.chrome,
|
||||
moreIcon: browser.android ? "more_vert" : "more_horiz",
|
||||
getSyncProfile: getSyncProfile,
|
||||
getDefaultLayout: function () {
|
||||
if (window.NativeShell) {
|
||||
return window.NativeShell.AppHost.getDefaultLayout();
|
||||
}
|
||||
|
||||
return getDefaultLayout();
|
||||
return getDefaultLayout()
|
||||
},
|
||||
getDeviceProfile: getDeviceProfile,
|
||||
init: function () {
|
||||
@@ -414,44 +410,58 @@ define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'g
|
||||
return {};
|
||||
},
|
||||
setThemeColor: function (color) {
|
||||
var metaThemeColor = document.querySelector('meta[name=theme-color]');
|
||||
var metaThemeColor = document.querySelector("meta[name=theme-color]");
|
||||
|
||||
if (metaThemeColor) {
|
||||
metaThemeColor.setAttribute('content', color);
|
||||
metaThemeColor.setAttribute("content", color);
|
||||
}
|
||||
},
|
||||
setUserScalable: function (scalable) {
|
||||
if (!browser.tv) {
|
||||
var att = scalable ? 'width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes' : 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no';
|
||||
document.querySelector('meta[name=viewport]').setAttribute('content', att);
|
||||
var att = scalable ? "width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes" : "width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no";
|
||||
document.querySelector("meta[name=viewport]").setAttribute("content", att);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var doc = self.document;
|
||||
var isHidden = false;
|
||||
var hidden;
|
||||
var visibilityChange;
|
||||
|
||||
if (typeof document.hidden !== 'undefined') { /* eslint-disable-line compat/compat */
|
||||
hidden = 'hidden';
|
||||
visibilityChange = 'visibilitychange';
|
||||
} else if (typeof document.webkitHidden !== 'undefined') {
|
||||
hidden = 'webkitHidden';
|
||||
visibilityChange = 'webkitvisibilitychange';
|
||||
if (doc) {
|
||||
if (void 0 !== doc.visibilityState) {
|
||||
visibilityChange = "visibilitychange";
|
||||
visibilityState = "hidden";
|
||||
} else {
|
||||
if (void 0 !== doc.mozHidden) {
|
||||
visibilityChange = "mozvisibilitychange";
|
||||
visibilityState = "mozVisibilityState";
|
||||
} else {
|
||||
if (void 0 !== doc.msHidden) {
|
||||
visibilityChange = "msvisibilitychange";
|
||||
visibilityState = "msVisibilityState";
|
||||
} else {
|
||||
if (void 0 !== doc.webkitHidden) {
|
||||
visibilityChange = "webkitvisibilitychange";
|
||||
visibilityState = "webkitVisibilityState";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener(visibilityChange, function () {
|
||||
/* eslint-disable-next-line compat/compat */
|
||||
if (document[hidden]) {
|
||||
onAppHidden();
|
||||
} else {
|
||||
onAppVisible();
|
||||
}
|
||||
}, false);
|
||||
if (doc) {
|
||||
doc.addEventListener(visibilityChange, function () {
|
||||
if (document[visibilityState]) {
|
||||
onAppHidden();
|
||||
} else {
|
||||
onAppVisible();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (self.addEventListener) {
|
||||
self.addEventListener('focus', onAppVisible);
|
||||
self.addEventListener('blur', onAppHidden);
|
||||
self.addEventListener("focus", onAppVisible);
|
||||
self.addEventListener("blur", onAppHidden);
|
||||
}
|
||||
|
||||
return appHost;
|
||||
|
||||
@@ -1,73 +1,71 @@
|
||||
/* eslint-disable indent */
|
||||
|
||||
/**
|
||||
* Module for performing auto-focus.
|
||||
* @module components/autoFocuser
|
||||
*/
|
||||
|
||||
import focusManager from 'focusManager';
|
||||
import layoutManager from 'layoutManager';
|
||||
define(["focusManager", "layoutManager"], function (focusManager, layoutManager) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Previously selected element.
|
||||
*/
|
||||
let activeElement;
|
||||
var activeElement;
|
||||
|
||||
/**
|
||||
* Returns _true_ if AutoFocuser is enabled.
|
||||
* Returns true if AutoFocuser is enabled.
|
||||
*/
|
||||
export function isEnabled() {
|
||||
function isEnabled() {
|
||||
return layoutManager.tv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Start AutoFocuser.
|
||||
* Start AutoFocuser
|
||||
*/
|
||||
export function enable() {
|
||||
function enable() {
|
||||
if (!isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
window.addEventListener('focusin', function (e) {
|
||||
window.addEventListener("focusin", function (e) {
|
||||
activeElement = e.target;
|
||||
});
|
||||
|
||||
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.
|
||||
* @param {HTMLElement} [container] - Element to limit scope.
|
||||
* @returns {HTMLElement} Focused element.
|
||||
*/
|
||||
export function autoFocus(container) {
|
||||
function autoFocus(container) {
|
||||
if (!isEnabled()) {
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
container = container || document.body;
|
||||
|
||||
let candidates = [];
|
||||
var candidates = [];
|
||||
|
||||
if (activeElement) {
|
||||
// These elements are recreated
|
||||
if (activeElement.classList.contains('btnPreviousPage')) {
|
||||
candidates.push(container.querySelector('.btnPreviousPage'));
|
||||
candidates.push(container.querySelector('.btnNextPage'));
|
||||
} else if (activeElement.classList.contains('btnNextPage')) {
|
||||
candidates.push(container.querySelector('.btnNextPage'));
|
||||
candidates.push(container.querySelector('.btnPreviousPage'));
|
||||
} else if (activeElement.classList.contains('btnSelectView')) {
|
||||
candidates.push(container.querySelector('.btnSelectView'));
|
||||
if (activeElement.classList.contains("btnPreviousPage")) {
|
||||
candidates.push(container.querySelector(".btnPreviousPage"));
|
||||
candidates.push(container.querySelector(".btnNextPage"));
|
||||
} else if (activeElement.classList.contains("btnNextPage")) {
|
||||
candidates.push(container.querySelector(".btnNextPage"));
|
||||
candidates.push(container.querySelector(".btnPreviousPage"));
|
||||
} else if (activeElement.classList.contains("btnSelectView")) {
|
||||
candidates.push(container.querySelector(".btnSelectView"));
|
||||
}
|
||||
|
||||
candidates.push(activeElement);
|
||||
}
|
||||
|
||||
candidates = candidates.concat(Array.from(container.querySelectorAll('.btnResume')));
|
||||
candidates = candidates.concat(Array.from(container.querySelectorAll('.btnPlay')));
|
||||
candidates = candidates.concat(arrayFrom(container.querySelectorAll(".btnResume")));
|
||||
candidates = candidates.concat(arrayFrom(container.querySelectorAll(".btnPlay")));
|
||||
|
||||
let focusedElement;
|
||||
var focusedElement;
|
||||
|
||||
candidates.every(function (element) {
|
||||
if (focusManager.isCurrentlyFocusable(element)) {
|
||||
@@ -81,7 +79,7 @@ import layoutManager from 'layoutManager';
|
||||
|
||||
if (!focusedElement) {
|
||||
// FIXME: Multiple itemsContainers
|
||||
const itemsContainer = container.querySelector('.itemsContainer');
|
||||
var itemsContainer = container.querySelector(".itemsContainer");
|
||||
|
||||
if (itemsContainer) {
|
||||
focusedElement = focusManager.autoFocus(itemsContainer);
|
||||
@@ -95,10 +93,9 @@ import layoutManager from 'layoutManager';
|
||||
return focusedElement;
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
|
||||
export default {
|
||||
isEnabled: isEnabled,
|
||||
enable: enable,
|
||||
autoFocus: autoFocus
|
||||
};
|
||||
return {
|
||||
isEnabled: isEnabled,
|
||||
enable: enable,
|
||||
autoFocus: autoFocus
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings', 'css!./backdrop'], function (browser, connectionManager, playbackManager, dom, userSettings) {
|
||||
define(['browser', 'connectionManager', 'playbackManager', 'dom', "userSettings", 'css!./backdrop'], function (browser, connectionManager, playbackManager, dom, userSettings) {
|
||||
'use strict';
|
||||
|
||||
function enableAnimation(elem) {
|
||||
@@ -180,7 +180,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
|
||||
if (item.BackdropImageTags && item.BackdropImageTags.length > 0) {
|
||||
return item.BackdropImageTags.map(function (imgTag, index) {
|
||||
return apiClient.getScaledImageUrl(item.BackdropItemId || item.Id, Object.assign(imageOptions, {
|
||||
type: 'Backdrop',
|
||||
type: "Backdrop",
|
||||
tag: imgTag,
|
||||
maxWidth: dom.getScreenWidth(),
|
||||
index: index
|
||||
@@ -191,7 +191,7 @@ define(['browser', 'connectionManager', 'playbackManager', 'dom', 'userSettings'
|
||||
if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
|
||||
return item.ParentBackdropImageTags.map(function (imgTag, index) {
|
||||
return apiClient.getScaledImageUrl(item.ParentBackdropItemId, Object.assign(imageOptions, {
|
||||
type: 'Backdrop',
|
||||
type: "Backdrop",
|
||||
tag: imgTag,
|
||||
maxWidth: dom.getScreenWidth(),
|
||||
index: index
|
||||
|
||||
56
src/components/backdropscreensaver/plugin.js
Normal file
56
src/components/backdropscreensaver/plugin.js
Normal file
@@ -0,0 +1,56 @@
|
||||
define(["connectionManager"], function (connectionManager) {
|
||||
|
||||
return function () {
|
||||
|
||||
var self = this;
|
||||
|
||||
self.name = "Backdrop ScreenSaver";
|
||||
self.type = "screensaver";
|
||||
self.id = "backdropscreensaver";
|
||||
self.supportsAnonymous = false;
|
||||
|
||||
var currentSlideshow;
|
||||
|
||||
self.show = function () {
|
||||
|
||||
var query = {
|
||||
ImageTypes: "Backdrop",
|
||||
EnableImageTypes: "Backdrop",
|
||||
IncludeItemTypes: "Movie,Series,MusicArtist",
|
||||
SortBy: "Random",
|
||||
Recursive: true,
|
||||
Fields: "Taglines",
|
||||
ImageTypeLimit: 1,
|
||||
StartIndex: 0,
|
||||
Limit: 200
|
||||
};
|
||||
|
||||
var apiClient = connectionManager.currentApiClient();
|
||||
apiClient.getItems(apiClient.getCurrentUserId(), query).then(function (result) {
|
||||
|
||||
if (result.Items.length) {
|
||||
|
||||
require(["slideshow"], function (slideshow) {
|
||||
|
||||
var newSlideShow = new slideshow({
|
||||
showTitle: true,
|
||||
cover: true,
|
||||
items: result.Items
|
||||
});
|
||||
|
||||
newSlideShow.show();
|
||||
currentSlideshow = newSlideShow;
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
self.hide = function () {
|
||||
|
||||
if (currentSlideshow) {
|
||||
currentSlideshow.hide();
|
||||
currentSlideshow = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
@@ -167,9 +167,8 @@ button::-moz-focus-inner {
|
||||
position: relative;
|
||||
background-clip: content-box !important;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.cardScalable .cardImageContainer {
|
||||
/* This is only needed for scalable cards */
|
||||
height: 100%;
|
||||
contain: strict;
|
||||
}
|
||||
@@ -193,14 +192,9 @@ button::-moz-focus-inner {
|
||||
|
||||
/* Needed in case this is a button */
|
||||
display: block;
|
||||
|
||||
/* Needed in case this is a button */
|
||||
margin: 0 !important;
|
||||
border: 0 !important;
|
||||
padding: 0 !important;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
|
||||
/* Needed in safari */
|
||||
height: 100%;
|
||||
@@ -209,12 +203,19 @@ button::-moz-focus-inner {
|
||||
contain: strict;
|
||||
}
|
||||
|
||||
.cardContent:not(.defaultCardBackground) {
|
||||
background-color: transparent;
|
||||
.cardContent-button {
|
||||
border: 0 !important;
|
||||
padding: 0 !important;
|
||||
cursor: pointer;
|
||||
color: inherit;
|
||||
width: 100%;
|
||||
vertical-align: middle;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.cardBox:not(.visualCardBox) .cardPadder {
|
||||
background-color: #242424;
|
||||
.cardContent-button:not(.defaultCardBackground) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.visualCardBox .cardContent {
|
||||
@@ -222,8 +223,7 @@ button::-moz-focus-inner {
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.cardContent-shadow,
|
||||
.cardBox:not(.visualCardBox) .cardPadder {
|
||||
.cardContent-shadow {
|
||||
box-shadow: 0 0.0725em 0.29em 0 rgba(0, 0, 0, 0.37);
|
||||
}
|
||||
|
||||
@@ -306,10 +306,6 @@ button::-moz-focus-inner {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dialog .cardText {
|
||||
text-overflow: initial;
|
||||
}
|
||||
|
||||
.cardText-secondary {
|
||||
font-size: 86%;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,23 +1,13 @@
|
||||
/* eslint-disable indent */
|
||||
define(['datetime', 'imageLoader', 'connectionManager', 'layoutManager', 'browser'], function (datetime, imageLoader, connectionManager, layoutManager, browser) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module for building cards from item data.
|
||||
* @module components/cardBuilder/chaptercardbuilder
|
||||
*/
|
||||
var enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
import datetime from 'datetime';
|
||||
import imageLoader from 'imageLoader';
|
||||
import connectionManager from 'connectionManager';
|
||||
import layoutManager from 'layoutManager';
|
||||
import browser from 'browser';
|
||||
|
||||
const enableFocusTransform = !browser.slow && !browser.edge;
|
||||
|
||||
function buildChapterCardsHtml(item, chapters, options) {
|
||||
function buildChapterCardsHtml(item, chapters, options) {
|
||||
|
||||
// TODO move card creation code to Card component
|
||||
|
||||
let className = 'card itemAction chapterCard';
|
||||
var className = 'card itemAction chapterCard';
|
||||
|
||||
if (layoutManager.tv) {
|
||||
className += ' show-focus';
|
||||
@@ -27,12 +17,12 @@ import browser from 'browser';
|
||||
}
|
||||
}
|
||||
|
||||
const mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || [];
|
||||
const videoStream = mediaStreams.filter(({Type}) => {
|
||||
return Type === 'Video';
|
||||
var mediaStreams = ((item.MediaSources || [])[0] || {}).MediaStreams || [];
|
||||
var videoStream = mediaStreams.filter(function (i) {
|
||||
return i.Type === 'Video';
|
||||
})[0] || {};
|
||||
|
||||
let shape = (options.backdropShape || 'backdrop');
|
||||
var shape = (options.backdropShape || 'backdrop');
|
||||
|
||||
if (videoStream.Width && videoStream.Height) {
|
||||
|
||||
@@ -41,24 +31,24 @@ import browser from 'browser';
|
||||
}
|
||||
}
|
||||
|
||||
className += ` ${shape}Card`;
|
||||
className += ' ' + shape + 'Card';
|
||||
|
||||
if (options.block || options.rows) {
|
||||
className += ' block';
|
||||
}
|
||||
|
||||
let html = '';
|
||||
let itemsInRow = 0;
|
||||
var html = '';
|
||||
var itemsInRow = 0;
|
||||
|
||||
const apiClient = connectionManager.getApiClient(item.ServerId);
|
||||
var apiClient = connectionManager.getApiClient(item.ServerId);
|
||||
|
||||
for (let i = 0, length = chapters.length; i < length; i++) {
|
||||
for (var i = 0, length = chapters.length; i < length; i++) {
|
||||
|
||||
if (options.rows && itemsInRow === 0) {
|
||||
html += '<div class="cardColumn">';
|
||||
}
|
||||
|
||||
const chapter = chapters[i];
|
||||
var chapter = chapters[i];
|
||||
|
||||
html += buildChapterCard(item, apiClient, chapter, i, options, className, shape);
|
||||
itemsInRow++;
|
||||
@@ -72,50 +62,50 @@ import browser from 'browser';
|
||||
return html;
|
||||
}
|
||||
|
||||
function getImgUrl({Id}, {ImageTag}, index, maxWidth, apiClient) {
|
||||
function getImgUrl(item, chapter, index, maxWidth, apiClient) {
|
||||
|
||||
if (ImageTag) {
|
||||
if (chapter.ImageTag) {
|
||||
|
||||
return apiClient.getScaledImageUrl(Id, {
|
||||
return apiClient.getScaledImageUrl(item.Id, {
|
||||
|
||||
maxWidth: maxWidth * 2,
|
||||
tag: ImageTag,
|
||||
type: 'Chapter',
|
||||
index
|
||||
tag: chapter.ImageTag,
|
||||
type: "Chapter",
|
||||
index: index
|
||||
});
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildChapterCard(item, apiClient, chapter, index, {width, coverImage}, className, shape) {
|
||||
function buildChapterCard(item, apiClient, chapter, index, options, className, shape) {
|
||||
|
||||
const imgUrl = getImgUrl(item, chapter, index, width || 400, apiClient);
|
||||
var imgUrl = getImgUrl(item, chapter, index, options.width || 400, apiClient);
|
||||
|
||||
let cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer';
|
||||
if (coverImage) {
|
||||
var cardImageContainerClass = 'cardContent cardContent-shadow cardImageContainer chapterCardImageContainer';
|
||||
if (options.coverImage) {
|
||||
cardImageContainerClass += ' coveredImage';
|
||||
}
|
||||
const dataAttributes = ` data-action="play" data-isfolder="${item.IsFolder}" data-id="${item.Id}" data-serverid="${item.ServerId}" data-type="${item.Type}" data-mediatype="${item.MediaType}" data-positionticks="${chapter.StartPositionTicks}"`;
|
||||
let cardImageContainer = imgUrl ? (`<div class="${cardImageContainerClass} lazy" data-src="${imgUrl}">`) : (`<div class="${cardImageContainerClass}">`);
|
||||
var dataAttributes = ' data-action="play" data-isfolder="' + item.IsFolder + '" data-id="' + item.Id + '" data-serverid="' + item.ServerId + '" data-type="' + item.Type + '" data-mediatype="' + item.MediaType + '" data-positionticks="' + chapter.StartPositionTicks + '"';
|
||||
var cardImageContainer = imgUrl ? ('<div class="' + cardImageContainerClass + ' lazy" data-src="' + imgUrl + '">') : ('<div class="' + cardImageContainerClass + '">');
|
||||
|
||||
if (!imgUrl) {
|
||||
cardImageContainer += '<span class="material-icons cardImageIcon local_movies"></span>';
|
||||
cardImageContainer += '<i class="material-icons cardImageIcon local_movies"></i>';
|
||||
}
|
||||
|
||||
let nameHtml = '';
|
||||
nameHtml += `<div class="cardText">${chapter.Name}</div>`;
|
||||
nameHtml += `<div class="cardText">${datetime.getDisplayRunningTime(chapter.StartPositionTicks)}</div>`;
|
||||
var nameHtml = '';
|
||||
nameHtml += '<div class="cardText">' + chapter.Name + '</div>';
|
||||
nameHtml += '<div class="cardText">' + datetime.getDisplayRunningTime(chapter.StartPositionTicks) + '</div>';
|
||||
|
||||
const cardBoxCssClass = 'cardBox';
|
||||
const cardScalableClass = 'cardScalable';
|
||||
var cardBoxCssClass = 'cardBox';
|
||||
var cardScalableClass = 'cardScalable';
|
||||
|
||||
const html = `<button type="button" class="${className}"${dataAttributes}><div class="${cardBoxCssClass}"><div class="${cardScalableClass}"><div class="cardPadder-${shape}"></div>${cardImageContainer}</div><div class="innerCardFooter">${nameHtml}</div></div></div></button>`;
|
||||
var html = '<button type="button" class="' + className + '"' + dataAttributes + '><div class="' + cardBoxCssClass + '"><div class="' + cardScalableClass + '"><div class="cardPadder-' + shape + '"></div>' + cardImageContainer + '</div><div class="innerCardFooter">' + nameHtml + '</div></div></div></button>';
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
export function buildChapterCards(item, chapters, options) {
|
||||
function buildChapterCards(item, chapters, options) {
|
||||
|
||||
if (options.parentContainer) {
|
||||
// Abort if the container has been disposed
|
||||
@@ -131,16 +121,15 @@ import browser from 'browser';
|
||||
}
|
||||
}
|
||||
|
||||
const html = buildChapterCardsHtml(item, chapters, options);
|
||||
var html = buildChapterCardsHtml(item, chapters, options);
|
||||
|
||||
options.itemsContainer.innerHTML = html;
|
||||
|
||||
imageLoader.lazyChildren(options.itemsContainer);
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
|
||||
export default {
|
||||
buildChapterCards: buildChapterCards
|
||||
};
|
||||
return {
|
||||
buildChapterCards: buildChapterCards
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -1,13 +1,7 @@
|
||||
/* eslint-disable indent */
|
||||
define(['cardBuilder'], function (cardBuilder) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module for building cards from item data.
|
||||
* @module components/cardBuilder/peoplecardbuilder
|
||||
*/
|
||||
|
||||
import cardBuilder from 'cardBuilder';
|
||||
|
||||
export function buildPeopleCards(items, options) {
|
||||
function buildPeopleCards(items, options) {
|
||||
|
||||
options = Object.assign(options || {}, {
|
||||
cardLayout: false,
|
||||
@@ -21,8 +15,8 @@ import cardBuilder from 'cardBuilder';
|
||||
cardBuilder.buildCards(items, options);
|
||||
}
|
||||
|
||||
/* eslint-enable indent */
|
||||
return {
|
||||
buildPeopleCards: buildPeopleCards
|
||||
};
|
||||
|
||||
export default {
|
||||
buildPeopleCards: buildPeopleCards
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
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,33 +1,44 @@
|
||||
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';
|
||||
define(["dialogHelper", "loading", "connectionManager", "globalize", "actionsheet", "emby-input", "paper-icon-button-light", "emby-button", "listViewStyle", "material-icons", "formDialogStyle"], function (dialogHelper, loading, connectionManager, globalize, actionsheet) {
|
||||
"use strict";
|
||||
|
||||
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) {
|
||||
loading.show();
|
||||
var providerId = options.providerId;
|
||||
connectionManager.getApiClient(options.serverId).ajax({
|
||||
type: 'POST',
|
||||
url: ApiClient.getUrl('LiveTv/ChannelMappings'),
|
||||
type: "POST",
|
||||
url: ApiClient.getUrl("LiveTv/ChannelMappings"),
|
||||
data: {
|
||||
providerId: providerId,
|
||||
tunerChannelId: channelId,
|
||||
providerChannelId: providerChannelId
|
||||
},
|
||||
dataType: 'json'
|
||||
dataType: "json"
|
||||
}).then(function (mapping) {
|
||||
var listItem = dom.parentWithClass(button, 'listItem');
|
||||
button.setAttribute('data-providerid', mapping.ProviderChannelId);
|
||||
listItem.querySelector('.secondary').innerHTML = getMappingSecondaryName(mapping, currentMappingOptions.ProviderName);
|
||||
var listItem = parentWithClass(button, "listItem");
|
||||
button.setAttribute("data-providerid", mapping.ProviderChannelId);
|
||||
listItem.querySelector(".secondary").innerHTML = getMappingSecondaryName(mapping, currentMappingOptions.ProviderName);
|
||||
loading.hide();
|
||||
});
|
||||
}
|
||||
|
||||
function onChannelsElementClick(e) {
|
||||
var btnMap = dom.parentWithClass(e.target, 'btnMap');
|
||||
var btnMap = parentWithClass(e.target, "btnMap");
|
||||
|
||||
if (btnMap) {
|
||||
var channelId = btnMap.getAttribute('data-id');
|
||||
var providerChannelId = btnMap.getAttribute('data-providerid');
|
||||
var channelId = btnMap.getAttribute("data-id");
|
||||
var providerChannelId = btnMap.getAttribute("data-providerid");
|
||||
var menuItems = currentMappingOptions.ProviderChannels.map(function (m) {
|
||||
return {
|
||||
name: m.Name,
|
||||
@@ -48,56 +59,56 @@ define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'act
|
||||
|
||||
function getChannelMappingOptions(serverId, providerId) {
|
||||
var apiClient = connectionManager.getApiClient(serverId);
|
||||
return apiClient.getJSON(apiClient.getUrl('LiveTv/ChannelMappingOptions', {
|
||||
return apiClient.getJSON(apiClient.getUrl("LiveTv/ChannelMappingOptions", {
|
||||
providerId: providerId
|
||||
}));
|
||||
}
|
||||
|
||||
function getMappingSecondaryName(mapping, providerName) {
|
||||
return (mapping.ProviderChannelName || '') + ' - ' + providerName;
|
||||
return (mapping.ProviderChannelName || "") + " - " + providerName;
|
||||
}
|
||||
|
||||
function getTunerChannelHtml(channel, providerName) {
|
||||
var html = '';
|
||||
var html = "";
|
||||
html += '<div class="listItem">';
|
||||
html += '<span class="material-icons listItemIcon dvr"></span>';
|
||||
html += '<i class="material-icons listItemIcon">dvr</i>';
|
||||
html += '<div class="listItemBody two-line">';
|
||||
html += '<h3 class="listItemBodyText">';
|
||||
html += channel.Name;
|
||||
html += '</h3>';
|
||||
html += "</h3>";
|
||||
html += '<div class="secondary listItemBodyText">';
|
||||
|
||||
if (channel.ProviderChannelName) {
|
||||
html += getMappingSecondaryName(channel, providerName);
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '<button class="btnMap autoSize" is="paper-icon-button-light" type="button" data-id="' + channel.Id + '" data-providerid="' + channel.ProviderChannelId + '"><span class="material-icons mode_edit"></span></button>';
|
||||
return html += '</div>';
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
html += '<button class="btnMap autoSize" is="paper-icon-button-light" type="button" data-id="' + channel.Id + '" data-providerid="' + channel.ProviderChannelId + '"><i class="material-icons mode_edit"></i></button>';
|
||||
return html += "</div>";
|
||||
}
|
||||
|
||||
function getEditorHtml() {
|
||||
var html = '';
|
||||
html += '<div class="formDialogContent smoothScrollY">';
|
||||
var html = "";
|
||||
html += '<div class="formDialogContent">';
|
||||
html += '<div class="dialogContentInner dialog-content-centered">';
|
||||
html += '<form style="margin:auto;">';
|
||||
html += '<h1>' + globalize.translate('HeaderChannels') + '</h1>';
|
||||
html += "<h1>" + globalize.translate("HeaderChannels") + "</h1>";
|
||||
html += '<div class="channels paperList">';
|
||||
html += '</div>';
|
||||
html += '</form>';
|
||||
html += '</div>';
|
||||
return html += '</div>';
|
||||
html += "</div>";
|
||||
html += "</form>";
|
||||
html += "</div>";
|
||||
return html += "</div>";
|
||||
}
|
||||
|
||||
function initEditor(dlg, options) {
|
||||
getChannelMappingOptions(options.serverId, options.providerId).then(function (result) {
|
||||
currentMappingOptions = result;
|
||||
var channelsElement = dlg.querySelector('.channels');
|
||||
var channelsElement = dlg.querySelector(".channels");
|
||||
channelsElement.innerHTML = result.TunerChannels.map(function (channel) {
|
||||
return getTunerChannelHtml(channel, result.ProviderName);
|
||||
}).join('');
|
||||
channelsElement.addEventListener('click', onChannelsElementClick);
|
||||
}).join("");
|
||||
channelsElement.addEventListener("click", onChannelsElementClick);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -108,27 +119,27 @@ define(['dom', 'dialogHelper', 'loading', 'connectionManager', 'globalize', 'act
|
||||
var dialogOptions = {
|
||||
removeOnClose: true
|
||||
};
|
||||
dialogOptions.size = 'small';
|
||||
dialogOptions.size = "small";
|
||||
var dlg = dialogHelper.createDialog(dialogOptions);
|
||||
dlg.classList.add('formDialog');
|
||||
dlg.classList.add('ui-body-a');
|
||||
dlg.classList.add('background-theme-a');
|
||||
var html = '';
|
||||
var title = globalize.translate('MapChannels');
|
||||
dlg.classList.add("formDialog");
|
||||
dlg.classList.add("ui-body-a");
|
||||
dlg.classList.add("background-theme-a");
|
||||
var html = "";
|
||||
var title = globalize.translate("MapChannels");
|
||||
html += '<div class="formDialogHeader">';
|
||||
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
|
||||
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="material-icons arrow_back"></i></button>';
|
||||
html += '<h3 class="formDialogHeaderTitle">';
|
||||
html += title;
|
||||
html += '</h3>';
|
||||
html += '</div>';
|
||||
html += "</h3>";
|
||||
html += "</div>";
|
||||
html += getEditorHtml();
|
||||
dlg.innerHTML = html;
|
||||
initEditor(dlg, options);
|
||||
dlg.querySelector('.btnCancel').addEventListener('click', function () {
|
||||
dlg.querySelector(".btnCancel").addEventListener("click", function () {
|
||||
dialogHelper.close(dlg);
|
||||
});
|
||||
return new Promise(function (resolve, reject) {
|
||||
dlg.addEventListener('close', resolve);
|
||||
dlg.addEventListener("close", resolve);
|
||||
dialogHelper.open(dlg);
|
||||
});
|
||||
};
|
||||
@@ -37,69 +37,69 @@ define(['events'], function (events) {
|
||||
// 5) It wasn't as smart as it could have been about what should be part of a
|
||||
// URL and what should be part of human language.
|
||||
|
||||
var protocols = '(?:(?:http|https|rtsp|ftp):\\/\\/)';
|
||||
var protocols = "(?:(?:http|https|rtsp|ftp):\\/\\/)";
|
||||
var credentials = "(?:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,64}" // username (1-64 normal or url escaped characters)
|
||||
+ "(?:\\:(?:[a-z0-9\\$\\-\\_\\.\\+\\!\\*\\'\\(\\)\\,\\;\\?\\&\\=]|(?:\\%[a-f0-9]{2})){1,25})?" // followed by optional password (: + 1-25 normal or url escaped characters)
|
||||
+ '\\@)';
|
||||
+ "\\@)";
|
||||
|
||||
// IPv6 Regex http://forums.intermapper.com/viewtopic.php?t=452
|
||||
// by Dartware, LLC is licensed under a Creative Commons Attribution-ShareAlike 3.0 Unported License
|
||||
// http://intermapper.com/
|
||||
var ipv6 = '('
|
||||
+ '(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ '|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ '|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))'
|
||||
+ ')(%.+)?';
|
||||
var ipv6 = "("
|
||||
+ "(([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))"
|
||||
+ "|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))"
|
||||
+ "|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))"
|
||||
+ "|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))"
|
||||
+ "|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))"
|
||||
+ "|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))"
|
||||
+ "|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))"
|
||||
+ "|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))"
|
||||
+ ")(%.+)?";
|
||||
|
||||
var ipv4 = '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\.'
|
||||
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
|
||||
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\.'
|
||||
+ '(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])';
|
||||
var ipv4 = "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9])\\."
|
||||
+ "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\."
|
||||
+ "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[1-9]|0)\\."
|
||||
+ "(?:25[0-5]|2[0-4][0-9]|[0-1][0-9]{2}|[1-9][0-9]|[0-9])";
|
||||
|
||||
// This would have been a lot cleaner if JS RegExp supported conditionals...
|
||||
var linkRegExpString =
|
||||
|
||||
// begin match for protocol / username / password / host
|
||||
'(?:'
|
||||
"(?:"
|
||||
|
||||
// ============================
|
||||
// If we have a recognized protocol at the beginning of the URL, we're
|
||||
// more relaxed about what we accept, because we assume the user wants
|
||||
// this to be a URL, and we're not accidentally matching human language
|
||||
+ protocols + '?'
|
||||
+ protocols + "?"
|
||||
|
||||
// optional username:password@
|
||||
+ credentials + '?'
|
||||
+ credentials + "?"
|
||||
|
||||
// IP address (both v4 and v6)
|
||||
+ '(?:'
|
||||
+ "(?:"
|
||||
|
||||
// IPv6
|
||||
+ ipv6
|
||||
|
||||
// IPv4
|
||||
+ '|' + ipv4
|
||||
+ "|" + ipv4
|
||||
|
||||
+ ')'
|
||||
+ ")"
|
||||
|
||||
// end match for protocol / username / password / host
|
||||
+ ')'
|
||||
+ ")"
|
||||
|
||||
// optional port number
|
||||
+ '(?:\\:\\d{1,5})?'
|
||||
+ "(?:\\:\\d{1,5})?"
|
||||
|
||||
// plus optional path and query params (no unicode allowed here?)
|
||||
+ '(?:'
|
||||
+ '\\/(?:'
|
||||
+ "(?:"
|
||||
+ "\\/(?:"
|
||||
// some characters we'll accept because it's unlikely human language
|
||||
// would use them after a URL unless they were part of the url
|
||||
+ '(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])'
|
||||
+ '|(?:\\%[a-f0-9]{2})'
|
||||
+ "(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])"
|
||||
+ "|(?:\\%[a-f0-9]{2})"
|
||||
// some characters are much more likely to be used AFTER a url and
|
||||
// were not intended to be included in the url itself. Mostly end
|
||||
// of sentence type things. It's also likely that the URL would
|
||||
@@ -108,9 +108,9 @@ define(['events'], function (events) {
|
||||
// they must be followed by another character that we're reasonably
|
||||
// sure is part of the url
|
||||
+ "|(?:[\\;\\?\\:\\.\\!\\'\\(\\)\\,\\=]+(?=(?:[a-z0-9\\/\\@\\&\\#\\~\\*\\_\\-\\+])|(?:\\%[a-f0-9]{2})))"
|
||||
+ ')*'
|
||||
+ '|\\b|\$'
|
||||
+ ')';
|
||||
+ ")*"
|
||||
+ "|\\b|\$"
|
||||
+ ")";
|
||||
|
||||
// regex = XRegExp(regex,'gi');
|
||||
var linkRegExp = RegExp(linkRegExpString, 'gi');
|
||||
@@ -120,7 +120,7 @@ define(['events'], function (events) {
|
||||
// if url doesn't begin with a known protocol, add http by default
|
||||
function ensureProtocol(url) {
|
||||
if (!url.match(protocolRegExp)) {
|
||||
url = 'http://' + url;
|
||||
url = "http://" + url;
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@@ -188,9 +188,9 @@ define(['events'], function (events) {
|
||||
return apiClient.getEndpointInfo().then(function (endpoint) {
|
||||
if (endpoint.IsInNetwork) {
|
||||
return apiClient.getPublicSystemInfo().then(function (info) {
|
||||
var localAddress = info.LocalAddress;
|
||||
var localAddress = info.LocalAddress
|
||||
if (!localAddress) {
|
||||
console.debug('No valid local address returned, defaulting to external one');
|
||||
console.debug("No valid local address returned, defaulting to external one")
|
||||
localAddress = serverAddress;
|
||||
}
|
||||
addToCache(serverAddress, localAddress);
|
||||
@@ -54,8 +54,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
|
||||
// production version registered with google
|
||||
// replace this value if you want to test changes on another instance
|
||||
var applicationStable = 'F007D354';
|
||||
var applicationNightly = '6F511C87';
|
||||
var applicationID = "F007D354";
|
||||
|
||||
var messageNamespace = 'urn:x-cast:com.connectsdk';
|
||||
|
||||
@@ -100,11 +99,6 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
return;
|
||||
}
|
||||
|
||||
var applicationID = applicationStable;
|
||||
if (userSettings.chromecastVersion() === 'nightly') {
|
||||
applicationID = applicationNightly;
|
||||
}
|
||||
|
||||
// request session
|
||||
var sessionRequest = new chrome.cast.SessionRequest(applicationID);
|
||||
var apiConfig = new chrome.cast.ApiConfig(sessionRequest,
|
||||
@@ -120,14 +114,14 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
*/
|
||||
CastPlayer.prototype.onInitSuccess = function () {
|
||||
this.isInitialized = true;
|
||||
console.debug('chromecast init success');
|
||||
console.debug("chromecast init success");
|
||||
};
|
||||
|
||||
/**
|
||||
* Generic error callback function
|
||||
*/
|
||||
CastPlayer.prototype.onError = function () {
|
||||
console.debug('chromecast error');
|
||||
console.debug("chromecast error");
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -183,10 +177,10 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
*/
|
||||
CastPlayer.prototype.receiverListener = function (e) {
|
||||
if (e === 'available') {
|
||||
console.debug('chromecast receiver found');
|
||||
console.debug("chromecast receiver found");
|
||||
this.hasReceivers = true;
|
||||
} else {
|
||||
console.debug('chromecast receiver list empty');
|
||||
console.debug("chromecast receiver list empty");
|
||||
this.hasReceivers = false;
|
||||
}
|
||||
};
|
||||
@@ -201,8 +195,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
this.session = null;
|
||||
this.deviceState = DEVICE_STATE.IDLE;
|
||||
this.castPlayerState = PLAYER_STATE.IDLE;
|
||||
document.removeEventListener('volumeupbutton', onVolumeUpKeyDown, false);
|
||||
document.removeEventListener('volumedownbutton', onVolumeDownKeyDown, false);
|
||||
document.removeEventListener("volumeupbutton", onVolumeUpKeyDown, false);
|
||||
document.removeEventListener("volumedownbutton", onVolumeDownKeyDown, false);
|
||||
|
||||
console.debug('sessionUpdateListener: setting currentMediaSession to null');
|
||||
this.currentMediaSession = null;
|
||||
@@ -217,7 +211,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
* session request in opt_sessionRequest.
|
||||
*/
|
||||
CastPlayer.prototype.launchApp = function () {
|
||||
console.debug('chromecast launching app...');
|
||||
console.debug("chromecast launching app...");
|
||||
chrome.cast.requestSession(this.onRequestSessionSuccess.bind(this), this.onLaunchError.bind(this));
|
||||
};
|
||||
|
||||
@@ -226,7 +220,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
* @param {Object} e A chrome.cast.Session object
|
||||
*/
|
||||
CastPlayer.prototype.onRequestSessionSuccess = function (e) {
|
||||
console.debug('chromecast session success: ' + e.sessionId);
|
||||
console.debug("chromecast session success: " + e.sessionId);
|
||||
this.onSessionConnected(e);
|
||||
};
|
||||
|
||||
@@ -238,8 +232,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
this.session.addMediaListener(this.sessionMediaListener.bind(this));
|
||||
this.session.addUpdateListener(this.sessionUpdateListener.bind(this));
|
||||
|
||||
document.addEventListener('volumeupbutton', onVolumeUpKeyDown, false);
|
||||
document.addEventListener('volumedownbutton', onVolumeDownKeyDown, false);
|
||||
document.addEventListener("volumeupbutton", onVolumeUpKeyDown, false);
|
||||
document.addEventListener("volumedownbutton", onVolumeDownKeyDown, false);
|
||||
|
||||
events.trigger(this, 'connect');
|
||||
this.sendMessage({
|
||||
@@ -268,7 +262,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
* Callback function for launch error
|
||||
*/
|
||||
CastPlayer.prototype.onLaunchError = function () {
|
||||
console.debug('chromecast launch error');
|
||||
console.debug("chromecast launch error");
|
||||
this.deviceState = DEVICE_STATE.ERROR;
|
||||
sendConnectionResult(false);
|
||||
};
|
||||
@@ -290,8 +284,8 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
|
||||
this.deviceState = DEVICE_STATE.IDLE;
|
||||
this.castPlayerState = PLAYER_STATE.IDLE;
|
||||
document.removeEventListener('volumeupbutton', onVolumeUpKeyDown, false);
|
||||
document.removeEventListener('volumedownbutton', onVolumeDownKeyDown, false);
|
||||
document.removeEventListener("volumeupbutton", onVolumeUpKeyDown, false);
|
||||
document.removeEventListener("volumedownbutton", onVolumeDownKeyDown, false);
|
||||
|
||||
this.currentMediaSession = null;
|
||||
};
|
||||
@@ -302,7 +296,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
*/
|
||||
CastPlayer.prototype.loadMedia = function (options, command) {
|
||||
if (!this.session) {
|
||||
console.debug('no session');
|
||||
console.debug("no session");
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
@@ -392,7 +386,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
*/
|
||||
CastPlayer.prototype.onMediaDiscovered = function (how, mediaSession) {
|
||||
|
||||
console.debug('chromecast new media session ID:' + mediaSession.mediaSessionId + ' (' + how + ')');
|
||||
console.debug("chromecast new media session ID:" + mediaSession.mediaSessionId + ' (' + how + ')');
|
||||
this.currentMediaSession = mediaSession;
|
||||
|
||||
if (how === 'loadMedia') {
|
||||
@@ -411,7 +405,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
* @param {!Boolean} e true/false
|
||||
*/
|
||||
CastPlayer.prototype.onMediaStatusUpdate = function (e) {
|
||||
console.debug('chromecast updating media: ' + e);
|
||||
console.debug("chromecast updating media: " + e);
|
||||
if (e === false) {
|
||||
this.castPlayerState = PLAYER_STATE.IDLE;
|
||||
}
|
||||
@@ -488,7 +482,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
} else {
|
||||
|
||||
query.Limit = query.Limit || 100;
|
||||
query.ExcludeLocationTypes = 'Virtual';
|
||||
query.ExcludeLocationTypes = "Virtual";
|
||||
query.EnableTotalRecordCount = false;
|
||||
|
||||
return apiClient.getItems(userId, query);
|
||||
@@ -512,13 +506,13 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
instance._castPlayer = new CastPlayer();
|
||||
|
||||
// To allow the native android app to override
|
||||
document.dispatchEvent(new CustomEvent('chromecastloaded', {
|
||||
document.dispatchEvent(new CustomEvent("chromecastloaded", {
|
||||
detail: {
|
||||
player: instance
|
||||
}
|
||||
}));
|
||||
|
||||
events.on(instance._castPlayer, 'connect', function (e) {
|
||||
events.on(instance._castPlayer, "connect", function (e) {
|
||||
|
||||
if (currentResolve) {
|
||||
sendConnectionResult(true);
|
||||
@@ -531,24 +525,24 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
instance.lastPlayerData = null;
|
||||
});
|
||||
|
||||
events.on(instance._castPlayer, 'playbackstart', function (e, data) {
|
||||
events.on(instance._castPlayer, "playbackstart", function (e, data) {
|
||||
|
||||
console.debug('cc: playbackstart');
|
||||
|
||||
instance._castPlayer.initializeCastPlayer();
|
||||
|
||||
var state = instance.getPlayerStateInternal(data);
|
||||
events.trigger(instance, 'playbackstart', [state]);
|
||||
events.trigger(instance, "playbackstart", [state]);
|
||||
});
|
||||
|
||||
events.on(instance._castPlayer, 'playbackstop', function (e, data) {
|
||||
events.on(instance._castPlayer, "playbackstop", function (e, data) {
|
||||
|
||||
console.debug('cc: playbackstop');
|
||||
var state = instance.getPlayerStateInternal(data);
|
||||
|
||||
events.trigger(instance, 'playbackstop', [state]);
|
||||
events.trigger(instance, "playbackstop", [state]);
|
||||
|
||||
state = instance.lastPlayerData.PlayState || {};
|
||||
var state = instance.lastPlayerData.PlayState || {};
|
||||
var volume = state.VolumeLevel || 0.5;
|
||||
var mute = state.IsMuted || false;
|
||||
|
||||
@@ -559,12 +553,12 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
instance.lastPlayerData.PlayState.IsMuted = mute;
|
||||
});
|
||||
|
||||
events.on(instance._castPlayer, 'playbackprogress', function (e, data) {
|
||||
events.on(instance._castPlayer, "playbackprogress", function (e, data) {
|
||||
|
||||
console.debug('cc: positionchange');
|
||||
var state = instance.getPlayerStateInternal(data);
|
||||
|
||||
events.trigger(instance, 'timeupdate', [state]);
|
||||
events.trigger(instance, "timeupdate", [state]);
|
||||
});
|
||||
|
||||
bindEventForRelay(instance, 'timeupdate');
|
||||
@@ -572,14 +566,13 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
bindEventForRelay(instance, 'unpause');
|
||||
bindEventForRelay(instance, 'volumechange');
|
||||
bindEventForRelay(instance, 'repeatmodechange');
|
||||
bindEventForRelay(instance, 'shufflequeuemodechange');
|
||||
|
||||
events.on(instance._castPlayer, 'playstatechange', function (e, data) {
|
||||
events.on(instance._castPlayer, "playstatechange", function (e, data) {
|
||||
|
||||
console.debug('cc: playstatechange');
|
||||
var state = instance.getPlayerStateInternal(data);
|
||||
|
||||
events.trigger(instance, 'pause', [state]);
|
||||
events.trigger(instance, "pause", [state]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -637,25 +630,24 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
name: PlayerName,
|
||||
id: PlayerName,
|
||||
playerName: PlayerName,
|
||||
playableMediaTypes: ['Audio', 'Video'],
|
||||
playableMediaTypes: ["Audio", "Video"],
|
||||
isLocalPlayer: false,
|
||||
appName: PlayerName,
|
||||
deviceName: appName,
|
||||
supportedCommands: [
|
||||
'VolumeUp',
|
||||
'VolumeDown',
|
||||
'Mute',
|
||||
'Unmute',
|
||||
'ToggleMute',
|
||||
'SetVolume',
|
||||
'SetAudioStreamIndex',
|
||||
'SetSubtitleStreamIndex',
|
||||
'DisplayContent',
|
||||
'SetRepeatMode',
|
||||
'SetShuffleQueue',
|
||||
'EndSession',
|
||||
'PlayMediaSource',
|
||||
'PlayTrailers'
|
||||
"VolumeUp",
|
||||
"VolumeDown",
|
||||
"Mute",
|
||||
"Unmute",
|
||||
"ToggleMute",
|
||||
"SetVolume",
|
||||
"SetAudioStreamIndex",
|
||||
"SetSubtitleStreamIndex",
|
||||
"DisplayContent",
|
||||
"SetRepeatMode",
|
||||
"EndSession",
|
||||
"PlayMediaSource",
|
||||
"PlayTrailers"
|
||||
]
|
||||
};
|
||||
};
|
||||
@@ -675,7 +667,7 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
console.debug(JSON.stringify(data));
|
||||
|
||||
if (triggerStateChange) {
|
||||
events.trigger(this, 'statechange', [data]);
|
||||
events.trigger(this, "statechange", [data]);
|
||||
}
|
||||
|
||||
return data;
|
||||
@@ -866,12 +858,6 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
return state.RepeatMode;
|
||||
};
|
||||
|
||||
ChromecastPlayer.prototype.getQueueShuffleMode = function () {
|
||||
var state = this.lastPlayerData || {};
|
||||
state = state.PlayState || {};
|
||||
return state.ShuffleMode;
|
||||
};
|
||||
|
||||
ChromecastPlayer.prototype.playTrailers = function (item) {
|
||||
|
||||
this._castPlayer.sendMessage({
|
||||
@@ -892,15 +878,6 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
||||
});
|
||||
};
|
||||
|
||||
ChromecastPlayer.prototype.setQueueShuffleMode = function (value) {
|
||||
this._castPlayer.sendMessage({
|
||||
options: {
|
||||
ShuffleMode: value
|
||||
},
|
||||
command: 'SetShuffleQueue'
|
||||
});
|
||||
};
|
||||
|
||||
ChromecastPlayer.prototype.toggleMute = function () {
|
||||
|
||||
this._castPlayer.sendMessage({
|
||||
@@ -1,12 +1,25 @@
|
||||
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) {
|
||||
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) {
|
||||
'use strict';
|
||||
|
||||
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) {
|
||||
loading.show();
|
||||
|
||||
var panel = dom.parentWithClass(this, 'dialog');
|
||||
var panel = parentWithClass(this, 'dialog');
|
||||
|
||||
var collectionId = panel.querySelector('#selectCollectionToAddTo').value;
|
||||
|
||||
@@ -24,7 +37,7 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
|
||||
|
||||
function createCollection(apiClient, dlg) {
|
||||
|
||||
var url = apiClient.getUrl('Collections', {
|
||||
var url = apiClient.getUrl("Collections", {
|
||||
|
||||
Name: dlg.querySelector('#txtNewCollectionName').value,
|
||||
IsLocked: !dlg.querySelector('#chkEnableInternetMetadata').checked,
|
||||
@@ -32,9 +45,9 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
|
||||
});
|
||||
|
||||
apiClient.ajax({
|
||||
type: 'POST',
|
||||
type: "POST",
|
||||
url: url,
|
||||
dataType: 'json'
|
||||
dataType: "json"
|
||||
|
||||
}).then(function (result) {
|
||||
|
||||
@@ -56,13 +69,13 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
|
||||
|
||||
function addToCollection(apiClient, dlg, id) {
|
||||
|
||||
var url = apiClient.getUrl('Collections/' + id + '/Items', {
|
||||
var url = apiClient.getUrl("Collections/" + id + "/Items", {
|
||||
|
||||
Ids: dlg.querySelector('.fldSelectedItemIds').value || ''
|
||||
});
|
||||
|
||||
apiClient.ajax({
|
||||
type: 'POST',
|
||||
type: "POST",
|
||||
url: url
|
||||
|
||||
}).then(function () {
|
||||
@@ -93,8 +106,8 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
|
||||
var options = {
|
||||
|
||||
Recursive: true,
|
||||
IncludeItemTypes: 'BoxSet',
|
||||
SortBy: 'SortName',
|
||||
IncludeItemTypes: "BoxSet",
|
||||
SortBy: "SortName",
|
||||
EnableTotalRecordCount: false
|
||||
};
|
||||
|
||||
@@ -230,13 +243,13 @@ define(['dom', 'dialogHelper', 'loading', 'apphost', 'layoutManager', 'connectio
|
||||
var title = items.length ? globalize.translate('HeaderAddToCollection') : globalize.translate('NewCollection');
|
||||
|
||||
html += '<div class="formDialogHeader">';
|
||||
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
|
||||
html += '<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="material-icons arrow_back"></i></button>';
|
||||
html += '<h3 class="formDialogHeaderTitle">';
|
||||
html += title;
|
||||
html += '</h3>';
|
||||
|
||||
if (appHost.supports('externallinks')) {
|
||||
html += '<a is="emby-linkbutton" class="button-link btnHelp flex align-items-center" href="https://web.archive.org/web/20181216120305/https://github.com/MediaBrowser/Wiki/wiki/Collections" target="_blank" style="margin-left:auto;margin-right:.5em;padding:.25em;" title="' + globalize.translate('Help') + '"><span class="material-icons info"></span><span style="margin-left:.25em;">' + globalize.translate('Help') + '</span></a>';
|
||||
html += '<a is="emby-linkbutton" class="button-link btnHelp flex align-items-center" href="https://web.archive.org/web/20181216120305/https://github.com/MediaBrowser/Wiki/wiki/Collections" target="_blank" style="margin-left:auto;margin-right:.5em;padding:.25em;" title="' + globalize.translate('Help') + '"><i class="material-icons">info</i><span style="margin-left:.25em;">' + globalize.translate('Help') + '</span></a>';
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
@@ -1,65 +1,40 @@
|
||||
define(['browser', 'dialog', 'globalize'], function(browser, dialog, globalize) {
|
||||
define(['dialog', 'globalize'], function (dialog, globalize) {
|
||||
'use strict';
|
||||
|
||||
function replaceAll(str, find, replace) {
|
||||
return str.split(find).join(replace);
|
||||
}
|
||||
return function (text, title) {
|
||||
|
||||
if (browser.tv && window.confirm) {
|
||||
// Use the native confirm dialog
|
||||
return function (options) {
|
||||
if (typeof options === 'string') {
|
||||
options = {
|
||||
title: '',
|
||||
text: options
|
||||
};
|
||||
}
|
||||
var options;
|
||||
if (typeof text === 'string') {
|
||||
options = {
|
||||
title: title,
|
||||
text: text
|
||||
};
|
||||
} else {
|
||||
options = text;
|
||||
}
|
||||
|
||||
var text = replaceAll(options.text || '', '<br/>', '\n');
|
||||
var result = confirm(text);
|
||||
var items = [];
|
||||
|
||||
if (result) {
|
||||
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();
|
||||
} 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();
|
||||
});
|
||||
};
|
||||
}
|
||||
return Promise.reject();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
27
src/components/confirm/nativeconfirm.js
Normal file
27
src/components/confirm/nativeconfirm.js
Normal file
@@ -0,0 +1,27 @@
|
||||
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();
|
||||
}
|
||||
};
|
||||
});
|
||||
57
src/components/deletehelper.js
Normal file
57
src/components/deletehelper.js
Normal file
@@ -0,0 +1,57 @@
|
||||
define(['connectionManager', 'confirm', 'appRouter', 'globalize'], function (connectionManager, confirm, appRouter, globalize) {
|
||||
'use strict';
|
||||
|
||||
function alertText(options) {
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
require(['alert'], function (alert) {
|
||||
alert(options).then(resolve, resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function deleteItem(options) {
|
||||
|
||||
var item = options.item;
|
||||
var itemId = item.Id;
|
||||
var parentId = item.SeasonId || item.SeriesId || item.ParentId;
|
||||
var serverId = item.ServerId;
|
||||
|
||||
var msg = globalize.translate('ConfirmDeleteItem');
|
||||
var title = globalize.translate('HeaderDeleteItem');
|
||||
var apiClient = connectionManager.getApiClient(item.ServerId);
|
||||
|
||||
return confirm({
|
||||
|
||||
title: title,
|
||||
text: msg,
|
||||
confirmText: globalize.translate('Delete'),
|
||||
primary: 'delete'
|
||||
|
||||
}).then(function () {
|
||||
|
||||
return apiClient.deleteItem(itemId).then(function () {
|
||||
|
||||
if (options.navigate) {
|
||||
if (parentId) {
|
||||
appRouter.showItem(parentId, serverId);
|
||||
} else {
|
||||
appRouter.goHome();
|
||||
}
|
||||
}
|
||||
}, function (err) {
|
||||
|
||||
var result = function () {
|
||||
return Promise.reject(err);
|
||||
};
|
||||
|
||||
return alertText(globalize.translate('ErrorDeletingItem')).then(result, result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
deleteItem: deleteItem
|
||||
};
|
||||
});
|
||||
@@ -4,8 +4,12 @@
|
||||
|
||||
<div class="formDialogContent smoothScrollY">
|
||||
<div class="dialogContentInner dialog-content-centered" style="padding-top:1em;padding-bottom: 1em; text-align: center;">
|
||||
<div class="text"></div>
|
||||
|
||||
<div class="text">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="formDialogFooter formDialogFooter-clear formDialogFooter-flex" style="margin:1em"></div>
|
||||
<div class="formDialogFooter formDialogFooter-clear formDialogFooter-flex" style="padding-bottom: 1.5em;">
|
||||
</div>
|
||||
|
||||
@@ -141,7 +141,7 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
|
||||
animateDialogOpen(dlg);
|
||||
|
||||
if (isHistoryEnabled(dlg)) {
|
||||
appRouter.pushState({ dialogId: hash }, 'Dialog', '#' + hash);
|
||||
appRouter.pushState({ dialogId: hash }, "Dialog", '#' + hash);
|
||||
|
||||
window.addEventListener('popstate', onHashChange);
|
||||
} else {
|
||||
@@ -169,15 +169,6 @@ define(['appRouter', 'focusManager', 'browser', 'layoutManager', 'inputManager',
|
||||
}, {
|
||||
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) {
|
||||
|
||||
@@ -126,10 +126,25 @@
|
||||
}
|
||||
|
||||
@media all and (min-width: 80em) and (min-height: 45em) {
|
||||
.dialog-medium {
|
||||
width: 80%;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.dialog-medium-tall {
|
||||
width: 80%;
|
||||
height: 90%;
|
||||
}
|
||||
|
||||
.dialog-small {
|
||||
width: 60%;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.dialog-fullscreen-border {
|
||||
width: 90%;
|
||||
height: 90%;
|
||||
}
|
||||
}
|
||||
|
||||
.noScroll {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-input', 'paper-icon-button-light', 'css!./directorybrowser', 'formDialogStyle', 'emby-button'], function(loading, dialogHelper, dom, globalize) {
|
||||
define(['loading', 'dialogHelper', 'dom', 'listViewStyle', 'emby-input', 'paper-icon-button-light', 'css!./directorybrowser', 'formDialogStyle', 'emby-button'], function(loading, dialogHelper, dom) {
|
||||
'use strict';
|
||||
|
||||
function getSystemInfo() {
|
||||
@@ -7,24 +7,24 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
|
||||
systemInfo = info;
|
||||
return info;
|
||||
}
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
function onDialogClosed() {
|
||||
loading.hide();
|
||||
loading.hide()
|
||||
}
|
||||
|
||||
function refreshDirectoryBrowser(page, path, fileOptions, updatePathOnError) {
|
||||
if (path && typeof path !== 'string') {
|
||||
throw new Error('invalid path');
|
||||
throw new Error("invalid path");
|
||||
}
|
||||
|
||||
loading.show();
|
||||
|
||||
var promises = [];
|
||||
|
||||
if ('Network' === path) {
|
||||
promises.push(ApiClient.getNetworkDevices());
|
||||
if ("Network" === path) {
|
||||
promises.push(ApiClient.getNetworkDevices())
|
||||
} else {
|
||||
if (path) {
|
||||
promises.push(ApiClient.getDirectoryContents(path, fileOptions));
|
||||
@@ -37,31 +37,31 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
|
||||
Promise.all(promises).then(
|
||||
function(responses) {
|
||||
var folders = responses[0];
|
||||
var parentPath = responses[1] || '';
|
||||
var html = '';
|
||||
var parentPath = responses[1] || "";
|
||||
var html = "";
|
||||
|
||||
page.querySelector('.results').scrollTop = 0;
|
||||
page.querySelector('#txtDirectoryPickerPath').value = path || '';
|
||||
page.querySelector(".results").scrollTop = 0;
|
||||
page.querySelector("#txtDirectoryPickerPath").value = path || "";
|
||||
|
||||
if (path) {
|
||||
html += getItem('lnkPath lnkDirectory', '', parentPath, '...');
|
||||
html += getItem("lnkPath lnkDirectory", "", parentPath, "...");
|
||||
}
|
||||
for (var i = 0, length = folders.length; i < length; i++) {
|
||||
var folder = folders[i];
|
||||
var cssClass = 'File' === folder.Type ? 'lnkPath lnkFile' : 'lnkPath lnkDirectory';
|
||||
var cssClass = "File" === folder.Type ? "lnkPath lnkFile" : "lnkPath lnkDirectory";
|
||||
html += getItem(cssClass, folder.Type, folder.Path, folder.Name);
|
||||
}
|
||||
|
||||
if (!path) {
|
||||
html += getItem('lnkPath lnkDirectory', '', 'Network', globalize.translate('ButtonNetwork'));
|
||||
html += getItem("lnkPath lnkDirectory", "", "Network", Globalize.translate("ButtonNetwork"));
|
||||
}
|
||||
|
||||
page.querySelector('.results').innerHTML = html;
|
||||
page.querySelector(".results").innerHTML = html;
|
||||
loading.hide();
|
||||
}, function() {
|
||||
if (updatePathOnError) {
|
||||
page.querySelector('#txtDirectoryPickerPath').value = '';
|
||||
page.querySelector('.results').innerHTML = '';
|
||||
page.querySelector("#txtDirectoryPickerPath").value = "";
|
||||
page.querySelector(".results").innerHTML = "";
|
||||
loading.hide();
|
||||
}
|
||||
}
|
||||
@@ -69,73 +69,74 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
|
||||
}
|
||||
|
||||
function getItem(cssClass, type, path, name) {
|
||||
var html = '';
|
||||
var html = "";
|
||||
html += '<div class="listItem listItem-border ' + cssClass + '" data-type="' + type + '" data-path="' + path + '">';
|
||||
html += '<div class="listItemBody" style="padding-left:0;padding-top:.5em;padding-bottom:.5em;">';
|
||||
html += '<div class="listItemBodyText">';
|
||||
html += name;
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '<span class="material-icons arrow_forward" style="font-size:inherit;"></span>';
|
||||
html += '</div>';
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
html += '<i class="material-icons arrow_forward" style="font-size:inherit;"></i>';
|
||||
html += "</div>";
|
||||
return html;
|
||||
}
|
||||
|
||||
function getEditorHtml(options, systemInfo) {
|
||||
var html = '';
|
||||
var html = "";
|
||||
html += '<div class="formDialogContent scrollY">';
|
||||
html += '<div class="dialogContentInner dialog-content-centered" style="padding-top:2em;">';
|
||||
if (!options.pathReadOnly) {
|
||||
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 += instruction;
|
||||
if ('bsd' === systemInfo.OperatingSystem.toLowerCase()) {
|
||||
html += '<br/>';
|
||||
html += '<br/>';
|
||||
html += globalize.translate('MessageDirectoryPickerBSDInstruction');
|
||||
html += '<br/>';
|
||||
} else if ('linux' === systemInfo.OperatingSystem.toLowerCase()) {
|
||||
html += '<br/>';
|
||||
html += '<br/>';
|
||||
html += globalize.translate('MessageDirectoryPickerLinuxInstruction');
|
||||
html += '<br/>';
|
||||
html += Globalize.translate("MessageDirectoryPickerInstruction").replace("{0}", "<b>\\\\server</b>").replace("{1}", "<b>\\\\192.168.1.101</b>");
|
||||
if ("bsd" === systemInfo.OperatingSystem.toLowerCase()) {
|
||||
html += "<br/>";
|
||||
html += "<br/>";
|
||||
html += Globalize.translate("MessageDirectoryPickerBSDInstruction");
|
||||
html += "<br/>";
|
||||
} else if ("linux" === systemInfo.OperatingSystem.toLowerCase()) {
|
||||
html += "<br/>";
|
||||
html += "<br/>";
|
||||
html += Globalize.translate("MessageDirectoryPickerLinuxInstruction");
|
||||
html += "<br/>";
|
||||
}
|
||||
html += '</div>';
|
||||
html += "</div>"
|
||||
}
|
||||
html += '<form style="margin:auto;">';
|
||||
html += '<div class="inputContainer" style="display: flex; align-items: center;">';
|
||||
html += '<div style="flex-grow:1;">';
|
||||
var labelKey;
|
||||
if (options.includeFiles !== true) {
|
||||
labelKey = 'LabelFolder';
|
||||
labelKey = "LabelFolder";
|
||||
} else {
|
||||
labelKey = 'LabelPath';
|
||||
labelKey = "LabelPath";
|
||||
}
|
||||
var readOnlyAttribute = options.pathReadOnly ? ' readonly' : '';
|
||||
html += '<input is="emby-input" id="txtDirectoryPickerPath" type="text" required="required" ' + readOnlyAttribute + ' label="' + globalize.translate(labelKey) + '"/>';
|
||||
html += '</div>';
|
||||
var readOnlyAttribute = options.pathReadOnly ? " readonly" : "";
|
||||
html += '<input is="emby-input" id="txtDirectoryPickerPath" type="text" required="required" ' + readOnlyAttribute + ' label="' + Globalize.translate(labelKey) + '"/>';
|
||||
html += "</div>";
|
||||
if (!readOnlyAttribute) {
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnRefreshDirectories emby-input-iconbutton" title="' + globalize.translate('ButtonRefresh') + '"><span class="material-icons search"></span></button>';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="btnRefreshDirectories emby-input-iconbutton" title="' + Globalize.translate("ButtonRefresh") + '"><i class="material-icons">search</i></button>';
|
||||
}
|
||||
html += '</div>';
|
||||
html += "</div>";
|
||||
if (!readOnlyAttribute) {
|
||||
html += '<div class="results paperList" style="max-height: 200px; overflow-y: auto;"></div>';
|
||||
}
|
||||
if (options.enableNetworkSharePath) {
|
||||
html += '<div class="inputContainer" style="margin-top:2em;">';
|
||||
html += '<input is="emby-input" id="txtNetworkPath" type="text" label="' + globalize.translate('LabelOptionalNetworkPath') + '"/>';
|
||||
html += '<input is="emby-input" id="txtNetworkPath" type="text" label="' + Globalize.translate("LabelOptionalNetworkPath") + '"/>';
|
||||
html += '<div class="fieldDescription">';
|
||||
html += globalize.translate('LabelOptionalNetworkPathHelp', '<b>\\\\server</b>', '<b>\\\\192.168.1.101</b>');
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += Globalize.translate("LabelOptionalNetworkPathHelp");
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
}
|
||||
html += '<div class="formDialogFooter">';
|
||||
html += '<button is="emby-button" type="submit" class="raised button-submit block formDialogFooterItem">' + globalize.translate('ButtonOk') + '</button>';
|
||||
html += '</div>';
|
||||
html += '</form>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '</div>';
|
||||
html += '<button is="emby-button" type="submit" class="raised button-submit block formDialogFooterItem">' + Globalize.translate("ButtonOk") + "</button>";
|
||||
html += "</div>";
|
||||
html += "</form>";
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
html += "</div>";
|
||||
|
||||
return html;
|
||||
}
|
||||
@@ -143,74 +144,75 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
|
||||
function alertText(text) {
|
||||
alertTextWithOptions({
|
||||
text: text
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function alertTextWithOptions(options) {
|
||||
require(['alert'], function(alert) {
|
||||
alert(options);
|
||||
});
|
||||
require(["alert"], function(alert) {
|
||||
alert(options)
|
||||
})
|
||||
}
|
||||
|
||||
function validatePath(path, validateWriteable, apiClient) {
|
||||
return apiClient.ajax({
|
||||
type: 'POST',
|
||||
url: apiClient.getUrl('Environment/ValidatePath'),
|
||||
type: "POST",
|
||||
url: apiClient.getUrl("Environment/ValidatePath"),
|
||||
data: {
|
||||
ValidateWriteable: validateWriteable,
|
||||
Path: path
|
||||
}
|
||||
}).catch(function(response) {
|
||||
if (response) {
|
||||
// TODO All alerts (across the project), should use Globalize.translate()
|
||||
if (response.status === 404) {
|
||||
alertText(globalize.translate('PathNotFound'));
|
||||
alertText("The path could not be found. Please ensure the path is valid and try again.");
|
||||
return Promise.reject();
|
||||
}
|
||||
if (response.status === 500) {
|
||||
if (validateWriteable) {
|
||||
alertText(globalize.translate('WriteAccessRequired'));
|
||||
alertText("Jellyfin Server requires write access to this folder. Please ensure write access and try again.");
|
||||
} else {
|
||||
alertText(globalize.translate('PathNotFound'));
|
||||
alertText("The path could not be found. Please ensure the path is valid and try again.")
|
||||
}
|
||||
return Promise.reject();
|
||||
return Promise.reject()
|
||||
}
|
||||
}
|
||||
return Promise.resolve();
|
||||
return Promise.resolve()
|
||||
});
|
||||
}
|
||||
|
||||
function initEditor(content, options, fileOptions) {
|
||||
content.addEventListener('click', function(e) {
|
||||
var lnkPath = dom.parentWithClass(e.target, 'lnkPath');
|
||||
content.addEventListener("click", function(e) {
|
||||
var lnkPath = dom.parentWithClass(e.target, "lnkPath");
|
||||
if (lnkPath) {
|
||||
var path = lnkPath.getAttribute('data-path');
|
||||
if (lnkPath.classList.contains('lnkFile')) {
|
||||
content.querySelector('#txtDirectoryPickerPath').value = path;
|
||||
var path = lnkPath.getAttribute("data-path");
|
||||
if (lnkPath.classList.contains("lnkFile")) {
|
||||
content.querySelector("#txtDirectoryPickerPath").value = path;
|
||||
} else {
|
||||
refreshDirectoryBrowser(content, path, fileOptions, true);
|
||||
refreshDirectoryBrowser(content, path, fileOptions, true)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
content.addEventListener('click', function(e) {
|
||||
if (dom.parentWithClass(e.target, 'btnRefreshDirectories')) {
|
||||
var path = content.querySelector('#txtDirectoryPickerPath').value;
|
||||
content.addEventListener("click", function(e) {
|
||||
if (dom.parentWithClass(e.target, "btnRefreshDirectories")) {
|
||||
var path = content.querySelector("#txtDirectoryPickerPath").value;
|
||||
refreshDirectoryBrowser(content, path, fileOptions);
|
||||
}
|
||||
});
|
||||
|
||||
content.addEventListener('change', function(e) {
|
||||
var txtDirectoryPickerPath = dom.parentWithTag(e.target, 'INPUT');
|
||||
if (txtDirectoryPickerPath && 'txtDirectoryPickerPath' === txtDirectoryPickerPath.id) {
|
||||
content.addEventListener("change", function(e) {
|
||||
var txtDirectoryPickerPath = dom.parentWithTag(e.target, "INPUT");
|
||||
if (txtDirectoryPickerPath && "txtDirectoryPickerPath" === txtDirectoryPickerPath.id) {
|
||||
refreshDirectoryBrowser(content, txtDirectoryPickerPath.value, fileOptions);
|
||||
}
|
||||
});
|
||||
|
||||
content.querySelector('form').addEventListener('submit', function(e) {
|
||||
content.querySelector("form").addEventListener("submit", function(e) {
|
||||
if (options.callback) {
|
||||
var networkSharePath = this.querySelector('#txtNetworkPath');
|
||||
var networkSharePath = this.querySelector("#txtNetworkPath");
|
||||
networkSharePath = networkSharePath ? networkSharePath.value : null;
|
||||
var path = this.querySelector('#txtDirectoryPickerPath').value;
|
||||
var path = this.querySelector("#txtDirectoryPickerPath").value;
|
||||
validatePath(path, options.validateWriteable, ApiClient).then(options.callback(path, networkSharePath));
|
||||
}
|
||||
e.preventDefault();
|
||||
@@ -223,11 +225,11 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
|
||||
if (options.path) {
|
||||
return Promise.resolve(options.path);
|
||||
} else {
|
||||
return ApiClient.getJSON(ApiClient.getUrl('Environment/DefaultDirectoryBrowser')).then(
|
||||
return ApiClient.getJSON(ApiClient.getUrl("Environment/DefaultDirectoryBrowser")).then(
|
||||
function(result) {
|
||||
return result.Path || '';
|
||||
return result.Path || "";
|
||||
}, function() {
|
||||
return '';
|
||||
return "";
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -252,35 +254,35 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
|
||||
var systemInfo = responses[0];
|
||||
var initialPath = responses[1];
|
||||
var dlg = dialogHelper.createDialog({
|
||||
size: 'small',
|
||||
size: "medium-tall",
|
||||
removeOnClose: true,
|
||||
scrollY: false
|
||||
});
|
||||
dlg.classList.add('ui-body-a');
|
||||
dlg.classList.add('background-theme-a');
|
||||
dlg.classList.add('directoryPicker');
|
||||
dlg.classList.add('formDialog');
|
||||
dlg.classList.add("ui-body-a");
|
||||
dlg.classList.add("background-theme-a");
|
||||
dlg.classList.add("directoryPicker");
|
||||
dlg.classList.add("formDialog");
|
||||
|
||||
var html = '';
|
||||
var html = "";
|
||||
html += '<div class="formDialogHeader">';
|
||||
html += '<button is="paper-icon-button-light" class="btnCloseDialog autoSize" tabindex="-1"><span class="material-icons arrow_back"></span></button>';
|
||||
html += '<button is="paper-icon-button-light" class="btnCloseDialog autoSize" tabindex="-1"><i class="material-icons arrow_back"></i></button>';
|
||||
html += '<h3 class="formDialogHeaderTitle">';
|
||||
html += options.header || globalize.translate('HeaderSelectPath');
|
||||
html += '</h3>';
|
||||
html += '</div>';
|
||||
html += options.header || Globalize.translate("HeaderSelectPath");
|
||||
html += "</h3>";
|
||||
html += "</div>";
|
||||
html += getEditorHtml(options, systemInfo);
|
||||
dlg.innerHTML = html;
|
||||
initEditor(dlg, options, fileOptions);
|
||||
dlg.addEventListener('close', onDialogClosed);
|
||||
dlg.addEventListener("close", onDialogClosed);
|
||||
dialogHelper.open(dlg);
|
||||
dlg.querySelector('.btnCloseDialog').addEventListener('click', function() {
|
||||
dialogHelper.close(dlg);
|
||||
dlg.querySelector(".btnCloseDialog").addEventListener("click", function() {
|
||||
dialogHelper.close(dlg)
|
||||
});
|
||||
currentDialog = dlg;
|
||||
dlg.querySelector('#txtDirectoryPickerPath').value = initialPath;
|
||||
var txtNetworkPath = dlg.querySelector('#txtNetworkPath');
|
||||
dlg.querySelector("#txtDirectoryPickerPath").value = initialPath;
|
||||
var txtNetworkPath = dlg.querySelector("#txtNetworkPath");
|
||||
if (txtNetworkPath) {
|
||||
txtNetworkPath.value = options.networkSharePath || '';
|
||||
txtNetworkPath.value = options.networkSharePath || "";
|
||||
}
|
||||
if (!options.pathReadOnly) {
|
||||
refreshDirectoryBrowser(dlg, initialPath, fileOptions, true);
|
||||
@@ -292,9 +294,9 @@ define(['loading', 'dialogHelper', 'dom', 'globalize', 'listViewStyle', 'emby-in
|
||||
if (currentDialog) {
|
||||
dialogHelper.close(currentDialog);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var systemInfo;
|
||||
return directoryBrowser;
|
||||
return directoryBrowser
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', 'apphost', 'focusManager', 'datetime', 'globalize', 'loading', 'connectionManager', 'skinManager', 'dom', 'events', 'emby-select', 'emby-checkbox', 'emby-button'], function (require, browser, layoutManager, appSettings, pluginManager, appHost, focusManager, datetime, globalize, loading, connectionManager, skinManager, dom, events) {
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
function fillThemes(select, isDashboard) {
|
||||
select.innerHTML = skinManager.getThemes().map(function (t) {
|
||||
@@ -181,15 +181,11 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
|
||||
context.querySelector('#chkThemeSong').checked = userSettings.enableThemeSongs();
|
||||
context.querySelector('#chkThemeVideo').checked = userSettings.enableThemeVideos();
|
||||
context.querySelector('#chkFadein').checked = userSettings.enableFastFadein();
|
||||
context.querySelector('#chkBlurhash').checked = userSettings.enableBlurhash();
|
||||
context.querySelector('#chkBackdrops').checked = userSettings.enableBackdrops();
|
||||
context.querySelector('#chkDetailsBanner').checked = userSettings.detailsBanner();
|
||||
|
||||
context.querySelector('#selectLanguage').value = userSettings.language() || '';
|
||||
context.querySelector('.selectDateTimeLocale').value = userSettings.dateTimeLocale() || '';
|
||||
|
||||
context.querySelector('#txtLibraryPageSize').value = userSettings.libraryPageSize();
|
||||
|
||||
selectDashboardTheme.value = userSettings.dashboardTheme() || '';
|
||||
selectTheme.value = userSettings.theme() || '';
|
||||
|
||||
@@ -219,14 +215,10 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
|
||||
userSettingsInstance.soundEffects(context.querySelector('.selectSoundEffects').value);
|
||||
userSettingsInstance.screensaver(context.querySelector('.selectScreensaver').value);
|
||||
|
||||
userSettingsInstance.libraryPageSize(context.querySelector('#txtLibraryPageSize').value);
|
||||
|
||||
userSettingsInstance.skin(context.querySelector('.selectSkin').value);
|
||||
|
||||
userSettingsInstance.enableFastFadein(context.querySelector('#chkFadein').checked);
|
||||
userSettingsInstance.enableBlurhash(context.querySelector('#chkBlurhash').checked);
|
||||
userSettingsInstance.enableBackdrops(context.querySelector('#chkBackdrops').checked);
|
||||
userSettingsInstance.detailsBanner(context.querySelector('#chkDetailsBanner').checked);
|
||||
|
||||
if (user.Id === apiClient.getCurrentUserId()) {
|
||||
skinManager.setTheme(userSettingsInstance.theme());
|
||||
@@ -273,7 +265,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
|
||||
}
|
||||
|
||||
function embed(options, self) {
|
||||
require(['text!./displaySettings.template.html'], function (template) {
|
||||
require(['text!./displaysettings.template.html'], function (template) {
|
||||
options.element.innerHTML = globalize.translateDocument(template, 'core');
|
||||
options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self));
|
||||
if (options.enableSaveButton) {
|
||||
@@ -56,14 +56,14 @@
|
||||
<div class="fieldDescription">
|
||||
<div>${LabelDisplayLanguageHelp}</div>
|
||||
<div class="learnHowToContributeContainer hide" style="margin-top: .25em;">
|
||||
<a is="emby-linkbutton" rel="noopener noreferrer" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">${LearnHowYouCanContribute}</a>
|
||||
<a is="emby-linkbutton" class="button-link" href="https://github.com/jellyfin/jellyfin" target="_blank">${LearnHowYouCanContribute}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer fldDateTimeLocale hide">
|
||||
<select is="emby-select" class="selectDateTimeLocale" label="${LabelDateTimeLocale}">
|
||||
<option value="">${Auto}</option>
|
||||
<option value="">${AutoBasedOnLanguageSetting}</option>
|
||||
<option value="ar">Arabic</option>
|
||||
<option value="be-BY">Belarusian (Belarus)</option>
|
||||
<option value="bg-BG">Bulgarian (Bulgaria)</option>
|
||||
@@ -143,33 +143,12 @@
|
||||
<select is="emby-select" class="selectSoundEffects" label="${LabelSoundEffects}"></select>
|
||||
</div>
|
||||
|
||||
<div class="inputContainer inputContainer-withDescription">
|
||||
<input is="emby-input" type="number" id="txtLibraryPageSize" pattern="[0-9]*" required="required" min="0" max="1000" step="1" label="${LabelLibraryPageSize}" />
|
||||
<div class="fieldDescription">${LabelLibraryPageSizeHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<div class="checkboxContainer checkboxContainer-withDescription fldFadein">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkFadein" />
|
||||
<span>${EnableFasterAnimations}</span>
|
||||
<span>${EnableFastImageFadeIn}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${EnableFasterAnimationsHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkBlurhash" />
|
||||
<span>${EnableBlurhash}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${EnableBlurhashHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" id="chkDetailsBanner" />
|
||||
<span>${EnableDetailsBanner}</span>
|
||||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${EnableDetailsBannerHelp}</div>
|
||||
<div class="fieldDescription checkboxFieldDescription">${EnableFastImageFadeInHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription fldBackdrops hide">
|
||||
199
src/components/dom.js
Normal file
199
src/components/dom.js
Normal file
@@ -0,0 +1,199 @@
|
||||
define([], function () {
|
||||
'use strict';
|
||||
|
||||
function parentWithAttribute(elem, name, value) {
|
||||
|
||||
while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) {
|
||||
elem = elem.parentNode;
|
||||
|
||||
if (!elem || !elem.getAttribute) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
function parentWithTag(elem, tagNames) {
|
||||
|
||||
// accept both string and array passed in
|
||||
if (!Array.isArray(tagNames)) {
|
||||
tagNames = [tagNames];
|
||||
}
|
||||
|
||||
while (tagNames.indexOf(elem.tagName || '') === -1) {
|
||||
elem = elem.parentNode;
|
||||
|
||||
if (!elem) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
function containsAnyClass(classList, classNames) {
|
||||
|
||||
for (var i = 0, length = classNames.length; i < length; i++) {
|
||||
if (classList.contains(classNames[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function parentWithClass(elem, classNames) {
|
||||
|
||||
// accept both string and array passed in
|
||||
if (!Array.isArray(classNames)) {
|
||||
classNames = [classNames];
|
||||
}
|
||||
|
||||
while (!elem.classList || !containsAnyClass(elem.classList, classNames)) {
|
||||
elem = elem.parentNode;
|
||||
|
||||
if (!elem) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return elem;
|
||||
}
|
||||
|
||||
var supportsCaptureOption = false;
|
||||
try {
|
||||
var opts = Object.defineProperty({}, 'capture', {
|
||||
// eslint-disable-next-line getter-return
|
||||
get: function () {
|
||||
supportsCaptureOption = true;
|
||||
}
|
||||
});
|
||||
window.addEventListener("test", null, opts);
|
||||
} catch (e) {
|
||||
console.debug('error checking capture support');
|
||||
}
|
||||
|
||||
function addEventListenerWithOptions(target, type, handler, options) {
|
||||
var optionsOrCapture = options;
|
||||
if (!supportsCaptureOption) {
|
||||
optionsOrCapture = options.capture;
|
||||
}
|
||||
target.addEventListener(type, handler, optionsOrCapture);
|
||||
}
|
||||
|
||||
function removeEventListenerWithOptions(target, type, handler, options) {
|
||||
var optionsOrCapture = options;
|
||||
if (!supportsCaptureOption) {
|
||||
optionsOrCapture = options.capture;
|
||||
}
|
||||
target.removeEventListener(type, handler, optionsOrCapture);
|
||||
}
|
||||
|
||||
var windowSize;
|
||||
var windowSizeEventsBound;
|
||||
function clearWindowSize() {
|
||||
windowSize = null;
|
||||
}
|
||||
|
||||
function getWindowSize() {
|
||||
if (!windowSize) {
|
||||
windowSize = {
|
||||
innerHeight: window.innerHeight,
|
||||
innerWidth: window.innerWidth
|
||||
};
|
||||
|
||||
if (!windowSizeEventsBound) {
|
||||
windowSizeEventsBound = true;
|
||||
addEventListenerWithOptions(window, "orientationchange", clearWindowSize, { passive: true });
|
||||
addEventListenerWithOptions(window, 'resize', clearWindowSize, { passive: true });
|
||||
}
|
||||
}
|
||||
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
var standardWidths = [480, 720, 1280, 1440, 1920, 2560, 3840, 5120, 7680];
|
||||
function getScreenWidth() {
|
||||
var width = window.innerWidth;
|
||||
var height = window.innerHeight;
|
||||
|
||||
if (height > width) {
|
||||
width = height * (16.0 / 9.0);
|
||||
}
|
||||
|
||||
var closest = standardWidths.sort(function (a, b) {
|
||||
return Math.abs(width - a) - Math.abs(width - b);
|
||||
})[0];
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
var _animationEvent;
|
||||
function whichAnimationEvent() {
|
||||
|
||||
if (_animationEvent) {
|
||||
return _animationEvent;
|
||||
}
|
||||
|
||||
var t;
|
||||
var el = document.createElement("div");
|
||||
var animations = {
|
||||
"animation": "animationend",
|
||||
"OAnimation": "oAnimationEnd",
|
||||
"MozAnimation": "animationend",
|
||||
"WebkitAnimation": "webkitAnimationEnd"
|
||||
};
|
||||
for (t in animations) {
|
||||
if (el.style[t] !== undefined) {
|
||||
_animationEvent = animations[t];
|
||||
return animations[t];
|
||||
}
|
||||
}
|
||||
|
||||
_animationEvent = 'animationend';
|
||||
return _animationEvent;
|
||||
}
|
||||
|
||||
function whichAnimationCancelEvent() {
|
||||
|
||||
return whichAnimationEvent().replace('animationend', 'animationcancel').replace('AnimationEnd', 'AnimationCancel');
|
||||
}
|
||||
|
||||
var _transitionEvent;
|
||||
function whichTransitionEvent() {
|
||||
if (_transitionEvent) {
|
||||
return _transitionEvent;
|
||||
}
|
||||
|
||||
var t;
|
||||
var el = document.createElement("div");
|
||||
var transitions = {
|
||||
"transition": "transitionend",
|
||||
"OTransition": "oTransitionEnd",
|
||||
"MozTransition": "transitionend",
|
||||
"WebkitTransition": "webkitTransitionEnd"
|
||||
};
|
||||
for (t in transitions) {
|
||||
if (el.style[t] !== undefined) {
|
||||
_transitionEvent = transitions[t];
|
||||
return transitions[t];
|
||||
}
|
||||
}
|
||||
|
||||
_transitionEvent = 'transitionend';
|
||||
return _transitionEvent;
|
||||
}
|
||||
|
||||
return {
|
||||
parentWithAttribute: parentWithAttribute,
|
||||
parentWithClass: parentWithClass,
|
||||
parentWithTag: parentWithTag,
|
||||
addEventListener: addEventListenerWithOptions,
|
||||
removeEventListener: removeEventListenerWithOptions,
|
||||
getWindowSize: getWindowSize,
|
||||
getScreenWidth: getScreenWidth,
|
||||
whichTransitionEvent: whichTransitionEvent,
|
||||
whichAnimationEvent: whichAnimationEvent,
|
||||
whichAnimationCancelEvent: whichAnimationCancelEvent
|
||||
};
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['emby-progressring', 'dom', 'serverNotifications', 'events', 'webcomponents'], function (EmbyProgressRing, dom, serverNotifications, events) {
|
||||
define(['emby-progressring', 'dom', 'serverNotifications', 'events', 'registerElement'], function (EmbyProgressRing, dom, serverNotifications, events) {
|
||||
'use strict';
|
||||
|
||||
function addNotificationEvent(instance, name, handler) {
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['itemShortcuts', 'inputManager', 'connectionManager', 'playbackManager', 'imageLoader', 'layoutManager', 'browser', 'dom', 'loading', 'focusManager', 'serverNotifications', 'events', 'webcomponents'], function (itemShortcuts, inputManager, connectionManager, playbackManager, imageLoader, layoutManager, browser, dom, loading, focusManager, serverNotifications, events) {
|
||||
define(['itemShortcuts', 'inputManager', 'connectionManager', 'playbackManager', 'imageLoader', 'layoutManager', 'browser', 'dom', 'loading', 'focusManager', 'serverNotifications', 'events', 'registerElement'], function (itemShortcuts, inputManager, connectionManager, playbackManager, imageLoader, layoutManager, browser, dom, loading, focusManager, serverNotifications, events) {
|
||||
'use strict';
|
||||
|
||||
var ItemsContainerPrototype = Object.create(HTMLDivElement.prototype);
|
||||
@@ -124,7 +124,7 @@ define(['itemShortcuts', 'inputManager', 'connectionManager', 'playbackManager',
|
||||
var self = this;
|
||||
require(['sortable'], function (Sortable) {
|
||||
self.sortable = new Sortable(self, {
|
||||
draggable: '.listItem',
|
||||
draggable: ".listItem",
|
||||
handle: '.listViewDragHandle',
|
||||
|
||||
// dragging ended
|
||||
@@ -6,13 +6,13 @@
|
||||
justify-content: center;
|
||||
min-width: 104px;
|
||||
min-height: 24px;
|
||||
padding-top: 0.85em;
|
||||
padding-top: 1.25em;
|
||||
z-index: 1;
|
||||
color: #fff;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.emby-scrollbuttons-button > .material-icons {
|
||||
.emby-scrollbuttons-button > i {
|
||||
min-width: 24px;
|
||||
min-height: 24px;
|
||||
display: block;
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'webcomponents', 'paper-icon-button-light'], function (layoutManager, dom) {
|
||||
define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'registerElement', 'paper-icon-button-light'], function (layoutManager, dom) {
|
||||
'use strict';
|
||||
|
||||
var EmbyScrollButtonsPrototype = Object.create(HTMLDivElement.prototype);
|
||||
@@ -7,10 +7,10 @@ define(['layoutManager', 'dom', 'css!./emby-scrollbuttons', 'webcomponents', 'pa
|
||||
|
||||
function getScrollButtonHtml(direction) {
|
||||
var html = '';
|
||||
var icon = direction === 'left' ? 'chevron_left' : 'chevron_right';
|
||||
var icon = direction === 'left' ? '' : '';
|
||||
|
||||
html += '<button type="button" is="paper-icon-button-light" data-ripple="false" data-direction="' + direction + '" class="emby-scrollbuttons-button">';
|
||||
html += '<span class="material-icons ' + icon + '"></span>';
|
||||
html += '<i class="material-icons">' + icon + '</i>';
|
||||
html += '</button>';
|
||||
|
||||
return html;
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'browser', 'webcomponents', 'css!./emby-scroller'], function (scroller, dom, layoutManager, inputManager, focusManager, browser) {
|
||||
define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'browser', 'registerElement', 'css!./emby-scroller'], function (scroller, dom, layoutManager, inputManager, focusManager, browser) {
|
||||
'use strict';
|
||||
|
||||
var ScrollerPrototype = Object.create(HTMLDivElement.prototype);
|
||||
@@ -96,6 +96,17 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
|
||||
}
|
||||
}
|
||||
|
||||
function initHeadroom(elem) {
|
||||
require(['headroom'], function (Headroom) {
|
||||
var headroom = new Headroom([], {
|
||||
scroller: elem
|
||||
});
|
||||
|
||||
headroom.add(document.querySelector('.skinHeader'));
|
||||
elem.headroom = headroom;
|
||||
});
|
||||
}
|
||||
|
||||
ScrollerPrototype.attachedCallback = function () {
|
||||
if (this.getAttribute('data-navcommands')) {
|
||||
inputManager.on(this, onInputCommand);
|
||||
@@ -109,6 +120,8 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
|
||||
slider.style['white-space'] = 'nowrap';
|
||||
}
|
||||
|
||||
var bindHeader = this.getAttribute('data-bindheader') === 'true';
|
||||
|
||||
var scrollFrame = this;
|
||||
var enableScrollButtons = layoutManager.desktop && horizontal && this.getAttribute('data-scrollbuttons') !== 'false';
|
||||
|
||||
@@ -124,7 +137,7 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
|
||||
dragHandle: 1,
|
||||
autoImmediate: true,
|
||||
skipSlideToWhenVisible: this.getAttribute('data-skipfocuswhenvisible') === 'true',
|
||||
dispatchScrollEvent: enableScrollButtons || this.getAttribute('data-scrollevent') === 'true',
|
||||
dispatchScrollEvent: enableScrollButtons || bindHeader || this.getAttribute('data-scrollevent') === 'true',
|
||||
hideScrollbar: enableScrollButtons || this.getAttribute('data-hidescrollbar') === 'true',
|
||||
allowNativeSmoothScroll: this.getAttribute('data-allownativesmoothscroll') === 'true' && !enableScrollButtons,
|
||||
allowNativeScroll: !enableScrollButtons,
|
||||
@@ -142,6 +155,10 @@ define(['scroller', 'dom', 'layoutManager', 'inputManager', 'focusManager', 'bro
|
||||
initCenterFocus(this, this.scroller);
|
||||
}
|
||||
|
||||
if (bindHeader && layoutManager.mobile) {
|
||||
initHeadroom(this);
|
||||
}
|
||||
|
||||
if (enableScrollButtons) {
|
||||
loadScrollButtons(this);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user