Compare commits

..

21 Commits

Author SHA1 Message Date
dkanada
bc66fadb0e Merge pull request #613 from redSpoutnik/reset-subtitle-sync
reset subtitle offset on next episode play

(cherry picked from commit b67e31791d)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-12-06 10:53:02 -05:00
dkanada
298d8d7929 Merge pull request #612 from grafixeyehero/fix-searchresult-btnscroller
Fix Scroll arrows missing when searching

(cherry picked from commit b35a6e17eb)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-12-06 10:52:38 -05:00
dkanada
bdf8553728 Merge pull request #610 from YouKnowBlom/library-path-fix
Fix existing library folder path returning undefined

(cherry picked from commit 50ca98b2a2)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-12-06 10:52:13 -05:00
dkanada
8ac155fb7a Merge pull request #604 from grafixeyehero/collection-padding
Fix collection padding in TV layout

(cherry picked from commit ba8949aa36)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-12-06 10:51:54 -05:00
dkanada
54dee197fd Merge pull request #596 from Wunax/fix-input-user-avatar
Fix input field user avatar in a wrong position

(cherry picked from commit a9c1425ddd)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-12-06 10:49:37 -05:00
dkanada
7a3eb3ffad Merge pull request #594 from grafixeyehero/backdrop
Fix some issues with the backdrop image

(cherry picked from commit 78fd41ef05)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-12-06 10:46:27 -05:00
Joshua Boniface
15b987e0de Bump version for 10.4.3 2019-12-06 10:44:58 -05:00
dkanada
54b05ab27d Merge pull request #545 from ferferga/artworks2
Improved appearance of images and artworks - Part 3

(cherry picked from commit 79cd6a7552)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-11-24 15:45:05 -05:00
dkanada
e1308f01b6 Merge pull request #576 from anthonylavado/fix-manifest
Remove the leading /web/ from the manifest to avoid PWA titles

(cherry picked from commit 88db651eea)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-11-24 15:44:19 -05:00
dkanada
c596c19bfd Merge pull request #583 from Wunax/fix-player-aspect-ratio
Fixed set aspect ratio option in the player

(cherry picked from commit dca0700770)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-11-24 15:39:50 -05:00
dkanada
087d06debf Merge pull request #578 from ryan-hartzell/login-autofill-attributes
Add autocomplete attributes to login fields

(cherry picked from commit 109ea9e50c)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-11-24 15:39:38 -05:00
Joshua Boniface
0b4402b642 Bump version for 10.4.2 2019-11-24 15:37:19 -05:00
Joshua M. Boniface
b36369b562 Merge pull request #540 from grafixeyehero/identify-path
Add file path to itemidentifier

(cherry picked from commit 3ad7993d13)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-26 11:48:46 -04:00
Joshua M. Boniface
78a2ba73e7 Merge pull request #507 from redSpoutnik/fix-subtitle-sync
Fix subtitle sync

(cherry picked from commit ee1149644d)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-26 11:47:44 -04:00
Joshua M. Boniface
1be5e20363 Merge pull request #550 from grafixeyehero/metadata-editor-livetvloading
Fix Metadata Manager Live TV Loading..

(cherry picked from commit 47b8c2bcb3)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-26 11:45:26 -04:00
Joshua M. Boniface
9a130f5869 Merge pull request #542 from grafixeyehero/wizardfinsih
Improve wizard finish page redirect

(cherry picked from commit 5eb6a4b5ba)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-24 09:36:37 -04:00
Joshua M. Boniface
d6cec24d9e Merge pull request #519 from dhartung/fix-#518
Fix subtitle display from previous file in queue

(cherry picked from commit 6208f2b1c3)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-23 11:39:34 -04:00
dkanada
2044efc0c3 Merge pull request #524 from grafixeyehero/Live-TV-Pagination
Fix minor style on home section Live TV

(cherry picked from commit a5e71664ba)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-23 11:35:53 -04:00
Vasily
e12a8bc39d Merge pull request #513 from thornbill/fix-swiper
Revert Swiper to fix initialization error

(cherry picked from commit 66f3de495b)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-20 14:46:17 -04:00
Vasily
a885964e46 Merge pull request #521 from dkanada/nowplaying
Fix save button in now playing controller

(cherry picked from commit 5ce69ae514)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-20 14:44:40 -04:00
Vasily
efdc07e032 Merge pull request #523 from grafixeyehero/itemcontextmenu-icon
Add icon on item context menu

(cherry picked from commit d4862df266)
Signed-off-by: Joshua Boniface <joshua@boniface.me>
2019-10-20 14:44:16 -04:00
753 changed files with 47896 additions and 77893 deletions

View File

@@ -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)'

View File

@@ -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'

View File

@@ -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)'

View File

@@ -2,16 +2,63 @@ 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.js'
inputs:
versionSpec: '10.x'
- script: |
yarn install
displayName: 'Install dependencies'
- script: |
test -d dist
displayName: 'Check dist directory'
- script: |
yarn pack --filename jellyfin-web.tgz
displayName: 'Build package'
- task: PublishPipelineArtifact@1
displayName: 'Publish package'
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.js'
inputs:
versionSpec: '10.x'
- script: |
yarn install
displayName: 'Install dependencies'
- script: |
yarn run lint
displayName: 'Run ESLint'

View File

@@ -1 +0,0 @@
../fedora/Makefile

View File

@@ -1,5 +0,0 @@
version: 1
update_configs:
- package_manager: "javascript"
directory: "/"
update_schedule: "weekly"

View File

@@ -7,6 +7,3 @@ charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
end_of_line = lf
[json]
indent_size = 2

View File

@@ -1,5 +0,0 @@
node_modules
dist
.idea
.vscode
src/libraries

View File

@@ -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'
]
}
}
]
}

4
.eslintrc.yml Normal file
View File

@@ -0,0 +1,4 @@
env:
es6: true
browser: true
amd: true

36
.gitattributes vendored
View File

@@ -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
View File

@@ -1,4 +0,0 @@
.ci @dkanada @EraYaN
.github @jellyfin/core
build.sh @joshuaboniface
deployment @joshuaboniface

1
.github/stale.yml vendored
View File

@@ -8,7 +8,6 @@ exemptLabels:
- future
- feature
- enhancement
- confirmed
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable

582
.gitignore vendored
View File

@@ -1,11 +1,575 @@
# npm
dist
web
node_modules
# ide
.idea
.vscode
# 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
# log
yarn-error.log
### 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/
# 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

View File

@@ -1,143 +0,0 @@
{
"plugins": [
"stylelint-no-browser-hacks/lib",
],
"rules": {
"at-rule-empty-line-before": [ "always", {
except: [
"blockless-after-same-name-blockless",
"first-nested",
],
ignore: ["after-comment"],
} ],
"at-rule-name-case": "lower",
"at-rule-name-space-after": "always-single-line",
"at-rule-no-unknown": true,
"at-rule-semicolon-newline-after": "always",
"block-closing-brace-empty-line-before": "never",
"block-closing-brace-newline-after": "always",
"block-closing-brace-newline-before": "always-multi-line",
"block-closing-brace-space-before": "always-single-line",
"block-no-empty": true,
"block-opening-brace-newline-after": "always-multi-line",
"block-opening-brace-space-after": "always-single-line",
"block-opening-brace-space-before": "always",
"color-hex-case": "lower",
"color-hex-length": "short",
"color-no-invalid-hex": true,
"comment-empty-line-before": [ "always", {
except: ["first-nested"],
ignore: ["stylelint-commands"],
} ],
"comment-no-empty": true,
"comment-whitespace-inside": "always",
"custom-property-empty-line-before": [ "always", {
except: [
"after-custom-property",
"first-nested",
],
ignore: [
"after-comment",
"inside-single-line-block",
],
} ],
"declaration-bang-space-after": "never",
"declaration-bang-space-before": "always",
"declaration-block-no-duplicate-properties": [
true,
{
ignore: ["consecutive-duplicates-with-different-values"]
}
],
"declaration-block-no-shorthand-property-overrides": true,
"declaration-block-semicolon-newline-after": "always-multi-line",
"declaration-block-semicolon-space-after": "always-single-line",
"declaration-block-semicolon-space-before": "never",
"declaration-block-single-line-max-declarations": 1,
"declaration-block-trailing-semicolon": "always",
"declaration-colon-newline-after": "always-multi-line",
"declaration-colon-space-after": "always-single-line",
"declaration-colon-space-before": "never",
"font-family-no-duplicate-names": true,
"function-calc-no-invalid": true,
"function-calc-no-unspaced-operator": true,
"function-comma-newline-after": "always-multi-line",
"function-comma-space-after": "always-single-line",
"function-comma-space-before": "never",
"function-linear-gradient-no-nonstandard-direction": true,
"function-max-empty-lines": 0,
"function-name-case": "lower",
"function-parentheses-newline-inside": "always-multi-line",
"function-parentheses-space-inside": "never-single-line",
"function-whitespace-after": "always",
"indentation": 4,
"keyframe-declaration-no-important": true,
"length-zero-no-unit": true,
"max-empty-lines": 1,
"media-feature-colon-space-after": "always",
"media-feature-colon-space-before": "never",
"media-feature-name-case": "lower",
"media-feature-name-no-unknown": true,
"media-feature-parentheses-space-inside": "never",
"media-feature-range-operator-space-after": "always",
"media-feature-range-operator-space-before": "always",
"media-query-list-comma-newline-after": "always-multi-line",
"media-query-list-comma-space-after": "always-single-line",
"media-query-list-comma-space-before": "never",
"no-descending-specificity": true,
"no-duplicate-at-import-rules": true,
"no-duplicate-selectors": true,
"no-empty-source": true,
"no-eol-whitespace": true,
"no-extra-semicolons": true,
"no-invalid-double-slash-comments": true,
"no-missing-end-of-source-newline": true,
"number-leading-zero": "always",
"number-no-trailing-zeros": true,
"plugin/no-browser-hacks": true,
"property-case": "lower",
"property-no-unknown": [
true,
{
"ignoreProperties": [
"user-drag"
]
}
],
"rule-empty-line-before": [ "always-multi-line", {
except: ["first-nested"],
ignore: ["after-comment"],
} ],
"selector-attribute-brackets-space-inside": "never",
"selector-attribute-operator-space-after": "never",
"selector-attribute-operator-space-before": "never",
"selector-combinator-space-after": "always",
"selector-combinator-space-before": "always",
"selector-descendant-combinator-no-non-space": true,
"selector-list-comma-newline-after": "always",
"selector-list-comma-space-before": "never",
"selector-max-empty-lines": 0,
"selector-pseudo-class-case": "lower",
"selector-pseudo-class-no-unknown": true,
"selector-pseudo-class-parentheses-space-inside": "never",
"selector-pseudo-element-case": "lower",
"selector-pseudo-element-colon-notation": "double",
"selector-pseudo-element-no-unknown": [
true,
{
"ignorePseudoElements": [
"cue"
]
}
],
"selector-type-case": "lower",
"selector-type-no-unknown": true,
"string-no-newline": true,
"unit-case": "lower",
"unit-no-unknown": true,
"value-list-comma-newline-after": "always-multi-line",
"value-list-comma-space-after": "always-single-line",
"value-list-comma-space-before": "never",
"value-list-max-empty-lines": 0,
}
}

View File

