1
0
Fork 0
mirror of https://github.com/jellyfin/jellyfin-web synced 2025-03-30 19:56:21 +00:00

Merge remote-tracking branch 'upstream/master' into media-session

This commit is contained in:
MrTimscampi 2020-05-01 16:14:54 +02:00
commit 81dffa3a93
189 changed files with 4537 additions and 2301 deletions

View file

@ -0,0 +1,130 @@
define(['dom', 'focusManager'], function (dom, focusManager) {
'use strict';
var inputDisplayElement;
var currentDisplayText = '';
var currentDisplayTextContainer;
function onKeyDown(e) {
if (e.ctrlKey) {
return;
}
if (e.shiftKey) {
return;
}
if (e.altKey) {
return;
}
var key = e.key;
var chr = key ? alphanumeric(key) : null;
if (chr) {
chr = chr.toString().toUpperCase();
if (chr.length === 1) {
currentDisplayTextContainer = this.options.itemsContainer;
onAlphanumericKeyPress(e, chr);
}
}
}
function alphanumeric(value) {
var letterNumber = /^[0-9a-zA-Z]+$/;
return value.match(letterNumber);
}
function ensureInputDisplayElement() {
if (!inputDisplayElement) {
inputDisplayElement = document.createElement('div');
inputDisplayElement.classList.add('alphanumeric-shortcut');
inputDisplayElement.classList.add('hide');
document.body.appendChild(inputDisplayElement);
}
}
var alpanumericShortcutTimeout;
function clearAlphaNumericShortcutTimeout() {
if (alpanumericShortcutTimeout) {
clearTimeout(alpanumericShortcutTimeout);
alpanumericShortcutTimeout = null;
}
}
function resetAlphaNumericShortcutTimeout() {
clearAlphaNumericShortcutTimeout();
alpanumericShortcutTimeout = setTimeout(onAlphanumericShortcutTimeout, 2000);
}
function onAlphanumericKeyPress(e, chr) {
if (currentDisplayText.length >= 3) {
return;
}
ensureInputDisplayElement();
currentDisplayText += chr;
inputDisplayElement.innerHTML = currentDisplayText;
inputDisplayElement.classList.remove('hide');
resetAlphaNumericShortcutTimeout();
}
function onAlphanumericShortcutTimeout() {
var value = currentDisplayText;
var container = currentDisplayTextContainer;
currentDisplayText = '';
currentDisplayTextContainer = null;
inputDisplayElement.innerHTML = '';
inputDisplayElement.classList.add('hide');
clearAlphaNumericShortcutTimeout();
selectByShortcutValue(container, value);
}
function selectByShortcutValue(container, value) {
value = value.toUpperCase();
var focusElem;
if (value === '#') {
focusElem = container.querySelector('*[data-prefix]');
}
if (!focusElem) {
focusElem = container.querySelector('*[data-prefix^=\'' + value + '\']');
}
if (focusElem) {
focusManager.focus(focusElem);
}
}
function AlphaNumericShortcuts(options) {
this.options = options;
var keyDownHandler = onKeyDown.bind(this);
dom.addEventListener(window, 'keydown', keyDownHandler, {
passive: true
});
this.keyDownHandler = keyDownHandler;
}
AlphaNumericShortcuts.prototype.destroy = function () {
var keyDownHandler = this.keyDownHandler;
if (keyDownHandler) {
dom.removeEventListener(window, 'keydown', keyDownHandler, {
passive: true
});
this.keyDownHandler = null;
}
this.options = null;
};
return AlphaNumericShortcuts;
});

View file

