diff --git a/package.json b/package.json index 6e2480ca05..ce29b55eae 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,7 @@ "src/components/alert.js", "src/components/alphaPicker/alphaPicker.js", "src/components/appFooter/appFooter.js", + "src/components/apphost.js", "src/components/autoFocuser.js", "src/components/backdrop/backdrop.js", "src/components/cardbuilder/cardBuilder.js", @@ -156,6 +157,7 @@ "src/components/recordingcreator/seriesrecordingeditor.js", "src/components/recordingcreator/recordinghelper.js", "src/components/refreshdialog/refreshdialog.js", + "src/components/qualityOptions.js", "src/components/sanatizefilename.js", "src/components/scrollManager.js", "src/plugins/htmlVideoPlayer/plugin.js", diff --git a/src/bundle.js b/src/bundle.js index ae2a59f0d5..86ebb1ccdf 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -2,159 +2,165 @@ * require.js module definitions bundled by webpack */ // Use define from require.js not webpack's define -var _define = window.define; +const _define = window.define; // fetch -var fetch = require('whatwg-fetch'); +const fetch = require('whatwg-fetch'); _define('fetch', function() { return fetch; }); // Blurhash -var blurhash = require('blurhash'); +const blurhash = require('blurhash'); _define('blurhash', function() { return blurhash; }); // query-string -var query = require('query-string'); +const query = require('query-string'); _define('queryString', function() { return query; }); // flvjs -var flvjs = require('flv.js/dist/flv').default; +const flvjs = require('flv.js/dist/flv').default; _define('flvjs', function() { return flvjs; }); // jstree -var jstree = require('jstree'); +const jstree = require('jstree'); require('jstree/dist/themes/default/style.css'); _define('jstree', function() { return jstree; }); // jquery -var jquery = require('jquery'); +const jquery = require('jquery'); _define('jQuery', function() { return jquery; }); // hlsjs -var hlsjs = require('hls.js'); +const hlsjs = require('hls.js'); _define('hlsjs', function() { return hlsjs; }); // howler -var howler = require('howler'); +const howler = require('howler'); _define('howler', function() { return howler; }); // resize-observer-polyfill -var resize = require('resize-observer-polyfill').default; +const resize = require('resize-observer-polyfill').default; _define('resize-observer-polyfill', function() { return resize; }); // swiper -var swiper = require('swiper/js/swiper'); +const swiper = require('swiper/js/swiper'); require('swiper/css/swiper.min.css'); _define('swiper', function() { return swiper; }); // sortable -var sortable = require('sortablejs').default; +const sortable = require('sortablejs').default; _define('sortable', function() { return sortable; }); // webcomponents -var webcomponents = require('webcomponents.js/webcomponents-lite'); +const webcomponents = require('webcomponents.js/webcomponents-lite'); _define('webcomponents', function() { return webcomponents; }); +// shaka +const shaka = require('shaka-player'); +_define('shaka', function() { + return shaka; +}); + // libass-wasm -var libassWasm = require('libass-wasm'); +const libassWasm = require('libass-wasm'); _define('JavascriptSubtitlesOctopus', function() { return libassWasm; }); // material-icons -var materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css'); +const materialIcons = require('material-design-icons-iconfont/dist/material-design-icons.css'); _define('material-icons', function() { return materialIcons; }); // noto font -var noto = require('jellyfin-noto'); +const noto = require('jellyfin-noto'); _define('jellyfin-noto', function () { return noto; }); -var epubjs = require('epubjs'); +const epubjs = require('epubjs'); _define('epubjs', function () { return epubjs; }); // page.js -var page = require('page'); +const page = require('page'); _define('page', function() { return page; }); // core-js -var polyfill = require('@babel/polyfill/dist/polyfill'); +const polyfill = require('@babel/polyfill/dist/polyfill'); _define('polyfill', function () { return polyfill; }); // domtokenlist-shim -var classlist = require('classlist.js'); +const classlist = require('classlist.js'); _define('classlist-polyfill', function () { return classlist; }); // Date-FNS -var dateFns = require('date-fns'); +const dateFns = require('date-fns'); _define('date-fns', function () { return dateFns; }); -var dateFnsLocale = require('date-fns/locale'); +const dateFnsLocale = require('date-fns/locale'); _define('date-fns/locale', function () { return dateFnsLocale; }); -var fast_text_encoding = require('fast-text-encoding'); +const fast_text_encoding = require('fast-text-encoding'); _define('fast-text-encoding', function () { return fast_text_encoding; }); // intersection-observer -var intersection_observer = require('intersection-observer'); +const intersection_observer = require('intersection-observer'); _define('intersection-observer', function () { return intersection_observer; }); // screenfull -var screenfull = require('screenfull'); +const screenfull = require('screenfull'); _define('screenfull', function () { return screenfull; }); // headroom.js -var headroom = require('headroom.js/dist/headroom'); +const headroom = require('headroom.js/dist/headroom'); _define('headroom', function () { return headroom; }); // apiclient -var apiclient = require('jellyfin-apiclient'); +const apiclient = require('jellyfin-apiclient'); _define('apiclient', function () { return apiclient.ApiClient; diff --git a/src/components/actionSheet/actionSheet.js b/src/components/actionSheet/actionSheet.js index 937cd2afe5..be84cf0a06 100644 --- a/src/components/actionSheet/actionSheet.js +++ b/src/components/actionSheet/actionSheet.js @@ -9,14 +9,14 @@ import 'scrollStyles'; import 'listViewStyle'; function getOffsets(elems) { - let results = []; + const results = []; if (!document) { return results; } for (const elem of elems) { - let box = elem.getBoundingClientRect(); + const box = elem.getBoundingClientRect(); results.push({ top: box.top, @@ -34,7 +34,7 @@ function getPosition(options, dlg) { const windowHeight = windowSize.innerHeight; const windowWidth = windowSize.innerWidth; - let pos = getOffsets([options.positionTo])[0]; + const pos = getOffsets([options.positionTo])[0]; if (options.positionY !== 'top') { pos.top += (pos.height || 0) / 2; @@ -82,7 +82,7 @@ export function show(options) { // positionTo // showCancel // title - let dialogOptions = { + const dialogOptions = { removeOnClose: true, enableHistory: options.enableHistory, scrollY: false @@ -103,7 +103,7 @@ export function show(options) { dialogOptions.autoFocus = false; } - let dlg = dialogHelper.createDialog(dialogOptions); + const dlg = dialogHelper.createDialog(dialogOptions); if (isFullscreen) { dlg.classList.add('actionsheet-fullscreen'); @@ -129,7 +129,7 @@ export function show(options) { } let renderIcon = false; - let icons = []; + const icons = []; let itemIcon; for (const item of options.items) { itemIcon = item.icon || (item.selected ? 'check' : null); @@ -241,7 +241,7 @@ export function show(options) { centerFocus(dlg.querySelector('.actionSheetScroller'), false, true); } - let btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet'); + const btnCloseActionSheet = dlg.querySelector('.btnCloseActionSheet'); if (btnCloseActionSheet) { btnCloseActionSheet.addEventListener('click', function () { dialogHelper.close(dlg); diff --git a/src/components/appRouter.js b/src/components/appRouter.js index da3b08317c..efabdab1cb 100644 --- a/src/components/appRouter.js +++ b/src/components/appRouter.js @@ -329,8 +329,8 @@ define(['loading', 'globalize', 'events', 'viewManager', 'skinManager', 'backdro } if (shouldExitApp) { - if (appHost.supports('exit')) { - appHost.exit(); + if (appHost.default.supports('exit')) { + appHost.default.exit(); return; } return; diff --git a/src/components/apphost.js b/src/components/apphost.js index 3ed590b546..90ae418de6 100644 --- a/src/components/apphost.js +++ b/src/components/apphost.js @@ -1,447 +1,445 @@ -define(['appSettings', 'browser', 'events', 'htmlMediaHelper', 'webSettings', 'globalize'], function (appSettings, browser, events, htmlMediaHelper, webSettings, globalize) { - 'use strict'; +import appSettings from 'appSettings'; +import browser from 'browser'; +import events from 'events'; +import htmlMediaHelper from 'htmlMediaHelper'; +import * as webSettings from 'webSettings'; +import globalize from 'globalize'; - browser = browser.default || browser; +function getBaseProfileOptions(item) { + const disableHlsVideoAudioCodecs = []; - function getBaseProfileOptions(item) { - var disableHlsVideoAudioCodecs = []; + if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) { + if (browser.edge) { + disableHlsVideoAudioCodecs.push('mp3'); + } - if (item && htmlMediaHelper.enableHlsJsPlayer(item.RunTimeTicks, item.MediaType)) { - if (browser.edge) { - disableHlsVideoAudioCodecs.push('mp3'); + disableHlsVideoAudioCodecs.push('ac3'); + disableHlsVideoAudioCodecs.push('eac3'); + disableHlsVideoAudioCodecs.push('opus'); + } + + return { + enableMkvProgressive: false, + disableHlsVideoAudioCodecs: disableHlsVideoAudioCodecs + }; +} + +function getDeviceProfile(item, options = {}) { + return new Promise(function (resolve) { + import(['browserdeviceprofile']).then(({default: profileBuilder}) => { + let profile; + + if (window.NativeShell) { + profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); + } else { + const builderOpts = getBaseProfileOptions(item); + builderOpts.enableSsaRender = (item && !options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats'); + profile = profileBuilder(builderOpts); } - disableHlsVideoAudioCodecs.push('ac3'); - disableHlsVideoAudioCodecs.push('eac3'); - disableHlsVideoAudioCodecs.push('opus'); - } - - return { - enableMkvProgressive: false, - disableHlsVideoAudioCodecs: disableHlsVideoAudioCodecs - }; - } - - function getDeviceProfileForWindowsUwp(item) { - return new Promise(function (resolve, reject) { - require(['browserdeviceprofile', 'environments/windows-uwp/mediacaps'], function (profileBuilder, uwpMediaCaps) { - var profileOptions = getBaseProfileOptions(item); - profileOptions.supportsDts = uwpMediaCaps.supportsDTS(); - profileOptions.supportsTrueHd = uwpMediaCaps.supportsDolby(); - profileOptions.audioChannels = uwpMediaCaps.getAudioChannels(); - resolve(profileBuilder(profileOptions)); - }); + resolve(profile); }); + }); +} + +function escapeRegExp(str) { + return str.replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1'); +} + +function replaceAll(originalString, strReplace, strWith) { + const strReplace2 = escapeRegExp(strReplace); + const reg = new RegExp(strReplace2, 'ig'); + return originalString.replace(reg, strWith); +} + +function generateDeviceId() { + const keys = []; + + if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), self.btoa) { + const result = replaceAll(btoa(keys.join('|')), '=', '1'); + return Promise.resolve(result); } - function getDeviceProfile(item, options) { - options = options || {}; + return Promise.resolve(new Date().getTime()); +} - if (self.Windows) { - return getDeviceProfileForWindowsUwp(item); - } +function getDeviceId() { + const key = '_deviceId2'; + const deviceId = appSettings.get(key); - return new Promise(function (resolve) { - require(['browserdeviceprofile'], function (profileBuilder) { - var profile; - - if (window.NativeShell) { - profile = window.NativeShell.AppHost.getDeviceProfile(profileBuilder); - } else { - var builderOpts = getBaseProfileOptions(item); - builderOpts.enableSsaRender = (item && !options.isRetry && appSettings.get('subtitleburnin') !== 'allcomplexformats'); - profile = profileBuilder(builderOpts); - } - - resolve(profile); - }); - }); + if (deviceId) { + return Promise.resolve(deviceId); } - function escapeRegExp(str) { - return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, '\\$1'); + return generateDeviceId().then(function (deviceId) { + appSettings.set(key, deviceId); + return deviceId; + }); +} + +function getDeviceName() { + var deviceName; + if (browser.tizen) { + deviceName = 'Samsung Smart TV'; + } else if (browser.web0s) { + deviceName = 'LG Smart TV'; + } else if (browser.operaTv) { + deviceName = 'Opera TV'; + } else if (browser.xboxOne) { + deviceName = 'Xbox One'; + } else if (browser.ps4) { + deviceName = 'Sony PS4'; + } else if (browser.chrome) { + deviceName = 'Chrome'; + } else if (browser.edgeChromium) { + deviceName = 'Edge Chromium'; + } else if (browser.edge) { + deviceName = 'Edge'; + } else if (browser.firefox) { + deviceName = 'Firefox'; + } else if (browser.opera) { + deviceName = 'Opera'; + } else if (browser.safari) { + deviceName = 'Safari'; + } else { + deviceName = 'Web Browser'; } - function replaceAll(originalString, strReplace, strWith) { - var strReplace2 = escapeRegExp(strReplace); - var reg = new RegExp(strReplace2, 'ig'); - return originalString.replace(reg, strWith); + if (browser.ipad) { + deviceName += ' iPad'; + } else if (browser.iphone) { + deviceName += ' iPhone'; + } else if (browser.android) { + deviceName += ' Android'; } - function generateDeviceId() { - var keys = []; - - if (keys.push(navigator.userAgent), keys.push(new Date().getTime()), self.btoa) { - var result = replaceAll(btoa(keys.join('|')), '=', '1'); - return Promise.resolve(result); - } - - return Promise.resolve(new Date().getTime()); + if (browser.ipad) { + deviceName += ' iPad'; + } else if (browser.iphone) { + deviceName += ' iPhone'; + } else if (browser.android) { + deviceName += ' Android'; } - function getDeviceId() { - var key = '_deviceId2'; - var deviceId = appSettings.get(key); + return deviceName; +} - if (deviceId) { - return Promise.resolve(deviceId); - } - - return generateDeviceId().then(function (deviceId) { - appSettings.set(key, deviceId); - return deviceId; - }); +function supportsVoiceInput() { + if (!browser.tv) { + return window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.oSpeechRecognition || window.msSpeechRecognition; } - function getDeviceName() { - var deviceName; - if (browser.tizen) { - deviceName = 'Samsung Smart TV'; - } else if (browser.web0s) { - deviceName = 'LG Smart TV'; - } else if (browser.operaTv) { - deviceName = 'Opera TV'; - } else if (browser.xboxOne) { - deviceName = 'Xbox One'; - } else if (browser.ps4) { - deviceName = 'Sony PS4'; - } else if (browser.chrome) { - deviceName = 'Chrome'; - } else if (browser.edgeChromium) { - deviceName = 'Edge Chromium'; - } else if (browser.edge) { - deviceName = 'Edge'; - } else if (browser.firefox) { - deviceName = 'Firefox'; - } else if (browser.opera) { - deviceName = 'Opera'; - } else if (browser.safari) { - deviceName = 'Safari'; - } else { - deviceName = 'Web Browser'; - } - - if (browser.ipad) { - deviceName += ' iPad'; - } else if (browser.iphone) { - deviceName += ' iPhone'; - } else if (browser.android) { - deviceName += ' Android'; - } - - return deviceName; - } - - function supportsVoiceInput() { - if (!browser.tv) { - return window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.oSpeechRecognition || window.msSpeechRecognition; - } + return false; +} +function supportsFullscreen() { + if (browser.tv) { return false; } - function supportsFullscreen() { - if (browser.tv) { - return false; - } + const element = document.documentElement; + return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement('video').webkitEnterFullscreen; +} - var element = document.documentElement; - return (element.requestFullscreen || element.mozRequestFullScreen || element.webkitRequestFullscreen || element.msRequestFullscreen) || document.createElement('video').webkitEnterFullscreen; - } +function getSyncProfile() { + return new Promise(function (resolve) { + require(['browserdeviceprofile', 'appSettings'], function (profileBuilder, appSettings) { + let profile; - function getSyncProfile() { - return new Promise(function (resolve) { - require(['browserdeviceprofile', 'appSettings'], function (profileBuilder, appSettings) { - var profile; + if (window.NativeShell) { + profile = window.NativeShell.AppHost.getSyncProfile(profileBuilder, appSettings); + } else { + profile = profileBuilder(); + profile.MaxStaticMusicBitrate = appSettings.maxStaticMusicBitrate(); + } - if (window.NativeShell) { - profile = window.NativeShell.AppHost.getSyncProfile(profileBuilder, appSettings); - } else { - profile = profileBuilder(); - profile.MaxStaticMusicBitrate = appSettings.maxStaticMusicBitrate(); - } - - resolve(profile); - }); + resolve(profile); }); - } + }); +} - function getDefaultLayout() { - return 'desktop'; - } - - function supportsHtmlMediaAutoplay() { - if (browser.edgeUwp || browser.tizen || browser.web0s || browser.orsay || browser.operaTv || browser.ps4 || browser.xboxOne) { - return true; - } - - if (browser.mobile) { - return false; - } +function getDefaultLayout() { + return 'desktop'; +} +function supportsHtmlMediaAutoplay() { + if (browser.edgeUwp || browser.tizen || browser.web0s || browser.orsay || browser.operaTv || browser.ps4 || browser.xboxOne) { return true; } - function supportsCue() { - try { - var video = document.createElement('video'); - var style = document.createElement('style'); - - style.textContent = 'video::cue {background: inherit}'; - document.body.appendChild(style); - document.body.appendChild(video); - - var cue = window.getComputedStyle(video, '::cue').background; - document.body.removeChild(style); - document.body.removeChild(video); - - return !!cue.length; - } catch (err) { - console.error('error detecting cue support: ' + err); - return false; - } + if (browser.mobile) { + return false; } - function onAppVisible() { - if (isHidden) { - isHidden = false; - console.debug('triggering app resume event'); - events.trigger(appHost, 'resume'); - } + return true; +} + +function supportsCue() { + try { + const video = document.createElement('video'); + const style = document.createElement('style'); + + style.textContent = 'video::cue {background: inherit}'; + document.body.appendChild(style); + document.body.appendChild(video); + + const cue = window.getComputedStyle(video, '::cue').background; + document.body.removeChild(style); + document.body.removeChild(video); + + return !!cue.length; + } catch (err) { + console.error('error detecting cue support: ' + err); + return false; + } +} + +function onAppVisible() { + if (isHidden) { + isHidden = false; + console.debug('triggering app resume event'); + events.trigger(appHost, 'resume'); + } +} + +function onAppHidden() { + if (!isHidden) { + isHidden = true; + console.debug('app is hidden'); + } +} + +const supportedFeatures = function () { + const features = []; + + if (navigator.share) { + features.push('sharing'); } - function onAppHidden() { - if (!isHidden) { - isHidden = true; - console.debug('app is hidden'); - } + if (!browser.edgeUwp && !browser.tv && !browser.xboxOne && !browser.ps4) { + features.push('filedownload'); } - var supportedFeatures = function () { - var features = []; + if (browser.operaTv || browser.tizen || browser.orsay || browser.web0s) { + features.push('exit'); + } else { + features.push('exitmenu'); + features.push('plugins'); + } - if (navigator.share) { - features.push('sharing'); - } + if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.ps4) { + features.push('externallinks'); + features.push('externalpremium'); + } - if (!browser.edgeUwp && !browser.tv && !browser.xboxOne && !browser.ps4) { - features.push('filedownload'); - } + if (!browser.operaTv) { + features.push('externallinkdisplay'); + } - if (browser.operaTv || browser.tizen || browser.orsay || browser.web0s) { - features.push('exit'); + if (supportsVoiceInput()) { + features.push('voiceinput'); + } + + if (supportsHtmlMediaAutoplay()) { + features.push('htmlaudioautoplay'); + features.push('htmlvideoautoplay'); + } + + if (browser.edgeUwp) { + features.push('sync'); + } + + if (supportsFullscreen()) { + features.push('fullscreenchange'); + } + + if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) { + features.push('physicalvolumecontrol'); + } + + if (!browser.tv && !browser.xboxOne && !browser.ps4) { + features.push('remotecontrol'); + } + + if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.edgeUwp) { + features.push('remotevideo'); + } + + features.push('displaylanguage'); + features.push('otherapppromotions'); + features.push('displaymode'); + features.push('targetblank'); + features.push('screensaver'); + + webSettings.enableMultiServer().then(enabled => { + if (enabled) features.push('multiserver'); + }); + + if (!browser.orsay && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { + features.push('subtitleappearancesettings'); + } + + if (!browser.orsay) { + features.push('subtitleburnsettings'); + } + + if (!browser.tv && !browser.ps4 && !browser.xboxOne) { + features.push('fileinput'); + } + + if (browser.chrome) { + features.push('chromecast'); + } + + return features; +}(); + +/** + * Do exit according to platform + */ +function doExit() { + try { + if (window.NativeShell) { + window.NativeShell.AppHost.exit(); + } else if (browser.tizen) { + tizen.application.getCurrentApplication().exit(); + } else if (browser.web0s) { + webOS.platformBack(); } else { - features.push('exitmenu'); - features.push('plugins'); + window.close(); } + } catch (err) { + console.error('error closing application: ' + err); + } +} - if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.ps4) { - features.push('externallinks'); - features.push('externalpremium'); - } +let exitPromise; - if (!browser.operaTv) { - features.push('externallinkdisplay'); - } - - if (supportsVoiceInput()) { - features.push('voiceinput'); - } - - if (supportsHtmlMediaAutoplay()) { - features.push('htmlaudioautoplay'); - features.push('htmlvideoautoplay'); - } - - if (browser.edgeUwp) { - features.push('sync'); - } - - if (supportsFullscreen()) { - features.push('fullscreenchange'); - } - - if (browser.tv || browser.xboxOne || browser.ps4 || browser.mobile) { - features.push('physicalvolumecontrol'); - } - - if (!browser.tv && !browser.xboxOne && !browser.ps4) { - features.push('remotecontrol'); - } - - if (!browser.operaTv && !browser.tizen && !browser.orsay && !browser.web0s && !browser.edgeUwp) { - features.push('remotevideo'); - } - - features.push('displaylanguage'); - features.push('otherapppromotions'); - features.push('displaymode'); - features.push('targetblank'); - features.push('screensaver'); - - webSettings.getMultiServer().then(enabled => { - if (enabled) features.push('multiserver'); - }); - - if (!browser.orsay && (browser.firefox || browser.ps4 || browser.edge || supportsCue())) { - features.push('subtitleappearancesettings'); - } - - if (!browser.orsay) { - features.push('subtitleburnsettings'); - } - - if (!browser.tv && !browser.ps4 && !browser.xboxOne) { - features.push('fileinput'); - } - - if (browser.chrome || browser.edgeChromium) { - features.push('chromecast'); - } - - return features; - }(); - - /** - * Do exit according to platform - */ - function doExit() { - try { - if (window.NativeShell) { - window.NativeShell.AppHost.exit(); - } else if (browser.tizen) { - tizen.application.getCurrentApplication().exit(); - } else if (browser.web0s) { - webOS.platformBack(); - } else { - window.close(); - } - } catch (err) { - console.error('error closing application: ' + err); - } +/** + * Ask user for exit + */ +function askForExit() { + if (exitPromise) { + return; } - var exitPromise; - - /** - * Ask user for exit - */ - function askForExit() { - if (exitPromise) { - return; - } - - require(['actionsheet'], function (actionsheet) { - exitPromise = actionsheet.show({ - title: globalize.translate('MessageConfirmAppExit'), - items: [ - {id: 'yes', name: globalize.translate('Yes')}, - {id: 'no', name: globalize.translate('No')} - ] - }).then(function (value) { - if (value === 'yes') { - doExit(); - } - }).finally(function () { - exitPromise = null; - }); - }); - } - - var deviceId; - var deviceName; - var appName = 'Jellyfin Web'; - var appVersion = '10.7.0'; - - var appHost = { - getWindowState: function () { - return document.windowState || 'Normal'; - }, - setWindowState: function (state) { - alert('setWindowState is not supported and should not be called'); - }, - exit: function () { - if (!!window.appMode && browser.tizen) { - askForExit(); - } else { + require(['actionsheet'], function (actionsheet) { + exitPromise = actionsheet.show({ + title: globalize.translate('MessageConfirmAppExit'), + items: [ + {id: 'yes', name: globalize.translate('Yes')}, + {id: 'no', name: globalize.translate('No')} + ] + }).then(function (value) { + if (value === 'yes') { doExit(); } - }, - supports: function (command) { - if (window.NativeShell) { - return window.NativeShell.AppHost.supports(command); - } + }).finally(function () { + exitPromise = null; + }); + }); +} - return supportedFeatures.indexOf(command.toLowerCase()) !== -1; - }, - preferVisualCards: browser.android || browser.chrome, - getSyncProfile: getSyncProfile, - getDefaultLayout: function () { - if (window.NativeShell) { - return window.NativeShell.AppHost.getDefaultLayout(); - } +let deviceId; +let deviceName; +const appName = 'Jellyfin Web'; +const appVersion = '10.6.0'; - return getDefaultLayout(); - }, - getDeviceProfile: getDeviceProfile, - init: function () { - if (window.NativeShell) { - return window.NativeShell.AppHost.init(); - } - - deviceName = getDeviceName(); - getDeviceId().then(function (id) { - deviceId = id; - }); - }, - deviceName: function () { - return window.NativeShell ? window.NativeShell.AppHost.deviceName() : deviceName; - }, - deviceId: function () { - return window.NativeShell ? window.NativeShell.AppHost.deviceId() : deviceId; - }, - appName: function () { - return window.NativeShell ? window.NativeShell.AppHost.appName() : appName; - }, - appVersion: function () { - return window.NativeShell ? window.NativeShell.AppHost.appVersion() : appVersion; - }, - getPushTokenInfo: function () { - return {}; - }, - setUserScalable: function (scalable) { - if (!browser.tv) { - var att = scalable ? 'width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes' : 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no'; - document.querySelector('meta[name=viewport]').setAttribute('content', att); - } - } - }; - - var isHidden = false; - var hidden; - var visibilityChange; - - if (typeof document.hidden !== 'undefined') { /* eslint-disable-line compat/compat */ - hidden = 'hidden'; - visibilityChange = 'visibilitychange'; - } else if (typeof document.webkitHidden !== 'undefined') { - hidden = 'webkitHidden'; - visibilityChange = 'webkitvisibilitychange'; - } - - document.addEventListener(visibilityChange, function () { - /* eslint-disable-next-line compat/compat */ - if (document[hidden]) { - onAppHidden(); +const appHost = { + getWindowState: function () { + return document.windowState || 'Normal'; + }, + setWindowState: function () { + alert('setWindowState is not supported and should not be called'); + }, + exit: function () { + if (!!window.appMode && browser.tizen) { + askForExit(); } else { - onAppVisible(); + doExit(); + } + }, + supports: function (command) { + if (window.NativeShell) { + return window.NativeShell.AppHost.supports(command); } - }, false); - if (self.addEventListener) { - self.addEventListener('focus', onAppVisible); - self.addEventListener('blur', onAppHidden); + return supportedFeatures.indexOf(command.toLowerCase()) !== -1; + }, + preferVisualCards: browser.android || browser.chrome, + getSyncProfile: getSyncProfile, + getDefaultLayout: function () { + if (window.NativeShell) { + return window.NativeShell.AppHost.getDefaultLayout(); + } + + return getDefaultLayout(); + }, + getDeviceProfile: getDeviceProfile, + init: function () { + if (window.NativeShell) { + return window.NativeShell.AppHost.init(); + } + + deviceName = getDeviceName(); + getDeviceId().then(function (id) { + deviceId = id; + }); + }, + deviceName: function () { + return window.NativeShell ? window.NativeShell.AppHost.deviceName() : deviceName; + }, + deviceId: function () { + return window.NativeShell ? window.NativeShell.AppHost.deviceId() : deviceId; + }, + appName: function () { + return window.NativeShell ? window.NativeShell.AppHost.appName() : appName; + }, + appVersion: function () { + return window.NativeShell ? window.NativeShell.AppHost.appVersion() : appVersion; + }, + getPushTokenInfo: function () { + return {}; + }, + setThemeColor: function (color) { + const metaThemeColor = document.querySelector('meta[name=theme-color]'); + + if (metaThemeColor) { + metaThemeColor.setAttribute('content', color); + } + }, + setUserScalable: function (scalable) { + if (!browser.tv) { + const att = scalable ? 'width=device-width, initial-scale=1, minimum-scale=1, user-scalable=yes' : 'width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no'; + document.querySelector('meta[name=viewport]').setAttribute('content', att); + } } +}; - return appHost; -}); +let isHidden = false; +let hidden; +let visibilityChange; + +if (typeof document.hidden !== 'undefined') { /* eslint-disable-line compat/compat */ + hidden = 'hidden'; + visibilityChange = 'visibilitychange'; +} else if (typeof document.webkitHidden !== 'undefined') { + hidden = 'webkitHidden'; + visibilityChange = 'webkitvisibilitychange'; +} + +document.addEventListener(visibilityChange, function () { + /* eslint-disable-next-line compat/compat */ + if (document[hidden]) { + onAppHidden(); + } else { + onAppVisible(); + } +}, false); + +if (self.addEventListener) { + self.addEventListener('focus', onAppVisible); + self.addEventListener('blur', onAppHidden); +} + +export default appHost; diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js index 4a37331ef4..e644365906 100644 --- a/src/components/cardbuilder/cardBuilder.js +++ b/src/components/cardbuilder/cardBuilder.js @@ -362,12 +362,12 @@ import 'programStyles'; let hasOpenRow; let hasOpenSection; - let sectionTitleTagName = options.sectionTitleTagName || 'div'; + const sectionTitleTagName = options.sectionTitleTagName || 'div'; let apiClient; let lastServerId; for (const [i, item] of items.entries()) { - let serverId = item.ServerId || options.serverId; + const serverId = item.ServerId || options.serverId; if (serverId !== lastServerId) { lastServerId = serverId; @@ -621,7 +621,7 @@ import 'programStyles'; }); } - let blurHashes = options.imageBlurhashes || item.ImageBlurHashes || {}; + const blurHashes = options.imageBlurhashes || item.ImageBlurHashes || {}; return { imgUrl: imgUrl, @@ -656,7 +656,7 @@ import 'programStyles'; for (let i = 0; i < character.length; i++) { sum += parseInt(character.charAt(i)); } - let index = String(sum).substr(-1); + const index = String(sum).substr(-1); return (index % numRandomColors) + 1; } else { @@ -682,7 +682,7 @@ import 'programStyles'; for (let i = 0; i < lines.length; i++) { let currentCssClass = cssClass; - let text = lines[i]; + const text = lines[i]; if (valid > 0 && isOuterFooter) { currentCssClass += ' cardText-secondary'; @@ -707,7 +707,7 @@ import 'programStyles'; } if (forceLines) { - let linesLength = maxLines || Math.min(lines.length, maxLines || lines.length); + const linesLength = maxLines || Math.min(lines.length, maxLines || lines.length); while (valid < linesLength) { html += "