@@ -32,10 +32,6 @@
- [bilde2910](https://github.com/bilde2910)
- [Daniel Hartung](https://github.com/dhartung)
- [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

View File

@@ -1,81 +1,15 @@
<h1 align="center">Jellyfin Web</h1>
<h3 align="center">Part of the <a href="https://jellyfin.org">Jellyfin Project</a></h3>
<h3 align="center">The Free Software Media System</h3>
---
<p align="center">
<img alt="Logo Banner" src="https://raw.githubusercontent.com/jellyfin/jellyfin-ux/master/branding/SVG/banner-logo-solid.svg?sanitize=true"/>
<br/>
<br/>
<a href="https://github.com/jellyfin/jellyfin-web">
<img alt="GPL 2.0 License" src="https://img.shields.io/github/license/jellyfin/jellyfin-web.svg"/>
</a>
<a href="https://github.com/jellyfin/jellyfin-web/releases">
<img alt="Current Release" src="https://img.shields.io/github/release/jellyfin/jellyfin-web.svg"/>
</a>
<a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-web/?utm_source=widget">
<img src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-web/svg-badge.svg" alt="Translation Status"/>
</a>
<br/>
<a href="https://opencollective.com/jellyfin">
<img alt="Donate" src="https://img.shields.io/opencollective/all/jellyfin.svg?label=backers"/>
</a>
<a href="https://features.jellyfin.org">
<img alt="Feature Requests" src="https://img.shields.io/badge/fider-vote%20on%20features-success.svg"/>
</a>
<a href="https://forum.jellyfin.org">
<img alt="Discuss on our Forum" src="https://img.shields.io/discourse/https/forum.jellyfin.org/users.svg"/>
</a>
<a href="https://matrix.to/#/+jellyfin:matrix.org">
<img alt="Chat on Matrix" src="https://img.shields.io/matrix/jellyfin:matrix.org.svg?logo=matrix"/>
</a>
<a href="https://www.reddit.com/r/jellyfin">
<img alt="Join our Subreddit" src="https://img.shields.io/badge/reddit-r%2Fjellyfin-%23FF5700.svg"/>
</a>
<br/><br/>
<a href="https://github.com/jellyfin/jellyfin-web"><img alt="GPL 2.0 License" src="https://img.shields.io/github/license/jellyfin/jellyfin-web.svg"/></a>
<a href="https://github.com/jellyfin/jellyfin-web/releases"><img alt="Current Release" src="https://img.shields.io/github/release/jellyfin/jellyfin-web.svg"/></a>
</p>
Jellyfin Web is the frontend used for most of the clients available for end users, such as desktop browsers, Android, and iOS. We welcome all contributions and pull requests! If you have a larger feature in mind please open an issue so we can discuss the implementation before you start. Translations can be improved very easily from our <a href="https://translate.jellyfin.org/projects/jellyfin/jellyfin-web">Weblate</a> instance. Look through the following graphic to see if your native language could use some work!
---
<a href="https://translate.jellyfin.org/engage/jellyfin/?utm_source=widget">
<img src="https://translate.jellyfin.org/widgets/jellyfin/-/jellyfin-web/multi-auto.svg" alt="Detailed Translation Status"/>
</a>
## Build Process
### Dependencies
- [Yarn 1.22.4](https://classic.yarnpkg.com/en/docs/install)
- Gulp-cli
### 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
```
Jellyfin is a free software media system that puts you in control of managing and streaming your media.

110
build.sh
View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -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
View File

@@ -1 +0,0 @@
8

16
debian/control vendored
View File

@@ -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
View File

@@ -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
View File

@@ -1,6 +0,0 @@
[DEFAULT]
pristine-tar = False
cleaner = fakeroot debian/rules clean
[import-orig]
filter = [ ".git*", ".hg*", ".vs*", ".vscode*" ]

1
debian/install vendored
View File

@@ -1 +0,0 @@
web usr/share/jellyfin/

View File

@@ -1 +0,0 @@
[type: gettext/rfc822deb] templates

View File

@@ -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
View File

@@ -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

View File

@@ -1 +0,0 @@
1.0

View File

@@ -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'

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -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}

View File

@@ -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"]

View File

@@ -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"]

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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)"

View File

@@ -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

View File

@@ -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);

View File

@@ -5,170 +5,31 @@
"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",
"copy-webpack-plugin": "^5.1.1",
"css-loader": "^3.6.0",
"cssnano": "^4.1.10",
"del": "^5.1.0",
"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",
"style-loader": "^1.1.3",
"stylelint": "^13.6.1",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-no-browser-hacks": "^1.2.1",
"stylelint-order": "^4.1.0",
"webpack": "^4.41.5",
"webpack-merge": "^4.2.2",
"webpack-stream": "^5.2.1"
"copy-webpack-plugin": "^5.0.3",
"css-loader": "^2.1.0",
"eslint": "^5.16.0",
"file-loader": "^3.0.1",
"style-loader": "^0.23.1",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9",
"webpack-dev-server": "^3.8.1",
"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",
"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",
"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",
"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",
"sortablejs": "^1.10.2",
"swiper": "^5.4.5",
"webcomponents.js": "^0.7.24",
"whatwg-fetch": "^3.2.0"
"hls.js": "^0.12.4",
"howler": "^2.1.2",
"jquery": "^3.4.1",
"jstree": "^3.3.7",
"libjass": "^0.11.0",
"shaka-player": "^2.5.5",
"sortablejs": "^1.9.0",
"swiper": "^3.4.2"
},
"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"
]
}
]
},
"browserslist": [
"last 2 Firefox versions",
"last 2 Chrome versions",
"last 2 ChromeAndroid versions",
"last 2 Safari versions",
"iOS > 10",
"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\"",
"prepare": "webpack --config webpack.prod.js"
}
}

View File

@@ -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;

View File

@@ -1,52 +0,0 @@
import sys
import os
import json
# load every key in the source language
# check the keys in all translations
# remove keys that only exist in translations
cwd = os.getcwd()
langdir = cwd + '/../src/strings'
langlst = os.listdir(langdir)
langlst.remove('en-us.json')
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:
keysus.append(key)
for lang in langlst:
with open(langdir + '/' + lang, 'r') as f:
inde = 2
if '\n \"' in f.read():
inde = 4
f.close()
with open(langdir + '/' + lang, 'r+') as f:
langjson = json.load(f)
langjnew = {}
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')

View File

@@ -1,40 +0,0 @@
import os
import subprocess
import json
# load all keys in the source language
# check entire codebase for usages
# print unused keys to a text file
# TODO: dynamic string usages cause false positives
cwd = os.getcwd()
langdir = cwd + '/../src/strings'
langlst = []
langlst.append('en-us.json')
# unused keys
dep = []
def grep(key):
command = 'grep -r -E "(\(\\\"|\(\'|\{)%s(\\\"|\'|\})" --include=\*.{js,html} --exclude-dir=../src/strings ../src' % key
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = p.stdout.readlines()
if output:
print('DONE: ' + key)
return True
print('UNUSED: ' + key)
dep.append(key)
return False
for lang in langlst:
with open(langdir + '/' + lang) as f:
langjson = json.load(f)
for key in langjson:
grep(key)
print(dep)
print('LENGTH: ' + str(len(dep)))
with open('unused.txt', 'w') as out:
for item in dep:
out.write(item + '\n')
out.close()

View File

@@ -1,34 +0,0 @@
import sys
import os
import json
# load text file containing unused keys
# remove the keys from all string files
cwd = os.getcwd()
langdir = cwd + '/../src/strings'
langlst = os.listdir(langdir)
keys = []
with open('scout.txt', 'r') as f:
for line in f:
keys.append(line.strip('\n'))
for lang in langlst:
with open(langdir + '/' + lang, 'r') as f:
inde = 2
if '\n \"' in f.read():
inde = 4
f.close()
with open(langdir + '/' + lang, 'r+') as f:
langjson = json.load(f)
for key in keys:
langjson.pop(key, None)
f.seek(0)
f.write(json.dumps(langjson, indent=inde, sort_keys=False, ensure_ascii=False))
f.write('\n')
f.truncate()
f.close()
print('DONE')

View File

@@ -1,15 +1,21 @@
<div id="addPluginPage" data-role="page" class="page type-interior pluginConfigurationPage" data-backbutton="true">
<div>
<div class="content-primary">
<div class="readOnlyContent">
<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">
@@ -21,12 +27,13 @@
<select id="selectVersion" name="selectVersion" is="emby-select" label="${LabelSelectVersionToInstall}"></select>
</div>
<div id="btnInstallDiv" class="hide">
<p id="btnInstallDiv" class="hide">
<button is="emby-button" type="submit" id="btnInstall" class="raised button-submit block">
<span>${Install}</span>
</button>
<div class="fieldDescription">${ServerRestartNeededAfterPluginInstall}</div>
</div>
</p>
<p id="nonServerMsg"></p>
</form>
</div>
</div>
@@ -35,6 +42,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>

View File

@@ -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 />

View File

@@ -2,25 +2,26 @@
<div>
<div class="content-primary">
<div class="detailSectionHeader">
<h2 style="margin:.6em 0;vertical-align:middle;display:inline-block;">${HeaderApiKeys}</h2>
<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="md-icon">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>
</table>
</div>
</div>
</div>
</div>

Binary file not shown.

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
.guideVerticalScroller {
padding-bottom: 15em;
}
@media all and (min-width: 62.5em) {
#guideTab {
padding-left: 0.5em;
}
}

View File

@@ -1,131 +0,0 @@
body,
html {
margin: 0;
padding: 0;
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";
}
.backgroundContainer {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
contain: strict;
}
html {
line-height: 1.35;
}
body {
overflow-x: hidden;
background-color: transparent !important;
-webkit-font-smoothing: antialiased;
}
.mainAnimatedPage {
contain: style size !important;
}
.pageContainer {
overflow-x: visible !important;
}
.bodyWithPopupOpen {
overflow-y: hidden !important;
}
div[data-role=page] {
outline: 0;
}
.pageTitle {
margin-top: 0;
font-family: inherit;
}
.fieldDescription {
padding-left: 0.15em;
font-weight: 400;
white-space: normal !important;
}
.fieldDescription + .fieldDescription {
margin-top: 0.3em;
}
.content-primary,
.padded-bottom-page,
.page,
.pageWithAbsoluteTabs .pageTabContent {
/* provides room for the music controls */
padding-bottom: 5em !important;
}
@media all and (min-width: 50em) {
.readOnlyContent,
form {
max-width: 54em;
}
}
.headerHelpButton {
margin-left: 1.25em !important;
padding-bottom: 0.4em !important;
padding-top: 0.4em !important;
}
.mediaInfoContent {
margin-left: auto;
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;
}

View File

@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M24 19H0a13.6 13.6 0 0 1 2.21-6.07A11.2 11.2 0 0 1 5.87 9.4l.41-.23-2.02-3.41a.51.51 0 0 1 .17-.7.5.5 0 0 1 .69.18l2.08 3.5a12.62 12.62 0 0 1 4.84-.9 12.2 12.2 0 0 1 4.75.9l2.07-3.5a.5.5 0 0 1 .7-.17.51.51 0 0 1 .16.7L17.7 9.19l.5.28a11.38 11.38 0 0 1 3.63 3.62A14.48 14.48 0 0 1 24 19zm-7.5-4.48a1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1 1 1 0 0 0-1 1zm-11 0a1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0-1-1 1 1 0 0 0-1 1z" fill="#fff"/>
</svg>

Before

Width:  |  Height:  |  Size: 563 B

View File

@@ -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

View File

@@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(-36.173 -93.796)">
<g transform="matrix(.08 0 0 .08 40.527 88.485)">
<path d="m53.295 119.35v-39.688h79.375v79.375h-79.375z" fill="#fcfdfd" stroke-width=".26458"/>
</g>
<g transform="matrix(1.3761 0 0 1.3825 -26.63 -38.456)" fill="#fff">
<path transform="matrix(.08 0 0 .08 40.527 88.485)" d="m86.822 141.89c-4.738-4.7596-5.2168-5.3235-5.2168-6.1442 0-0.82158 0.47505-1.3787 5.2329-6.1365 4.7552-4.7552 5.3153-5.2329 6.1353-5.2329 0.81617 0 1.3676 0.46161 5.7678 4.8286 4.8692 4.8324 5.6182 5.7452 5.6182 6.8466 0 0.41218-1.5697 2.1641-5.2274 5.834-4.8206 4.8367-5.3 5.2449-6.1603 5.2449-0.86046 0-1.3378-0.40681-6.1497-5.2406zm22.168-12.455c-0.43656-0.27248-2.9071-2.6371-5.4901-5.2547-4.1957-4.2519-4.6964-4.8534-4.6964-5.6418 0-0.7938 0.52954-1.414 5.2644-6.1655 4.6582-4.6746 5.362-5.2829 6.1127-5.2829 0.75071 0 1.4546 0.60829 6.1127 5.2829 4.7729 4.7898 5.2644 5.3668 5.2644 6.1818 0 0.81542-0.48628 1.3851-5.2394 6.1382-5.6104 5.6104-5.7707 5.7142-7.3283 4.742zm-40.16-5.2731c-3.5636-3.5816-4.9518-5.1483-4.9518-5.5886 0-0.75745 9.3384-10.601 10.057-10.601 0.2584 0 0.54208 0.18833 0.63041 0.41851s0.1606 4.7624 0.1606 10.072c0 9.1098-0.10948 10.677-0.74606 10.677-0.10905 0-2.4266-2.2396-5.1501-4.9768zm13.2-1.5272c-0.08833-0.23018-0.1606-5.3558-0.1606-11.39 0-8.9734 0.06852-11.102 0.37621-11.686 0.20691-0.39296 2.447-2.7683 4.9781-5.2785 4.3226-4.2871 4.6624-4.5641 5.5987-4.5641 0.94583 0 1.2591 0.26717 6.1277 5.2255 4.658 4.7439 5.1315 5.3102 5.1376 6.1439 6e-3 0.85888-0.67407 1.6-10.506 11.443-5.782 5.7887-10.71 10.525-10.952 10.525s-0.51144-0.18833-0.59977-0.41852z" fill="#fff" stroke-width=".26458"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg id="svg3390" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="141.25" viewBox="0 0 138.75 141.25" width="138.75" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<metadata id="metadata3396">
<rdf:RDF>
<cc:Work rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/>
<dc:title/>
</cc:Work>
</rdf:RDF>
</metadata>
<g id="layer1" fill="#f93208">
<path id="path3412" d="m20.154 40.829c-28.149 27.622-13.657 61.011-5.734 71.931 35.254 41.954 92.792 25.339 111.89-5.9071 4.7608-8.2027 22.554-53.467-23.976-78.009z"/>
<path id="path3471" d="m39.613 39.265 4.7778-8.8607 28.406-5.0384 11.119 9.2082z"/>
</g>
<g id="layer2">
<path id="path3437" d="m39.436 8.5696 8.9682-5.2826 6.7569 15.479c3.7925-6.3226 13.79-16.316 24.939-4.6684-4.7281 1.2636-7.5161 3.8553-7.7397 8.4768 15.145-4.1697 31.343 3.2127 33.539 9.0911-10.951-4.314-27.695 10.377-41.771 2.334 0.009 15.045-12.617 16.636-19.902 17.076 2.077-4.996 5.591-9.994 1.474-14.987-7.618 8.171-13.874 10.668-33.17 4.668 4.876-1.679 14.843-11.39 24.448-11.425-6.775-2.467-12.29-2.087-17.814-1.475 2.917-3.961 12.149-15.197 28.625-8.476z" fill="#02902e"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="145" height="140"><path fill="#0fc755" d="M47.4 35.342c-13.607-7.935-12.32-25.203 2.097-31.88 26.124-6.531 29.117 13.78 22.652 30.412-6.542 24.11 18.095 23.662 19.925 10.067 3.605-18.412 19.394-26.695 31.67-16.359 12.598 12.135 7.074 36.581-17.827 34.187-16.03-1.545-19.552 19.585.839 21.183 32.228 1.915 42.49 22.167 31.04 35.865-15.993 15.15-37.691-4.439-45.512-19.505-6.8-9.307-17.321.11-13.423 6.502 12.983 19.465 2.923 31.229-10.906 30.62-13.37-.85-20.96-9.06-13.214-29.15 3.897-12.481-8.595-15.386-16.57-5.45-11.707 19.61-28.865 13.68-33.976 4.19-3.243-7.621-2.921-25.846 24.119-23.696 16.688 4.137 11.776-12.561-.63-13.633-9.245-.443-30.501-7.304-22.86-24.54 7.34-11.056 24.958-11.768 33.348 6.293 3.037 4.232 8.361 11.042 18.037 5.033 3.51-5.197 1.21-13.9-8.809-20.135z"/></svg>

Before

Width:  |  Height:  |  Size: 833 B

View File

@@ -5,4 +5,4 @@
<div id="pluginTiles" style="text-align:left;"></div>
</div>
</div>
</div>
</div>

419
src/bower_components/alameda/alameda.js vendored Normal file
View File

@@ -0,0 +1,419 @@
var requirejs, require, define;
! function(global, Promise, undef) {
function commentReplace(match, singlePrefix) {
return singlePrefix || ""
}
function hasProp(obj, prop) {
return hasOwn.call(obj, prop)
}
function getOwn(obj, prop) {
return obj && hasProp(obj, prop) && obj[prop]
}
function obj() {
return Object.create(null)
}
function eachProp(obj, func) {
var prop;
for (prop in obj)
if (hasProp(obj, prop) && func(obj[prop], prop)) break
}
function mixin(target, source, force, deepStringMixin) {
return source && eachProp(source, function(value, prop) {
!force && hasProp(target, prop) || (!deepStringMixin || "object" != typeof value || !value || Array.isArray(value) || "function" == typeof value || value instanceof RegExp ? target[prop] = value : (target[prop] || (target[prop] = {}), mixin(target[prop], value, force, deepStringMixin)))
}), target
}
function getGlobal(value) {
if (!value) return value;
var g = global;
return value.split(".").forEach(function(part) {
g = g[part]
}), g
}
function newContext(contextName) {
function trimDots(ary) {
var i, part, length = ary.length;
for (i = 0; i < length; i++)
if ("." === (part = ary[i])) ary.splice(i, 1), i -= 1;
else if (".." === part) {
if (0 === i || 1 === i && ".." === ary[2] || ".." === ary[i - 1]) continue;
i > 0 && (ary.splice(i - 1, 2), i -= 2)
}
}
function normalize(name, baseName, applyMap) {
var mapValue, nameParts, i, j, nameSegment, lastIndex, foundMap, foundI, foundStarMap, starI, baseParts = baseName && baseName.split("/"),
normalizedBaseParts = baseParts,
map = config.map,
starMap = map && map["*"];
if (name && (name = name.split("/"), lastIndex = name.length - 1, config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex]) && (name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, "")), "." === name[0].charAt(0) && baseParts && (normalizedBaseParts = baseParts.slice(0, baseParts.length - 1), name = normalizedBaseParts.concat(name)), trimDots(name), name = name.join("/")), applyMap && map && (baseParts || starMap)) {
nameParts = name.split("/");
outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
if (nameSegment = nameParts.slice(0, i).join("/"), baseParts)
for (j = baseParts.length; j > 0; j -= 1)
if ((mapValue = getOwn(map, baseParts.slice(0, j).join("/"))) && (mapValue = getOwn(mapValue, nameSegment))) {
foundMap = mapValue, foundI = i;
break outerLoop
}! foundStarMap && starMap && getOwn(starMap, nameSegment) && (foundStarMap = getOwn(starMap, nameSegment), starI = i)
}!foundMap && foundStarMap && (foundMap = foundStarMap, foundI = starI), foundMap && (nameParts.splice(0, foundI, foundMap), name = nameParts.join("/"))
}
return getOwn(config.pkgs, name) || name
}
function makeShimExports(value) {
function fn() {
var ret;
return value.init && (ret = value.init.apply(global, arguments)), ret || value.exports && getGlobal(value.exports)
}
return fn
}
function takeQueue(anonId) {
var i, id, args, shim;
for (i = 0; i < queue.length; i += 1) {
if ("string" != typeof queue[i][0]) {
if (!anonId) break;
queue[i].unshift(anonId), anonId = undef
}
args = queue.shift(), id = args[0], i -= 1, id in defined || id in waiting || (id in deferreds ? main.apply(undef, args) : waiting[id] = args)
}
anonId && (shim = getOwn(config.shim, anonId) || {}, main(anonId, shim.deps || [], shim.exportsFn))
}
function makeRequire(relName, topLevel) {
var req = function(deps, callback, errback, alt) {
var name, cfg;
if (topLevel && takeQueue(), "string" == typeof deps) {
if (handlers[deps]) return handlers[deps](relName);
if (!((name = makeMap(deps, relName, !0).id) in defined)) throw new Error("Not loaded: " + name);
return defined[name]
}
return deps && !Array.isArray(deps) && (cfg = deps, deps = undef, Array.isArray(callback) && (deps = callback, callback = errback, errback = alt), topLevel) ? req.config(cfg)(deps, callback, errback) : (callback = callback || function() {
return slice.call(arguments, 0)
}, asyncResolve.then(function() {
return takeQueue(), main(undef, deps || [], callback, errback, relName)
}))
};
return req.isBrowser = "undefined" != typeof document && "undefined" != typeof navigator, req.nameToUrl = function(moduleName, ext, skipExt) {
var paths, syms, i, parentModule, url, parentPath, bundleId, pkgMain = getOwn(config.pkgs, moduleName);
if (pkgMain && (moduleName = pkgMain), bundleId = getOwn(bundlesMap, moduleName)) return req.nameToUrl(bundleId, ext, skipExt);
if (urlRegExp.test(moduleName)) url = moduleName + (ext || "");
else {
for (paths = config.paths, syms = moduleName.split("/"), i = syms.length; i > 0; i -= 1)
if (parentModule = syms.slice(0, i).join("/"), parentPath = getOwn(paths, parentModule)) {
Array.isArray(parentPath) && (parentPath = parentPath[0]), syms.splice(0, i, parentPath);
break
} url = syms.join("/"), url += ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? "" : ".js"), url = ("/" === url.charAt(0) || url.match(/^[\w\+\.\-]+:/) ? "" : config.baseUrl) + url
}
return config.urlArgs && !/^blob\:/.test(url) ? url + config.urlArgs(moduleName, url) : url
}, req.toUrl = function(moduleNamePlusExt) {
var ext, index = moduleNamePlusExt.lastIndexOf("."),
segment = moduleNamePlusExt.split("/")[0],
isRelative = "." === segment || ".." === segment;
return -1 !== index && (!isRelative || index > 1) && (ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length), moduleNamePlusExt = moduleNamePlusExt.substring(0, index)), req.nameToUrl(normalize(moduleNamePlusExt, relName), ext, !0)
}, req.defined = function(id) {
return makeMap(id, relName, !0).id in defined
}, req.specified = function(id) {
return (id = makeMap(id, relName, !0).id) in defined || id in deferreds
}, req
}
function resolve(name, d, value) {
name && (defined[name] = value, requirejs.onResourceLoad && requirejs.onResourceLoad(context, d.map, d.deps)), d.finished = !0, d.resolve(value)
}
function reject(d, err) {
d.finished = !0, d.rejected = !0, d.reject(err)
}
function makeNormalize(relName) {
return function(name) {
return normalize(name, relName, !0)
}
}
function defineModule(d) {
d.factoryCalled = !0;
var ret, name = d.map.id;
try {
ret = context.execCb(name, d.factory, d.values, defined[name])
} catch (err) {
return reject(d, err)
}
name ? ret === undef && (d.cjsModule ? ret = d.cjsModule.exports : d.usingExports && (ret = defined[name])) : requireDeferreds.splice(requireDeferreds.indexOf(d), 1), resolve(name, d, ret)
}
function depFinished(val, i) {
this.rejected || this.depDefined[i] || (this.depDefined[i] = !0, this.depCount += 1, this.values[i] = val, this.depending || this.depCount !== this.depMax || defineModule(this))
}
function makeDefer(name, calculatedMap) {
var d = {};
return d.promise = new Promise(function(resolve, reject) {
d.resolve = resolve, d.reject = function(err) {
name || requireDeferreds.splice(requireDeferreds.indexOf(d), 1), reject(err)
}
}), d.map = name ? calculatedMap || makeMap(name) : {}, d.depCount = 0, d.depMax = 0, d.values = [], d.depDefined = [], d.depFinished = depFinished, d.map.pr && (d.deps = [makeMap(d.map.pr)]), d
}
function getDefer(name, calculatedMap) {
var d;
return name ? (d = name in deferreds && deferreds[name]) || (d = deferreds[name] = makeDefer(name, calculatedMap)) : (d = makeDefer(), requireDeferreds.push(d)), d
}
function makeErrback(d, name) {
return function(err) {
d.rejected || (err.dynaId || (err.dynaId = "id" + (errCount += 1), err.requireModules = [name]), reject(d, err))
}
}
function waitForDep(depMap, relName, d, i) {
d.depMax += 1, callDep(depMap, relName).then(function(val) {
d.depFinished(val, i)
}, makeErrback(d, depMap.id)).catch(makeErrback(d, d.map.id))
}
function makeLoad(id) {
function load(value) {
fromTextCalled || resolve(id, getDefer(id), value)
}
var fromTextCalled;
return load.error = function(err) {
reject(getDefer(id), err)
}, load.fromText = function(text, textAlt) {
var execError, d = getDefer(id),
map = makeMap(makeMap(id).n),
plainId = map.id;
fromTextCalled = !0, d.factory = function(p, val) {
return val
}, textAlt && (text = textAlt), hasProp(config.config, id) && (config.config[plainId] = config.config[id]);
try {
req.exec(text)
} catch (e) {
execError = new Error("fromText eval for " + plainId + " failed: " + e), execError.requireType = "fromtexteval", reject(d, execError)
}
takeQueue(plainId), d.deps = [map], waitForDep(map, null, d, d.deps.length)
}, load
}
function callPlugin(plugin, map, relName) {
plugin.load(map.n, makeRequire(relName), makeLoad(map.id), config)
}
function splitPrefix(name) {
var prefix, index = name ? name.indexOf("!") : -1;
return index > -1 && (prefix = name.substring(0, index), name = name.substring(index + 1, name.length)), [prefix, name]
}
function breakCycle(d, traced, processed) {
var id = d.map.id;
traced[id] = !0, !d.finished && d.deps && d.deps.forEach(function(depMap) {
var depId = depMap.id,
dep = !hasProp(handlers, depId) && getDefer(depId, depMap);
!dep || dep.finished || processed[depId] || (hasProp(traced, depId) ? d.deps.forEach(function(depMap, i) {
depMap.id === depId && d.depFinished(defined[depId], i)
}) : breakCycle(dep, traced, processed))
}), processed[id] = !0
}
function check(d) {
var err, mid, dfd, notFinished = [],
waitInterval = 1e3 * config.waitSeconds,
expired = waitInterval && startTime + waitInterval < (new Date).getTime();
if (0 === loadCount && (d ? d.finished || breakCycle(d, {}, {}) : requireDeferreds.length && requireDeferreds.forEach(function(d) {
breakCycle(d, {}, {})
})), expired) {
for (mid in deferreds) dfd = deferreds[mid], dfd.finished || notFinished.push(dfd.map.id);
err = new Error("Timeout for modules: " + notFinished), err.requireModules = notFinished, err.requireType = "timeout", notFinished.forEach(function(id) {
reject(getDefer(id), err)
})
} else(loadCount || requireDeferreds.length) && (checkingLater || (checkingLater = !0, setTimeout(function() {
checkingLater = !1, check()
}, 70)))
}
function delayedError(e) {
console.log(e.stack);
return setTimeout(function() {
e.dynaId && trackedErrors[e.dynaId] || (trackedErrors[e.dynaId] = !0, req.onError(e))
}), e
}
var req, main, makeMap, callDep, handlers, checkingLater, load, context, defined = obj(),
waiting = obj(),
config = {
waitSeconds: 7,
baseUrl: "./",
paths: {},
bundles: {},
pkgs: {},
shim: {},
config: {}
},
mapCache = obj(),
requireDeferreds = [],
deferreds = obj(),
calledDefine = obj(),
calledPlugin = obj(),
loadCount = 0,
startTime = (new Date).getTime(),
errCount = 0,
trackedErrors = obj(),
urlFetched = obj(),
bundlesMap = obj(),
asyncResolve = Promise.resolve(undefined);
return load = "function" == typeof importScripts ? function(map) {
var url = map.url;
urlFetched[url] || (urlFetched[url] = !0, getDefer(map.id), importScripts(url), takeQueue(map.id))
} : function(map) {
var script, id = map.id,
url = map.url;
urlFetched[url] || (urlFetched[url] = !0, script = document.createElement("script"), script.setAttribute("data-requiremodule", id), script.type = config.scriptType || "text/javascript", script.charset = "utf-8", script.async = !0, loadCount += 1, script.addEventListener("load", function() {
loadCount -= 1, takeQueue(id)
}, !1), script.addEventListener("error", function() {
loadCount -= 1;
var err, pathConfig = getOwn(config.paths, id);
if (pathConfig && Array.isArray(pathConfig) && pathConfig.length > 1) {
script.parentNode.removeChild(script), pathConfig.shift();
var d = getDefer(id);
d.map = makeMap(id), d.map.url = req.nameToUrl(id), load(d.map)
} else err = new Error("Load failed: " + id + ": " + script.src), err.requireModules = [id], err.requireType = "scripterror", reject(getDefer(id), err)
}, !1), script.src = url, 10 === document.documentMode ? asap.then(function() {
document.head.appendChild(script)
}) : document.head.appendChild(script))
}, callDep = function(map, relName) {
var args, bundleId, name = map.id,
shim = config.shim[name];
if (name in waiting) args = waiting[name], delete waiting[name], main.apply(undef, args);
else if (!(name in deferreds))
if (map.pr) {
if (!(bundleId = getOwn(bundlesMap, name))) return callDep(makeMap(map.pr)).then(function(plugin) {
var newMap = map.prn ? map : makeMap(name, relName, !0),
newId = newMap.id,
shim = getOwn(config.shim, newId);
return newId in calledPlugin || (calledPlugin[newId] = !0, shim && shim.deps ? req(shim.deps, function() {
callPlugin(plugin, newMap, relName)
}) : callPlugin(plugin, newMap, relName)), getDefer(newId).promise
});
map.url = req.nameToUrl(bundleId), load(map)
} else shim && shim.deps ? req(shim.deps, function() {
load(map)
}) : load(map);
return getDefer(name).promise
}, makeMap = function(name, relName, applyMap) {
if ("string" != typeof name) return name;
var plugin, url, parts, prefix, result, prefixNormalized, cacheKey = name + " & " + (relName || "") + " & " + !!applyMap;
return parts = splitPrefix(name), prefix = parts[0], name = parts[1], !prefix && cacheKey in mapCache ? mapCache[cacheKey] : (prefix && (prefix = normalize(prefix, relName, applyMap), plugin = prefix in defined && defined[prefix]), prefix ? plugin && plugin.normalize ? (name = plugin.normalize(name, makeNormalize(relName)), prefixNormalized = !0) : name = -1 === name.indexOf("!") ? normalize(name, relName, applyMap) : name : (name = normalize(name, relName, applyMap), parts = splitPrefix(name), prefix = parts[0], name = parts[1], url = req.nameToUrl(name)), result = {
id: prefix ? prefix + "!" + name : name,
n: name,
pr: prefix,
url: url,
prn: prefix && prefixNormalized
}, prefix || (mapCache[cacheKey] = result), result)
}, handlers = {
require: function(name) {
return makeRequire(name)
},
exports: function(name) {
var e = defined[name];
return void 0 !== e ? e : defined[name] = {}
},
module: function(name) {
return {
id: name,
uri: "",
exports: handlers.exports(name),
config: function() {
return getOwn(config.config, name) || {}
}
}
}
}, main = function(name, deps, factory, errback, relName) {
if (name) {
if (name in calledDefine) return;
calledDefine[name] = !0
}
var d = getDefer(name);
return deps && !Array.isArray(deps) && (factory = deps, deps = []), deps = deps ? slice.call(deps, 0) : null, errback || (hasProp(config, "defaultErrback") ? config.defaultErrback && (errback = config.defaultErrback) : errback = delayedError), errback && d.promise.catch(errback), relName = relName || name, "function" == typeof factory ? (!deps.length && factory.length && (factory.toString().replace(commentRegExp, commentReplace).replace(cjsRequireRegExp, function(match, dep) {
deps.push(dep)
}), deps = (1 === factory.length ? ["require"] : ["require", "exports", "module"]).concat(deps)), d.factory = factory, d.deps = deps, d.depending = !0, deps.forEach(function(depName, i) {
var depMap;
deps[i] = depMap = makeMap(depName, relName, !0), depName = depMap.id, "require" === depName ? d.values[i] = handlers.require(name) : "exports" === depName ? (d.values[i] = handlers.exports(name), d.usingExports = !0) : "module" === depName ? d.values[i] = d.cjsModule = handlers.module(name) : void 0 === depName ? d.values[i] = void 0 : waitForDep(depMap, relName, d, i)
}), d.depending = !1, d.depCount === d.depMax && defineModule(d)) : name && resolve(name, d, factory), startTime = (new Date).getTime(), name || check(d), d.promise
}, req = makeRequire(null, !0), req.config = function(cfg) {
if (cfg.context && cfg.context !== contextName) {
var existingContext = getOwn(contexts, cfg.context);
return existingContext ? existingContext.req.config(cfg) : newContext(cfg.context).config(cfg)
}
if (mapCache = obj(), cfg.baseUrl && "/" !== cfg.baseUrl.charAt(cfg.baseUrl.length - 1) && (cfg.baseUrl += "/"), "string" == typeof cfg.urlArgs) {
var urlArgs = cfg.urlArgs;
cfg.urlArgs = function(id, url) {
return (-1 === url.indexOf("?") ? "?" : "&") + urlArgs
}
}
var shim = config.shim,
objs = {
paths: !0,
bundles: !0,
config: !0,
map: !0
};
return eachProp(cfg, function(value, prop) {
objs[prop] ? (config[prop] || (config[prop] = {}), mixin(config[prop], value, !0, !0)) : config[prop] = value
}), cfg.bundles && eachProp(cfg.bundles, function(value, prop) {
value.forEach(function(v) {
v !== prop && (bundlesMap[v] = prop)
})
}), cfg.shim && (eachProp(cfg.shim, function(value, id) {
Array.isArray(value) && (value = {
deps: value
}), !value.exports && !value.init || value.exportsFn || (value.exportsFn = makeShimExports(value)), shim[id] = value
}), config.shim = shim), cfg.packages && cfg.packages.forEach(function(pkgObj) {
var location, name;
pkgObj = "string" == typeof pkgObj ? {
name: pkgObj
} : pkgObj, name = pkgObj.name, location = pkgObj.location, location && (config.paths[name] = pkgObj.location), config.pkgs[name] = pkgObj.name + "/" + (pkgObj.main || "main").replace(currDirRegExp, "").replace(jsSuffixRegExp, "")
}), (cfg.deps || cfg.callback) && req(cfg.deps, cfg.callback), req
}, req.onError = function(err) {
throw err
}, context = {
id: contextName,
defined: defined,
waiting: waiting,
config: config,
deferreds: deferreds,
req: req,
execCb: function(name, callback, args, exports) {
return callback.apply(exports, args)
}
}, contexts[contextName] = context, req
}
if (!Promise) throw new Error("No Promise implementation available");
var topReq, dataMain, src, subPath, bootstrapConfig = requirejs || require,
hasOwn = Object.prototype.hasOwnProperty,
contexts = {},
queue = [],
currDirRegExp = /^\.\//,
urlRegExp = /^\/|\:|\?|\.js$/,
commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/,
slice = Array.prototype.slice;
if ("function" != typeof requirejs) {
var asap = Promise.resolve(void 0);
requirejs = topReq = newContext("_"), "function" != typeof require && (require = topReq), topReq.exec = function(text) {
return eval(text)
}, topReq.contexts = contexts, define = function() {
queue.push(slice.call(arguments, 0))
}, define.amd = {
jQuery: !0
}, bootstrapConfig && topReq.config(bootstrapConfig), topReq.isBrowser && !contexts._.config.skipDataMain && (dataMain = document.querySelectorAll("script[data-main]")[0], (dataMain = dataMain && dataMain.getAttribute("data-main")) && (dataMain = dataMain.replace(jsSuffixRegExp, ""), bootstrapConfig && bootstrapConfig.baseUrl || -1 !== dataMain.indexOf("!") || (src = dataMain.split("/"), dataMain = src.pop(), subPath = src.length ? src.join("/") + "/" : "./", topReq.config({
baseUrl: subPath
})), topReq([dataMain])))
}
}(this, "undefined" != typeof Promise ? Promise : void 0);

View File

@@ -0,0 +1,236 @@
//TODO: (vitorsemeano) modify this lines for webpack
define(["bower_components/apiclient/apiclientcore", "localassetmanager"], function(ApiClient, localassetmanager) {
"use strict";
if ("cordova" !== window.appMode && "android" !== window.appMode) {
return ApiClient;
}
function isLocalId(str) {
return startsWith(str, localPrefix)
}
function isLocalViewId(str) {
return startsWith(str, localViewPrefix)
}
function isTopLevelLocalViewId(str) {
return "localview" === str
}
function stripLocalPrefix(str) {
var res = stripStart(str, localPrefix);
return res = stripStart(res, localViewPrefix)
}
function startsWith(str, find) {
return !!(str && find && str.length > find.length && 0 === str.indexOf(find))
}
function stripStart(str, find) {
return startsWith(str, find) ? str.substr(find.length) : str
}
function createEmptyList() {
return {
Items: [],
TotalRecordCount: 0
}
}
function convertGuidToLocal(guid) {
return guid ? isLocalId(guid) ? guid : "local:" + guid : null
}
function adjustGuidProperties(downloadedItem) {
downloadedItem.Id = convertGuidToLocal(downloadedItem.Id), downloadedItem.SeriesId = convertGuidToLocal(downloadedItem.SeriesId), downloadedItem.SeasonId = convertGuidToLocal(downloadedItem.SeasonId), downloadedItem.AlbumId = convertGuidToLocal(downloadedItem.AlbumId), downloadedItem.ParentId = convertGuidToLocal(downloadedItem.ParentId), downloadedItem.ParentThumbItemId = convertGuidToLocal(downloadedItem.ParentThumbItemId), downloadedItem.ParentPrimaryImageItemId = convertGuidToLocal(downloadedItem.ParentPrimaryImageItemId), downloadedItem.PrimaryImageItemId = convertGuidToLocal(downloadedItem.PrimaryImageItemId), downloadedItem.ParentLogoItemId = convertGuidToLocal(downloadedItem.ParentLogoItemId), downloadedItem.ParentBackdropItemId = convertGuidToLocal(downloadedItem.ParentBackdropItemId), downloadedItem.ParentBackdropImageTags = null
}
function getLocalView(instance, serverId, userId) {
return instance.getLocalFolders(serverId, userId).then(function(views) {
var localView = null;
return views.length > 0 && (localView = {
Name: instance.downloadsTitleText || "Downloads",
ServerId: serverId,
Id: "localview",
Type: "localview",
IsFolder: !0
}), Promise.resolve(localView)
})
}
function ApiClientEx(serverAddress, clientName, applicationVersion, deviceName, deviceId, devicePixelRatio) {
ApiClient.call(this, serverAddress, clientName, applicationVersion, deviceName, deviceId, devicePixelRatio)
}
var localPrefix = "local:",
localViewPrefix = "localview:";
return Object.assign(ApiClientEx.prototype, ApiClient.prototype), ApiClientEx.prototype.getPlaybackInfo = function(itemId, options, deviceProfile) {
var onFailure = function() {
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
};
if (isLocalId(itemId)) return localassetmanager.getLocalItem(this.serverId(), stripLocalPrefix(itemId)).then(function(item) {
return {
MediaSources: item.Item.MediaSources.map(function(m) {
return m.SupportsDirectPlay = !0, m.SupportsDirectStream = !1, m.SupportsTranscoding = !1, m.IsLocal = !0, m
})
}
}, onFailure);
var instance = this;
return localassetmanager.getLocalItem(this.serverId(), itemId).then(function(item) {
if (item) {
var mediaSources = item.Item.MediaSources.map(function(m) {
return m.SupportsDirectPlay = !0, m.SupportsDirectStream = !1, m.SupportsTranscoding = !1, m.IsLocal = !0, m
});
return localassetmanager.fileExists(item.LocalPath).then(function(exists) {
if (exists) {
var res = {
MediaSources: mediaSources
};
return Promise.resolve(res)
}
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
}, onFailure)
}
return ApiClient.prototype.getPlaybackInfo.call(instance, itemId, options, deviceProfile)
}, onFailure)
}, ApiClientEx.prototype.getItems = function(userId, options) {
var i, serverInfo = this.serverInfo();
if (serverInfo && "localview" === options.ParentId) return this.getLocalFolders(serverInfo.Id, userId).then(function(items) {
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
});
if (serverInfo && options && (isLocalId(options.ParentId) || isLocalId(options.SeriesId) || isLocalId(options.SeasonId) || isLocalViewId(options.ParentId) || isLocalId(options.AlbumIds))) return localassetmanager.getViewItems(serverInfo.Id, userId, options).then(function(items) {
items.forEach(function(item) {
adjustGuidProperties(item)
});
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
});
if (options && options.ExcludeItemIds && options.ExcludeItemIds.length) {
var exItems = options.ExcludeItemIds.split(",");
for (i = 0; i < exItems.length; i++)
if (isLocalId(exItems[i])) return Promise.resolve(createEmptyList())
} else if (options && options.Ids && options.Ids.length) {
var ids = options.Ids.split(","),
hasLocal = !1;
for (i = 0; i < ids.length; i++) isLocalId(ids[i]) && (hasLocal = !0);
if (hasLocal) return localassetmanager.getItemsFromIds(serverInfo.Id, ids).then(function(items) {
items.forEach(function(item) {
adjustGuidProperties(item)
});
var result = {
Items: items,
TotalRecordCount: items.length
};
return Promise.resolve(result)
})
}
return ApiClient.prototype.getItems.call(this, userId, options)
}, ApiClientEx.prototype.getUserViews = function(options, userId) {
var instance = this;
options = options || {};
var basePromise = ApiClient.prototype.getUserViews.call(instance, options, userId);
return options.enableLocalView ? basePromise.then(function(result) {
var serverInfo = instance.serverInfo();
return serverInfo ? getLocalView(instance, serverInfo.Id, userId).then(function(localView) {
return localView && (result.Items.push(localView), result.TotalRecordCount++), Promise.resolve(result)
}) : Promise.resolve(result)
}) : basePromise
}, ApiClientEx.prototype.getItem = function(userId, itemId) {
if (!itemId) throw new Error("null itemId");
itemId && (itemId = itemId.toString());
var serverInfo;
return isTopLevelLocalViewId(itemId) && (serverInfo = this.serverInfo()) ? getLocalView(this, serverInfo.Id, userId) : isLocalViewId(itemId) && (serverInfo = this.serverInfo()) ? this.getLocalFolders(serverInfo.Id, userId).then(function(items) {
var views = items.filter(function(item) {
return item.Id === itemId
});
return views.length > 0 ? Promise.resolve(views[0]) : Promise.reject()
}) : isLocalId(itemId) && (serverInfo = this.serverInfo()) ? localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(itemId)).then(function(item) {
return adjustGuidProperties(item.Item), Promise.resolve(item.Item)
}) : ApiClient.prototype.getItem.call(this, userId, itemId)
}, ApiClientEx.prototype.getLocalFolders = function(userId) {
var serverInfo = this.serverInfo();
return userId = userId || serverInfo.UserId, localassetmanager.getViews(serverInfo.Id, userId)
}, ApiClientEx.prototype.getNextUpEpisodes = function(options) {
return options.SeriesId && isLocalId(options.SeriesId) ? Promise.resolve(createEmptyList()) : ApiClient.prototype.getNextUpEpisodes.call(this, options)
}, ApiClientEx.prototype.getSeasons = function(itemId, options) {
return isLocalId(itemId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Season", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : ApiClient.prototype.getSeasons.call(this, itemId, options)
}, ApiClientEx.prototype.getEpisodes = function(itemId, options) {
return isLocalId(options.SeasonId) || isLocalId(options.seasonId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Episode", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : isLocalId(itemId) ? (options.SeriesId = itemId, options.IncludeItemTypes = "Episode", options.SortBy = "SortName", this.getItems(this.getCurrentUserId(), options)) : ApiClient.prototype.getEpisodes.call(this, itemId, options)
}, ApiClientEx.prototype.getLatestOfflineItems = function(options) {
options.SortBy = "DateCreated", options.SortOrder = "Descending";
var serverInfo = this.serverInfo();
return serverInfo ? localassetmanager.getViewItems(serverInfo.Id, null, options).then(function(items) {
return items.forEach(function(item) {
adjustGuidProperties(item)
}), Promise.resolve(items)
}) : Promise.resolve([])
}, ApiClientEx.prototype.getThemeMedia = function(userId, itemId, inherit) {
return isLocalViewId(itemId) || isLocalId(itemId) || isTopLevelLocalViewId(itemId) ? Promise.reject() : ApiClient.prototype.getThemeMedia.call(this, userId, itemId, inherit)
}, ApiClientEx.prototype.getSpecialFeatures = function(userId, itemId) {
return isLocalId(itemId) ? Promise.resolve([]) : ApiClient.prototype.getSpecialFeatures.call(this, userId, itemId)
}, ApiClientEx.prototype.getSimilarItems = function(itemId, options) {
return isLocalId(itemId) ? Promise.resolve(createEmptyList()) : ApiClient.prototype.getSimilarItems.call(this, itemId, options)
}, ApiClientEx.prototype.updateFavoriteStatus = function(userId, itemId, isFavorite) {
return isLocalId(itemId) ? Promise.resolve() : ApiClient.prototype.updateFavoriteStatus.call(this, userId, itemId, isFavorite)
}, ApiClientEx.prototype.getScaledImageUrl = function(itemId, options) {
if (isLocalId(itemId) || options && options.itemid && isLocalId(options.itemid)) {
var serverInfo = this.serverInfo(),
id = stripLocalPrefix(itemId);
return localassetmanager.getImageUrl(serverInfo.Id, id, options)
}
return ApiClient.prototype.getScaledImageUrl.call(this, itemId, options)
}, ApiClientEx.prototype.reportPlaybackStart = function(options) {
if (!options) throw new Error("null options");
return isLocalId(options.ItemId) ? Promise.resolve() : ApiClient.prototype.reportPlaybackStart.call(this, options)
}, ApiClientEx.prototype.reportPlaybackProgress = function(options) {
if (!options) throw new Error("null options");
if (isLocalId(options.ItemId)) {
var serverInfo = this.serverInfo();
return serverInfo ? localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(options.ItemId)).then(function(item) {
var libraryItem = item.Item;
return "Video" === libraryItem.MediaType || "AudioBook" === libraryItem.Type ? (libraryItem.UserData = libraryItem.UserData || {}, libraryItem.UserData.PlaybackPositionTicks = options.PositionTicks, libraryItem.UserData.PlayedPercentage = Math.min(libraryItem.RunTimeTicks ? (options.PositionTicks || 0) / libraryItem.RunTimeTicks * 100 : 0, 100), localassetmanager.addOrUpdateLocalItem(item)) : Promise.resolve()
}) : Promise.resolve()
}
return ApiClient.prototype.reportPlaybackProgress.call(this, options)
}, ApiClientEx.prototype.reportPlaybackStopped = function(options) {
if (!options) throw new Error("null options");
if (isLocalId(options.ItemId)) {
var serverInfo = this.serverInfo(),
action = {
Date: (new Date).getTime(),
ItemId: stripLocalPrefix(options.ItemId),
PositionTicks: options.PositionTicks,
ServerId: serverInfo.Id,
Type: 0,
UserId: this.getCurrentUserId()
};
return localassetmanager.recordUserAction(action)
}
return ApiClient.prototype.reportPlaybackStopped.call(this, options)
}, ApiClientEx.prototype.getIntros = function(itemId) {
return isLocalId(itemId) ? Promise.resolve({
Items: [],
TotalRecordCount: 0
}) : ApiClient.prototype.getIntros.call(this, itemId)
}, ApiClientEx.prototype.getInstantMixFromItem = function(itemId, options) {
return isLocalId(itemId) ? Promise.resolve({
Items: [],
TotalRecordCount: 0
}) : ApiClient.prototype.getInstantMixFromItem.call(this, itemId, options)
}, ApiClientEx.prototype.getItemDownloadUrl = function(itemId) {
if (isLocalId(itemId)) {
var serverInfo = this.serverInfo();
if (serverInfo) return localassetmanager.getLocalItem(serverInfo.Id, stripLocalPrefix(itemId)).then(function(item) {
return Promise.resolve(item.LocalPath)
})
}
return ApiClient.prototype.getItemDownloadUrl.call(this, itemId)
}, ApiClientEx
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,53 @@
define([], function() {
"use strict";
function onCachePutFail(e) {
console.log(e);
}
function updateCache(instance) {
if (instance.cache) {
instance.cache.put("data", new Response(JSON.stringify(instance.localData))).catch(onCachePutFail);
}
}
function onCacheOpened(result) {
this.cache = result;
this.localData = {};
}
function MyStore() {
this.setItem = function(name, value) {
localStorage.setItem(name, value);
if (this.localData && this.localData[name] !== value) {
this.localData[name] = value;
updateCache(this);
}
};
this.getItem = function(name) {
return localStorage.getItem(name);
};
this.removeItem = function(name) {
localStorage.removeItem(name);
if (this.localData) {
delete this.localData[name];
updateCache(this);
}
};
try {
if (self.caches) {
self.caches.open("embydata").then(onCacheOpened.bind(this));
}
} catch (err) {
console.log("Error opening cache: " + err);
}
}
return new MyStore;
});

View File

@@ -0,0 +1,747 @@
define(["events", "apiclient", "appStorage"], function(events, apiClientFactory, appStorage) {
"use strict";
function getServerAddress(server, mode) {
switch (mode) {
case ConnectionMode.Local:
return server.LocalAddress;
case ConnectionMode.Manual:
return server.ManualAddress;
case ConnectionMode.Remote:
return server.RemoteAddress;
default:
return server.ManualAddress || server.LocalAddress || server.RemoteAddress
}
}
function paramsToString(params) {
var values = [];
for (var key in params) {
var value = params[key];
null !== value && void 0 !== value && "" !== value && values.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
}
return values.join("&")
}
function resolveFailure(instance, resolve) {
resolve({
State: "Unavailable",
ConnectUser: instance.connectUser()
})
}
function mergeServers(credentialProvider, list1, list2) {
for (var i = 0, length = list2.length; i < length; i++) credentialProvider.addOrUpdateServer(list1, list2[i]);
return list1
}
function updateServerInfo(server, systemInfo) {
server.Name = systemInfo.ServerName, systemInfo.Id && (server.Id = systemInfo.Id), systemInfo.LocalAddress && (server.LocalAddress = systemInfo.LocalAddress)
}
function getEmbyServerUrl(baseUrl, handler) {
return baseUrl + "/" + handler
}
function getFetchPromise(request) {
var headers = request.headers || {};
"json" === request.dataType && (headers.accept = "application/json");
var fetchRequest = {
headers: headers,
method: request.type,
credentials: "same-origin"
},
contentType = request.contentType;
return request.data && ("string" == typeof request.data ? fetchRequest.body = request.data : (fetchRequest.body = paramsToString(request.data), contentType = contentType || "application/x-www-form-urlencoded; charset=UTF-8")), contentType && (headers["Content-Type"] = contentType), request.timeout ? fetchWithTimeout(request.url, fetchRequest, request.timeout) : fetch(request.url, fetchRequest)
}
function fetchWithTimeout(url, options, timeoutMs) {
return console.log("fetchWithTimeout: timeoutMs: " + timeoutMs + ", url: " + url), new Promise(function(resolve, reject) {
var timeout = setTimeout(reject, timeoutMs);
options = options || {}, options.credentials = "same-origin", fetch(url, options).then(function(response) {
clearTimeout(timeout), console.log("fetchWithTimeout: succeeded connecting to url: " + url), resolve(response)
}, function(error) {
clearTimeout(timeout), console.log("fetchWithTimeout: timed out connecting to url: " + url), reject()
})
})
}
function ajax(request) {
if (!request) throw new Error("Request cannot be null");
return request.headers = request.headers || {}, console.log("ConnectionManager requesting url: " + request.url), getFetchPromise(request).then(function(response) {
return console.log("ConnectionManager response status: " + response.status + ", url: " + request.url), response.status < 400 ? "json" === request.dataType || "application/json" === request.headers.accept ? response.json() : response : Promise.reject(response)
}, function(err) {
throw console.log("ConnectionManager request failed to url: " + request.url), err
})
}
function getConnectUrl(handler) {
return "https://connect.emby.media/service/" + handler
}
function replaceAll(originalString, strReplace, strWith) {
var reg = new RegExp(strReplace, "ig");
return originalString.replace(reg, strWith)
}
function normalizeAddress(address) {
return address = address.trim(), 0 !== address.toLowerCase().indexOf("http") && (address = "http://" + address), address = replaceAll(address, "Http:", "http:"), address = replaceAll(address, "Https:", "https:")
}
function stringEqualsIgnoreCase(str1, str2) {
return (str1 || "").toLowerCase() === (str2 || "").toLowerCase()
}
function compareVersions(a, b) {
a = a.split("."), b = b.split(".");
for (var i = 0, length = Math.max(a.length, b.length); i < length; i++) {
var aVal = parseInt(a[i] || "0"),
bVal = parseInt(b[i] || "0");
if (aVal < bVal) return -1;
if (aVal > bVal) return 1
}
return 0
}
var defaultTimeout = 2e4,
ConnectionMode = {
Local: 0,
Remote: 1,
Manual: 2
},
ConnectionManager = function(credentialProvider, appName, appVersion, deviceName, deviceId, capabilities, devicePixelRatio) {
function onConnectUserSignIn(user) {
connectUser = user, events.trigger(self, "connectusersignedin", [user])
}
function onAuthenticated(apiClient, result, options, saveCredentials) {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.filter(function(s) {
return s.Id === result.ServerId
}),
server = servers.length ? servers[0] : apiClient.serverInfo();
return !1 !== options.updateDateLastAccessed && (server.DateLastAccessed = (new Date).getTime()), server.Id = result.ServerId, saveCredentials ? (server.UserId = result.User.Id, server.AccessToken = result.AccessToken) : (server.UserId = null, server.AccessToken = null), credentialProvider.addOrUpdateServer(credentials.Servers, server), credentialProvider.credentials(credentials), apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, apiClient.serverInfo(server), afterConnected(apiClient, options), onLocalUserSignIn(server, apiClient.serverAddress(), result.User)
}
function afterConnected(apiClient, options) {
options = options || {}, !1 !== options.reportCapabilities && apiClient.reportCapabilities(capabilities), apiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, !1 !== options.enableWebSocket && (console.log("calling apiClient.ensureWebSocket"), apiClient.ensureWebSocket())
}
function onLocalUserSignIn(server, serverUrl, user) {
return self._getOrAddApiClient(server, serverUrl), (self.onLocalUserSignedIn ? self.onLocalUserSignedIn.call(self, user) : Promise.resolve()).then(function() {
events.trigger(self, "localusersignedin", [user])
})
}
function ensureConnectUser(credentials) {
return connectUser && connectUser.Id === credentials.ConnectUserId ? Promise.resolve() : credentials.ConnectUserId && credentials.ConnectAccessToken ? (connectUser = null, getConnectUser(credentials.ConnectUserId, credentials.ConnectAccessToken).then(function(user) {
return onConnectUserSignIn(user), Promise.resolve()
}, function() {
return Promise.resolve()
})) : Promise.resolve()
}
function getConnectUser(userId, accessToken) {
if (!userId) throw new Error("null userId");
if (!accessToken) throw new Error("null accessToken");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/user?id=" + userId,
dataType: "json",
headers: {
"X-Application": appName + "/" + appVersion,
"X-Connect-UserToken": accessToken
}
})
}
function addAuthenticationInfoFromConnect(server, serverUrl, credentials) {
if (!server.ExchangeToken) throw new Error("server.ExchangeToken cannot be null");
if (!credentials.ConnectUserId) throw new Error("credentials.ConnectUserId cannot be null");
var url = getEmbyServerUrl(serverUrl, "Connect/Exchange?format=json&ConnectUserId=" + credentials.ConnectUserId),
auth = 'MediaBrowser Client="' + appName + '", Device="' + deviceName + '", DeviceId="' + deviceId + '", Version="' + appVersion + '"';
return ajax({
type: "GET",
url: url,
dataType: "json",
headers: {
"X-MediaBrowser-Token": server.ExchangeToken,
"X-Emby-Authorization": auth
}
}).then(function(auth) {
return server.UserId = auth.LocalUserId, server.AccessToken = auth.AccessToken, auth
}, function() {
return server.UserId = null, server.AccessToken = null, Promise.reject()
})
}
function validateAuthentication(server, serverUrl) {
return ajax({
type: "GET",
url: getEmbyServerUrl(serverUrl, "System/Info"),
dataType: "json",
headers: {
"X-MediaBrowser-Token": server.AccessToken
}
}).then(function(systemInfo) {
return updateServerInfo(server, systemInfo), Promise.resolve()
}, function() {
return server.UserId = null, server.AccessToken = null, Promise.resolve()
})
}
function getImageUrl(localUser) {
if (connectUser && connectUser.ImageUrl) return {
url: connectUser.ImageUrl
};
if (localUser && localUser.PrimaryImageTag) {
return {
url: self.getApiClient(localUser).getUserImageUrl(localUser.Id, {
tag: localUser.PrimaryImageTag,
type: "Primary"
}),
supportsParams: !0
}
}
return {
url: null,
supportsParams: !1
}
}
function logoutOfServer(apiClient) {
var serverInfo = apiClient.serverInfo() || {},
logoutInfo = {
serverId: serverInfo.Id
};
return apiClient.logout().then(function() {
events.trigger(self, "localusersignedout", [logoutInfo])
}, function() {
events.trigger(self, "localusersignedout", [logoutInfo])
})
}
function getConnectServers(credentials) {
return console.log("Begin getConnectServers"), credentials.ConnectAccessToken && credentials.ConnectUserId ? ajax({
type: "GET",
url: "https://connect.emby.media/service/servers?userId=" + credentials.ConnectUserId,
dataType: "json",
headers: {
"X-Application": appName + "/" + appVersion,
"X-Connect-UserToken": credentials.ConnectAccessToken
}
}).then(function(servers) {
return servers.map(function(i) {
return {
ExchangeToken: i.AccessKey,
ConnectServerId: i.Id,
Id: i.SystemId,
Name: i.Name,
RemoteAddress: i.Url,
LocalAddress: i.LocalAddress,
UserLinkType: "guest" === (i.UserType || "").toLowerCase() ? "Guest" : "LinkedUser"
}
})
}, function() {
return credentials.Servers.slice(0).filter(function(s) {
return s.ExchangeToken
})
}) : Promise.resolve([])
}
function filterServers(servers, connectServers) {
return servers.filter(function(server) {
return !server.ExchangeToken || connectServers.filter(function(connectServer) {
return server.Id === connectServer.Id
}).length > 0
})
}
function findServers() {
return new Promise(function(resolve, reject) {
var onFinish = function(foundServers) {
var servers = foundServers.map(function(foundServer) {
var info = {
Id: foundServer.Id,
LocalAddress: convertEndpointAddressToManualAddress(foundServer) || foundServer.Address,
Name: foundServer.Name
};
return info.LastConnectionMode = info.ManualAddress ? ConnectionMode.Manual : ConnectionMode.Local, info
});
resolve(servers)
};
if (window.NativeShell && typeof window.NativeShell.findServers === 'function') {
window.NativeShell.findServers(1e3).then(onFinish, function() {
onFinish([])
});
} else {
resolve([]);
}
});
}
function convertEndpointAddressToManualAddress(info) {
if (info.Address && info.EndpointAddress) {
var address = info.EndpointAddress.split(":")[0],
parts = info.Address.split(":");
if (parts.length > 1) {
var portString = parts[parts.length - 1];
isNaN(parseInt(portString)) || (address += ":" + portString)
}
return normalizeAddress(address)
}
return null
}
function getTryConnectPromise(url, connectionMode, state, resolve, reject) {
console.log("getTryConnectPromise " + url), ajax({
url: getEmbyServerUrl(url, "system/info/public"),
timeout: defaultTimeout,
type: "GET",
dataType: "json"
}).then(function(result) {
state.resolved || (state.resolved = !0, console.log("Reconnect succeeded to " + url), resolve({
url: url,
connectionMode: connectionMode,
data: result
}))
}, function() {
state.resolved || (console.log("Reconnect failed to " + url), ++state.rejects >= state.numAddresses && reject())
})
}
function tryReconnect(serverInfo) {
var addresses = [],
addressesStrings = [];
return !serverInfo.manualAddressOnly && serverInfo.LocalAddress && -1 === addressesStrings.indexOf(serverInfo.LocalAddress) && (addresses.push({
url: serverInfo.LocalAddress,
mode: ConnectionMode.Local,
timeout: 0
}), addressesStrings.push(addresses[addresses.length - 1].url)), serverInfo.ManualAddress && -1 === addressesStrings.indexOf(serverInfo.ManualAddress) && (addresses.push({
url: serverInfo.ManualAddress,
mode: ConnectionMode.Manual,
timeout: 100
}), addressesStrings.push(addresses[addresses.length - 1].url)), !serverInfo.manualAddressOnly && serverInfo.RemoteAddress && -1 === addressesStrings.indexOf(serverInfo.RemoteAddress) && (addresses.push({
url: serverInfo.RemoteAddress,
mode: ConnectionMode.Remote,
timeout: 200
}), addressesStrings.push(addresses[addresses.length - 1].url)), console.log("tryReconnect: " + addressesStrings.join("|")), new Promise(function(resolve, reject) {
var state = {};
state.numAddresses = addresses.length, state.rejects = 0, addresses.map(function(url) {
setTimeout(function() {
state.resolved || getTryConnectPromise(url.url, url.mode, state, resolve, reject)
}, url.timeout)
})
})
}
function onSuccessfulConnection(server, systemInfo, connectionMode, serverUrl, options, resolve) {
var credentials = credentialProvider.credentials();
options = options || {}, credentials.ConnectAccessToken && !1 !== options.enableAutoLogin ? ensureConnectUser(credentials).then(function() {
server.ExchangeToken ? addAuthenticationInfoFromConnect(server, serverUrl, credentials).then(function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}, function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}) : afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}) : afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !0, options, resolve)
}
function afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, verifyLocalAuthentication, options, resolve) {
if (options = options || {}, !1 === options.enableAutoLogin) server.UserId = null, server.AccessToken = null;
else if (verifyLocalAuthentication && server.AccessToken && !1 !== options.enableAutoLogin) return void validateAuthentication(server, serverUrl).then(function() {
afterConnectValidated(server, credentials, systemInfo, connectionMode, serverUrl, !1, options, resolve)
});
updateServerInfo(server, systemInfo), server.LastConnectionMode = connectionMode, !1 !== options.updateDateLastAccessed && (server.DateLastAccessed = (new Date).getTime()), credentialProvider.addOrUpdateServer(credentials.Servers, server), credentialProvider.credentials(credentials);
var result = {
Servers: []
};
result.ApiClient = self._getOrAddApiClient(server, serverUrl), result.ApiClient.setSystemInfo(systemInfo), result.State = server.AccessToken && !1 !== options.enableAutoLogin ? "SignedIn" : "ServerSignIn", result.Servers.push(server), result.ApiClient.enableAutomaticBitrateDetection = options.enableAutomaticBitrateDetection, result.ApiClient.updateServerInfo(server, serverUrl);
var resolveActions = function() {
resolve(result), events.trigger(self, "connected", [result])
};
"SignedIn" === result.State ? (afterConnected(result.ApiClient, options), result.ApiClient.getCurrentUser().then(function(user) {
onLocalUserSignIn(server, serverUrl, user).then(resolveActions, resolveActions)
}, resolveActions)) : resolveActions()
}
function getCacheKey(feature, apiClient, options) {
options = options || {};
var viewOnly = options.viewOnly,
cacheKey = "regInfo-" + apiClient.serverId();
return viewOnly && (cacheKey += "-viewonly"), cacheKey
}
function addAppInfoToConnectRequest(request) {
request.headers = request.headers || {}, request.headers["X-Application"] = appName + "/" + appVersion
}
function exchangePin(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
var request = {
type: "POST",
url: getConnectUrl("pin/authenticate"),
data: {
deviceId: pinInfo.DeviceId,
pin: pinInfo.Pin
},
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}
console.log("Begin ConnectionManager constructor");
var self = this;
this._apiClients = [];
var connectUser;
self.connectUser = function() {
return connectUser
}, self._minServerVersion = "3.2.33", self.appVersion = function() {
return appVersion
}, self.appName = function() {
return appName
}, self.capabilities = function() {
return capabilities
}, self.deviceId = function() {
return deviceId
}, self.credentialProvider = function() {
return credentialProvider
}, self.connectUserId = function() {
return credentialProvider.credentials().ConnectUserId
}, self.connectToken = function() {
return credentialProvider.credentials().ConnectAccessToken
}, self.getServerInfo = function(id) {
return credentialProvider.credentials().Servers.filter(function(s) {
return s.Id === id
})[0]
}, self.getLastUsedServer = function() {
var servers = credentialProvider.credentials().Servers;
return servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), servers.length ? servers[0] : null
}, self.addApiClient = function(apiClient) {
self._apiClients.push(apiClient);
var existingServers = credentialProvider.credentials().Servers.filter(function(s) {
return stringEqualsIgnoreCase(s.ManualAddress, apiClient.serverAddress()) || stringEqualsIgnoreCase(s.LocalAddress, apiClient.serverAddress()) || stringEqualsIgnoreCase(s.RemoteAddress, apiClient.serverAddress())
}),
existingServer = existingServers.length ? existingServers[0] : apiClient.serverInfo();
if (existingServer.DateLastAccessed = (new Date).getTime(), existingServer.LastConnectionMode = ConnectionMode.Manual, existingServer.ManualAddress = apiClient.serverAddress(), apiClient.manualAddressOnly && (existingServer.manualAddressOnly = !0), apiClient.serverInfo(existingServer), apiClient.onAuthenticated = function(instance, result) {
return onAuthenticated(instance, result, {}, !0)
}, !existingServers.length) {
var credentials = credentialProvider.credentials();
credentials.Servers = [existingServer], credentialProvider.credentials(credentials)
}
events.trigger(self, "apiclientcreated", [apiClient])
}, self.clearData = function() {
console.log("connection manager clearing data"), connectUser = null;
var credentials = credentialProvider.credentials();
credentials.ConnectAccessToken = null, credentials.ConnectUserId = null, credentials.Servers = [], credentialProvider.credentials(credentials)
}, self._getOrAddApiClient = function(server, serverUrl) {
var apiClient = self.getApiClient(server.Id);
return apiClient || (apiClient = new apiClientFactory(serverUrl, appName, appVersion, deviceName, deviceId, devicePixelRatio), self._apiClients.push(apiClient), apiClient.serverInfo(server), apiClient.onAuthenticated = function(instance, result) {
return onAuthenticated(instance, result, {}, !0)
}, events.trigger(self, "apiclientcreated", [apiClient])), console.log("returning instance from getOrAddApiClient"), apiClient
}, self.getOrCreateApiClient = function(serverId) {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.filter(function(s) {
return stringEqualsIgnoreCase(s.Id, serverId)
});
if (!servers.length) throw new Error("Server not found: " + serverId);
var server = servers[0];
return self._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode))
}, self.user = function(apiClient) {
return new Promise(function(resolve, reject) {
function onLocalUserDone(e) {
var image = getImageUrl(localUser);
resolve({
localUser: localUser,
name: connectUser ? connectUser.Name : localUser ? localUser.Name : null,
imageUrl: image.url,
supportsImageParams: image.supportsParams,
connectUser: connectUser
})
}
function onEnsureConnectUserDone() {
apiClient && apiClient.getCurrentUserId() ? apiClient.getCurrentUser().then(function(u) {
localUser = u, onLocalUserDone()
}, onLocalUserDone) : onLocalUserDone()
}
var localUser, credentials = credentialProvider.credentials();
!credentials.ConnectUserId || !credentials.ConnectAccessToken || apiClient && apiClient.getCurrentUserId() ? onEnsureConnectUserDone() : ensureConnectUser(credentials).then(onEnsureConnectUserDone, onEnsureConnectUserDone)
})
}, self.logout = function() {
console.log("begin connectionManager loguot");
for (var promises = [], i = 0, length = self._apiClients.length; i < length; i++) {
var apiClient = self._apiClients[i];
apiClient.accessToken() && promises.push(logoutOfServer(apiClient))
}
return Promise.all(promises).then(function() {
for (var credentials = credentialProvider.credentials(), servers = credentials.Servers.filter(function(u) {
return "Guest" !== u.UserLinkType
}), j = 0, numServers = servers.length; j < numServers; j++) {
var server = servers[j];
server.UserId = null, server.AccessToken = null, server.ExchangeToken = null
}
credentials.Servers = servers, credentials.ConnectAccessToken = null, credentials.ConnectUserId = null, credentialProvider.credentials(credentials), connectUser && (connectUser = null, events.trigger(self, "connectusersignedout"))
})
}, self.getSavedServers = function() {
var credentials = credentialProvider.credentials(),
servers = credentials.Servers.slice(0);
return servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), servers
}, self.getAvailableServers = function() {
console.log("Begin getAvailableServers");
var credentials = credentialProvider.credentials();
return Promise.all([getConnectServers(credentials), findServers()]).then(function(responses) {
var connectServers = responses[0],
foundServers = responses[1],
servers = credentials.Servers.slice(0);
return mergeServers(credentialProvider, servers, foundServers), mergeServers(credentialProvider, servers, connectServers), servers = filterServers(servers, connectServers), servers.sort(function(a, b) {
return (b.DateLastAccessed || 0) - (a.DateLastAccessed || 0)
}), credentials.Servers = servers, credentialProvider.credentials(credentials), servers
})
}, self.connectToServers = function(servers, options) {
console.log("Begin connectToServers, with " + servers.length + " servers");
var firstServer = servers.length ? servers[0] : null;
return firstServer ? self.connectToServer(firstServer, options).then(function(result) {
return "Unavailable" === result.State && (result.State = "ServerSelection"), console.log("resolving connectToServers with result.State: " + result.State), result
}) : Promise.resolve({
Servers: servers,
State: servers.length || self.connectUser() ? "ServerSelection" : "ConnectSignIn",
ConnectUser: self.connectUser()
})
}, self.connectToServer = function(server, options) {
return console.log("begin connectToServer"), new Promise(function(resolve, reject) {
options = options || {}, tryReconnect(server).then(function(result) {
var serverUrl = result.url,
connectionMode = result.connectionMode;
result = result.data, 1 === compareVersions(self.minServerVersion(), result.Version) ? (console.log("minServerVersion requirement not met. Server version: " + result.Version), resolve({
State: "ServerUpdateNeeded",
Servers: [server]
})) : server.Id && result.Id !== server.Id ? (console.log("http request succeeded, but found a different server Id than what was expected"), resolveFailure(self, resolve)) : onSuccessfulConnection(server, result, connectionMode, serverUrl, options, resolve)
}, function() {
resolveFailure(self, resolve)
})
})
}, self.connectToAddress = function(address, options) {
function onFail() {
return console.log("connectToAddress " + address + " failed"), Promise.resolve({
State: "Unavailable",
ConnectUser: instance.connectUser()
})
}
if (!address) return Promise.reject();
address = normalizeAddress(address);
var instance = this,
server = {
ManualAddress: address,
LastConnectionMode: ConnectionMode.Manual
};
return self.connectToServer(server, options).catch(onFail)
}, self.loginToConnect = function(username, password) {
return username && password ? ajax({
type: "POST",
url: "https://connect.emby.media/service/user/authenticate",
data: {
nameOrEmail: username,
rawpw: password
},
dataType: "json",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
headers: {
"X-Application": appName + "/" + appVersion
}
}).then(function(result) {
var credentials = credentialProvider.credentials();
return credentials.ConnectAccessToken = result.AccessToken, credentials.ConnectUserId = result.User.Id, credentialProvider.credentials(credentials), onConnectUserSignIn(result.User), result
}) : Promise.reject()
}, self.signupForConnect = function(options) {
var email = options.email,
username = options.username,
password = options.password,
passwordConfirm = options.passwordConfirm;
if (!email) return Promise.reject({
errorCode: "invalidinput"
});
if (!username) return Promise.reject({
errorCode: "invalidinput"
});
if (!password) return Promise.reject({
errorCode: "invalidinput"
});
if (!passwordConfirm) return Promise.reject({
errorCode: "passwordmatch"
});
if (password !== passwordConfirm) return Promise.reject({
errorCode: "passwordmatch"
});
var data = {
email: email,
userName: username,
rawpw: password
};
return options.grecaptcha && (data.grecaptcha = options.grecaptcha), ajax({
type: "POST",
url: "https://connect.emby.media/service/register",
data: data,
dataType: "json",
contentType: "application/x-www-form-urlencoded; charset=UTF-8",
headers: {
"X-Application": appName + "/" + appVersion,
"X-CONNECT-TOKEN": "CONNECT-REGISTER"
}
}).catch(function(response) {
try {
return response.json()
} catch (err) {
throw err
}
}).then(function(result) {
if (result && result.Status) return "SUCCESS" === result.Status ? Promise.resolve(result) : Promise.reject({
errorCode: result.Status
});
Promise.reject()
})
}, self.getUserInvitations = function() {
var connectToken = self.connectToken();
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/servers?userId=" + self.connectUserId() + "&status=Waiting",
dataType: "json",
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.deleteServer = function(serverId) {
if (!serverId) throw new Error("null serverId");
var server = credentialProvider.credentials().Servers.filter(function(s) {
return s.Id === serverId
});
return server = server.length ? server[0] : null, new Promise(function(resolve, reject) {
function onDone() {
var credentials = credentialProvider.credentials();
credentials.Servers = credentials.Servers.filter(function(s) {
return s.Id !== serverId
}), credentialProvider.credentials(credentials), resolve()
}
if (!server.ConnectServerId) return void onDone();
var connectToken = self.connectToken(),
connectUserId = self.connectUserId();
if (!connectToken || !connectUserId) return void onDone();
ajax({
type: "DELETE",
url: "https://connect.emby.media/service/serverAuthorizations?serverId=" + server.ConnectServerId + "&userId=" + connectUserId,
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
}).then(onDone, onDone)
})
}, self.rejectServer = function(serverId) {
var connectToken = self.connectToken();
if (!serverId) throw new Error("null serverId");
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
var url = "https://connect.emby.media/service/serverAuthorizations?serverId=" + serverId + "&userId=" + self.connectUserId();
return fetch(url, {
method: "DELETE",
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.acceptServer = function(serverId) {
var connectToken = self.connectToken();
if (!serverId) throw new Error("null serverId");
if (!connectToken) throw new Error("null connectToken");
if (!self.connectUserId()) throw new Error("null connectUserId");
return ajax({
type: "GET",
url: "https://connect.emby.media/service/ServerAuthorizations/accept?serverId=" + serverId + "&userId=" + self.connectUserId(),
headers: {
"X-Connect-UserToken": connectToken,
"X-Application": appName + "/" + appVersion
}
})
}, self.resetRegistrationInfo = function(apiClient) {
var cacheKey = getCacheKey("themes", apiClient, {
viewOnly: !0
});
appStorage.removeItem(cacheKey), cacheKey = getCacheKey("themes", apiClient, {
viewOnly: !1
}), appStorage.removeItem(cacheKey)
}, self.getRegistrationInfo = function(feature, apiClient, options) {
var cacheKey = getCacheKey(feature, apiClient, options);
appStorage.setItem(cacheKey, JSON.stringify({
lastValidDate: new Date().getTime(),
deviceId: self.deviceId()
}));
return Promise.resolve();
}, self.createPin = function() {
var request = {
type: "POST",
url: getConnectUrl("pin"),
data: {
deviceId: deviceId
},
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}, self.getPinStatus = function(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
var queryString = {
deviceId: pinInfo.DeviceId,
pin: pinInfo.Pin
},
request = {
type: "GET",
url: getConnectUrl("pin") + "?" + paramsToString(queryString),
dataType: "json"
};
return addAppInfoToConnectRequest(request), ajax(request)
}, self.exchangePin = function(pinInfo) {
if (!pinInfo) throw new Error("pinInfo cannot be null");
return exchangePin(pinInfo).then(function(result) {
var credentials = credentialProvider.credentials();
return credentials.ConnectAccessToken = result.AccessToken, credentials.ConnectUserId = result.UserId, credentialProvider.credentials(credentials), ensureConnectUser(credentials)
})
}
};
return ConnectionManager.prototype.connect = function(options) {
console.log("Begin connect");
var instance = this;
return instance.getAvailableServers().then(function(servers) {
return instance.connectToServers(servers, options)
})
}, ConnectionManager.prototype.isLoggedIntoConnect = function() {
return !(!this.connectToken() || !this.connectUserId())
}, ConnectionManager.prototype.getApiClients = function() {
for (var servers = this.getSavedServers(), i = 0, length = servers.length; i < length; i++) {
var server = servers[i];
server.Id && this._getOrAddApiClient(server, getServerAddress(server, server.LastConnectionMode))
}
return this._apiClients
}, ConnectionManager.prototype.getApiClient = function(item) {
if (!item) throw new Error("item or serverId cannot be null");
return item.ServerId && (item = item.ServerId), this._apiClients.filter(function(a) {
var serverInfo = a.serverInfo();
return !serverInfo || serverInfo.Id === item
})[0]
}, ConnectionManager.prototype.minServerVersion = function(val) {
return val && (this._minServerVersion = val), this._minServerVersion
}, ConnectionManager.prototype.handleMessageReceived = function(msg) {
var serverId = msg.ServerId;
if (serverId) {
var apiClient = this.getApiClient(serverId);
if (apiClient) {
if ("string" == typeof msg.Data) try {
msg.Data = JSON.parse(msg.Data)
} catch (err) {}
apiClient.handleMessageReceived(msg)
}
}
}, ConnectionManager
});

View File

@@ -0,0 +1,29 @@
define(["events", "appStorage"], function(events, appStorage) {
"use strict";
function ensure(instance, data) {
if (!instance._credentials) {
var json = appStorage.getItem(instance.key) || "{}";
console.log("credentials initialized with: " + json), instance._credentials = JSON.parse(json), instance._credentials.Servers = instance._credentials.Servers || []
}
}
function set(instance, data) {
data ? (instance._credentials = data, appStorage.setItem(instance.key, JSON.stringify(data))) : instance.clear(), events.trigger(instance, "credentialsupdated")
}
function Credentials(key) {
this.key = key || "jellyfin_credentials"
}
return Credentials.prototype.clear = function() {
this._credentials = null, appStorage.removeItem(this.key)
}, Credentials.prototype.credentials = function(data) {
return data && set(this, data), ensure(this), this._credentials
}, Credentials.prototype.addOrUpdateServer = function(list, server) {
if (!server.Id) throw new Error("Server.Id cannot be null or empty");
var existing = list.filter(function(s) {
return s.Id === server.Id
})[0];
return existing ? (existing.DateLastAccessed = Math.max(existing.DateLastAccessed || 0, server.DateLastAccessed || 0), existing.UserLinkType = server.UserLinkType, server.AccessToken && (existing.AccessToken = server.AccessToken, existing.UserId = server.UserId), server.ExchangeToken && (existing.ExchangeToken = server.ExchangeToken), server.RemoteAddress && (existing.RemoteAddress = server.RemoteAddress), server.ManualAddress && (existing.ManualAddress = server.ManualAddress), server.LocalAddress && (existing.LocalAddress = server.LocalAddress), server.Name && (existing.Name = server.Name), null != server.LastConnectionMode && (existing.LastConnectionMode = server.LastConnectionMode), server.ConnectServerId && (existing.ConnectServerId = server.ConnectServerId), existing) : (list.push(server), server)
}, Credentials
});

View File

@@ -0,0 +1,30 @@
define([], function() {
"use strict";
function getCallbacks(obj, name) {
if (!obj) throw new Error("obj cannot be null!");
obj._callbacks = obj._callbacks || {};
var list = obj._callbacks[name];
return list || (obj._callbacks[name] = [], list = obj._callbacks[name]), list
}
return {
on: function(obj, eventName, fn) {
getCallbacks(obj, eventName).push(fn)
},
off: function(obj, eventName, fn) {
var list = getCallbacks(obj, eventName),
i = list.indexOf(fn); - 1 !== i && list.splice(i, 1)
},
trigger: function(obj, eventName) {
var eventObject = {
type: eventName
},
eventArgs = [];
eventArgs.push(eventObject);
for (var additionalArgs = arguments[2] || [], i = 0, length = additionalArgs.length; i < length; i++) eventArgs.push(additionalArgs[i]);
getCallbacks(obj, eventName).slice(0).forEach(function(c) {
c.apply(obj, eventArgs)
})
}
}
});

View File

@@ -0,0 +1,406 @@
define(["filerepository", "itemrepository", "useractionrepository", "transfermanager"], function(filerepository, itemrepository, useractionrepository, transfermanager) {
"use strict";
function getLocalItem(serverId, itemId) {
return console.log("[lcoalassetmanager] Begin getLocalItem"), itemrepository.get(serverId, itemId)
}
function recordUserAction(action) {
return action.Id = createGuid(), useractionrepository.set(action.Id, action)
}
function getUserActions(serverId) {
return useractionrepository.getByServerId(serverId)
}
function deleteUserAction(action) {
return useractionrepository.remove(action.Id)
}
function deleteUserActions(actions) {
var results = [];
return actions.forEach(function(action) {
results.push(deleteUserAction(action))
}), Promise.all(results)
}
function getServerItems(serverId) {
return console.log("[localassetmanager] Begin getServerItems"), itemrepository.getAll(serverId)
}
function getItemsFromIds(serverId, ids) {
var actions = ids.map(function(id) {
var strippedId = stripStart(id, "local:");
return getLocalItem(serverId, strippedId)
});
return Promise.all(actions).then(function(items) {
var libItems = items.map(function(locItem) {
return locItem.Item
});
return Promise.resolve(libItems)
})
}
function getViews(serverId, userId) {
return itemrepository.getServerItemTypes(serverId, userId).then(function(types) {
var item, list = [];
return types.indexOf("Audio") > -1 && (item = {
Name: "Music",
ServerId: serverId,
Id: "localview:MusicView",
Type: "MusicView",
CollectionType: "music",
IsFolder: !0
}, list.push(item)), types.indexOf("Photo") > -1 && (item = {
Name: "Photos",
ServerId: serverId,
Id: "localview:PhotosView",
Type: "PhotosView",
CollectionType: "photos",
IsFolder: !0
}, list.push(item)), types.indexOf("Episode") > -1 && (item = {
Name: "TV",
ServerId: serverId,
Id: "localview:TVView",
Type: "TVView",
CollectionType: "tvshows",
IsFolder: !0
}, list.push(item)), types.indexOf("Movie") > -1 && (item = {
Name: "Movies",
ServerId: serverId,
Id: "localview:MoviesView",
Type: "MoviesView",
CollectionType: "movies",
IsFolder: !0
}, list.push(item)), types.indexOf("Video") > -1 && (item = {
Name: "Videos",
ServerId: serverId,
Id: "localview:VideosView",
Type: "VideosView",
CollectionType: "videos",
IsFolder: !0
}, list.push(item)), types.indexOf("MusicVideo") > -1 && (item = {
Name: "Music Videos",
ServerId: serverId,
Id: "localview:MusicVideosView",
Type: "MusicVideosView",
CollectionType: "videos",
IsFolder: !0
}, list.push(item)), Promise.resolve(list)
})
}
function updateFiltersForTopLevelView(parentId, mediaTypes, includeItemTypes, query) {
switch (parentId) {
case "MusicView":
return query.Recursive ? includeItemTypes.push("Audio") : includeItemTypes.push("MusicAlbum"), !0;
case "PhotosView":
return query.Recursive ? includeItemTypes.push("Photo") : includeItemTypes.push("PhotoAlbum"), !0;
case "TVView":
return query.Recursive ? includeItemTypes.push("Episode") : includeItemTypes.push("Series"), !0;
case "VideosView":
return query.Recursive, includeItemTypes.push("Video"), !0;
case "MoviesView":
return query.Recursive, includeItemTypes.push("Movie"), !0;
case "MusicVideosView":
return query.Recursive, includeItemTypes.push("MusicVideo"), !0
}
return !1
}
function normalizeId(id) {
return id ? (id = stripStart(id, "localview:"), id = stripStart(id, "local:")) : null
}
function normalizeIdList(val) {
return val ? val.split(",").map(normalizeId) : []
}
function shuffle(array) {
for (var temporaryValue, randomIndex, currentIndex = array.length; 0 !== currentIndex;) randomIndex = Math.floor(Math.random() * currentIndex), currentIndex -= 1, temporaryValue = array[currentIndex], array[currentIndex] = array[randomIndex], array[randomIndex] = temporaryValue;
return array
}
function sortItems(items, query) {
if (!query.SortBy || 0 === query.SortBy.length) return items;
if ("Random" === query.SortBy) return shuffle(items);
var sortSpec = getSortSpec(query);
return items.sort(function(a, b) {
for (var i = 0; i < sortSpec.length; i++) {
var result = compareValues(a, b, sortSpec[i].Field, sortSpec[i].OrderDescending);
if (0 !== result) return result
}
return 0
}), items
}
function compareValues(a, b, field, orderDesc) {
if (!a.hasOwnProperty(field) || !b.hasOwnProperty(field)) return 0;
var valA = a[field],
valB = b[field],
result = 0;
return "string" == typeof valA || "string" == typeof valB ? (valA = valA || "", valB = valB || "", result = valA.toLowerCase().localeCompare(valB.toLowerCase())) : valA > valB ? result = 1 : valA < valB && (result = -1), orderDesc && (result *= -1), result
}
function getSortSpec(query) {
for (var sortFields = (query.SortBy || "").split(","), sortOrders = (query.SortOrder || "").split(","), sortSpec = [], i = 0; i < sortFields.length; i++) {
var orderDesc = !1;
i < sortOrders.length && -1 !== sortOrders[i].toLowerCase().indexOf("desc") && (orderDesc = !0), sortSpec.push({
Field: sortFields[i],
OrderDescending: orderDesc
})
}
return sortSpec
}
function getViewItems(serverId, userId, options) {
var searchParentId = options.ParentId;
searchParentId = normalizeId(searchParentId);
var seasonId = normalizeId(options.SeasonId || options.seasonId),
seriesId = normalizeId(options.SeriesId || options.seriesId),
albumIds = normalizeIdList(options.AlbumIds || options.albumIds),
includeItemTypes = options.IncludeItemTypes ? options.IncludeItemTypes.split(",") : [],
filters = options.Filters ? options.Filters.split(",") : [],
mediaTypes = options.MediaTypes ? options.MediaTypes.split(",") : [];
return updateFiltersForTopLevelView(searchParentId, mediaTypes, includeItemTypes, options) && (searchParentId = null), getServerItems(serverId).then(function(items) {
var itemsMap = new Map,
subtreeIdSet = new Set;
if (items.forEach(function(item) {
item.Item.LocalChildren = [], itemsMap.set(item.Item.Id, item.Item)
}), itemsMap.forEach(function(item, ignored, ignored2) {
if (item.ParentId && itemsMap.has(item.ParentId)) {
itemsMap.get(item.ParentId).LocalChildren.push(item)
}
}), options.Recursive && searchParentId && itemsMap.has(searchParentId)) {
var addSubtreeIds = function(recurseItem) {
subtreeIdSet.has(recurseItem.Id) || subtreeIdSet.add(recurseItem.Id), recurseItem.LocalChildren.forEach(function(childItem) {
addSubtreeIds(childItem)
})
},
searchParentItem = itemsMap.get(searchParentId);
addSubtreeIds(searchParentItem)
}
var resultItems = items.filter(function(item) {
return (!item.SyncStatus || "synced" === item.SyncStatus) && ((!mediaTypes.length || -1 !== mediaTypes.indexOf(item.Item.MediaType || "")) && ((!seriesId || item.Item.SeriesId === seriesId) && ((!seasonId || item.Item.SeasonId === seasonId) && ((!albumIds.length || -1 !== albumIds.indexOf(item.Item.AlbumId || "")) && ((!item.Item.IsFolder || -1 === filters.indexOf("IsNotFolder")) && (!(!item.Item.IsFolder && -1 !== filters.indexOf("IsFolder")) && ((!includeItemTypes.length || -1 !== includeItemTypes.indexOf(item.Item.Type || "")) && (!searchParentId || (options.Recursive ? subtreeIdSet.has(item.Item.Id) : item.Item.ParentId === searchParentId)))))))))
}).map(function(item2) {
return item2.Item
});
return resultItems = sortItems(resultItems, options), options.Limit && (resultItems = resultItems.slice(0, options.Limit)), Promise.resolve(resultItems)
})
}
function removeObsoleteContainerItems(serverId) {
return getServerItems(serverId).then(function(items) {
var seriesItems = items.filter(function(item) {
return "series" === (item.Item.Type || "").toLowerCase()
}),
seasonItems = items.filter(function(item) {
return "season" === (item.Item.Type || "").toLowerCase()
}),
albumItems = items.filter(function(item) {
var type = (item.Item.Type || "").toLowerCase();
return "musicalbum" === type || "photoalbum" === type
}),
requiredSeriesIds = items.filter(function(item) {
return "episode" === (item.Item.Type || "").toLowerCase()
}).map(function(item2) {
return item2.Item.SeriesId
}).filter(filterDistinct),
requiredSeasonIds = items.filter(function(item) {
return "episode" === (item.Item.Type || "").toLowerCase()
}).map(function(item2) {
return item2.Item.SeasonId
}).filter(filterDistinct),
requiredAlbumIds = items.filter(function(item) {
var type = (item.Item.Type || "").toLowerCase();
return "audio" === type || "photo" === type
}).map(function(item2) {
return item2.Item.AlbumId
}).filter(filterDistinct),
obsoleteItems = [];
seriesItems.forEach(function(item) {
requiredSeriesIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
}), seasonItems.forEach(function(item) {
requiredSeasonIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
}), albumItems.forEach(function(item) {
requiredAlbumIds.indexOf(item.Item.Id) < 0 && obsoleteItems.push(item)
});
var p = Promise.resolve();
return obsoleteItems.forEach(function(item) {
p = p.then(function() {
return itemrepository.remove(item.ServerId, item.Id)
})
}), p
})
}
function removeLocalItem(localItem) {
return itemrepository.get(localItem.ServerId, localItem.Id).then(function(item) {
var onFileDeletedSuccessOrFail = function() {
return itemrepository.remove(localItem.ServerId, localItem.Id)
},
p = Promise.resolve();
return item.LocalPath && (p = p.then(function() {
return filerepository.deleteFile(item.LocalPath)
})), item && item.Item && item.Item.MediaSources && item.Item.MediaSources.forEach(function(mediaSource) {
mediaSource.MediaStreams && mediaSource.MediaStreams.length > 0 && mediaSource.MediaStreams.forEach(function(mediaStream) {
mediaStream.Path && (p = p.then(function() {
return filerepository.deleteFile(mediaStream.Path)
}))
})
}), p.then(onFileDeletedSuccessOrFail, onFileDeletedSuccessOrFail)
}, function(item) {
return Promise.resolve()
})
}
function addOrUpdateLocalItem(localItem) {
return itemrepository.set(localItem.ServerId, localItem.Id, localItem)
}
function getSubtitleSaveFileName(localItem, mediaPath, language, isForced, format) {
var name = getNameWithoutExtension(mediaPath);
name = filerepository.getValidFileName(name), language && (name += "." + language.toLowerCase()), isForced && (name += ".foreign"), name = name + "." + format.toLowerCase();
var mediaFolder = filerepository.getParentPath(localItem.LocalPath);
return filerepository.combinePath(mediaFolder, name)
}
function getItemFileSize(path) {
return filerepository.getItemFileSize(path)
}
function getNameWithoutExtension(path) {
var fileName = path,
pos = fileName.lastIndexOf(".");
return pos > 0 && (fileName = fileName.substring(0, pos)), fileName
}
function downloadFile(url, localItem) {
var imageUrl = getImageUrl(localItem.Item.ServerId, localItem.Item.Id, {
type: "Primary",
index: 0
});
return transfermanager.downloadFile(url, localItem, imageUrl)
}
function downloadSubtitles(url, fileName) {
return transfermanager.downloadSubtitles(url, fileName)
}
function getImageUrl(serverId, itemId, imageOptions) {
var imageType = imageOptions.type,
index = imageOptions.index,
pathArray = getImagePath(serverId, itemId, imageType, index);
return filerepository.getImageUrl(pathArray)
}
function hasImage(serverId, itemId, imageType, index) {
var pathArray = getImagePath(serverId, itemId, imageType, index),
localFilePath = filerepository.getFullMetadataPath(pathArray);
return filerepository.fileExists(localFilePath).then(function(exists) {
return Promise.resolve(exists)
}, function(err) {
return Promise.resolve(!1)
})
}
function fileExists(localFilePath) {
return filerepository.fileExists(localFilePath)
}
function downloadImage(localItem, url, serverId, itemId, imageType, index) {
var localPathParts = getImagePath(serverId, itemId, imageType, index);
return transfermanager.downloadImage(url, localPathParts)
}
function isDownloadFileInQueue(path) {
return transfermanager.isDownloadFileInQueue(path)
}
function getDownloadItemCount() {
return transfermanager.getDownloadItemCount()
}
function getDirectoryPath(item) {
var parts = [],
itemtype = item.Type.toLowerCase(),
mediaType = (item.MediaType || "").toLowerCase();
"episode" === itemtype || "series" === itemtype || "season" === itemtype ? parts.push("TV") : "video" === mediaType ? parts.push("Videos") : "audio" === itemtype || "musicalbum" === itemtype || "musicartist" === itemtype ? parts.push("Music") : "photo" === itemtype || "photoalbum" === itemtype ? parts.push("Photos") : null;
var albumArtist = item.AlbumArtist;
albumArtist && parts.push(albumArtist);
var seriesName = item.SeriesName;
seriesName && parts.push(seriesName);
var seasonName = item.SeasonName;
seasonName && parts.push(seasonName), item.Album && parts.push(item.Album), ("video" === mediaType && "episode" !== itemtype || item.IsFolder) && parts.push(item.Name);
for (var finalParts = [], i = 0; i < parts.length; i++) finalParts.push(filerepository.getValidFileName(parts[i]));
return finalParts
}
function getImagePath(serverId, itemId, imageType, index) {
var parts = [];
parts.push("images"), index = index || 0, parts.push(itemId + "_" + imageType + "_" + index.toString());
for (var finalParts = [], i = 0; i < parts.length; i++) finalParts.push(parts[i]);
return finalParts
}
function getLocalFileName(item, originalFileName) {
var filename = originalFileName || item.Name;
return filerepository.getValidFileName(filename)
}
function resyncTransfers() {
return transfermanager.resyncTransfers()
}
function createGuid() {
var d = (new Date).getTime();
return window.performance && "function" == typeof window.performance.now && (d += performance.now()), "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
var r = (d + 16 * Math.random()) % 16 | 0;
return d = Math.floor(d / 16), ("x" === c ? r : 3 & r | 8).toString(16)
})
}
function startsWith(str, find) {
return !!(str && find && str.length > find.length && 0 === str.indexOf(find))
}
function stripStart(str, find) {
return startsWith(str, find) ? str.substr(find.length) : str
}
function filterDistinct(value, index, self) {
return self.indexOf(value) === index
}
function enableBackgroundCompletion() {
return transfermanager.enableBackgroundCompletion
}
return {
getLocalItem: getLocalItem,
getDirectoryPath: getDirectoryPath,
getLocalFileName: getLocalFileName,
recordUserAction: recordUserAction,
getUserActions: getUserActions,
deleteUserAction: deleteUserAction,
deleteUserActions: deleteUserActions,
removeLocalItem: removeLocalItem,
addOrUpdateLocalItem: addOrUpdateLocalItem,
downloadFile: downloadFile,
downloadSubtitles: downloadSubtitles,
hasImage: hasImage,
downloadImage: downloadImage,
getImageUrl: getImageUrl,
getSubtitleSaveFileName: getSubtitleSaveFileName,
getServerItems: getServerItems,
getItemFileSize: getItemFileSize,
isDownloadFileInQueue: isDownloadFileInQueue,
getDownloadItemCount: getDownloadItemCount,
getViews: getViews,
getViewItems: getViewItems,
resyncTransfers: resyncTransfers,
getItemsFromIds: getItemsFromIds,
removeObsoleteContainerItems: removeObsoleteContainerItems,
fileExists: fileExists,
enableBackgroundCompletion: enableBackgroundCompletion
}
});

View File

@@ -0,0 +1,3 @@
{
"main": "apiclient.js"
}

View File

@@ -0,0 +1,45 @@
define([], function() {
"use strict";
function getValidFileName(path) {
return path
}
function getFullLocalPath(pathArray) {
return pathArray.join("/")
}
function getPathFromArray(pathArray) {
return pathArray.join("/")
}
function deleteFile(path) {
return Promise.resolve()
}
function deleteDirectory(path) {
return Promise.resolve()
}
function fileExists(path) {
return Promise.resolve()
}
function getItemFileSize(path) {
return Promise.resolve(0)
}
function getImageUrl(pathParts) {
return pathParts.join("/")
}
return {
getValidFileName: getValidFileName,
getFullLocalPath: getFullLocalPath,
getPathFromArray: getPathFromArray,
deleteFile: deleteFile,
deleteDirectory: deleteDirectory,
fileExists: fileExists,
getItemFileSize: getItemFileSize,
getImageUrl: getImageUrl
}
});

View File

@@ -0,0 +1,123 @@
define([], function() {
"use strict";
function ServerDatabase(dbName, readyCallback) {
var request = indexedDB.open(dbName, dbVersion);
request.onerror = function(event) {}, request.onupgradeneeded = function(event) {
var db = event.target.result;
db.createObjectStore(dbName).transaction.oncomplete = function(event) {
readyCallback(db)
}
}, request.onsuccess = function(event) {
var db = event.target.result;
readyCallback(db)
}
}
function getDbName(serverId) {
return "items_" + serverId
}
function getDb(serverId, callback) {
var dbName = getDbName(serverId),
db = databases[dbName];
if (db) return void callback(db);
new ServerDatabase(dbName, function(db) {
databases[dbName] = db, callback(db)
})
}
function getServerItemTypes(serverId, userId) {
return getAll(serverId, userId).then(function(all) {
return all.map(function(item2) {
return item2.Item.Type || ""
}).filter(filterDistinct)
})
}
function getAll(serverId, userId) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var request, storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName);
if ("getAll" in objectStore) request = objectStore.getAll(null, 1e4), request.onsuccess = function(event) {
resolve(event.target.result)
};
else {
var results = [];
request = objectStore.openCursor(), request.onsuccess = function(event) {
var cursor = event.target.result;
cursor ? (results.push(cursor.value), cursor.continue()) : resolve(results)
}
}
request.onerror = reject
})
})
}
function get(serverId, key) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName),
request = objectStore.get(key);
request.onerror = reject, request.onsuccess = function(event) {
resolve(request.result)
}
})
})
}
function set(serverId, key, val) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.put(val, key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function remove(serverId, key) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.delete(key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function clear(serverId) {
return new Promise(function(resolve, reject) {
getDb(serverId, function(db) {
var storeName = getDbName(serverId),
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.clear();
request.onerror = reject, request.onsuccess = resolve
})
})
}
function filterDistinct(value, index, self) {
return self.indexOf(value) === index
}
var indexedDB = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB,
dbVersion = (self.IDBTransaction || self.webkitIDBTransaction || self.msIDBTransaction, self.IDBKeyRange || self.webkitIDBKeyRange || self.msIDBKeyRange, 1),
databases = {};
return {
get: get,
set: set,
remove: remove,
clear: clear,
getAll: getAll,
getServerItemTypes: getServerItemTypes
}
});

View File

@@ -0,0 +1,17 @@
define(["connectionManager"], function(connectionManager) {
"use strict";
var isSyncing;
return {
sync: function(options) {
return console.log("localSync.sync starting..."), isSyncing ? Promise.resolve() : (isSyncing = !0, new Promise(function(resolve, reject) {
require(["multiserversync", "appSettings"], function(MultiServerSync, appSettings) {
options = options || {}, options.cameraUploadServers = appSettings.cameraUploadServers(), (new MultiServerSync).sync(connectionManager, options).then(function() {
isSyncing = null, resolve()
}, function(err) {
isSyncing = null, reject(err)
})
})
}))
}
}
});

View File

@@ -0,0 +1,368 @@
define(["localassetmanager"], function(localassetmanager) {
"use strict";
function processDownloadStatus(apiClient, serverInfo, options) {
return console.log("[mediasync] Begin processDownloadStatus"), localassetmanager.resyncTransfers().then(function() {
return localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
console.log("[mediasync] Begin processDownloadStatus getServerItems completed");
var p = Promise.resolve(),
cnt = 0;
return items.filter(function(item) {
return "transferring" === item.SyncStatus || "queued" === item.SyncStatus
}).forEach(function(item) {
p = p.then(function() {
return reportTransfer(apiClient, item)
}), cnt++
}), p.then(function() {
return console.log("[mediasync] Exit processDownloadStatus. Items reported: " + cnt.toString()), Promise.resolve()
})
})
})
}
function reportTransfer(apiClient, item) {
return localassetmanager.getItemFileSize(item.LocalPath).then(function(size) {
return size > 0 ? apiClient.reportSyncJobItemTransferred(item.SyncJobItemId).then(function() {
return item.SyncStatus = "synced", console.log("[mediasync] reportSyncJobItemTransferred called for " + item.LocalPath), localassetmanager.addOrUpdateLocalItem(item)
}, function(error) {
return console.error("[mediasync] Mediasync error on reportSyncJobItemTransferred", error), item.SyncStatus = "error", localassetmanager.addOrUpdateLocalItem(item)
}) : localassetmanager.isDownloadFileInQueue(item.LocalPath).then(function(result) {
return result ? Promise.resolve() : (console.log("[mediasync] reportTransfer: Size is 0 and download no longer in queue. Deleting item."), localassetmanager.removeLocalItem(item).then(function() {
return console.log("[mediasync] reportTransfer: Item deleted."), Promise.resolve()
}, function(err2) {
return console.log("[mediasync] reportTransfer: Failed to delete item.", err2), Promise.resolve()
}))
})
}, function(error) {
return console.error("[mediasync] reportTransfer: error on getItemFileSize. Deleting item.", error), localassetmanager.removeLocalItem(item).then(function() {
return console.log("[mediasync] reportTransfer: Item deleted."), Promise.resolve()
}, function(err2) {
return console.log("[mediasync] reportTransfer: Failed to delete item.", err2), Promise.resolve()
})
})
}
function reportOfflineActions(apiClient, serverInfo) {
return console.log("[mediasync] Begin reportOfflineActions"), localassetmanager.getUserActions(serverInfo.Id).then(function(actions) {
return actions.length ? apiClient.reportOfflineActions(actions).then(function() {
return localassetmanager.deleteUserActions(actions).then(function() {
return console.log("[mediasync] Exit reportOfflineActions (actions reported and deleted.)"), Promise.resolve()
})
}, function(err) {
return console.error("[mediasync] error on apiClient.reportOfflineActions: " + err.toString()), localassetmanager.deleteUserActions(actions)
}) : (console.log("[mediasync] Exit reportOfflineActions (no actions)"), Promise.resolve())
})
}
function syncData(apiClient, serverInfo) {
return console.log("[mediasync] Begin syncData"), localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
var completedItems = items.filter(function(item) {
return item && ("synced" === item.SyncStatus || "error" === item.SyncStatus)
}),
request = {
TargetId: apiClient.deviceId(),
LocalItemIds: completedItems.map(function(xitem) {
return xitem.ItemId
})
};
return apiClient.syncData(request).then(function(result) {
return afterSyncData(apiClient, serverInfo, result).then(function() {
return console.log("[mediasync] Exit syncData"), Promise.resolve()
}, function(err) {
return console.error("[mediasync] Error in syncData: " + err.toString()), Promise.resolve()
})
})
})
}
function afterSyncData(apiClient, serverInfo, syncDataResult) {
console.log("[mediasync] Begin afterSyncData");
var p = Promise.resolve();
return syncDataResult.ItemIdsToRemove && syncDataResult.ItemIdsToRemove.length > 0 && syncDataResult.ItemIdsToRemove.forEach(function(itemId) {
p = p.then(function() {
return removeLocalItem(itemId, serverInfo.Id)
})
}), p = p.then(function() {
return removeObsoleteContainerItems(serverInfo.Id)
}), p.then(function() {
return console.log("[mediasync] Exit afterSyncData"), Promise.resolve()
})
}
function removeObsoleteContainerItems(serverId) {
return console.log("[mediasync] Begin removeObsoleteContainerItems"), localassetmanager.removeObsoleteContainerItems(serverId)
}
function removeLocalItem(itemId, serverId) {
return console.log("[mediasync] Begin removeLocalItem"), localassetmanager.getLocalItem(serverId, itemId).then(function(item) {
return item ? localassetmanager.removeLocalItem(item) : Promise.resolve()
}, function(err2) {
return console.error("[mediasync] removeLocalItem: Failed: ", err2), Promise.resolve()
})
}
function getNewMedia(apiClient, downloadCount) {
return console.log("[mediasync] Begin getNewMedia"), apiClient.getReadySyncItems(apiClient.deviceId()).then(function(jobItems) {
console.log("[mediasync] getReadySyncItems returned " + jobItems.length + " items");
var p = Promise.resolve(),
currentCount = downloadCount;
return jobItems.forEach(function(jobItem) {
currentCount++ <= 10 && (p = p.then(function() {
return getNewItem(jobItem, apiClient)
}))
}), p.then(function() {
return console.log("[mediasync] Exit getNewMedia"), Promise.resolve()
})
}, function(err) {
return console.error("[mediasync] getReadySyncItems: Failed: ", err), Promise.resolve()
})
}
function afterMediaDownloaded(apiClient, jobItem, localItem) {
return console.log("[mediasync] Begin afterMediaDownloaded"), getImages(apiClient, jobItem, localItem).then(function() {
var libraryItem = jobItem.Item;
return downloadParentItems(apiClient, jobItem, libraryItem).then(function() {
return getSubtitles(apiClient, jobItem, localItem)
})
})
}
function createLocalItem(libraryItem, jobItem) {
console.log("[localassetmanager] Begin createLocalItem");
var item = {
Item: libraryItem,
ItemId: libraryItem.Id,
ServerId: libraryItem.ServerId,
Id: libraryItem.Id
};
return jobItem && (item.SyncJobItemId = jobItem.SyncJobItemId), console.log("[localassetmanager] End createLocalItem"), item
}
function getNewItem(jobItem, apiClient) {
console.log("[mediasync] Begin getNewItem");
var libraryItem = jobItem.Item;
return localassetmanager.getLocalItem(libraryItem.ServerId, libraryItem.Id).then(function(existingItem) {
if (existingItem && ("queued" === existingItem.SyncStatus || "transferring" === existingItem.SyncStatus || "synced" === existingItem.SyncStatus) && (console.log("[mediasync] getNewItem: getLocalItem found existing item"), localassetmanager.enableBackgroundCompletion())) return Promise.resolve();
libraryItem.CanDelete = !1, libraryItem.CanDownload = !1, libraryItem.SupportsSync = !1, libraryItem.People = [], libraryItem.Chapters = [], libraryItem.Studios = [], libraryItem.SpecialFeatureCount = null, libraryItem.LocalTrailerCount = null, libraryItem.RemoteTrailers = [];
var localItem = createLocalItem(libraryItem, jobItem);
return localItem.SyncStatus = "queued", downloadMedia(apiClient, jobItem, localItem)
})
}
function downloadParentItems(apiClient, jobItem, libraryItem) {
var p = Promise.resolve();
return libraryItem.SeriesId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.SeriesId)
})), libraryItem.SeasonId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.SeasonId).then(function(seasonItem) {
return libraryItem.SeasonPrimaryImageTag = (seasonItem.Item.ImageTags || {}).Primary, Promise.resolve()
})
})), libraryItem.AlbumId && (p = p.then(function() {
return downloadItem(apiClient, libraryItem.AlbumId)
})), p
}
function downloadItem(apiClient, itemId) {
return apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function(downloadedItem) {
downloadedItem.CanDelete = !1, downloadedItem.CanDownload = !1, downloadedItem.SupportsSync = !1, downloadedItem.People = [], downloadedItem.SpecialFeatureCount = null, downloadedItem.BackdropImageTags = null, downloadedItem.ParentBackdropImageTags = null, downloadedItem.ParentArtImageTag = null, downloadedItem.ParentLogoImageTag = null;
var localItem = createLocalItem(downloadedItem, null);
return localassetmanager.addOrUpdateLocalItem(localItem).then(function() {
return Promise.resolve(localItem)
}, function(err) {
return console.error("[mediasync] downloadItem failed: " + err.toString()), Promise.resolve(null)
})
})
}
function ensureLocalPathParts(localItem, jobItem) {
if (!localItem.LocalPathParts) {
var libraryItem = localItem.Item,
parts = localassetmanager.getDirectoryPath(libraryItem);
parts.push(localassetmanager.getLocalFileName(libraryItem, jobItem.OriginalFileName)), localItem.LocalPathParts = parts
}
}
function downloadMedia(apiClient, jobItem, localItem) {
console.log("[mediasync] downloadMedia: start.");
var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/File", {
api_key: apiClient.accessToken()
});
return ensureLocalPathParts(localItem, jobItem), localassetmanager.downloadFile(url, localItem).then(function(result) {
console.log("[mediasync] downloadMedia-downloadFile returned path: " + result.path);
var localPath = result.path,
libraryItem = localItem.Item;
if (localPath && libraryItem.MediaSources)
for (var i = 0; i < libraryItem.MediaSources.length; i++) {
var mediaSource = libraryItem.MediaSources[i];
mediaSource.Path = localPath, mediaSource.Protocol = "File"
}
return localItem.LocalPath = localPath, localItem.SyncStatus = "transferring", localassetmanager.addOrUpdateLocalItem(localItem).then(function() {
return afterMediaDownloaded(apiClient, jobItem, localItem).then(function() {
return result.isComplete ? (localItem.SyncStatus = "synced", reportTransfer(apiClient, localItem)) : Promise.resolve()
}, function(err) {
return console.log("[mediasync] downloadMedia: afterMediaDownloaded failed: " + err), Promise.reject(err)
})
}, function(err) {
return console.log("[mediasync] downloadMedia: addOrUpdateLocalItem failed: " + err), Promise.reject(err)
})
}, function(err) {
return console.log("[mediasync] downloadMedia: localassetmanager.downloadFile failed: " + err), Promise.reject(err)
})
}
function getImages(apiClient, jobItem, localItem) {
console.log("[mediasync] Begin getImages");
var p = Promise.resolve(),
libraryItem = localItem.Item,
serverId = libraryItem.ServerId,
mainImageTag = (libraryItem.ImageTags || {}).Primary;
libraryItem.Id && mainImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, mainImageTag, "Primary")
}));
var logoImageTag = (libraryItem.ImageTags || {}).Logo;
libraryItem.Id && logoImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, logoImageTag, "Logo")
}));
var artImageTag = (libraryItem.ImageTags || {}).Art;
libraryItem.Id && artImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, artImageTag, "Art")
}));
var bannerImageTag = (libraryItem.ImageTags || {}).Banner;
libraryItem.Id && bannerImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, bannerImageTag, "Banner")
}));
var thumbImageTag = (libraryItem.ImageTags || {}).Thumb;
if (libraryItem.Id && thumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.Id, thumbImageTag, "Thumb")
})), libraryItem.Id && libraryItem.BackdropImageTags)
for (var i = 0; i < libraryItem.BackdropImageTags.length; i++);
return libraryItem.SeriesId && libraryItem.SeriesPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeriesId, libraryItem.SeriesPrimaryImageTag, "Primary")
})), libraryItem.SeriesId && libraryItem.SeriesThumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeriesId, libraryItem.SeriesThumbImageTag, "Thumb")
})), libraryItem.SeasonId && libraryItem.SeasonPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.SeasonId, libraryItem.SeasonPrimaryImageTag, "Primary")
})), libraryItem.AlbumId && libraryItem.AlbumPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.AlbumId, libraryItem.AlbumPrimaryImageTag, "Primary")
})), libraryItem.ParentThumbItemId && libraryItem.ParentThumbImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.ParentThumbItemId, libraryItem.ParentThumbImageTag, "Thumb")
})), libraryItem.ParentPrimaryImageItemId && libraryItem.ParentPrimaryImageTag && (p = p.then(function() {
return downloadImage(localItem, apiClient, serverId, libraryItem.ParentPrimaryImageItemId, libraryItem.ParentPrimaryImageTag, "Primary")
})), p.then(function() {
return console.log("[mediasync] Finished getImages"), localassetmanager.addOrUpdateLocalItem(localItem)
}, function(err) {
return console.log("[mediasync] Error getImages: " + err.toString()), Promise.resolve()
})
}
function downloadImage(localItem, apiClient, serverId, itemId, imageTag, imageType, index) {
return index = index || 0, localassetmanager.hasImage(serverId, itemId, imageType, index).then(function(hasImage) {
if (hasImage) return console.log("[mediasync] downloadImage - skip existing: " + itemId + " " + imageType + "_" + index.toString()), Promise.resolve();
var maxWidth = 400;
"backdrop" === imageType && (maxWidth = null);
var imageUrl = apiClient.getScaledImageUrl(itemId, {
tag: imageTag,
type: imageType,
maxWidth: maxWidth,
api_key: apiClient.accessToken()
});
return console.log("[mediasync] downloadImage " + itemId + " " + imageType + "_" + index.toString()), localassetmanager.downloadImage(localItem, imageUrl, serverId, itemId, imageType, index).then(function(result) {
return Promise.resolve(result)
}, function(err) {
return console.log("[mediasync] Error downloadImage: " + err.toString()), Promise.resolve()
})
}, function(err) {
return console.log("[mediasync] Error downloadImage: " + err.toString()), Promise.resolve()
})
}
function getSubtitles(apiClient, jobItem, localItem) {
if (console.log("[mediasync] Begin getSubtitles"), !jobItem.Item.MediaSources.length) return console.log("[mediasync] Cannot download subtitles because video has no media source info."), Promise.resolve();
var files = jobItem.AdditionalFiles.filter(function(f) {
return "Subtitles" === f.Type
}),
mediaSource = jobItem.Item.MediaSources[0],
p = Promise.resolve();
return files.forEach(function(file) {
p = p.then(function() {
return getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource)
})
}), p.then(function() {
return console.log("[mediasync] Exit getSubtitles"), Promise.resolve()
})
}
function getItemSubtitle(file, apiClient, jobItem, localItem, mediaSource) {
console.log("[mediasync] Begin getItemSubtitle");
var subtitleStream = mediaSource.MediaStreams.filter(function(m) {
return "Subtitle" === m.Type && m.Index === file.Index
})[0];
if (!subtitleStream) return console.log("[mediasync] Cannot download subtitles because matching stream info was not found."), Promise.resolve();
var url = apiClient.getUrl("Sync/JobItems/" + jobItem.SyncJobItemId + "/AdditionalFiles", {
Name: file.Name,
api_key: apiClient.accessToken()
}),
fileName = localassetmanager.getSubtitleSaveFileName(localItem, jobItem.OriginalFileName, subtitleStream.Language, subtitleStream.IsForced, subtitleStream.Codec);
return localassetmanager.downloadSubtitles(url, fileName).then(function(subtitleResult) {
return localItem.AdditionalFiles && localItem.AdditionalFiles.forEach(function(item) {
item.Name === file.Name && (item.Path = subtitleResult.path)
}), subtitleStream.Path = subtitleResult.path, subtitleStream.DeliveryMethod = "External", localassetmanager.addOrUpdateLocalItem(localItem)
})
}
function checkLocalFileExistence(apiClient, serverInfo, options) {
return options.checkFileExistence ? (console.log("[mediasync] Begin checkLocalFileExistence"), localassetmanager.getServerItems(serverInfo.Id).then(function(items) {
var completedItems = items.filter(function(item) {
return item && ("synced" === item.SyncStatus || "error" === item.SyncStatus)
}),
p = Promise.resolve();
return completedItems.forEach(function(completedItem) {
p = p.then(function() {
return localassetmanager.fileExists(completedItem.LocalPath).then(function(exists) {
return exists ? Promise.resolve() : localassetmanager.removeLocalItem(completedItem).then(function() {
return Promise.resolve()
}, function() {
return Promise.resolve()
})
})
})
}), p
})) : Promise.resolve()
}
return function() {
var self = this;
"string" == typeof webWorkerBaseUrl && -1 !== webWorkerBaseUrl.indexOf("ms-appx://") ? self.sync = function(apiClient, serverInfo, options) {
return console.log("[mediasync]************************************* Start sync"), checkLocalFileExistence(apiClient, serverInfo, options).then(function() {
return processDownloadStatus(apiClient, serverInfo, options).then(function() {
return localassetmanager.getDownloadItemCount().then(function(downloadCount) {
return !0 === options.syncCheckProgressOnly && downloadCount > 2 ? Promise.resolve() : reportOfflineActions(apiClient, serverInfo).then(function() {
return getNewMedia(apiClient, downloadCount).then(function() {
return syncData(apiClient, serverInfo).then(function() {
return console.log("[mediasync]************************************* Exit sync"), Promise.resolve()
})
})
})
})
})
}, function(err) {
console.error(err.toString())
})
} : self.sync = function(apiClient, serverInfo, options) {
return console.log("[mediasync]************************************* Start sync"), checkLocalFileExistence(apiClient, serverInfo, options).then(function() {
return syncData(apiClient, serverInfo).then(function() {
return processDownloadStatus(apiClient, serverInfo, options).then(function() {
return localassetmanager.getDownloadItemCount().then(function(downloadCount) {
return !0 === options.syncCheckProgressOnly && downloadCount > 2 ? Promise.resolve() : reportOfflineActions(apiClient, serverInfo).then(function() {
return getNewMedia(apiClient, downloadCount).then(function() {
return syncData(apiClient, serverInfo)
})
})
})
})
})
}, function(err) {
console.error(err.toString())
})
}
}
});

View File

@@ -0,0 +1,22 @@
define(["serversync"], function(ServerSync) {
"use strict";
function syncNext(connectionManager, servers, index, options, resolve, reject) {
var length = servers.length;
if (index >= length) return console.log("MultiServerSync.sync complete"), void resolve();
var server = servers[index];
console.log("Creating ServerSync to server: " + server.Id), (new ServerSync).sync(connectionManager, server, options).then(function() {
console.log("ServerSync succeeded to server: " + server.Id), syncNext(connectionManager, servers, index + 1, options, resolve, reject)
}, function(err) {
console.log("ServerSync failed to server: " + server.Id + ". " + err), syncNext(connectionManager, servers, index + 1, options, resolve, reject)
})
}
function MultiServerSync() {}
return MultiServerSync.prototype.sync = function(connectionManager, options) {
return console.log("MultiServerSync.sync starting..."), new Promise(function(resolve, reject) {
var servers = connectionManager.getSavedServers();
syncNext(connectionManager, servers, 0, options, resolve, reject)
})
}, MultiServerSync
});

View File

@@ -0,0 +1,42 @@
define([], function() {
"use strict";
function performSync(connectionManager, server, options) {
console.log("ServerSync.performSync to server: " + server.Id), options = options || {};
var cameraUploadServers = options.cameraUploadServers || [];
console.log("ServerSync cameraUploadServers: " + JSON.stringify(cameraUploadServers));
var uploadPhotos = -1 !== cameraUploadServers.indexOf(server.Id);
return console.log("ServerSync uploadPhotos: " + uploadPhotos), (uploadPhotos ? uploadContent(connectionManager, server, options) : Promise.resolve()).then(function() {
return syncMedia(connectionManager, server, options)
})
}
function uploadContent(connectionManager, server, options) {
return new Promise().reject();
}
function syncMedia(connectionManager, server, options) {
return new Promise(function(resolve, reject) {
require(["mediasync"], function(MediaSync) {
var apiClient = connectionManager.getApiClient(server.Id);
(new MediaSync).sync(apiClient, server, options).then(resolve, reject)
})
})
}
function ServerSync() {}
return ServerSync.prototype.sync = function(connectionManager, server, options) {
if (!server.AccessToken && !server.ExchangeToken) return console.log("Skipping sync to server " + server.Id + " because there is no saved authentication information."), Promise.resolve();
var connectionOptions = {
updateDateLastAccessed: !1,
enableWebSocket: !1,
reportCapabilities: !1,
enableAutomaticBitrateDetection: !1
};
return connectionManager.connectToServer(server, connectionOptions).then(function(result) {
return "SignedIn" === result.State ? performSync(connectionManager, server, options) : (console.log("Unable to connect to server id: " + server.Id), Promise.reject())
}, function(err) {
throw console.log("Unable to connect to server id: " + server.Id), err
})
}, ServerSync
});

View File

@@ -0,0 +1,30 @@
define([], function() {
"use strict";
function downloadFile(url, folder, localItem, imageUrl) {
return Promise.reject()
}
function downloadSubtitles(url, folder, fileName) {
return Promise.reject()
}
function downloadImage(url, folder, fileName) {
return Promise.reject()
}
function resyncTransfers() {
return Promise.resolve()
}
function getDownloadItemCount() {
return Promise.resolve(0)
}
return {
downloadFile: downloadFile,
downloadSubtitles: downloadSubtitles,
downloadImage: downloadImage,
resyncTransfers: resyncTransfers,
getDownloadItemCount: getDownloadItemCount
}
});

View File

@@ -0,0 +1,108 @@
define([], function() {
"use strict";
function getDb(callback) {
var db = databaseInstance;
if (db) return void callback(db);
var request = indexedDB.open(dbName, dbVersion);
request.onerror = function(event) {}, request.onupgradeneeded = function(event) {
var db = event.target.result;
db.createObjectStore(dbName).transaction.oncomplete = function(event) {
callback(db)
}
}, request.onsuccess = function(event) {
var db = event.target.result;
callback(db)
}
}
function getByServerId(serverId) {
return getAll().then(function(items) {
return items.filter(function(item) {
return item.ServerId === serverId
})
})
}
function getAll() {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var request, storeName = dbName,
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName);
if ("getAll" in objectStore) request = objectStore.getAll(null, 1e4), request.onsuccess = function(event) {
resolve(event.target.result)
};
else {
var results = [];
request = objectStore.openCursor(), request.onsuccess = function(event) {
var cursor = event.target.result;
cursor ? (results.push(cursor.value), cursor.continue()) : resolve(results)
}
}
request.onerror = reject
})
})
}
function get(key) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readonly"),
objectStore = transaction.objectStore(storeName),
request = objectStore.get(key);
request.onerror = reject, request.onsuccess = function(event) {
resolve(request.result)
}
})
})
}
function set(key, val) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.put(val, key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function remove(key) {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.delete(key);
request.onerror = reject, request.onsuccess = resolve
})
})
}
function clear() {
return new Promise(function(resolve, reject) {
getDb(function(db) {
var storeName = dbName,
transaction = db.transaction([storeName], "readwrite"),
objectStore = transaction.objectStore(storeName),
request = objectStore.clear();
request.onerror = reject, request.onsuccess = resolve
})
})
}
var databaseInstance, indexedDB = self.indexedDB || self.mozIndexedDB || self.webkitIndexedDB || self.msIndexedDB,
dbName = (self.IDBTransaction || self.webkitIDBTransaction || self.msIDBTransaction, self.IDBKeyRange || self.webkitIDBKeyRange || self.msIDBKeyRange, "useractions"),
dbVersion = 1;
return {
get: get,
set: set,
remove: remove,
clear: clear,
getAll: getAll,
getByServerId: getByServerId
}
});

View File

@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<title>testing my-element</title>
<script src="build/document-register-element.js"></script>
<script src="test/my-element.js"></script>
</head>
<body>
<my-element>
some content
</my-element>
</body>

View File

@@ -0,0 +1,246 @@
/*! (C) WebReflection Mit Style License */
(function(e, t, n, r) {
"use strict";
function rt(e, t) {
for (var n = 0, r = e.length; n < r; n++) vt(e[n], t)
}
function it(e) {
for (var t = 0, n = e.length, r; t < n; t++) r = e[t], nt(r, b[ot(r)])
}
function st(e) {
return function(t) {
j(t) && (vt(t, e), rt(t.querySelectorAll(w), e))
}
}
function ot(e) {
var t = e.getAttribute("is"),
n = e.nodeName.toUpperCase(),
r = S.call(y, t ? v + t.toUpperCase() : d + n);
return t && -1 < r && !ut(n, t) ? -1 : r
}
function ut(e, t) {
return -1 < w.indexOf(e + '[is="' + t + '"]')
}
function at(e) {
var t = e.currentTarget,
n = e.attrChange,
r = e.attrName,
i = e.target;
Q && (!i || i === t) && t.attributeChangedCallback && r !== "style" && e.prevValue !== e.newValue && t.attributeChangedCallback(r, n === e[a] ? null : e.prevValue, n === e[l] ? null : e.newValue)
}
function ft(e) {
var t = st(e);
return function(e) {
X.push(t, e.target)
}
}
function lt(e) {
K && (K = !1, e.currentTarget.removeEventListener(h, lt)), rt((e.target || t).querySelectorAll(w), e.detail === o ? o : s), B && pt()
}
function ct(e, t) {
var n = this;
q.call(n, e, t), G.call(n, {
target: n
})
}
function ht(e, t) {
D(e, t), et ? et.observe(e, z) : (J && (e.setAttribute = ct, e[i] = Z(e), e.addEventListener(p, G)), e.addEventListener(c, at)), e.createdCallback && Q && (e.created = !0, e.createdCallback(), e.created = !1)
}
function pt() {
for (var e, t = 0, n = F.length; t < n; t++) e = F[t], E.contains(e) || (n--, F.splice(t--, 1), vt(e, o))
}
function dt(e) {
throw new Error("A " + e + " type is already registered")
}
function vt(e, t) {
var n, r = ot(e); - 1 < r && (tt(e, b[r]), r = 0, t === s && !e[s] ? (e[o] = !1, e[s] = !0, r = 1, B && S.call(F, e) < 0 && F.push(e)) : t === o && !e[o] && (e[s] = !1, e[o] = !0, r = 1), r && (n = e[t + "Callback"]) && n.call(e))
}
if (r in t) return;
var i = "__" + r + (Math.random() * 1e5 >> 0),
s = "attached",
o = "detached",
u = "extends",
a = "ADDITION",
f = "MODIFICATION",
l = "REMOVAL",
c = "DOMAttrModified",
h = "DOMContentLoaded",
p = "DOMSubtreeModified",
d = "<",
v = "=",
m = /^[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)+$/,
g = ["ANNOTATION-XML", "COLOR-PROFILE", "FONT-FACE", "FONT-FACE-SRC", "FONT-FACE-URI", "FONT-FACE-FORMAT", "FONT-FACE-NAME", "MISSING-GLYPH"],
y = [],
b = [],
w = "",
E = t.documentElement,
S = y.indexOf || function(e) {
for (var t = this.length; t-- && this[t] !== e;);
return t
},
x = n.prototype,
T = x.hasOwnProperty,
N = x.isPrototypeOf,
C = n.defineProperty,
k = n.getOwnPropertyDescriptor,
L = n.getOwnPropertyNames,
A = n.getPrototypeOf,
O = n.setPrototypeOf,
M = !!n.__proto__,
_ = n.create || function mt(e) {
return e ? (mt.prototype = e, new mt) : this
},
D = O || (M ? function(e, t) {
return e.__proto__ = t, e
} : L && k ? function() {
function e(e, t) {
for (var n, r = L(t), i = 0, s = r.length; i < s; i++) n = r[i], T.call(e, n) || C(e, n, k(t, n))
}
return function(t, n) {
do e(t, n); while ((n = A(n)) && !N.call(n, t));
return t
}
}() : function(e, t) {
for (var n in t) e[n] = t[n];
return e
}),
P = e.MutationObserver || e.WebKitMutationObserver,
H = (e.HTMLElement || e.Element || e.Node).prototype,
B = !N.call(H, E),
j = B ? function(e) {
return e.nodeType === 1
} : function(e) {
return N.call(H, e)
},
F = B && [],
I = H.cloneNode,
q = H.setAttribute,
R = H.removeAttribute,
U = t.createElement,
z = P && {
attributes: !0,
characterData: !0,
attributeOldValue: !0
},
W = P || function(e) {
J = !1, E.removeEventListener(c, W)
},
X, V = e.requestAnimationFrame || e.webkitRequestAnimationFrame || e.mozRequestAnimationFrame || e.msRequestAnimationFrame || function(e) {
setTimeout(e, 10)
},
$ = !1,
J = !0,
K = !0,
Q = !0,
G, Y, Z, et, tt, nt;
O || M ? (tt = function(e, t) {
N.call(t, e) || ht(e, t)
}, nt = ht) : (tt = function(e, t) {
e[i] || (e[i] = n(!0), ht(e, t))
}, nt = tt), B ? (J = !1, function() {
var e = k(H, "addEventListener"),
t = e.value,
n = function(e) {
var t = new CustomEvent(c, {
bubbles: !0
});
t.attrName = e, t.prevValue = this.getAttribute(e), t.newValue = null, t[l] = t.attrChange = 2, R.call(this, e), this.dispatchEvent(t)
},
r = function(e, t) {
var n = this.hasAttribute(e),
r = n && this.getAttribute(e),
i = new CustomEvent(c, {
bubbles: !0
});
q.call(this, e, t), i.attrName = e, i.prevValue = n ? r : null, i.newValue = t, n ? i[f] = i.attrChange = 1 : i[a] = i.attrChange = 0, this.dispatchEvent(i)
},
s = function(e) {
var t = e.currentTarget,
n = t[i],
r = e.propertyName,
s;
n.hasOwnProperty(r) && (n = n[r], s = new CustomEvent(c, {
bubbles: !0
}), s.attrName = n.name, s.prevValue = n.value || null, s.newValue = n.value = t[r] || null, s.prevValue == null ? s[a] = s.attrChange = 0 : s[f] = s.attrChange = 1, t.dispatchEvent(s))
};
e.value = function(e, o, u) {
e === c && this.attributeChangedCallback && this.setAttribute !== r && (this[i] = {
className: {
name: "class",
value: this.className
}
}, this.setAttribute = r, this.removeAttribute = n, t.call(this, "propertychange", s)), t.call(this, e, o, u)
}, C(H, "addEventListener", e)
}()) : P || (E.addEventListener(c, W), E.setAttribute(i, 1), E.removeAttribute(i), J && (G = function(e) {
var t = this,
n, r, s;
if (t === e.target) {
n = t[i], t[i] = r = Z(t);
for (s in r) {
if (!(s in n)) return Y(0, t, s, n[s], r[s], a);
if (r[s] !== n[s]) return Y(1, t, s, n[s], r[s], f)
}
for (s in n)
if (!(s in r)) return Y(2, t, s, n[s], r[s], l)
}
}, Y = function(e, t, n, r, i, s) {
var o = {
attrChange: e,
currentTarget: t,
attrName: n,
prevValue: r,
newValue: i
};
o[s] = e, at(o)
}, Z = function(e) {
for (var t, n, r = {}, i = e.attributes, s = 0, o = i.length; s < o; s++) t = i[s], n = t.name, n !== "setAttribute" && (r[n] = t.value);
return r
})), t[r] = function(n, r) {
c = n.toUpperCase(), $ || ($ = !0, P ? (et = function(e, t) {
function n(e, t) {
for (var n = 0, r = e.length; n < r; t(e[n++]));
}
return new P(function(r) {
for (var i, s, o, u = 0, a = r.length; u < a; u++) i = r[u], i.type === "childList" ? (n(i.addedNodes, e), n(i.removedNodes, t)) : (s = i.target, Q && s.attributeChangedCallback && i.attributeName !== "style" && (o = s.getAttribute(i.attributeName), o !== i.oldValue && s.attributeChangedCallback(i.attributeName, i.oldValue, o)))
})
}(st(s), st(o)), et.observe(t, {
childList: !0,
subtree: !0
})) : (X = [], V(function E() {
while (X.length) X.shift().call(null, X.shift());
V(E)
}), t.addEventListener("DOMNodeInserted", ft(s)), t.addEventListener("DOMNodeRemoved", ft(o))), t.addEventListener(h, lt), t.addEventListener("readystatechange", lt), t.createElement = function(e, n) {
var r = U.apply(t, arguments),
i = "" + e,
s = S.call(y, (n ? v : d) + (n || i).toUpperCase()),
o = -1 < s;
return n && (r.setAttribute("is", n = n.toLowerCase()), o && (o = ut(i.toUpperCase(), n))), Q = !t.createElement.innerHTMLHelper, o && nt(r, b[s]), r
}, H.cloneNode = function(e) {
var t = I.call(this, !!e),
n = ot(t);
return -1 < n && nt(t, b[n]), e && it(t.querySelectorAll(w)), t
}), -2 < S.call(y, v + c) + S.call(y, d + c) && dt(n);
if (!m.test(c) || -1 < S.call(g, c)) throw new Error("The type " + n + " is invalid");
var i = function() {
return f ? t.createElement(l, c) : t.createElement(l)
},
a = r || x,
f = T.call(a, u),
l = f ? r[u].toUpperCase() : c,
c, p;
return f && -1 < S.call(y, d + l) && dt(l), p = y.push((f ? v : d) + c) - 1, w = w.concat(w.length ? "," : "", f ? l + '[is="' + n.toLowerCase() + '"]' : l), i.prototype = b[p] = T.call(a, "prototype") ? a.prototype : _(H), rt(t.querySelectorAll(w), s), i
}
})(window, document, Object, "registerElement");

263
src/bower_components/fetch/fetch.js vendored Normal file
View File

@@ -0,0 +1,263 @@
! function(self) {
"use strict";
function normalizeName(name) {
if ("string" != typeof name && (name = String(name)), /[^a-z0-9\-#$%&'*+.\^_`|~]/i.test(name)) throw new TypeError("Invalid character in header field name");
return name.toLowerCase()
}
function normalizeValue(value) {
return "string" != typeof value && (value = String(value)), value
}
function iteratorFor(items) {
var iterator = {
next: function() {
var value = items.shift();
return {
done: void 0 === value,
value: value
}
}
};
return support.iterable && (iterator[Symbol.iterator] = function() {
return iterator
}), iterator
}
function Headers(headers) {
this.map = {}, headers instanceof Headers ? headers.forEach(function(value, name) {
this.append(name, value)
}, this) : headers && Object.getOwnPropertyNames(headers).forEach(function(name) {
this.append(name, headers[name])
}, this)
}
function consumed(body) {
if (body.bodyUsed) return Promise.reject(new TypeError("Already read"));
body.bodyUsed = !0
}
function fileReaderReady(reader) {
return new Promise(function(resolve, reject) {
reader.onload = function() {
resolve(reader.result)
}, reader.onerror = function() {
reject(reader.error)
}
})
}
function readBlobAsArrayBuffer(blob) {
var reader = new FileReader,
promise = fileReaderReady(reader);
return reader.readAsArrayBuffer(blob), promise
}
function readBlobAsText(blob) {
var reader = new FileReader,
promise = fileReaderReady(reader);
return reader.readAsText(blob), promise
}
function readArrayBufferAsText(buf) {
for (var view = new Uint8Array(buf), chars = new Array(view.length), i = 0; i < view.length; i++) chars[i] = String.fromCharCode(view[i]);
return chars.join("")
}
function bufferClone(buf) {
if (buf.slice) return buf.slice(0);
var view = new Uint8Array(buf.byteLength);
return view.set(new Uint8Array(buf)), view.buffer
}
function Body() {
return this.bodyUsed = !1, this._initBody = function(body) {
if (this._bodyInit = body, body)
if ("string" == typeof body) this._bodyText = body;
else if (support.blob && Blob.prototype.isPrototypeOf(body)) this._bodyBlob = body;
else if (support.formData && FormData.prototype.isPrototypeOf(body)) this._bodyFormData = body;
else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) this._bodyText = body.toString();
else if (support.arrayBuffer && support.blob && isDataView(body)) this._bodyArrayBuffer = bufferClone(body.buffer), this._bodyInit = new Blob([this._bodyArrayBuffer]);
else {
if (!support.arrayBuffer || !ArrayBuffer.prototype.isPrototypeOf(body) && !isArrayBufferView(body)) throw new Error("unsupported BodyInit type");
this._bodyArrayBuffer = bufferClone(body)
} else this._bodyText = "";
this.headers.get("content-type") || ("string" == typeof body ? this.headers.set("content-type", "text/plain;charset=UTF-8") : this._bodyBlob && this._bodyBlob.type ? this.headers.set("content-type", this._bodyBlob.type) : support.searchParams && URLSearchParams.prototype.isPrototypeOf(body) && this.headers.set("content-type", "application/x-www-form-urlencoded;charset=UTF-8"))
}, support.blob && (this.blob = function() {
var rejected = consumed(this);
if (rejected) return rejected;
if (this._bodyBlob) return Promise.resolve(this._bodyBlob);
if (this._bodyArrayBuffer) return Promise.resolve(new Blob([this._bodyArrayBuffer]));
if (this._bodyFormData) throw new Error("could not read FormData body as blob");
return Promise.resolve(new Blob([this._bodyText]))
}, this.arrayBuffer = function() {
return this._bodyArrayBuffer ? consumed(this) || Promise.resolve(this._bodyArrayBuffer) : this.blob().then(readBlobAsArrayBuffer)
}), this.text = function() {
var rejected = consumed(this);
if (rejected) return rejected;
if (this._bodyBlob) return readBlobAsText(this._bodyBlob);
if (this._bodyArrayBuffer) return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
if (this._bodyFormData) throw new Error("could not read FormData body as text");
return Promise.resolve(this._bodyText)
}, support.formData && (this.formData = function() {
return this.text().then(decode)
}), this.json = function() {
return this.text().then(JSON.parse)
}, this
}
function normalizeMethod(method) {
var upcased = method.toUpperCase();
return methods.indexOf(upcased) > -1 ? upcased : method
}
function Request(input, options) {
options = options || {};
var body = options.body;
if ("string" == typeof input) this.url = input;
else {
if (input.bodyUsed) throw new TypeError("Already read");
this.url = input.url, this.credentials = input.credentials, options.headers || (this.headers = new Headers(input.headers)), this.method = input.method, this.mode = input.mode, body || null == input._bodyInit || (body = input._bodyInit, input.bodyUsed = !0)
}
if (this.credentials = options.credentials || this.credentials || "omit", !options.headers && this.headers || (this.headers = new Headers(options.headers)), this.method = normalizeMethod(options.method || this.method || "GET"), this.mode = options.mode || this.mode || null, this.referrer = null, ("GET" === this.method || "HEAD" === this.method) && body) throw new TypeError("Body not allowed for GET or HEAD requests");
this._initBody(body)
}
function decode(body) {
var form = new FormData;
return body.trim().split("&").forEach(function(bytes) {
if (bytes) {
var split = bytes.split("="),
name = split.shift().replace(/\+/g, " "),
value = split.join("=").replace(/\+/g, " ");
form.append(decodeURIComponent(name), decodeURIComponent(value))
}
}), form
}
function parseHeaders(rawHeaders) {
var headers = new Headers;
return rawHeaders.split("\r\n").forEach(function(line) {
var parts = line.split(":"),
key = parts.shift().trim();
if (key) {
var value = parts.join(":").trim();
headers.append(key, value)
}
}), headers
}
function Response(bodyInit, options) {
options || (options = {}), this.type = "default", this.status = "status" in options ? options.status : 200, this.ok = this.status >= 200 && this.status < 300, this.statusText = "statusText" in options ? options.statusText : "OK", this.headers = new Headers(options.headers), this.url = options.url || "", this._initBody(bodyInit)
}
if (!self.fetch) {
var support = {
searchParams: "URLSearchParams" in self,
iterable: "Symbol" in self && "iterator" in Symbol,
blob: "FileReader" in self && "Blob" in self && function() {
try {
return new Blob, !0
} catch (e) {
return !1
}
}(),
formData: "FormData" in self,
arrayBuffer: "ArrayBuffer" in self
};
if (support.arrayBuffer) var viewClasses = ["[object Int8Array]", "[object Uint8Array]", "[object Uint8ClampedArray]", "[object Int16Array]", "[object Uint16Array]", "[object Int32Array]", "[object Uint32Array]", "[object Float32Array]", "[object Float64Array]"],
isDataView = function(obj) {
return obj && DataView.prototype.isPrototypeOf(obj)
},
isArrayBufferView = ArrayBuffer.isView || function(obj) {
return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1
};
Headers.prototype.append = function(name, value) {
name = normalizeName(name), value = normalizeValue(value);
var list = this.map[name];
list || (list = [], this.map[name] = list), list.push(value)
}, Headers.prototype.delete = function(name) {
delete this.map[normalizeName(name)]
}, Headers.prototype.get = function(name) {
var values = this.map[normalizeName(name)];
return values ? values[0] : null
}, Headers.prototype.getAll = function(name) {
return this.map[normalizeName(name)] || []
}, Headers.prototype.has = function(name) {
return this.map.hasOwnProperty(normalizeName(name))
}, Headers.prototype.set = function(name, value) {
this.map[normalizeName(name)] = [normalizeValue(value)]
}, Headers.prototype.forEach = function(callback, thisArg) {
Object.getOwnPropertyNames(this.map).forEach(function(name) {
this.map[name].forEach(function(value) {
callback.call(thisArg, value, name, this)
}, this)
}, this)
}, Headers.prototype.keys = function() {
var items = [];
return this.forEach(function(value, name) {
items.push(name)
}), iteratorFor(items)
}, Headers.prototype.values = function() {
var items = [];
return this.forEach(function(value) {
items.push(value)
}), iteratorFor(items)
}, Headers.prototype.entries = function() {
var items = [];
return this.forEach(function(value, name) {
items.push([name, value])
}), iteratorFor(items)
}, support.iterable && (Headers.prototype[Symbol.iterator] = Headers.prototype.entries);
var methods = ["DELETE", "GET", "HEAD", "OPTIONS", "POST", "PUT"];
Request.prototype.clone = function() {
return new Request(this, {
body: this._bodyInit
})
}, Body.call(Request.prototype), Body.call(Response.prototype), Response.prototype.clone = function() {
return new Response(this._bodyInit, {
status: this.status,
statusText: this.statusText,
headers: new Headers(this.headers),
url: this.url
})
}, Response.error = function() {
var response = new Response(null, {
status: 0,
statusText: ""
});
return response.type = "error", response
};
var redirectStatuses = [301, 302, 303, 307, 308];
Response.redirect = function(url, status) {
if (-1 === redirectStatuses.indexOf(status)) throw new RangeError("Invalid status code");
return new Response(null, {
status: status,
headers: {
location: url
}
})
}, self.Headers = Headers, self.Request = Request, self.Response = Response, self.fetch = function(input, init) {
return new Promise(function(resolve, reject) {
var request = new Request(input, init),
xhr = new XMLHttpRequest;
xhr.onload = function() {
var options = {
status: xhr.status,
statusText: xhr.statusText,
headers: parseHeaders(xhr.getAllResponseHeaders() || "")
};
options.url = "responseURL" in xhr ? xhr.responseURL : options.headers.get("X-Request-URL");
var body = "response" in xhr ? xhr.response : xhr.responseText;
resolve(new Response(body, options))
}, xhr.onerror = function() {
reject(new TypeError("Network request failed"))
}, xhr.ontimeout = function() {
reject(new TypeError("Network request failed"))
}, xhr.open(request.method, request.url, !0), "include" === request.credentials && (xhr.withCredentials = !0), "responseType" in xhr && support.blob && (xhr.responseType = "blob"), request.headers.forEach(function(value, name) {
xhr.setRequestHeader(name, value)
}), xhr.send(void 0 === request._bodyInit ? null : request._bodyInit)
})
}, self.fetch.polyfill = !0
}
}("undefined" != typeof self ? self : this);