@ -29,6 +29,16 @@
);
}
try {
Promise.resolve();
} catch (ex) {
// this checks for several cases actually, typical is
// Promise() being missing on some legacy browser, and a funky one
// is Promise() present but buggy on WebOS 2
window.Promise = undefined;
self.Promise = undefined;
}
if (!self.Promise) {
// Load Promise polyfill if they are not natively supported
injectScriptElement(

View file

@ -22,7 +22,7 @@ define([], function () {
return true;
}
if (userAgent.indexOf('webos') !== -1) {
if (userAgent.indexOf('web0s') !== -1) {
return true;
}
@ -77,10 +77,10 @@ define([], function () {
var camel = prop.replace(/-([a-z]|[0-9])/ig, function (all, letter) {
return (letter + '').toUpperCase();
});
// Check if the property is supported
var support = (camel in el.style);
// Create test element
var el = document.createElement('div');
// Check if the property is supported
var support = (camel in el.style);
// Assign the property and value to invoke
// the CSS interpreter
el.style.cssText = prop + ':' + value;

View file

@ -887,6 +887,16 @@ define(['browser'], function (browser) {
Method: 'External'
});
}
if (options.enableSsaRender) {
profile.SubtitleProfiles.push({
Format: 'ass',
Method: 'External'
});
profile.SubtitleProfiles.push({
Format: 'ssa',
Method: 'External'
});
}
profile.ResponseProfiles = [];
profile.ResponseProfiles.push({

View file

@ -1,62 +1,62 @@
import { ar, be, bg, ca, cs, da, de, el, enGB, enUS, es, faIR, fi, fr, frCA, he, hi, hr, hu, id, it, ja, kk, ko, lt, ms, nb,
nl, pl, ptBR, pt, ro, ru, sk, sl, sv, tr, uk, vi, zhCN, zhTW } from 'date-fns/locale';
import globalize from 'globalize';
const dateLocales = (locale) => ({
'ar': ar,
'be-by': be,
'bg-bg': bg,
'ca': ca,
'cs': cs,
'da': da,
'de': de,
'el': el,
'en-gb': enGB,
'en-us': enUS,
'es': es,
'es-ar': es,
'es-mx': es,
'fa': faIR,
'fi': fi,
'fr': fr,
'fr-ca': frCA,
'gsw': de,
'he': he,
'hi-in': hi,
'hr': hr,
'hu': hu,
'id': id,
'it': it,
'ja': ja,
'kk': kk,
'ko': ko,
'lt-lt': lt,
'ms': ms,
'nb': nb,
'nl': nl,
'pl': pl,
'pt-br': ptBR,
'pt-pt': pt,
'ro': ro,
'ru': ru,
'sk': sk,
'sl-si': sl,
'sv': sv,
'tr': tr,
'uk': uk,
'vi': vi,
'zh-cn': zhCN,
'zh-hk': zhCN,
'zh-tw': zhTW
})[locale];
export function getLocale() {
return dateLocales(globalize.getCurrentLocale()) || enUS;
}
export const localeWithSuffix = { addSuffix: true, locale: getLocale() };
export default {
getLocale: getLocale,
localeWithSuffix: localeWithSuffix
};
import { ar, be, bg, ca, cs, da, de, el, enGB, enUS, es, faIR, fi, fr, frCA, he, hi, hr, hu, id, it, ja, kk, ko, lt, ms, nb,
nl, pl, ptBR, pt, ro, ru, sk, sl, sv, tr, uk, vi, zhCN, zhTW } from 'date-fns/locale';
import globalize from 'globalize';
const dateLocales = (locale) => ({
'ar': ar,
'be-by': be,
'bg-bg': bg,
'ca': ca,
'cs': cs,
'da': da,
'de': de,
'el': el,
'en-gb': enGB,
'en-us': enUS,
'es': es,
'es-ar': es,
'es-mx': es,
'fa': faIR,
'fi': fi,
'fr': fr,
'fr-ca': frCA,
'gsw': de,
'he': he,
'hi-in': hi,
'hr': hr,
'hu': hu,
'id': id,
'it': it,
'ja': ja,
'kk': kk,
'ko': ko,
'lt-lt': lt,
'ms': ms,
'nb': nb,
'nl': nl,
'pl': pl,
'pt-br': ptBR,
'pt-pt': pt,
'ro': ro,
'ru': ru,
'sk': sk,
'sl-si': sl,
'sv': sv,
'tr': tr,
'uk': uk,
'vi': vi,
'zh-cn': zhCN,
'zh-hk': zhCN,
'zh-tw': zhTW
})[locale];
export function getLocale() {
return dateLocales(globalize.getCurrentLocale()) || enUS;
}
export const localeWithSuffix = { addSuffix: true, locale: getLocale() };
export default {
getLocale: getLocale,
localeWithSuffix: localeWithSuffix
};

278
src/scripts/dom.js Normal file
View file

@ -0,0 +1,278 @@
/* eslint-disable indent */
/**
* Useful DOM utilities.
* @module components/dom
*/
/**
* Returns parent of element with specified attribute value.
* @param {HTMLElement} elem - Element whose parent need to find.
* @param {string} name - Attribute name.
* @param {mixed} value - Attribute value.
* @returns {HTMLElement} Parent with specified attribute value.
*/
export function parentWithAttribute(elem, name, value) {
while ((value ? elem.getAttribute(name) !== value : !elem.getAttribute(name))) {
elem = elem.parentNode;
if (!elem || !elem.getAttribute) {
return null;
}
}
return elem;
}
/**
* Returns parent of element with one of specified tag names.
* @param {HTMLElement} elem - Element whose parent need to find.
* @param {(string|Array)} tagNames - Tag name or array of tag names.
* @returns {HTMLElement} Parent with one of specified tag names.
*/
export function parentWithTag(elem, tagNames) {
// accept both string and array passed in
if (!Array.isArray(tagNames)) {
tagNames = [tagNames];
}
while (tagNames.indexOf(elem.tagName || '') === -1) {
elem = elem.parentNode;
if (!elem) {
return null;
}
}
return elem;
}
/**
* Returns _true_ if class list contains one of specified names.
* @param {DOMTokenList} classList - Class list.
* @param {Array} classNames - Array of class names.
* @returns {boolean} _true_ if class list contains one of specified names.
*/
function containsAnyClass(classList, classNames) {
for (let i = 0, length = classNames.length; i < length; i++) {
if (classList.contains(classNames[i])) {
return true;
}
}
return false;
}
/**
* Returns parent of element with one of specified class names.
* @param {HTMLElement} elem - Element whose parent need to find.
* @param {(string|Array)} classNames - Class name or array of class names.
* @returns {HTMLElement} Parent with one of specified class names.
*/
export function parentWithClass(elem, classNames) {
// accept both string and array passed in
if (!Array.isArray(classNames)) {
classNames = [classNames];
}
while (!elem.classList || !containsAnyClass(elem.classList, classNames)) {
elem = elem.parentNode;
if (!elem) {
return null;
}
}
return elem;
}
let supportsCaptureOption = false;
try {
const opts = Object.defineProperty({}, 'capture', {
// eslint-disable-next-line getter-return
get: function () {
supportsCaptureOption = true;
}
});
window.addEventListener("test", null, opts);
} catch (e) {
console.debug('error checking capture support');
}
/**
* Adds event listener to specified target.
* @param {EventTarget} target - Event target.
* @param {string} type - Event type.
* @param {function} handler - Event handler.
* @param {Object} [options] - Listener options.
*/
export function addEventListener(target, type, handler, options) {
let optionsOrCapture = options || {};
if (!supportsCaptureOption) {
optionsOrCapture = optionsOrCapture.capture;
}
target.addEventListener(type, handler, optionsOrCapture);
}
/**
* Removes event listener from specified target.
* @param {EventTarget} target - Event target.
* @param {string} type - Event type.
* @param {function} handler - Event handler.
* @param {Object} [options] - Listener options.
*/
export function removeEventListener(target, type, handler, options) {
let optionsOrCapture = options || {};
if (!supportsCaptureOption) {
optionsOrCapture = optionsOrCapture.capture;
}
target.removeEventListener(type, handler, optionsOrCapture);
}
/**
* Cached window size.
*/
let windowSize;
/**
* Flag of event listener bound.
*/
let windowSizeEventsBound;
/**
* Resets cached window size.
*/
function clearWindowSize() {
windowSize = null;
}
/**
* Returns window size.
* @returns {Object} Window size.
*/
export function getWindowSize() {
if (!windowSize) {
windowSize = {
innerHeight: window.innerHeight,
innerWidth: window.innerWidth
};
if (!windowSizeEventsBound) {
windowSizeEventsBound = true;
addEventListener(window, "orientationchange", clearWindowSize, { passive: true });
addEventListener(window, 'resize', clearWindowSize, { passive: true });
}
}
return windowSize;
}
/**
* Standard screen widths.
*/
const standardWidths = [480, 720, 1280, 1440, 1920, 2560, 3840, 5120, 7680];
/**
* Returns screen width.
* @returns {number} Screen width.
*/
export function getScreenWidth() {
let width = window.innerWidth;
const height = window.innerHeight;
if (height > width) {
width = height * (16.0 / 9.0);
}
const closest = standardWidths.sort(function (a, b) {
return Math.abs(width - a) - Math.abs(width - b);
})[0];
return closest;
}
/**
* Name of animation end event.
*/
let _animationEvent;
/**
* Returns name of animation end event.
* @returns {string} Name of animation end event.
*/
export function whichAnimationEvent() {
if (_animationEvent) {
return _animationEvent;
}
const el = document.createElement("div");
const animations = {
"animation": "animationend",
"OAnimation": "oAnimationEnd",
"MozAnimation": "animationend",
"WebkitAnimation": "webkitAnimationEnd"
};
for (let t in animations) {
if (el.style[t] !== undefined) {
_animationEvent = animations[t];
return animations[t];
}
}
_animationEvent = 'animationend';
return _animationEvent;
}
/**
* Returns name of animation cancel event.
* @returns {string} Name of animation cancel event.
*/
export function whichAnimationCancelEvent() {
return whichAnimationEvent().replace('animationend', 'animationcancel').replace('AnimationEnd', 'AnimationCancel');
}
/**
* Name of transition end event.
*/
let _transitionEvent;
/**
* Returns name of transition end event.
* @returns {string} Name of transition end event.
*/
export function whichTransitionEvent() {
if (_transitionEvent) {
return _transitionEvent;
}
const el = document.createElement("div");
const transitions = {
"transition": "transitionend",
"OTransition": "oTransitionEnd",
"MozTransition": "transitionend",
"WebkitTransition": "webkitTransitionEnd"
};
for (let t in transitions) {
if (el.style[t] !== undefined) {
_transitionEvent = transitions[t];
return transitions[t];
}
}
_transitionEvent = 'transitionend';
return _transitionEvent;
}
/* eslint-enable indent */
export default {
parentWithAttribute: parentWithAttribute,
parentWithClass: parentWithClass,
parentWithTag: parentWithTag,
addEventListener: addEventListener,
removeEventListener: removeEventListener,
getWindowSize: getWindowSize,
getScreenWidth: getScreenWidth,
whichTransitionEvent: whichTransitionEvent,
whichAnimationEvent: whichAnimationEvent,
whichAnimationCancelEvent: whichAnimationCancelEvent
};

View file

@ -1,4 +1,4 @@
define(["datetime", "jQuery", "material-icons"], function (datetime, $) {
define(["datetime", "jQuery", "globalize", "material-icons"], function (datetime, $, globalize) {
"use strict";
function getNode(item, folderState, selected) {
@ -70,7 +70,7 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) {
var nodes = [];
nodes.push({
id: "MediaFolders",
text: Globalize.translate("HeaderMediaFolders"),
text: globalize.translate("HeaderMediaFolders"),
state: {
opened: true
},
@ -83,7 +83,7 @@ define(["datetime", "jQuery", "material-icons"], function (datetime, $) {
if (result.TotalRecordCount) {
nodes.push({
id: "livetv",
text: Globalize.translate("HeaderLiveTV"),
text: globalize.translate("HeaderLiveTV"),
state: {
opened: false
},

13
src/scripts/filesystem.js Normal file
View file

@ -0,0 +1,13 @@
export function fileExists(path) {
if (window.NativeShell && window.NativeShell.FileSystem) {
return window.NativeShell.FileSystem.fileExists(path);
}
return Promise.reject();
}
export function directoryExists(path) {
if (window.NativeShell && window.NativeShell.FileSystem) {
return window.NativeShell.FileSystem.directoryExists(path);
}
return Promise.reject();
}

410
src/scripts/gamepadtokey.js Normal file
View file

@ -0,0 +1,410 @@
// # The MIT License (MIT)
// #
// # Copyright (c) 2016 Microsoft. All rights reserved.
// #
// # Permission is hereby granted, free of charge, to any person obtaining a copy
// # of this software and associated documentation files (the "Software"), to deal
// # in the Software without restriction, including without limitation the rights
// # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// # copies of the Software, and to permit persons to whom the Software is
// # furnished to do so, subject to the following conditions:
// #
// # The above copyright notice and this permission notice shall be included in
// # all copies or substantial portions of the Software.
// #
// # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// # THE SOFTWARE.
require(['apphost'], function (appHost) {
"use strict";
var _GAMEPAD_A_BUTTON_INDEX = 0;
var _GAMEPAD_B_BUTTON_INDEX = 1;
var _GAMEPAD_DPAD_UP_BUTTON_INDEX = 12;
var _GAMEPAD_DPAD_DOWN_BUTTON_INDEX = 13;
var _GAMEPAD_DPAD_LEFT_BUTTON_INDEX = 14;
var _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX = 15;
var _GAMEPAD_A_KEY = "GamepadA";
var _GAMEPAD_B_KEY = "GamepadB";
var _GAMEPAD_DPAD_UP_KEY = "GamepadDPadUp";
var _GAMEPAD_DPAD_DOWN_KEY = "GamepadDPadDown";
var _GAMEPAD_DPAD_LEFT_KEY = "GamepadDPadLeft";
var _GAMEPAD_DPAD_RIGHT_KEY = "GamepadDPadRight";
var _GAMEPAD_LEFT_THUMBSTICK_UP_KEY = "GamepadLeftThumbStickUp";
var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY = "GamepadLeftThumbStickDown";
var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY = "GamepadLeftThumbStickLeft";
var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY = "GamepadLeftThumbStickRight";
var _GAMEPAD_A_KEYCODE = 0;
var _GAMEPAD_B_KEYCODE = 27;
var _GAMEPAD_DPAD_UP_KEYCODE = 38;
var _GAMEPAD_DPAD_DOWN_KEYCODE = 40;
var _GAMEPAD_DPAD_LEFT_KEYCODE = 37;
var _GAMEPAD_DPAD_RIGHT_KEYCODE = 39;
var _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE = 38;
var _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE = 40;
var _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE = 37;
var _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE = 39;
var _THUMB_STICK_THRESHOLD = 0.75;
var _leftThumbstickUpPressed = false;
var _leftThumbstickDownPressed = false;
var _leftThumbstickLeftPressed = false;
var _leftThumbstickRightPressed = false;
var _dPadUpPressed = false;
var _dPadDownPressed = false;
var _dPadLeftPressed = false;
var _dPadRightPressed = false;
var _gamepadAPressed = false;
var _gamepadBPressed = false;
// The set of buttons on the gamepad we listen for.
var ProcessedButtons = [
_GAMEPAD_DPAD_UP_BUTTON_INDEX,
_GAMEPAD_DPAD_DOWN_BUTTON_INDEX,
_GAMEPAD_DPAD_LEFT_BUTTON_INDEX,
_GAMEPAD_DPAD_RIGHT_BUTTON_INDEX,
_GAMEPAD_A_BUTTON_INDEX,
_GAMEPAD_B_BUTTON_INDEX
];
var _ButtonPressedState = {};
_ButtonPressedState.getgamepadA = function () {
return _gamepadAPressed;
};
_ButtonPressedState.setgamepadA = function (newPressedState) {
raiseKeyEvent(_gamepadAPressed, newPressedState, _GAMEPAD_A_KEY, _GAMEPAD_A_KEYCODE, false, true);
_gamepadAPressed = newPressedState;
};
_ButtonPressedState.getgamepadB = function () {
return _gamepadBPressed;
};
_ButtonPressedState.setgamepadB = function (newPressedState) {
raiseKeyEvent(_gamepadBPressed, newPressedState, _GAMEPAD_B_KEY, _GAMEPAD_B_KEYCODE);
_gamepadBPressed = newPressedState;
};
_ButtonPressedState.getleftThumbstickUp = function () {
return _leftThumbstickUpPressed;
};
_ButtonPressedState.setleftThumbstickUp = function (newPressedState) {
raiseKeyEvent(_leftThumbstickUpPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_UP_KEY, _GAMEPAD_LEFT_THUMBSTICK_UP_KEYCODE, true);
_leftThumbstickUpPressed = newPressedState;
};
_ButtonPressedState.getleftThumbstickDown = function () {
return _leftThumbstickDownPressed;
};
_ButtonPressedState.setleftThumbstickDown = function (newPressedState) {
raiseKeyEvent(_leftThumbstickDownPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEY, _GAMEPAD_LEFT_THUMBSTICK_DOWN_KEYCODE, true);
_leftThumbstickDownPressed = newPressedState;
};
_ButtonPressedState.getleftThumbstickLeft = function () {
return _leftThumbstickLeftPressed;
};
_ButtonPressedState.setleftThumbstickLeft = function (newPressedState) {
raiseKeyEvent(_leftThumbstickLeftPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEY, _GAMEPAD_LEFT_THUMBSTICK_LEFT_KEYCODE, true);
_leftThumbstickLeftPressed = newPressedState;
};
_ButtonPressedState.getleftThumbstickRight = function () {
return _leftThumbstickRightPressed;
};
_ButtonPressedState.setleftThumbstickRight = function (newPressedState) {
raiseKeyEvent(_leftThumbstickRightPressed, newPressedState, _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEY, _GAMEPAD_LEFT_THUMBSTICK_RIGHT_KEYCODE, true);
_leftThumbstickRightPressed = newPressedState;
};
_ButtonPressedState.getdPadUp = function () {
return _dPadUpPressed;
};
_ButtonPressedState.setdPadUp = function (newPressedState) {
raiseKeyEvent(_dPadUpPressed, newPressedState, _GAMEPAD_DPAD_UP_KEY, _GAMEPAD_DPAD_UP_KEYCODE, true);
_dPadUpPressed = newPressedState;
};
_ButtonPressedState.getdPadDown = function () {
return _dPadDownPressed;
};
_ButtonPressedState.setdPadDown = function (newPressedState) {
raiseKeyEvent(_dPadDownPressed, newPressedState, _GAMEPAD_DPAD_DOWN_KEY, _GAMEPAD_DPAD_DOWN_KEYCODE, true);
_dPadDownPressed = newPressedState;
};
_ButtonPressedState.getdPadLeft = function () {
return _dPadLeftPressed;
};
_ButtonPressedState.setdPadLeft = function (newPressedState) {
raiseKeyEvent(_dPadLeftPressed, newPressedState, _GAMEPAD_DPAD_LEFT_KEY, _GAMEPAD_DPAD_LEFT_KEYCODE, true);
_dPadLeftPressed = newPressedState;
};
_ButtonPressedState.getdPadRight = function () {
return _dPadRightPressed;
};
_ButtonPressedState.setdPadRight = function (newPressedState) {
raiseKeyEvent(_dPadRightPressed, newPressedState, _GAMEPAD_DPAD_RIGHT_KEY, _GAMEPAD_DPAD_RIGHT_KEYCODE, true);
_dPadRightPressed = newPressedState;
};
var times = {};
function throttle(key) {
var time = times[key] || 0;
var now = new Date().getTime();
if ((now - time) >= 200) {
//times[key] = now;
return true;
}
return false;
}
function resetThrottle(key) {
times[key] = new Date().getTime();
}
var isElectron = navigator.userAgent.toLowerCase().indexOf('electron') !== -1;
function allowInput() {
// This would be nice but always seems to return true with electron
if (!isElectron && document.hidden) { /* eslint-disable-line compat/compat */
return false;
}
if (appHost.getWindowState() === 'Minimized') {
return false;
}
return true;
}
function raiseEvent(name, key, keyCode) {
if (!allowInput()) {
return;
}
var event = document.createEvent('Event');
event.initEvent(name, true, true);
event.key = key;
event.keyCode = keyCode;
(document.activeElement || document.body).dispatchEvent(event);
}
function clickElement(elem) {
if (!allowInput()) {
return;
}
elem.click();
}
function raiseKeyEvent(oldPressedState, newPressedState, key, keyCode, enableRepeatKeyDown, clickonKeyUp) {
// No-op if oldPressedState === newPressedState
if (newPressedState === true) {
// button down
var fire = false;
// always fire if this is the initial down press
if (oldPressedState === false) {
fire = true;
resetThrottle(key);
} else if (enableRepeatKeyDown) {
fire = throttle(key);
}
if (fire && keyCode) {
raiseEvent("keydown", key, keyCode);
}
} else if (newPressedState === false && oldPressedState === true) {
resetThrottle(key);
// button up
if (keyCode) {
raiseEvent("keyup", key, keyCode);
}
if (clickonKeyUp) {
clickElement(document.activeElement || window);
}
}
}
var inputLoopTimer;
function runInputLoop() {
// Get the latest gamepad state.
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
for (var i = 0, len = gamepads.length; i < len; i++) {
var gamepad = gamepads[i];
if (!gamepad) {
continue;
}
// Iterate through the axes
var axes = gamepad.axes;
var leftStickX = axes[0];
var leftStickY = axes[1];
if (leftStickX > _THUMB_STICK_THRESHOLD) { // Right
_ButtonPressedState.setleftThumbstickRight(true);
} else if (leftStickX < -_THUMB_STICK_THRESHOLD) { // Left
_ButtonPressedState.setleftThumbstickLeft(true);
} else if (leftStickY < -_THUMB_STICK_THRESHOLD) { // Up
_ButtonPressedState.setleftThumbstickUp(true);
} else if (leftStickY > _THUMB_STICK_THRESHOLD) { // Down
_ButtonPressedState.setleftThumbstickDown(true);
} else {
_ButtonPressedState.setleftThumbstickLeft(false);
_ButtonPressedState.setleftThumbstickRight(false);
_ButtonPressedState.setleftThumbstickUp(false);
_ButtonPressedState.setleftThumbstickDown(false);
}
// Iterate through the buttons to see if Left thumbstick, DPad, A and B are pressed.
var buttons = gamepad.buttons;
for (var j = 0, len = buttons.length; j < len; j++) {
if (ProcessedButtons.indexOf(j) !== -1) {
if (buttons[j].pressed) {
switch (j) {
case _GAMEPAD_DPAD_UP_BUTTON_INDEX:
_ButtonPressedState.setdPadUp(true);
break;
case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX:
_ButtonPressedState.setdPadDown(true);
break;
case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX:
_ButtonPressedState.setdPadLeft(true);
break;
case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX:
_ButtonPressedState.setdPadRight(true);
break;
case _GAMEPAD_A_BUTTON_INDEX:
_ButtonPressedState.setgamepadA(true);
break;
case _GAMEPAD_B_BUTTON_INDEX:
_ButtonPressedState.setgamepadB(true);
break;
default:
// No-op
break;
}
} else {
switch (j) {
case _GAMEPAD_DPAD_UP_BUTTON_INDEX:
if (_ButtonPressedState.getdPadUp()) {
_ButtonPressedState.setdPadUp(false);
}
break;
case _GAMEPAD_DPAD_DOWN_BUTTON_INDEX:
if (_ButtonPressedState.getdPadDown()) {
_ButtonPressedState.setdPadDown(false);
}
break;
case _GAMEPAD_DPAD_LEFT_BUTTON_INDEX:
if (_ButtonPressedState.getdPadLeft()) {
_ButtonPressedState.setdPadLeft(false);
}
break;
case _GAMEPAD_DPAD_RIGHT_BUTTON_INDEX:
if (_ButtonPressedState.getdPadRight()) {
_ButtonPressedState.setdPadRight(false);
}
break;
case _GAMEPAD_A_BUTTON_INDEX:
if (_ButtonPressedState.getgamepadA()) {
_ButtonPressedState.setgamepadA(false);
}
break;
case _GAMEPAD_B_BUTTON_INDEX:
if (_ButtonPressedState.getgamepadB()) {
_ButtonPressedState.setgamepadB(false);
}
break;
default:
// No-op
break;
}
}
}
}
}
// Schedule the next one
inputLoopTimer = requestAnimationFrame(runInputLoop);
}
function startInputLoop() {
if (!inputLoopTimer) {
runInputLoop();
}
}
function stopInputLoop() {
cancelAnimationFrame(inputLoopTimer);
inputLoopTimer = undefined;
}
function isGamepadConnected() {
var gamepads = navigator.getGamepads(); /* eslint-disable-line compat/compat */
for (var i = 0, len = gamepads.length; i < len; i++) {
var gamepad = gamepads[i];
if (gamepad && gamepad.connected) {
return true;
}
}
return false;
}
function onFocusOrGamepadAttach(e) {
/* eslint-disable-next-line compat/compat */
if (isGamepadConnected() && document.hasFocus()) {
console.log("Gamepad connected! Starting input loop");
startInputLoop();
}
}
function onFocusOrGamepadDetach(e) {
/* eslint-disable-next-line compat/compat */
if (!isGamepadConnected() || !document.hasFocus()) {
console.log("Gamepad disconnected! No other gamepads are connected, stopping input loop");
stopInputLoop();
} else {
console.log("Gamepad disconnected! There are gamepads still connected.");
}
}
// Event listeners for any change in gamepads' state.
window.addEventListener("gamepaddisconnected", onFocusOrGamepadDetach);
window.addEventListener("gamepadconnected", onFocusOrGamepadAttach);
window.addEventListener("blur", onFocusOrGamepadDetach);
window.addEventListener("focus", onFocusOrGamepadAttach);
onFocusOrGamepadAttach();
// The gamepadInputEmulation is a string property that exists in JavaScript UWAs and in WebViews in UWAs.
// It won't exist in Win8.1 style apps or browsers.
if (window.navigator && typeof window.navigator.gamepadInputEmulation === "string") {
// We want the gamepad to provide gamepad VK keyboard events rather than moving a
// mouse like cursor. Set to "keyboard", the gamepad will provide such keyboard events
// and provide input to the DOM navigator.getGamepads API.
window.navigator.gamepadInputEmulation = "gamepad";
}
});

View file

@ -14,6 +14,7 @@ import browser from 'browser';
case "Kodi":
return baseUrl + "kodi.svg";
case "Jellyfin Android":
case "Android TV":
return baseUrl + "android.svg";
case "Jellyfin Web":
switch (device.Name || device.DeviceName) {

View file

@ -235,6 +235,9 @@ import appHost from 'apphost';
}
}
// Alias for backward compatibility
export const trigger = handleCommand;
dom.addEventListener(document, 'click', notify, {
passive: true
});

View file

@ -1,4 +1,4 @@
define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryBrowser", "emby-itemscontainer", "emby-button"], function (connectionManager, listView, cardBuilder, imageLoader, libraryBrowser) {
define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryBrowser", "globalize", "emby-itemscontainer", "emby-button"], function (connectionManager, listView, cardBuilder, imageLoader, libraryBrowser, globalize) {
"use strict";
function renderItems(page, item) {
@ -6,56 +6,56 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB
if (item.ArtistCount) {
sections.push({
name: Globalize.translate("TabArtists"),
name: globalize.translate("TabArtists"),
type: "MusicArtist"
});
}
if (item.ProgramCount && "Person" == item.Type) {
sections.push({
name: Globalize.translate("HeaderUpcomingOnTV"),
name: globalize.translate("HeaderUpcomingOnTV"),
type: "Program"
});
}
if (item.MovieCount) {
sections.push({
name: Globalize.translate("TabMovies"),
name: globalize.translate("TabMovies"),
type: "Movie"
});
}
if (item.SeriesCount) {
sections.push({
name: Globalize.translate("TabShows"),
name: globalize.translate("TabShows"),
type: "Series"
});
}
if (item.EpisodeCount) {
sections.push({
name: Globalize.translate("TabEpisodes"),
name: globalize.translate("TabEpisodes"),
type: "Episode"
});
}
if (item.TrailerCount) {
sections.push({
name: Globalize.translate("TabTrailers"),
name: globalize.translate("TabTrailers"),
type: "Trailer"
});
}
if (item.AlbumCount) {
sections.push({
name: Globalize.translate("TabAlbums"),
name: globalize.translate("TabAlbums"),
type: "MusicAlbum"
});
}
if (item.MusicVideoCount) {
sections.push({
name: Globalize.translate("TabMusicVideos"),
name: globalize.translate("TabMusicVideos"),
type: "MusicVideo"
});
}
@ -74,7 +74,7 @@ define(["connectionManager", "listView", "cardBuilder", "imageLoader", "libraryB
html += '<h2 class="sectionTitle sectionTitle-cards padded-left">';
html += section.name;
html += "</h2>";
html += '<a is="emby-linkbutton" href="#" class="clearLink hide" style="margin-left:1em;vertical-align:middle;"><button is="emby-button" type="button" class="raised more raised-mini noIcon">' + Globalize.translate("ButtonMore") + "</button></a>";
html += '<a is="emby-linkbutton" href="#" class="clearLink hide" style="margin-left:1em;vertical-align:middle;"><button is="emby-button" type="button" class="raised more raised-mini noIcon">' + globalize.translate("ButtonMore") + "</button></a>";
html += "</div>";
html += '<div is="emby-itemscontainer" class="itemsContainer padded-left padded-right">';
html += "</div>";

View file

@ -0,0 +1,170 @@
/**
* Module for performing keyboard navigation.
* @module components/input/keyboardnavigation
*/
import inputManager from "inputManager";
import layoutManager from "layoutManager";
/**
* Key name mapping.
*/
const KeyNames = {
13: "Enter",
19: "Pause",
27: "Escape",
32: "Space",
37: "ArrowLeft",
38: "ArrowUp",
39: "ArrowRight",
40: "ArrowDown",
// MediaRewind (Tizen/WebOS)
412: "MediaRewind",
// MediaStop (Tizen/WebOS)
413: "MediaStop",
// MediaPlay (Tizen/WebOS)
415: "MediaPlay",
// MediaFastForward (Tizen/WebOS)
417: "MediaFastForward",
// Back (WebOS)
461: "Back",
// Back (Tizen)
10009: "Back",
// MediaTrackPrevious (Tizen)
10232: "MediaTrackPrevious",
// MediaTrackNext (Tizen)
10233: "MediaTrackNext",
// MediaPlayPause (Tizen)
10252: "MediaPlayPause"
};
/**
* Keys used for keyboard navigation.
*/
const NavigationKeys = ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"];
let hasFieldKey = false;
try {
hasFieldKey = "key" in new KeyboardEvent("keydown");
} catch (e) {
console.error("error checking 'key' field");
}
if (!hasFieldKey) {
// Add [a..z]
for (let i = 65; i <= 90; i++) {
KeyNames[i] = String.fromCharCode(i).toLowerCase();
}
}
/**
* Returns key name from event.
*
* @param {KeyboardEvent} event - Keyboard event.
* @return {string} Key name.
*/
export function getKeyName(event) {
return KeyNames[event.keyCode] || event.key;
}
/**
* Returns _true_ if key is used for navigation.
*
* @param {string} key - Key name.
* @return {boolean} _true_ if key is used for navigation.
*/
export function isNavigationKey(key) {
return NavigationKeys.indexOf(key) != -1;
}
export function enable() {
document.addEventListener("keydown", function (e) {
const key = getKeyName(e);
// Ignore navigation keys for non-TV
if (!layoutManager.tv && isNavigationKey(key)) {
return;
}
let capture = true;
switch (key) {
case "ArrowLeft":
inputManager.handle("left");
break;
case "ArrowUp":
inputManager.handle("up");
break;
case "ArrowRight":
inputManager.handle("right");
break;
case "ArrowDown":
inputManager.handle("down");
break;
case "Back":
inputManager.handle("back");
break;
case "Escape":
if (layoutManager.tv) {
inputManager.handle("back");
} else {
capture = false;
}
break;
case "MediaPlay":
inputManager.handle("play");
break;
case "Pause":
inputManager.handle("pause");
break;
case "MediaPlayPause":
inputManager.handle("playpause");
break;
case "MediaRewind":
inputManager.handle("rewind");
break;
case "MediaFastForward":
inputManager.handle("fastforward");
break;
case "MediaStop":
inputManager.handle("stop");
break;
case "MediaTrackPrevious":
inputManager.handle("previoustrack");
break;
case "MediaTrackNext":
inputManager.handle("nexttrack");
break;
default:
capture = false;
}
if (capture) {
console.debug("disabling default event handling");
e.preventDefault();
}
});
}
// Gamepad initialisation. No script is required if no gamepads are present at init time, saving a bit of resources.
// Whenever the gamepad is connected, we hand all the control of the gamepad to gamepadtokey.js by removing the event handler
function attachGamepadScript(e) {
console.log("Gamepad connected! Attaching gamepadtokey.js script");
window.removeEventListener("gamepadconnected", attachGamepadScript);
require(["scripts/gamepadtokey"]);
}
// No need to check for gamepads manually at load time, the eventhandler will be fired for that
if (navigator.getGamepads) { /* eslint-disable-line compat/compat */
window.addEventListener("gamepadconnected", attachGamepadScript);
}
export default {
enable: enable,
getKeyName: getKeyName,
isNavigationKey: isNavigationKey
};

View file

@ -1,4 +1,4 @@
define(["userSettings"], function (userSettings) {
define(["userSettings", "globalize"], function (userSettings, globalize) {
"use strict";
var libraryBrowser = {
@ -45,7 +45,7 @@ define(["userSettings"], function (userSettings) {
var menuItems = views.map(function (v) {
return {
name: Globalize.translate("Option" + v),
name: globalize.translate("Option" + v),
id: v,
selected: currentLayout == v
};
@ -83,7 +83,7 @@ define(["userSettings"], function (userSettings) {
if (html += '<div class="listPaging">', showControls) {
html += '<span style="vertical-align:middle;">';
html += Globalize.translate("ListPaging", (totalRecordCount ? startIndex + 1 : 0), recordsEnd, totalRecordCount);
html += globalize.translate("ListPaging", (totalRecordCount ? startIndex + 1 : 0), recordsEnd, totalRecordCount);
html += "</span>";
}
@ -96,15 +96,15 @@ define(["userSettings"], function (userSettings) {
}
if (options.addLayoutButton) {
html += '<button is="paper-icon-button-light" title="' + Globalize.translate("ButtonSelectView") + '" class="btnChangeLayout autoSize" data-layouts="' + (options.layouts || "") + '" onclick="LibraryBrowser.showLayoutMenu(this, \'' + (options.currentLayout || "") + '\');"><i class="material-icons view_comfy"></i></button>';
html += '<button is="paper-icon-button-light" title="' + globalize.translate("ButtonSelectView") + '" class="btnChangeLayout autoSize" data-layouts="' + (options.layouts || "") + '" onclick="LibraryBrowser.showLayoutMenu(this, \'' + (options.currentLayout || "") + '\');"><i class="material-icons view_comfy"></i></button>';
}
if (options.sortButton) {
html += '<button is="paper-icon-button-light" class="btnSort autoSize" title="' + Globalize.translate("ButtonSort") + '"><i class="material-icons sort_by_alpha"></i></button>';
html += '<button is="paper-icon-button-light" class="btnSort autoSize" title="' + globalize.translate("ButtonSort") + '"><i class="material-icons sort_by_alpha"></i></button>';
}
if (options.filterButton) {
html += '<button is="paper-icon-button-light" class="btnFilter autoSize" title="' + Globalize.translate("ButtonFilter") + '"><i class="material-icons filter_list"></i></button>';
html += '<button is="paper-icon-button-light" class="btnFilter autoSize" title="' + globalize.translate("ButtonFilter") + '"><i class="material-icons filter_list"></i></button>';
}
html += "</div>";
@ -154,7 +154,7 @@ define(["userSettings"], function (userSettings) {
var html = "";
html += '<div style="margin:0;padding:1.25em 1.5em 1.5em;">';
html += '<h2 style="margin:0 0 .5em;">';
html += Globalize.translate("HeaderSortBy");
html += globalize.translate("HeaderSortBy");
html += "</h2>";
var i;
var length;
@ -169,13 +169,13 @@ define(["userSettings"], function (userSettings) {
html += "</div>";
html += '<h2 style="margin: 1em 0 .5em;">';
html += Globalize.translate("HeaderSortOrder");
html += globalize.translate("HeaderSortOrder");
html += "</h2>";
html += "<div>";
isChecked = "Ascending" == options.query.SortOrder ? " checked" : "";
html += '<label class="radio-label-block"><input type="radio" is="emby-radio" name="SortOrder" value="Ascending" class="menuSortOrder" ' + isChecked + " /><span>" + Globalize.translate("OptionAscending") + "</span></label>";
html += '<label class="radio-label-block"><input type="radio" is="emby-radio" name="SortOrder" value="Ascending" class="menuSortOrder" ' + isChecked + " /><span>" + globalize.translate("OptionAscending") + "</span></label>";
isChecked = "Descending" == options.query.SortOrder ? " checked" : "";
html += '<label class="radio-label-block"><input type="radio" is="emby-radio" name="SortOrder" value="Descending" class="menuSortOrder" ' + isChecked + " /><span>" + Globalize.translate("OptionDescending") + "</span></label>";
html += '<label class="radio-label-block"><input type="radio" is="emby-radio" name="SortOrder" value="Descending" class="menuSortOrder" ' + isChecked + " /><span>" + globalize.translate("OptionDescending") + "</span></label>";
html += "</div>";
html += "</div>";
dlg.innerHTML = html;

View file

@ -73,7 +73,7 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", "
}
if (user && user.localUser) {
if (headerHomeButton && !layoutManager.mobile) {
if (headerHomeButton) {
headerHomeButton.classList.remove("hide");
}
@ -733,8 +733,8 @@ define(["dom", "layoutManager", "inputManager", "connectionManager", "events", "
function initHeadRoom(elem) {
require(["headroom"], function (Headroom) {
var headroom = new Headroom([], {});
headroom.add(elem);
var headroom = new Headroom(elem);
headroom.init();
});
}

169
src/scripts/mouseManager.js Normal file
View file

@ -0,0 +1,169 @@
define(['inputManager', 'focusManager', 'browser', 'layoutManager', 'events', 'dom'], function (inputManager, focusManager, browser, layoutManager, events, dom) {
'use strict';
var self = {};
var lastMouseInputTime = new Date().getTime();
var isMouseIdle;
function mouseIdleTime() {
return new Date().getTime() - lastMouseInputTime;
}
function notifyApp() {
inputManager.notifyMouseMove();
}
function removeIdleClasses() {
var classList = document.body.classList;
classList.remove('mouseIdle');
classList.remove('mouseIdle-tv');
}
function addIdleClasses() {
var classList = document.body.classList;
classList.add('mouseIdle');
if (layoutManager.tv) {
classList.add('mouseIdle-tv');
}
}
var lastPointerMoveData;
function onPointerMove(e) {
var eventX = e.screenX;
var eventY = e.screenY;
// if coord don't exist how could it move
if (typeof eventX === "undefined" && typeof eventY === "undefined") {
return;
}
var obj = lastPointerMoveData;
if (!obj) {
lastPointerMoveData = {
x: eventX,
y: eventY
};
return;
}
// if coord are same, it didn't move
if (Math.abs(eventX - obj.x) < 10 && Math.abs(eventY - obj.y) < 10) {
return;
}
obj.x = eventX;
obj.y = eventY;
lastMouseInputTime = new Date().getTime();
notifyApp();
if (isMouseIdle) {
isMouseIdle = false;
removeIdleClasses();
events.trigger(self, 'mouseactive');
}
}
function onPointerEnter(e) {
var pointerType = e.pointerType || (layoutManager.mobile ? 'touch' : 'mouse');
if (pointerType === 'mouse') {
if (!isMouseIdle) {
var parent = focusManager.focusableParent(e.target);
if (parent) {
focusManager.focus(parent);
}
}
}
}
function enableFocusWithMouse() {
if (!layoutManager.tv) {
return false;
}
if (browser.web0s) {
return false;
}
if (browser.tv) {
return true;
}
return false;
}
function onMouseInterval() {
if (!isMouseIdle && mouseIdleTime() >= 5000) {
isMouseIdle = true;
addIdleClasses();
events.trigger(self, 'mouseidle');
}
}
var mouseInterval;
function startMouseInterval() {
if (!mouseInterval) {
mouseInterval = setInterval(onMouseInterval, 5000);
}
}
function stopMouseInterval() {
var interval = mouseInterval;
if (interval) {
clearInterval(interval);
mouseInterval = null;
}
removeIdleClasses();
}
function initMouse() {
stopMouseInterval();
dom.removeEventListener(document, (window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove, {
passive: true
});
if (!layoutManager.mobile) {
startMouseInterval();
dom.addEventListener(document, (window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove, {
passive: true
});
}
dom.removeEventListener(document, (window.PointerEvent ? 'pointerenter' : 'mouseenter'), onPointerEnter, {
capture: true,
passive: true
});
if (enableFocusWithMouse()) {
dom.addEventListener(document, (window.PointerEvent ? 'pointerenter' : 'mouseenter'), onPointerEnter, {
capture: true,
passive: true
});
}
}
initMouse();
events.on(layoutManager, 'modechange', initMouse);
return self;
});

View file

@ -23,6 +23,35 @@ define([
console.debug("defining core routes");
defineRoute({
path: "/addserver.html",
autoFocus: false,
anonymous: true,
startup: true,
controller: "auth/addserver"
});
defineRoute({
path: "/selectserver.html",
autoFocus: false,
anonymous: true,
startup: true,
controller: "auth/selectserver",
type: "selectserver"
});
defineRoute({
path: "/forgotpassword.html",
anonymous: true,
startup: true,
controller: "auth/forgotpassword"
});
defineRoute({
path: "/forgotpasswordpin.html",
autoFocus: false,
anonymous: true,
startup: true,
controller: "auth/forgotpasswordpin"
});
defineRoute({
path: "/addplugin.html",
autoFocus: false,
@ -41,13 +70,6 @@ define([
transition: "fade",
controller: "user/profile"
});
defineRoute({
path: "/addserver.html",
autoFocus: false,
anonymous: true,
startup: true,
controller: "auth/addserver"
});
defineRoute({
path: "/mypreferencesdisplay.html",
autoFocus: false,
@ -95,31 +117,31 @@ define([
path: "/devices.html",
autoFocus: false,
roles: "admin",
controller: "devices"
controller: "dashboard/devices/devices"
});
defineRoute({
path: "/device.html",
autoFocus: false,
roles: "admin",
controller: "device"
controller: "dashboard/devices/device"
});
defineRoute({
path: "/dlnaprofile.html",
autoFocus: false,
roles: "admin",
controller: "dlnaprofile"
controller: "dashboard/dlna/dlnaprofile"
});
defineRoute({
path: "/dlnaprofiles.html",
autoFocus: false,
roles: "admin",
controller: "dlnaprofiles"
controller: "dashboard/dlna/dlnaprofiles"
});
defineRoute({
path: "/dlnasettings.html",
autoFocus: false,
roles: "admin",
controller: "dlnasettings"
controller: "dashboard/dlna/dlnasettings"
});
defineRoute({
path: "/edititemmetadata.html",
@ -130,20 +152,7 @@ define([
path: "/encodingsettings.html",
autoFocus: false,
roles: "admin",
controller: "encodingsettings"
});
defineRoute({
path: "/forgotpassword.html",
anonymous: true,
startup: true,
controller: "auth/forgotpassword"
});
defineRoute({
path: "/forgotpasswordpin.html",
autoFocus: false,
anonymous: true,
startup: true,
controller: "auth/forgotpasswordpin"
controller: "dashboard/encodingsettings"
});
defineRoute({
path: "/home.html",
@ -158,11 +167,6 @@ define([
controller: "list",
transition: "fade"
});
defineRoute({
path: "/index.html",
autoFocus: false,
isDefaultRoute: true
});
defineRoute({
path: "/itemdetails.html",
controller: "itemdetailpage",
@ -173,19 +177,13 @@ define([
path: "/library.html",
autoFocus: false,
roles: "admin",
controller: "medialibrarypage"
controller: "dashboard/medialibrarypage"
});
defineRoute({
path: "/librarydisplay.html",
autoFocus: false,
roles: "admin",
controller: "librarydisplay"
});
defineRoute({
path: "/librarysettings.html",
autoFocus: false,
roles: "admin",
controller: "librarysettings"
controller: "dashboard/librarydisplay"
});
defineRoute({
path: "/livetv.html",
@ -233,13 +231,13 @@ define([
path: "/metadataimages.html",
autoFocus: false,
roles: "admin",
controller: "metadataimagespage"
controller: "dashboard/metadataimagespage"
});
defineRoute({
path: "/metadatanfo.html",
autoFocus: false,
roles: "admin",
controller: "metadatanfo"
controller: "dashboard/metadatanfo"
});
defineRoute({
path: "/movies.html",
@ -265,20 +263,11 @@ define([
autoFocus: false,
roles: "admin"
});
defineRoute({
path: "/nowplaying.html",
controller: "playback/nowplaying",
autoFocus: false,
transition: "fade",
fullscreen: true,
supportsThemeMedia: true,
enableMediaControl: false
});
defineRoute({
path: "/playbackconfiguration.html",
autoFocus: false,
roles: "admin",
controller: "playbackconfiguration"
controller: "dashboard/playbackconfiguration"
});
defineRoute({
path: "/availableplugins.html",
@ -308,31 +297,23 @@ define([
path: "/search.html",
controller: "searchpage"
});
defineRoute({
path: "/selectserver.html",
autoFocus: false,
anonymous: true,
startup: true,
controller: "auth/selectserver",
type: "selectserver"
});
defineRoute({
path: "/serveractivity.html",
autoFocus: false,
roles: "admin",
controller: "serveractivity"
controller: "dashboard/serveractivity"
});
defineRoute({
path: "/apikeys.html",
autoFocus: false,
roles: "admin",
controller: "apikeys"
controller: "dashboard/apikeys"
});
defineRoute({
path: "/streamingsettings.html",
autoFocus: false,
roles: "admin",
controller: "streamingsettings"
controller: "dashboard/streamingsettings"
});
defineRoute({
path: "/tv.html",
@ -423,6 +404,15 @@ define([
fullscreen: true,
enableMediaControl: false
});
defineRoute({
path: "/nowplaying.html",
controller: "playback/nowplaying",
autoFocus: false,
transition: "fade",
fullscreen: true,
supportsThemeMedia: true,
enableMediaControl: false
});
defineRoute({
path: "/configurationpage",
autoFocus: false,
@ -436,4 +426,9 @@ define([
isDefaultRoute: true,
autoFocus: false
});
defineRoute({
path: "/index.html",
autoFocus: false,
isDefaultRoute: true
});
});

View file

@ -12,7 +12,7 @@ import events from 'events';
}
export function enableAutoLogin(val) {
if (val != null) {
if (val !== undefined) {
this.set('enableAutoLogin', val.toString());
}
@ -20,7 +20,7 @@ import events from 'events';
}
export function enableSystemExternalPlayers(val) {
if (val !== null) {
if (val !== undefined) {
this.set('enableSystemExternalPlayers', val.toString());
}
@ -29,7 +29,7 @@ import events from 'events';
export function enableAutomaticBitrateDetection(isInNetwork, mediaType, val) {
var key = 'enableautobitratebitrate-' + mediaType + '-' + isInNetwork;
if (val != null) {
if (val !== undefined) {
if (isInNetwork && mediaType === 'Audio') {
val = true;
}
@ -46,7 +46,7 @@ import events from 'events';
export function maxStreamingBitrate(isInNetwork, mediaType, val) {
var key = 'maxbitrate-' + mediaType + '-' + isInNetwork;
if (val != null) {
if (val !== undefined) {
if (isInNetwork && mediaType === 'Audio') {
// nothing to do, this is always a max value
} else {
@ -72,7 +72,7 @@ import events from 'events';
}
export function maxChromecastBitrate(val) {
if (val != null) {
if (val !== undefined) {
this.set('chromecastBitrate1', val);
}
@ -81,7 +81,7 @@ import events from 'events';
}
export function syncOnlyOnWifi(val) {
if (val != null) {
if (val !== undefined) {
this.set('syncOnlyOnWifi', val.toString());
}
@ -89,7 +89,7 @@ import events from 'events';
}
export function syncPath(val) {
if (val != null) {
if (val !== undefined) {
this.set('syncPath', val);
}
@ -97,7 +97,7 @@ import events from 'events';
}
export function cameraUploadServers(val) {
if (val != null) {
if (val !== undefined) {
this.set('cameraUploadServers', val.join(','));
}
@ -110,7 +110,7 @@ import events from 'events';
}
export function runAtStartup(val) {
if (val != null) {
if (val !== undefined) {
this.set('runatstartup', val.toString());
}

View file

@ -84,7 +84,7 @@ import events from 'events';
}
export function enableCinemaMode(val) {
if (val != null) {
if (val !== undefined) {
return this.set('enableCinemaMode', val.toString(), false);
}
@ -93,7 +93,7 @@ import events from 'events';
}
export function enableNextVideoInfoOverlay(val) {
if (val != null) {
if (val !== undefined) {
return this.set('enableNextVideoInfoOverlay', val.toString());
}
@ -102,7 +102,7 @@ import events from 'events';
}
export function enableThemeSongs(val) {
if (val != null) {
if (val !== undefined) {
return this.set('enableThemeSongs', val.toString(), false);
}
@ -111,7 +111,7 @@ import events from 'events';
}
export function enableThemeVideos(val) {
if (val != null) {
if (val !== undefined) {
return this.set('enableThemeVideos', val.toString(), false);
}
@ -120,7 +120,7 @@ import events from 'events';
}
export function enableFastFadein(val) {
if (val != null) {
if (val !== undefined) {
return this.set('fastFadein', val.toString(), false);
}
@ -129,7 +129,7 @@ import events from 'events';
}
export function enableBackdrops(val) {
if (val != null) {
if (val !== undefined) {
return this.set('enableBackdrops', val.toString(), false);
}
@ -138,7 +138,7 @@ import events from 'events';
}
export function language(val) {
if (val != null) {
if (val !== undefined) {
return this.set('language', val.toString(), false);
}
@ -146,7 +146,7 @@ import events from 'events';
}
export function dateTimeLocale(val) {
if (val != null) {
if (val !== undefined) {
return this.set('datetimelocale', val.toString(), false);
}
@ -154,7 +154,7 @@ import events from 'events';
}
export function skipBackLength(val) {
if (val != null) {
if (val !== undefined) {
return this.set('skipBackLength', val.toString());
}
@ -162,7 +162,7 @@ import events from 'events';
}
export function skipForwardLength(val) {
if (val != null) {
if (val !== undefined) {
return this.set('skipForwardLength', val.toString());
}
@ -170,7 +170,7 @@ import events from 'events';
}
export function dashboardTheme(val) {
if (val != null) {
if (val !== undefined) {
return this.set('dashboardTheme', val);
}
@ -178,7 +178,7 @@ import events from 'events';
}
export function skin(val) {
if (val != null) {
if (val !== undefined) {
return this.set('skin', val, false);
}
@ -186,7 +186,7 @@ import events from 'events';
}
export function theme(val) {
if (val != null) {
if (val !== undefined) {
return this.set('appTheme', val, false);
}
@ -194,7 +194,7 @@ import events from 'events';
}
export function screensaver(val) {
if (val != null) {
if (val !== undefined) {
return this.set('screensaver', val, false);
}
@ -202,7 +202,7 @@ import events from 'events';
}
export function libraryPageSize(val) {
if (val != null) {
if (val !== undefined) {
return this.set('libraryPageSize', parseInt(val, 10), false);
}
@ -216,7 +216,7 @@ import events from 'events';
}
export function soundEffects(val) {
if (val != null) {
if (val !== undefined) {
return this.set('soundeffects', val, false);
}

View file

@ -11,5 +11,8 @@ function getConfig() {
export function enableMultiServer() {
return getConfig().then(config => {
return config.multiserver;
}).catch(error => {
console.log("cannot get web config:", error);
return false;
});
}

View file

@ -350,11 +350,6 @@ var AppInfo = {};
return layoutManager;
}
function createWindowHeadroom(Headroom) {
var headroom = new Headroom([], {});
return headroom;
}
function createSharedAppFooter(appFooter) {
return new appFooter({});
}
@ -376,8 +371,9 @@ var AppInfo = {};
function initRequireWithBrowser(browser) {
var bowerPath = getBowerPath();
var componentsPath = getComponentsPath();
var scriptsPath = getScriptsPath();
define("filesystem", [componentsPath + "/filesystem"], returnFirstDependency);
define("filesystem", [scriptsPath + "/filesystem"], returnFirstDependency);
if (window.IntersectionObserver && !browser.edge) {
define("lazyLoader", [componentsPath + "/lazyloader/lazyloader-intersectionobserver"], returnFirstDependency);
@ -430,9 +426,6 @@ var AppInfo = {};
if (!window.fetch) {
promises.push(require(["fetch"]));
}
if ("function" != typeof Object.assign) {
promises.push(require(["objectassign"]));
}
Promise.all(promises).then(function () {
createConnectionManager().then(function () {
@ -570,7 +563,7 @@ var AppInfo = {};
require(["playerSelectionMenu", "components/playback/remotecontrolautoplay"]);
}
require(["components/screensavermanager"]);
require(["libraries/screensavermanager"]);
if (!appHost.supports("physicalvolumecontrol") || browser.touch) {
require(["components/playback/volumeosd"]);
@ -591,7 +584,7 @@ var AppInfo = {};
}
}
require(["playerSelectionMenu", "fullscreenManager"]);
require(["playerSelectionMenu"]);
var apiClient = window.ConnectionManager && window.ConnectionManager.currentApiClient();
if (apiClient) {
@ -665,7 +658,7 @@ var AppInfo = {};
medialibraryeditor: componentsPath + "/medialibraryeditor/medialibraryeditor",
imageoptionseditor: componentsPath + "/imageoptionseditor/imageoptionseditor",
apphost: componentsPath + "/apphost",
visibleinviewport: componentsPath + "/visibleinviewport",
visibleinviewport: bowerPath + "/visibleinviewport",
qualityoptions: componentsPath + "/qualityoptions",
focusManager: componentsPath + "/focusManager",
itemHelper: componentsPath + "/itemhelper",
@ -709,7 +702,9 @@ var AppInfo = {};
"polyfill",
"fast-text-encoding",
"intersection-observer",
"classlist-polyfill"
"classlist-polyfill",
"screenfull",
"headroom"
]
},
urlArgs: urlArgs,
@ -717,6 +712,7 @@ var AppInfo = {};
onError: onRequireJsError
});
require(["fetch"]);
require(["polyfill"]);
require(["fast-text-encoding"]);
require(["intersection-observer"]);
@ -768,9 +764,8 @@ var AppInfo = {};
// TODO remove these libraries
// all of these have been modified so we need to fix that first
define("headroom", [componentsPath + "/headroom/headroom"], returnFirstDependency);
define("scroller", [componentsPath + "/scroller"], returnFirstDependency);
define("navdrawer", [componentsPath + "/navdrawer/navdrawer"], returnFirstDependency);
define("scroller", [bowerPath + "/scroller"], returnFirstDependency);
define("navdrawer", [bowerPath + "/navdrawer/navdrawer"], returnFirstDependency);
define("emby-button", [elementsPath + "/emby-button/emby-button"], returnFirstDependency);
define("paper-icon-button-light", [elementsPath + "/emby-button/paper-icon-button-light"], returnFirstDependency);
@ -810,7 +805,7 @@ var AppInfo = {};
define("playerSettingsMenu", [componentsPath + "/playback/playersettingsmenu"], returnFirstDependency);
define("playMethodHelper", [componentsPath + "/playback/playmethodhelper"], returnFirstDependency);
define("brightnessOsd", [componentsPath + "/playback/brightnessosd"], returnFirstDependency);
define("alphaNumericShortcuts", [componentsPath + "/alphanumericshortcuts/alphanumericshortcuts"], returnFirstDependency);
define("alphaNumericShortcuts", [scriptsPath + "/alphanumericshortcuts"], returnFirstDependency);
define("multiSelect", [componentsPath + "/multiselect/multiselect"], returnFirstDependency);
define("alphaPicker", [componentsPath + "/alphapicker/alphapicker"], returnFirstDependency);
define("tabbedView", [componentsPath + "/tabbedview/tabbedview"], returnFirstDependency);
@ -832,13 +827,11 @@ var AppInfo = {};
define("itemContextMenu", [componentsPath + "/itemcontextmenu"], returnFirstDependency);
define("imageEditor", [componentsPath + "/imageeditor/imageeditor"], returnFirstDependency);
define("imageDownloader", [componentsPath + "/imagedownloader/imagedownloader"], returnFirstDependency);
define("dom", [componentsPath + "/dom"], returnFirstDependency);
define("dom", [scriptsPath + "/dom"], returnFirstDependency);
define("playerStats", [componentsPath + "/playerstats/playerstats"], returnFirstDependency);
define("searchFields", [componentsPath + "/search/searchfields"], returnFirstDependency);
define("searchResults", [componentsPath + "/search/searchresults"], returnFirstDependency);
define("upNextDialog", [componentsPath + "/upnextdialog/upnextdialog"], returnFirstDependency);
define("fullscreen-doubleclick", [componentsPath + "/fullscreen/fullscreen-dc"], returnFirstDependency);
define("fullscreenManager", [componentsPath + "/fullscreenManager", "events"], returnFirstDependency);
define("subtitleAppearanceHelper", [componentsPath + "/subtitlesettings/subtitleappearancehelper"], returnFirstDependency);
define("subtitleSettings", [componentsPath + "/subtitlesettings/subtitlesettings"], returnFirstDependency);
define("displaySettings", [componentsPath + "/displaysettings/displaysettings"], returnFirstDependency);
@ -864,8 +857,7 @@ var AppInfo = {};
return viewManager;
});
define("slideshow", [componentsPath + "/slideshow/slideshow"], returnFirstDependency);
define("objectassign", [componentsPath + "/polyfills/objectassign"], returnFirstDependency);
define("focusPreventScroll", [componentsPath + "/polyfills/focusPreventScroll"], returnFirstDependency);
define("focusPreventScroll", ["legacy/focusPreventScroll"], returnFirstDependency);
define("userdataButtons", [componentsPath + "/userdatabuttons/userdatabuttons"], returnFirstDependency);
define("listView", [componentsPath + "/listview/listview"], returnFirstDependency);
define("indicators", [componentsPath + "/indicators/indicators"], returnFirstDependency);
@ -883,8 +875,8 @@ var AppInfo = {};
define("dialogHelper", [componentsPath + "/dialogHelper/dialogHelper"], returnFirstDependency);
define("serverNotifications", [componentsPath + "/serverNotifications"], returnFirstDependency);
define("skinManager", [componentsPath + "/skinManager"], returnFirstDependency);
define("keyboardnavigation", [componentsPath + "/input/keyboardnavigation"], returnFirstDependency);
define("mouseManager", [componentsPath + "/input/mouseManager"], returnFirstDependency);
define("keyboardnavigation", [scriptsPath + "/keyboardnavigation"], returnFirstDependency);
define("mouseManager", [scriptsPath + "/mouseManager"], returnFirstDependency);
define("scrollManager", [componentsPath + "/scrollManager"], returnFirstDependency);
define("autoFocuser", [componentsPath + "/autoFocuser"], returnFirstDependency);
define("connectionManager", [], function () {

View file

@ -1,4 +1,4 @@
define(["events", "userSettings", "serverNotifications", "connectionManager", "emby-button"], function (events, userSettings, serverNotifications, connectionManager) {
define(["events", "userSettings", "serverNotifications", "connectionManager", "globalize", "emby-button"], function (events, userSettings, serverNotifications, connectionManager, globalize) {
"use strict";
return function (options) {
@ -48,11 +48,11 @@ define(["events", "userSettings", "serverNotifications", "connectionManager", "e
var lastResult = task.LastExecutionResult ? task.LastExecutionResult.Status : '';
if (lastResult == "Failed") {
options.lastResultElem.html('<span style="color:#FF0000;">(' + Globalize.translate('LabelFailed') + ')</span>');
options.lastResultElem.html('<span style="color:#FF0000;">(' + globalize.translate('LabelFailed') + ')</span>');
} else if (lastResult == "Cancelled") {
options.lastResultElem.html('<span style="color:#0026FF;">(' + Globalize.translate('LabelCancelled') + ')</span>');
options.lastResultElem.html('<span style="color:#0026FF;">(' + globalize.translate('LabelCancelled') + ')</span>');
} else if (lastResult == "Aborted") {
options.lastResultElem.html('<span style="color:#FF0000;">' + Globalize.translate('LabelAbortedByServerShutdown') + '</span>');
options.lastResultElem.html('<span style="color:#FF0000;">' + globalize.translate('LabelAbortedByServerShutdown') + '</span>');
} else {
options.lastResultElem.html(lastResult);
}