View File

@@ -0,0 +1,18 @@
"use strict";
window.queryString = {}, window.queryString.extract = function(maybeUrl) {
return maybeUrl.split("?")[1] || ""
}, window.queryString.parse = function(str) {
return "string" != typeof str ? {} : (str = str.trim().replace(/^(\?|#|&)/, ""), str ? str.split("&").reduce(function(ret, param) {
var parts = param.replace(/\+/g, " ").split("="),
key = parts[0],
val = parts[1];
return key = decodeURIComponent(key), val = void 0 === val ? null : decodeURIComponent(val), ret.hasOwnProperty(key) ? Array.isArray(ret[key]) ? ret[key].push(val) : ret[key] = [ret[key], val] : ret[key] = val, ret
}, {}) : {})
}, window.queryString.stringify = function(obj) {
return obj ? Object.keys(obj).sort().map(function(key) {
var val = obj[key];
return Array.isArray(val) ? val.sort().map(function(val2) {
return encodeURIComponent(key) + "=" + encodeURIComponent(val2)
}).join("&") : encodeURIComponent(key) + "=" + encodeURIComponent(val)
}).join("&") : ""
};

View File

@@ -0,0 +1,83 @@
"use strict";
var assert = require("assert"),
qs = require("./");
describe(".parse()", function() {
it("query strings starting with a `?`", function() {
assert.deepEqual(qs.parse("?foo=bar"), {
foo: "bar"
})
}), it("query strings starting with a `#`", function() {
assert.deepEqual(qs.parse("#foo=bar"), {
foo: "bar"
})
}), it("query strings starting with a `&", function() {
assert.deepEqual(qs.parse("&foo=bar&foo=baz"), {
foo: ["bar", "baz"]
})
}), it("parse a query string", function() {
assert.deepEqual(qs.parse("foo=bar"), {
foo: "bar"
})
}), it("parse multiple query string", function() {
assert.deepEqual(qs.parse("foo=bar&key=val"), {
foo: "bar",
key: "val"
})
}), it("parse query string without a value", function() {
assert.deepEqual(qs.parse("foo"), {
foo: null
}), assert.deepEqual(qs.parse("foo&key"), {
foo: null,
key: null
}), assert.deepEqual(qs.parse("foo=bar&key"), {
foo: "bar",
key: null
})
}), it("return empty object if no qss can be found", function() {
assert.deepEqual(qs.parse("?"), {}), assert.deepEqual(qs.parse("&"), {}), assert.deepEqual(qs.parse("#"), {}), assert.deepEqual(qs.parse(" "), {})
}), it("handle `+` correctly", function() {
assert.deepEqual(qs.parse("foo+faz=bar+baz++"), {
"foo faz": "bar baz "
})
}), it("handle multiple of the same key", function() {
assert.deepEqual(qs.parse("foo=bar&foo=baz"), {
foo: ["bar", "baz"]
})
}), it("query strings params including embedded `=`", function() {
assert.deepEqual(qs.parse("?param=http%3A%2F%2Fsomeurl%3Fid%3D2837"), {
param: "http://someurl?id=2837"
})
})
}), describe(".stringify()", function() {
it("stringify", function() {
assert.strictEqual(qs.stringify({
foo: "bar"
}), "foo=bar"), assert.strictEqual(qs.stringify({
foo: "bar",
bar: "baz"
}), "bar=baz&foo=bar")
}), it("different types", function() {
assert.strictEqual(qs.stringify(), ""), assert.strictEqual(qs.stringify(0), "")
}), it("URI encode", function() {
assert.strictEqual(qs.stringify({
"foo bar": "baz faz"
}), "foo%20bar=baz%20faz")
}), it("handle array value", function() {
assert.strictEqual(qs.stringify({
abc: "abc",
foo: ["bar", "baz"]
}), "abc=abc&foo=bar&foo=baz")
})
}), describe(".extract()", function() {
it("should extract qs from url", function() {
assert.equal(qs.extract("http://foo.bar/?abc=def&hij=klm"), "abc=def&hij=klm"), assert.equal(qs.extract("http://foo.bar/?"), "")
}), it("should handle strings not containing qs", function() {
assert.equal(qs.extract("http://foo.bar/"), ""), assert.equal(qs.extract(""), "")
}), it("should throw for invalid values", function() {
assert.throws(function() {
qs.extract(null)
}, TypeError), assert.throws(function() {
qs.extract(void 0)
}, TypeError)
})
});

View File

@@ -0,0 +1,607 @@
var requirejs, require, define;
! function(global, setTimeout) {
function commentReplace(match, singlePrefix) {
return singlePrefix || ""
}
function isFunction(it) {
return "[object Function]" === ostring.call(it)
}
function isArray(it) {
return "[object Array]" === ostring.call(it)
}
function each(ary, func) {
if (ary) {
var i;
for (i = 0; i < ary.length && (!ary[i] || !func(ary[i], i, ary)); i += 1);
}
}
function eachReverse(ary, func) {
if (ary) {
var i;
for (i = ary.length - 1; i > -1 && (!ary[i] || !func(ary[i], i, ary)); i -= 1);
}
}
function hasProp(obj, prop) {
return hasOwn.call(obj, prop)
}
function getOwn(obj, prop) {
return hasProp(obj, prop) && obj[prop]
}
function eachProp(obj, func) {
var prop;
for (prop in obj)
if (hasProp(obj, prop) && func(obj[prop], prop)) break
}
function mixin(target, source, force, deepStringMixin) {
return source && eachProp(source, function(value, prop) {
!force && hasProp(target, prop) || (!deepStringMixin || "object" != typeof value || !value || isArray(value) || isFunction(value) || value instanceof RegExp ? target[prop] = value : (target[prop] || (target[prop] = {}), mixin(target[prop], value, force, deepStringMixin)))
}), target
}
function bind(obj, fn) {
return function() {
return fn.apply(obj, arguments)
}
}
function scripts() {
return document.getElementsByTagName("script")
}
function defaultOnError(err) {
throw err
}
function getGlobal(value) {
if (!value) return value;
var g = global;
return each(value.split("."), function(part) {
g = g[part]
}), g
}
function makeError(id, msg, err, requireModules) {
var e = new Error(msg + "\nhttp://requirejs.org/docs/errors.html#" + id);
return e.requireType = id, e.requireModules = requireModules, err && (e.originalError = err), e
}
function newContext(contextName) {
function trimDots(ary) {
var i, part;
for (i = 0; i < ary.length; i++)
if ("." === (part = ary[i])) ary.splice(i, 1), i -= 1;
else if (".." === part) {
if (0 === i || 1 === i && ".." === ary[2] || ".." === ary[i - 1]) continue;
i > 0 && (ary.splice(i - 1, 2), i -= 2)
}
}
function normalize(name, baseName, applyMap) {
var mapValue, nameParts, i, j, nameSegment, lastIndex, foundMap, foundI, foundStarMap, starI, normalizedBaseParts, baseParts = baseName && baseName.split("/"),
map = config.map,
starMap = map && map["*"];
if (name && (name = name.split("/"), lastIndex = name.length - 1, config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex]) && (name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, "")), "." === name[0].charAt(0) && baseParts && (normalizedBaseParts = baseParts.slice(0, baseParts.length - 1), name = normalizedBaseParts.concat(name)), trimDots(name), name = name.join("/")), applyMap && map && (baseParts || starMap)) {
nameParts = name.split("/");
outerLoop: for (i = nameParts.length; i > 0; i -= 1) {
if (nameSegment = nameParts.slice(0, i).join("/"), baseParts)
for (j = baseParts.length; j > 0; j -= 1)
if ((mapValue = getOwn(map, baseParts.slice(0, j).join("/"))) && (mapValue = getOwn(mapValue, nameSegment))) {
foundMap = mapValue, foundI = i;
break outerLoop
}! foundStarMap && starMap && getOwn(starMap, nameSegment) && (foundStarMap = getOwn(starMap, nameSegment), starI = i)
}!foundMap && foundStarMap && (foundMap = foundStarMap, foundI = starI), foundMap && (nameParts.splice(0, foundI, foundMap), name = nameParts.join("/"))
}
return getOwn(config.pkgs, name) || name
}
function removeScript(name) {
isBrowser && each(scripts(), function(scriptNode) {
if (scriptNode.getAttribute("data-requiremodule") === name && scriptNode.getAttribute("data-requirecontext") === context.contextName) return scriptNode.parentNode.removeChild(scriptNode), !0
})
}
function hasPathFallback(id) {
var pathConfig = getOwn(config.paths, id);
if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) return pathConfig.shift(), context.require.undef(id), context.makeRequire(null, {
skipMap: !0
})([id]), !0
}
function splitPrefix(name) {
var prefix, index = name ? name.indexOf("!") : -1;
return index > -1 && (prefix = name.substring(0, index), name = name.substring(index + 1, name.length)), [prefix, name]
}
function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) {
var url, pluginModule, suffix, nameParts, prefix = null,
parentName = parentModuleMap ? parentModuleMap.name : null,
originalName = name,
isDefine = !0,
normalizedName = "";
return name || (isDefine = !1, name = "_@r" + (requireCounter += 1)), nameParts = splitPrefix(name), prefix = nameParts[0], name = nameParts[1], prefix && (prefix = normalize(prefix, parentName, applyMap), pluginModule = getOwn(defined, prefix)), name && (prefix ? normalizedName = isNormalized ? name : pluginModule && pluginModule.normalize ? pluginModule.normalize(name, function(name) {
return normalize(name, parentName, applyMap)
}) : -1 === name.indexOf("!") ? normalize(name, parentName, applyMap) : name : (normalizedName = normalize(name, parentName, applyMap), nameParts = splitPrefix(normalizedName), prefix = nameParts[0], normalizedName = nameParts[1], isNormalized = !0, url = context.nameToUrl(normalizedName))), suffix = !prefix || pluginModule || isNormalized ? "" : "_unnormalized" + (unnormalizedCounter += 1), {
prefix: prefix,
name: normalizedName,
parentMap: parentModuleMap,
unnormalized: !!suffix,
url: url,
originalName: originalName,
isDefine: isDefine,
id: (prefix ? prefix + "!" + normalizedName : normalizedName) + suffix
}
}
function getModule(depMap) {
var id = depMap.id,
mod = getOwn(registry, id);
return mod || (mod = registry[id] = new context.Module(depMap)), mod
}
function on(depMap, name, fn) {
var id = depMap.id,
mod = getOwn(registry, id);
!hasProp(defined, id) || mod && !mod.defineEmitComplete ? (mod = getModule(depMap), mod.error && "error" === name ? fn(mod.error) : mod.on(name, fn)) : "defined" === name && fn(defined[id])
}
function onError(err, errback) {
var ids = err.requireModules,
notified = !1;
errback ? errback(err) : (each(ids, function(id) {
var mod = getOwn(registry, id);
mod && (mod.error = err, mod.events.error && (notified = !0, mod.emit("error", err)))
}), notified || req.onError(err))
}
function takeGlobalQueue() {
globalDefQueue.length && (each(globalDefQueue, function(queueItem) {
var id = queueItem[0];
"string" == typeof id && (context.defQueueMap[id] = !0), defQueue.push(queueItem)
}), globalDefQueue = [])
}
function cleanRegistry(id) {
delete registry[id], delete enabledRegistry[id]
}
function breakCycle(mod, traced, processed) {
var id = mod.map.id;
mod.error ? mod.emit("error", mod.error) : (traced[id] = !0, each(mod.depMaps, function(depMap, i) {
var depId = depMap.id,
dep = getOwn(registry, depId);
!dep || mod.depMatched[i] || processed[depId] || (getOwn(traced, depId) ? (mod.defineDep(i, defined[depId]), mod.check()) : breakCycle(dep, traced, processed))
}), processed[id] = !0)
}
function checkLoaded() {
var err, usingPathFallback, waitInterval = 1e3 * config.waitSeconds,
expired = waitInterval && context.startTime + waitInterval < (new Date).getTime(),
noLoads = [],
reqCalls = [],
stillLoading = !1,
needCycleCheck = !0;
if (!inCheckLoaded) {
if (inCheckLoaded = !0, eachProp(enabledRegistry, function(mod) {
var map = mod.map,
modId = map.id;
if (mod.enabled && (map.isDefine || reqCalls.push(mod), !mod.error))
if (!mod.inited && expired) hasPathFallback(modId) ? (usingPathFallback = !0, stillLoading = !0) : (noLoads.push(modId), removeScript(modId));
else if (!mod.inited && mod.fetched && map.isDefine && (stillLoading = !0, !map.prefix)) return needCycleCheck = !1
}), expired && noLoads.length) return err = makeError("timeout", "Load timeout for modules: " + noLoads, null, noLoads), err.contextName = context.contextName, onError(err);
needCycleCheck && each(reqCalls, function(mod) {
breakCycle(mod, {}, {})
}), expired && !usingPathFallback || !stillLoading || !isBrowser && !isWebWorker || checkLoadedTimeoutId || (checkLoadedTimeoutId = setTimeout(function() {
checkLoadedTimeoutId = 0, checkLoaded()
}, 50)), inCheckLoaded = !1
}
}
function callGetModule(args) {
hasProp(defined, args[0]) || getModule(makeModuleMap(args[0], null, !0)).init(args[1], args[2])
}
function removeListener(node, func, name, ieName) {
node.detachEvent && !isOpera ? ieName && node.detachEvent(ieName, func) : node.removeEventListener(name, func, !1)
}
function getScriptData(evt) {
var node = evt.currentTarget || evt.srcElement;
return removeListener(node, context.onScriptLoad, "load", "onreadystatechange"), removeListener(node, context.onScriptError, "error"), {
node: node,
id: node && node.getAttribute("data-requiremodule")
}
}
function intakeDefines() {
var args;
for (takeGlobalQueue(); defQueue.length;) {
if (args = defQueue.shift(), null === args[0]) return onError(makeError("mismatch", "Mismatched anonymous define() module: " + args[args.length - 1]));
callGetModule(args)
}
context.defQueueMap = {}
}
var inCheckLoaded, Module, context, handlers, checkLoadedTimeoutId, config = {
waitSeconds: 7,
baseUrl: "./",
paths: {},
bundles: {},
pkgs: {},
shim: {},
config: {}
},
registry = {},
enabledRegistry = {},
undefEvents = {},
defQueue = [],
defined = {},
urlFetched = {},
bundlesMap = {},
requireCounter = 1,
unnormalizedCounter = 1;
return handlers = {
require: function(mod) {
return mod.require ? mod.require : mod.require = context.makeRequire(mod.map)
},
exports: function(mod) {
if (mod.usingExports = !0, mod.map.isDefine) return mod.exports ? defined[mod.map.id] = mod.exports : mod.exports = defined[mod.map.id] = {}
},
module: function(mod) {
return mod.module ? mod.module : mod.module = {
id: mod.map.id,
uri: mod.map.url,
config: function() {
return getOwn(config.config, mod.map.id) || {}
},
exports: mod.exports || (mod.exports = {})
}
}
}, Module = function(map) {
this.events = getOwn(undefEvents, map.id) || {}, this.map = map, this.shim = getOwn(config.shim, map.id), this.depExports = [], this.depMaps = [], this.depMatched = [], this.pluginMaps = {}, this.depCount = 0
}, Module.prototype = {
init: function(depMaps, factory, errback, options) {
options = options || {}, this.inited || (this.factory = factory, errback ? this.on("error", errback) : this.events.error && (errback = bind(this, function(err) {
this.emit("error", err)
})), this.depMaps = depMaps && depMaps.slice(0), this.errback = errback, this.inited = !0, this.ignore = options.ignore, options.enabled || this.enabled ? this.enable() : this.check())
},
defineDep: function(i, depExports) {
this.depMatched[i] || (this.depMatched[i] = !0, this.depCount -= 1, this.depExports[i] = depExports)
},
fetch: function() {
if (!this.fetched) {
this.fetched = !0, context.startTime = (new Date).getTime();
var map = this.map;
if (!this.shim) return map.prefix ? this.callPlugin() : this.load();
context.makeRequire(this.map, {
enableBuildCallback: !0
})(this.shim.deps || [], bind(this, function() {
return map.prefix ? this.callPlugin() : this.load()
}))
}
},
load: function() {
var url = this.map.url;
urlFetched[url] || (urlFetched[url] = !0, context.load(this.map.id, url))
},
check: function() {
if (this.enabled && !this.enabling) {
var err, cjsModule, id = this.map.id,
depExports = this.depExports,
exports = this.exports,
factory = this.factory;
if (this.inited) {
if (this.error) this.emit("error", this.error);
else if (!this.defining) {
if (this.defining = !0, this.depCount < 1 && !this.defined) {
if (isFunction(factory)) {
if (this.events.error && this.map.isDefine || req.onError !== defaultOnError) try {
exports = context.execCb(id, factory, depExports, exports)
} catch (e) {
err = e
} else exports = context.execCb(id, factory, depExports, exports);
if (this.map.isDefine && void 0 === exports && (cjsModule = this.module, cjsModule ? exports = cjsModule.exports : this.usingExports && (exports = this.exports)), err) return err.requireMap = this.map, err.requireModules = this.map.isDefine ? [this.map.id] : null, err.requireType = this.map.isDefine ? "define" : "require", onError(this.error = err)
} else exports = factory;
if (this.exports = exports, this.map.isDefine && !this.ignore && (defined[id] = exports, req.onResourceLoad)) {
var resLoadMaps = [];
each(this.depMaps, function(depMap) {
resLoadMaps.push(depMap.normalizedMap || depMap)
}), req.onResourceLoad(context, this.map, resLoadMaps)
}
cleanRegistry(id), this.defined = !0
}
this.defining = !1, this.defined && !this.defineEmitted && (this.defineEmitted = !0, this.emit("defined", this.exports), this.defineEmitComplete = !0)
}
} else hasProp(context.defQueueMap, id) || this.fetch()
}
},
callPlugin: function() {
var map = this.map,
id = map.id,
pluginMap = makeModuleMap(map.prefix);
this.depMaps.push(pluginMap), on(pluginMap, "defined", bind(this, function(plugin) {
var load, normalizedMap, normalizedMod, bundleId = getOwn(bundlesMap, this.map.id),
name = this.map.name,
parentName = this.map.parentMap ? this.map.parentMap.name : null,
localRequire = context.makeRequire(map.parentMap, {
enableBuildCallback: !0
});
return this.map.unnormalized ? (plugin.normalize && (name = plugin.normalize(name, function(name) {
return normalize(name, parentName, !0)
}) || ""), normalizedMap = makeModuleMap(map.prefix + "!" + name, this.map.parentMap, !0), on(normalizedMap, "defined", bind(this, function(value) {
this.map.normalizedMap = normalizedMap, this.init([], function() {
return value
}, null, {
enabled: !0,
ignore: !0
})
})), void((normalizedMod = getOwn(registry, normalizedMap.id)) && (this.depMaps.push(normalizedMap), this.events.error && normalizedMod.on("error", bind(this, function(err) {
this.emit("error", err)
})), normalizedMod.enable()))) : bundleId ? (this.map.url = context.nameToUrl(bundleId), void this.load()) : (load = bind(this, function(value) {
this.init([], function() {
return value
}, null, {
enabled: !0
})
}), load.error = bind(this, function(err) {
this.inited = !0, this.error = err, err.requireModules = [id], eachProp(registry, function(mod) {
0 === mod.map.id.indexOf(id + "_unnormalized") && cleanRegistry(mod.map.id)
}), onError(err)
}), load.fromText = bind(this, function(text, textAlt) {
var moduleName = map.name,
moduleMap = makeModuleMap(moduleName),
hasInteractive = useInteractive;
textAlt && (text = textAlt), hasInteractive && (useInteractive = !1), getModule(moduleMap), hasProp(config.config, id) && (config.config[moduleName] = config.config[id]);
try {
req.exec(text)
} catch (e) {
return onError(makeError("fromtexteval", "fromText eval for " + id + " failed: " + e, e, [id]))
}
hasInteractive && (useInteractive = !0), this.depMaps.push(moduleMap), context.completeLoad(moduleName), localRequire([moduleName], load)
}), void plugin.load(map.name, localRequire, load, config))
})), context.enable(pluginMap, this), this.pluginMaps[pluginMap.id] = pluginMap
},
enable: function() {
enabledRegistry[this.map.id] = this, this.enabled = !0, this.enabling = !0, each(this.depMaps, bind(this, function(depMap, i) {
var id, mod, handler;
if ("string" == typeof depMap) {
if (depMap = makeModuleMap(depMap, this.map.isDefine ? this.map : this.map.parentMap, !1, !this.skipMap), this.depMaps[i] = depMap, handler = getOwn(handlers, depMap.id)) return void(this.depExports[i] = handler(this));
this.depCount += 1, on(depMap, "defined", bind(this, function(depExports) {
this.undefed || (this.defineDep(i, depExports), this.check())
})), this.errback ? on(depMap, "error", bind(this, this.errback)) : this.events.error && on(depMap, "error", bind(this, function(err) {
this.emit("error", err)
}))
}
id = depMap.id, mod = registry[id], hasProp(handlers, id) || !mod || mod.enabled || context.enable(depMap, this)
})), eachProp(this.pluginMaps, bind(this, function(pluginMap) {
var mod = getOwn(registry, pluginMap.id);
mod && !mod.enabled && context.enable(pluginMap, this)
})), this.enabling = !1, this.check()
},
on: function(name, cb) {
var cbs = this.events[name];
cbs || (cbs = this.events[name] = []), cbs.push(cb)
},
emit: function(name, evt) {
each(this.events[name], function(cb) {
cb(evt)
}), "error" === name && delete this.events[name]
}
}, context = {
config: config,
contextName: contextName,
registry: registry,
defined: defined,
urlFetched: urlFetched,
defQueue: defQueue,
defQueueMap: {},
Module: Module,
makeModuleMap: makeModuleMap,
nextTick: req.nextTick,
onError: onError,
configure: function(cfg) {
if (cfg.baseUrl && "/" !== cfg.baseUrl.charAt(cfg.baseUrl.length - 1) && (cfg.baseUrl += "/"), "string" == typeof cfg.urlArgs) {
var urlArgs = cfg.urlArgs;
cfg.urlArgs = function(id, url) {
return (-1 === url.indexOf("?") ? "?" : "&") + urlArgs
}
}
var shim = config.shim,
objs = {
paths: !0,
bundles: !0,
config: !0,
map: !0
};
eachProp(cfg, function(value, prop) {
objs[prop] ? (config[prop] || (config[prop] = {}), mixin(config[prop], value, !0, !0)) : config[prop] = value
}), cfg.bundles && eachProp(cfg.bundles, function(value, prop) {
each(value, function(v) {
v !== prop && (bundlesMap[v] = prop)
})
}), cfg.shim && (eachProp(cfg.shim, function(value, id) {
isArray(value) && (value = {
deps: value
}), !value.exports && !value.init || value.exportsFn || (value.exportsFn = context.makeShimExports(value)), shim[id] = value
}), config.shim = shim), cfg.packages && each(cfg.packages, function(pkgObj) {
var location, name;
pkgObj = "string" == typeof pkgObj ? {
name: pkgObj
} : pkgObj, name = pkgObj.name, location = pkgObj.location, location && (config.paths[name] = pkgObj.location), config.pkgs[name] = pkgObj.name + "/" + (pkgObj.main || "main").replace(currDirRegExp, "").replace(jsSuffixRegExp, "")
}), eachProp(registry, function(mod, id) {
mod.inited || mod.map.unnormalized || (mod.map = makeModuleMap(id, null, !0))
}), (cfg.deps || cfg.callback) && context.require(cfg.deps || [], cfg.callback)
},
makeShimExports: function(value) {
function fn() {
var ret;
return value.init && (ret = value.init.apply(global, arguments)), ret || value.exports && getGlobal(value.exports)
}
return fn
},
makeRequire: function(relMap, options) {
function localRequire(deps, callback, errback) {
var id, map, requireMod;
return options.enableBuildCallback && callback && isFunction(callback) && (callback.__requireJsBuild = !0), "string" == typeof deps ? isFunction(callback) ? onError(makeError("requireargs", "Invalid require call"), errback) : relMap && hasProp(handlers, deps) ? handlers[deps](registry[relMap.id]) : req.get ? req.get(context, deps, relMap, localRequire) : (map = makeModuleMap(deps, relMap, !1, !0), id = map.id, hasProp(defined, id) ? defined[id] : onError(makeError("notloaded", 'Module name "' + id + '" has not been loaded yet for context: ' + contextName + (relMap ? "" : ". Use require([])")))) : (intakeDefines(), context.nextTick(function() {
intakeDefines(), requireMod = getModule(makeModuleMap(null, relMap)), requireMod.skipMap = options.skipMap, requireMod.init(deps, callback, errback, {
enabled: !0
}), checkLoaded()
}), localRequire)
}
return options = options || {}, mixin(localRequire, {
isBrowser: isBrowser,
toUrl: function(moduleNamePlusExt) {
var ext, index = moduleNamePlusExt.lastIndexOf("."),
segment = moduleNamePlusExt.split("/")[0],
isRelative = "." === segment || ".." === segment;
return -1 !== index && (!isRelative || index > 1) && (ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length), moduleNamePlusExt = moduleNamePlusExt.substring(0, index)), context.nameToUrl(normalize(moduleNamePlusExt, relMap && relMap.id, !0), ext, !0)
},
defined: function(id) {
return hasProp(defined, makeModuleMap(id, relMap, !1, !0).id)
},
specified: function(id) {
return id = makeModuleMap(id, relMap, !1, !0).id, hasProp(defined, id) || hasProp(registry, id)
}
}), relMap || (localRequire.undef = function(id) {
takeGlobalQueue();
var map = makeModuleMap(id, relMap, !0),
mod = getOwn(registry, id);
mod.undefed = !0, removeScript(id), delete defined[id], delete urlFetched[map.url], delete undefEvents[id], eachReverse(defQueue, function(args, i) {
args[0] === id && defQueue.splice(i, 1)
}), delete context.defQueueMap[id], mod && (mod.events.defined && (undefEvents[id] = mod.events), cleanRegistry(id))
}), localRequire
},
enable: function(depMap) {
getOwn(registry, depMap.id) && getModule(depMap).enable()
},
completeLoad: function(moduleName) {
var found, args, mod, shim = getOwn(config.shim, moduleName) || {},
shExports = shim.exports;
for (takeGlobalQueue(); defQueue.length;) {
if (args = defQueue.shift(), null === args[0]) {
if (args[0] = moduleName, found) break;
found = !0
} else args[0] === moduleName && (found = !0);
callGetModule(args)
}
if (context.defQueueMap = {}, mod = getOwn(registry, moduleName), !found && !hasProp(defined, moduleName) && mod && !mod.inited) {
if (!(!config.enforceDefine || shExports && getGlobal(shExports))) return hasPathFallback(moduleName) ? void 0 : onError(makeError("nodefine", "No define call for " + moduleName, null, [moduleName]));
callGetModule([moduleName, shim.deps || [], shim.exportsFn])
}
checkLoaded()
},
nameToUrl: function(moduleName, ext, skipExt) {
var paths, syms, i, parentModule, url, parentPath, bundleId, pkgMain = getOwn(config.pkgs, moduleName);
if (pkgMain && (moduleName = pkgMain), bundleId = getOwn(bundlesMap, moduleName)) return context.nameToUrl(bundleId, ext, skipExt);
if (req.jsExtRegExp.test(moduleName)) url = moduleName + (ext || "");
else {
for (paths = config.paths, syms = moduleName.split("/"), i = syms.length; i > 0; i -= 1)
if (parentModule = syms.slice(0, i).join("/"), parentPath = getOwn(paths, parentModule)) {
isArray(parentPath) && (parentPath = parentPath[0]), syms.splice(0, i, parentPath);
break
} url = syms.join("/"), url += ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? "" : ".js"), url = ("/" === url.charAt(0) || url.match(/^[\w\+\.\-]+:/) ? "" : config.baseUrl) + url
}
return config.urlArgs && !/^blob\:/.test(url) ? url + config.urlArgs(moduleName, url) : url
},
load: function(id, url) {
req.load(context, id, url)
},
execCb: function(name, callback, args, exports) {
return callback.apply(exports, args)
},
onScriptLoad: function(evt) {
if ("load" === evt.type || readyRegExp.test((evt.currentTarget || evt.srcElement).readyState)) {
interactiveScript = null;
var data = getScriptData(evt);
context.completeLoad(data.id)
}
},
onScriptError: function(evt) {
var data = getScriptData(evt);
if (!hasPathFallback(data.id)) {
var parents = [];
return eachProp(registry, function(value, key) {
0 !== key.indexOf("_@r") && each(value.depMaps, function(depMap) {
if (depMap.id === data.id) return parents.push(key), !0
})
}), onError(makeError("scripterror", 'Script error for "' + data.id + (parents.length ? '", needed by: ' + parents.join(", ") : '"'), evt, [data.id]))
}
}
}, context.require = context.makeRequire(), context
}
function getInteractiveScript() {
return interactiveScript && "interactive" === interactiveScript.readyState ? interactiveScript : (eachReverse(scripts(), function(script) {
if ("interactive" === script.readyState) return interactiveScript = script
}), interactiveScript)
}
var req, s, head, baseElement, dataMain, src, interactiveScript, currentlyAddingScript, mainScript, subPath, version = "2.3.5",
commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/gm,
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
jsSuffixRegExp = /\.js$/,
currDirRegExp = /^\.\//,
op = Object.prototype,
ostring = op.toString,
hasOwn = op.hasOwnProperty,
isBrowser = !("undefined" == typeof window || "undefined" == typeof navigator || !window.document),
isWebWorker = !isBrowser && "undefined" != typeof importScripts,
readyRegExp = isBrowser && "PLAYSTATION 3" === navigator.platform ? /^complete$/ : /^(complete|loaded)$/,
defContextName = "_",
isOpera = "undefined" != typeof opera && "[object Opera]" === opera.toString(),
contexts = {},
cfg = {},
globalDefQueue = [],
useInteractive = !1;
if (void 0 === define) {
if (void 0 !== requirejs) {
if (isFunction(requirejs)) return;
cfg = requirejs, requirejs = void 0
}
void 0 === require || isFunction(require) || (cfg = require, require = void 0), req = requirejs = function(deps, callback, errback, optional) {
var context, config, contextName = defContextName;
return isArray(deps) || "string" == typeof deps || (config = deps, isArray(callback) ? (deps = callback, callback = errback, errback = optional) : deps = []), config && config.context && (contextName = config.context), context = getOwn(contexts, contextName), context || (context = contexts[contextName] = req.s.newContext(contextName)), config && context.configure(config), context.require(deps, callback, errback)
}, req.config = function(config) {
return req(config)
}, req.nextTick = void 0 !== setTimeout ? function(fn) {
setTimeout(fn, 4)
} : function(fn) {
fn()
}, require || (require = req), req.version = version, req.jsExtRegExp = /^\/|:|\?|\.js$/, req.isBrowser = isBrowser, s = req.s = {
contexts: contexts,
newContext: newContext
}, req({}), each(["toUrl", "undef", "defined", "specified"], function(prop) {
req[prop] = function() {
var ctx = contexts[defContextName];
return ctx.require[prop].apply(ctx, arguments)
}
}), isBrowser && (head = s.head = document.getElementsByTagName("head")[0], (baseElement = document.getElementsByTagName("base")[0]) && (head = s.head = baseElement.parentNode)), req.onError = defaultOnError, req.createNode = function(config, moduleName, url) {
var node = config.xhtml ? document.createElementNS("http://www.w3.org/1999/xhtml", "html:script") : document.createElement("script");
return node.type = config.scriptType || "text/javascript", node.charset = "utf-8", node.async = !0, node
}, req.load = function(context, moduleName, url) {
var node, config = context && context.config || {};
if (isBrowser) return node = req.createNode(config, moduleName, url), node.setAttribute("data-requirecontext", context.contextName), node.setAttribute("data-requiremodule", moduleName), !node.attachEvent || node.attachEvent.toString && node.attachEvent.toString().indexOf("[native code") < 0 || isOpera ? (node.addEventListener("load", context.onScriptLoad, !1), node.addEventListener("error", context.onScriptError, !1)) : (useInteractive = !0, node.attachEvent("onreadystatechange", context.onScriptLoad)), node.src = url, config.onNodeCreated && config.onNodeCreated(node, config, moduleName, url), currentlyAddingScript = node, baseElement ? head.insertBefore(node, baseElement) : head.appendChild(node), currentlyAddingScript = null, node;
if (isWebWorker) try {
setTimeout(function() {}, 0), importScripts(url), context.completeLoad(moduleName)
} catch (e) {
context.onError(makeError("importscripts", "importScripts failed for " + moduleName + " at " + url, e, [moduleName]))
}
}, isBrowser && !cfg.skipDataMain && eachReverse(scripts(), function(script) {
if (head || (head = script.parentNode), dataMain = script.getAttribute("data-main")) return mainScript = dataMain, cfg.baseUrl || -1 !== mainScript.indexOf("!") || (src = mainScript.split("/"), mainScript = src.pop(), subPath = src.length ? src.join("/") + "/" : "./", cfg.baseUrl = subPath), mainScript = mainScript.replace(jsSuffixRegExp, ""), req.jsExtRegExp.test(mainScript) && (mainScript = dataMain), cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript], !0
}), define = function(name, deps, callback) {
var node, context;
"string" != typeof name && (callback = deps, deps = name, name = null), isArray(deps) || (callback = deps, deps = null), !deps && isFunction(callback) && (deps = [], callback.length && (callback.toString().replace(commentRegExp, commentReplace).replace(cjsRequireRegExp, function(match, dep) {
deps.push(dep)
}), deps = (1 === callback.length ? ["require"] : ["require", "exports", "module"]).concat(deps))), useInteractive && (node = currentlyAddingScript || getInteractiveScript()) && (name || (name = node.getAttribute("data-requiremodule")), context = contexts[node.getAttribute("data-requirecontext")]), context ? (context.defQueue.push([name, deps, callback]), context.defQueueMap[name] = !0) : globalDefQueue.push([name, deps, callback])
}, define.amd = {
jQuery: !0
}, req.exec = function(text) {
return eval(text)
}, req(cfg)
}
}(this, "undefined" == typeof setTimeout ? void 0 : setTimeout);

File diff suppressed because it is too large Load Diff

View File

@@ -4,180 +4,33 @@
// Use define from require.js not webpack's define
var _define = window.define;
// 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;
});
// flvjs
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() {
return jstree;
});
var jstree = require("jstree");
require("jstree/dist/themes/default/style.css");
_define("jstree", function() { return jstree; });
// jquery
var jquery = require('jquery');
_define('jQuery', function() {
return jquery;
});
var jquery = require("jquery");
_define("jQuery", function() { return jquery; });
// hlsjs
var hlsjs = require('hls.js');
_define('hlsjs', function() {
return hlsjs;
});
var hlsjs = require("hls.js");
_define("hlsjs", function() { return hlsjs; });
// howler
var howler = require('howler');
_define('howler', function() {
return howler;
});
// resize-observer-polyfill
var resize = require('resize-observer-polyfill').default;
_define('resize-observer-polyfill', function() {
return resize;
});
var howler = require("howler");
_define("howler", function() { return howler; });
// swiper
var swiper = require('swiper/js/swiper');
require('swiper/css/swiper.min.css');
_define('swiper', function() {
return swiper;
});
var swiper = require("swiper");
require("swiper/dist/css/swiper.min.css");
_define("swiper", function() { return swiper; });
// sortable
var sortable = require('sortablejs').default;
_define('sortable', function() {
return sortable;
});
var sortable = require("sortablejs");
_define("sortable", function() { return sortable; });
// webcomponents
var webcomponents = require('webcomponents.js/webcomponents-lite');
_define('webcomponents', function() {
return webcomponents;
});
// shaka
var shaka = require('shaka-player');
_define('shaka', function() {
return shaka;
});
// libass-wasm
var libassWasm = require('libass-wasm');
_define('JavascriptSubtitlesOctopus', function() {
return libassWasm;
});
// material-icons
var materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css');
_define('material-icons', function() {
return materialIcons;
});
// 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;
});
// libjass
var libjass = require("libjass");
require("libjass/libjass.css");
_define("libjass", function() { return libjass; });

View File

@@ -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
};

View File

@@ -0,0 +1,51 @@
define(["dialogHelper", "datetime", "emby-select", "paper-icon-button-light", "formDialogStyle"], function(dialogHelper, datetime) {
"use strict";
function getDisplayTime(hours) {
var minutes = 0,
pct = hours % 1;
return pct && (minutes = parseInt(60 * pct)), datetime.getDisplayTime(new Date(2e3, 1, 1, hours, minutes, 0, 0))
}
function populateHours(context) {
for (var html = "", 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 = !0, 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", !0), xhr.onload = function(e) {
var template = this.response,
dlg = dialogHelper.createDialog({
removeOnClose: !0,
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() {
dlg.submitted ? resolve(options.schedule) : reject()
}), dlg.querySelector(".btnCancel").addEventListener("click", function(e) {
dialogHelper.close(dlg)
}), dlg.querySelector("form").addEventListener("submit", function(e) {
return submitSchedule(dlg, options), e.preventDefault(), !1
})
}, xhr.send()
})
}
}
});

View File

@@ -1,7 +1,5 @@
<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>
<button is="paper-icon-button-light" class="btnCancel autoSize" tabindex="-1"><i class="md-icon">&#xE5C4;</i></button>
<h3 class="formDialogHeaderTitle">
${HeaderAccessSchedule}
</h3>
@@ -38,4 +36,4 @@
</div>
</form>
</div>
</div>
</div>

View File

@@ -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
};

View File

@@ -4,7 +4,7 @@
padding: 0;
border: none;
max-height: 84%;
border-radius: 0.1em !important;
border-radius: .1em !important;
}
.actionsheet-not-fullscreen {
@@ -24,7 +24,7 @@
.actionSheetContent {
margin: 0 !important;
padding: 0.4em 0 !important;
padding: .4em 0 !important;
flex-direction: column;
display: flex;
justify-content: center;
@@ -37,15 +37,14 @@
box-shadow: none;
flex-shrink: 0;
border-radius: 0;
margin: 0;
}
.actionSheetMenuItem:focus {
transform: none !important;
}
.actionSheetMenuItem:focus {
transform: none !important;
}
.actionsheetListItemBody {
padding: 0.4em 1em 0.4em 0.6em !important;
padding: .4em 1em .4em .6em !important;
}
.actionSheetItemText {
@@ -59,13 +58,13 @@
}
.actionSheetItemAsideText {
opacity: 0.7;
opacity: .7;
font-size: 90%;
display: flex;
justify-content: flex-end;
flex-shrink: 0;
margin-left: 5em;
margin-right: 0.5em;
margin-right: .5em;
}
.actionSheetScroller {
@@ -83,14 +82,14 @@
}
.actionsheetDivider {
height: 0.07em;
margin: 0.25em 0;
height: .07em;
margin: .25em 0;
flex-shrink: 0;
}
.actionSheetTitle {
margin: 0.6em 0 0.7em !important;
padding: 0 0.9em;
margin: .6em 0 .7em !important;
padding: 0 .9em;
flex-grow: 0;
}
@@ -100,16 +99,16 @@
}
.actionsheetMenuItemIcon {
margin: 0 0.85em 0 0.45em !important;
margin: 0 .85em 0 .45em !important;
padding: 0 !important;
}
.actionsheet-xlargeFont {
font-size: 112% !important;
font-size: 112%!important;
}
.btnCloseActionSheet {
position: fixed;
top: 0.75em;
left: 0.5em;
top: .75em;
left: .5em;
}

View File

@@ -0,0 +1,359 @@
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, length, 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="md-icon">&#xE5C4;</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 md-icon">' + itemIcon + '</i>';
}
else if (renderIcon && !center) {
html += '<i class="actionsheetMenuItemIcon listItemIcon listItemIcon-transparent md-icon" 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
};
});

View File

@@ -0,0 +1,3 @@
{
"main": "actionsheet.js"
}

View File

@@ -1,160 +1,96 @@
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';
if ('Error' == entry.Severity || 'Fatal' == entry.Severity || 'Warn' == entry.Severity) {
color = '#cc0000';
icon = 'notification_important';
var color = "#00a4dc";
var icon = "notifications";
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',
tag: entry.UserPrimaryImageTag
}) + "');background-repeat:no-repeat;background-position:center center;background-size: cover;\"></span>";
html += '<i class="listItemIcon md-icon" 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,
height: 40
}) + "');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 md-icon" style="background-color:' + color + '">' + icon + '</i>';
}
html += '<div class="listItemBody three-line">';
html += '<div class="listItemBodyText">';
html += entry.Name;
html += '</div>';
html += '<div class="listItemBodyText secondary">';
html += datefns.formatRelative(Date.parse(entry.Date), Date.parse(new Date()), { locale: dfnshelper.getLocale() });
html += '</div>';
html += '<div class="listItemBodyText secondary listItemBodyText-nowrap">';
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 += '</div>';
return html;
html += '<div class="listItemBody three-line">', html += '<div class="listItemBodyText">', html += entry.Name, html += "</div>", html += '<div class="listItemBodyText secondary">';
var date = datetime.parseISO8601Date(entry.Date, !0);
return html += datetime.toLocaleString(date).toLowerCase(), html += "</div>", html += '<div class="listItemBodyText secondary listItemBodyText-nowrap">', html += entry.ShortOverview || "", html += "</div>", html += "</div>", entry.Overview && (html += '<button type="button" is="paper-icon-button-light" class="btnEntryInfo" data-id="' + entry.Id + '" title="' + globalize.translate("Info") + '"><i class="md-icon">info</i></button>'), html += "</div>"
}
function renderList(elem, apiClient, result, startIndex, limit) {
elem.innerHTML = result.Items.map(function (i) {
return getEntryHtml(i, apiClient);
}).join('');
elem.innerHTML = result.Items.map(function(i) {
return getEntryHtml(i, apiClient)
}).join("")
}
function reloadData(instance, elem, apiClient, startIndex, limit) {
if (null == startIndex) {
startIndex = parseInt(elem.getAttribute('data-activitystartindex') || '0');
}
limit = limit || parseInt(elem.getAttribute('data-activitylimit') || '7');
var minDate = new Date();
var hasUserId = 'false' !== elem.getAttribute('data-useractivity');
if (hasUserId) {
minDate.setTime(minDate.getTime() - 24 * 60 * 60 * 1000); // one day back
} else {
minDate.setTime(minDate.getTime() - 7 * 24 * 60 * 60 * 1000); // one week back
}
ApiClient.getJSON(ApiClient.getUrl('System/ActivityLog/Entries', {
null == startIndex && (startIndex = parseInt(elem.getAttribute("data-activitystartindex") || "0")), limit = limit || parseInt(elem.getAttribute("data-activitylimit") || "7");
var minDate = new Date,
hasUserId = "false" !== elem.getAttribute("data-useractivity");
hasUserId ? minDate.setTime(minDate.getTime() - 864e5) : minDate.setTime(minDate.getTime() - 6048e5), 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);
if (!startIndex) {
var activityContainer = dom.parentWithClass(elem, 'activityContainer');
if (activityContainer) {
if (result.Items.length) {
activityContainer.classList.remove('hide');
} else {
activityContainer.classList.add('hide');
}
}
})).then(function(result) {
if (elem.setAttribute("data-activitystartindex", startIndex), elem.setAttribute("data-activitylimit", limit), !startIndex) {
var activityContainer = dom.parentWithClass(elem, "activityContainer");
activityContainer && (result.Items.length ? activityContainer.classList.remove("hide") : activityContainer.classList.add("hide"))
}
instance.items = result.Items;
renderList(elem, apiClient, result, startIndex, limit);
});
instance.items = result.Items, renderList(elem, apiClient, result, startIndex, limit)
})
}
function onActivityLogUpdate(e, apiClient, data) {
var options = this.options;
if (options && options.serverId === apiClient.serverId()) {
reloadData(this, options.element, apiClient);
}
options && options.serverId === apiClient.serverId() && reloadData(this, options.element, apiClient)
}
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 items = this.items;
var id = btnEntryInfo.getAttribute("data-id"),
items = this.items;
if (items) {
var item = items.filter(function (i) {
return i.Id.toString() === id;
var item = items.filter(function(i) {
return i.Id.toString() === id
})[0];
if (item) {
showItemOverview(item);
}
item && showItemOverview(item)
}
}
}
function showItemOverview(item) {
require(['alert'], function (alert) {
require(["alert"], function(alert) {
alert({
text: item.Overview
});
});
})
})
}
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');
this.updateFn = onUpdate, events.on(serverNotifications, "ActivityLogEntry", onUpdate), apiClient.sendMessage("ActivityLogEntryStart", "0,1500")
}
ActivityLog.prototype.destroy = function () {
return 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);
}
this.items = null;
this.options = null;
};
return ActivityLog;
onUpdate && events.off(serverNotifications, "ActivityLogEntry", onUpdate), this.items = null, this.options = null
}, ActivityLog
});

View File

@@ -35,11 +35,11 @@ define(['browser', 'dialog', 'globalize'], function (browser, dialog, globalize)
if (result === 'ok') {
return Promise.resolve();
}
return Promise.reject();
});
}
return Promise.resolve();
};
});
});

View File

@@ -10,7 +10,7 @@ define(['dom', 'focusManager'], function (dom, focusManager) {
if (e.ctrlKey) {
return;
}
if (e.shiftKey) {
if (!!e.shiftKey) {
return;
}
if (e.altKey) {
@@ -127,4 +127,4 @@ define(['dom', 'focusManager'], function (dom, focusManager) {
};
return AlphaNumericShortcuts;
});
});

View File

@@ -50,7 +50,9 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
var vertical = element.classList.contains('alphaPicker-vertical');
if (!vertical) {
if (vertical) {
} else {
element.classList.add('focuscontainer-x');
}
@@ -67,7 +69,8 @@ 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>';
// space_bar icon
html += '<button data-value=" " is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><i class="md-icon alphaPickerButtonIcon">&#xE256;</i></button>';
} else {
letters = ['#'];
html += mapLetters(letters, vertical).join('');
@@ -77,7 +80,8 @@ 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>';
// backspace icon
html += '<button data-value="backspace" is="paper-icon-button-light" class="' + alphaPickerButtonClassName + '"><i class="md-icon alphaPickerButtonIcon">&#xE14A;</i></button>';
html += '</div>';
letters = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'];
@@ -132,7 +136,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
@@ -226,8 +230,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
AlphaPicker.prototype.value = function (value, applyValue) {
var element = this.options.element;
var btn;
var selected;
var btn, selected;
if (value !== undefined) {
if (value != null) {
@@ -241,7 +244,7 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
try {
btn = element.querySelector('.alphaPickerButton[data-value=\'' + value + '\']');
} catch (err) {
console.error('error in querySelector: ' + err);
console.log('Error in querySelector: ' + err);
}
if (btn && btn !== selected) {
@@ -262,7 +265,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
@@ -318,4 +321,4 @@ define(['focusManager', 'layoutManager', 'dom', 'css!./style.css', 'paper-icon-b
};
return AlphaPicker;
});
});

View File

@@ -35,15 +35,16 @@
font-size: inherit;
min-width: initial;
margin: 0;
padding: 0.1em 0.4em;
padding: .1em .4em;
width: auto;
border-radius: 0.1em;
border-radius: .1em;
font-weight: normal;
flex-shrink: 0;
flex-grow: 1;
}
@media all and (max-height: 50em) {
.alphaPicker-fixed {
bottom: 5em;
}
@@ -55,12 +56,14 @@
}
@media all and (max-height: 49em) {
.alphaPicker-vertical {
font-size: 94%;
}
}
@media all and (max-height: 44em) {
.alphaPicker-vertical {
font-size: 90%;
}
@@ -72,12 +75,14 @@
}
@media all and (max-height: 37em) {
.alphaPicker-vertical {
font-size: 82%;
}
}
@media all and (max-height: 32em) {
.alphaPicker-vertical {
font-size: 74%;
}
@@ -107,17 +112,27 @@
bottom: 1%;
}
.alphaPicker-fixed-left {
left: .4em;
}
.alphaPicker-fixed-right {
right: 0.4em;
right: .4em;
}
@media all and (min-width: 62.5em) {
.alphaPicker-fixed-left {
left: 1em;
}
.alphaPicker-fixed-right {
right: 1em;
}
}
@media all and (max-height: 31.25em) {
.alphaPicker-fixed {
display: none !important;
}

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