mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' of https://github.com/jellyfin/jellyfin-web into fix-accese
# Conflicts: # src/scripts/site.js
This commit is contained in:
commit
8e724d3119
168 changed files with 12834 additions and 5898 deletions
|
@ -667,12 +667,13 @@ import browser from './browser';
|
|||
}
|
||||
}
|
||||
|
||||
if (canPlayVp8) {
|
||||
if (webmAudioCodecs.length && webmVideoCodecs.length) {
|
||||
profile.TranscodingProfiles.push({
|
||||
Container: 'webm',
|
||||
Type: 'Video',
|
||||
AudioCodec: 'vorbis',
|
||||
VideoCodec: 'vpx',
|
||||
AudioCodec: webmAudioCodecs.join(','),
|
||||
// TODO: Remove workaround when servers migrate away from 'vpx' for transcoding profiles.
|
||||
VideoCodec: (canPlayVp8 ? webmVideoCodecs.concat('vpx') : webmVideoCodecs).join(','),
|
||||
Context: 'Streaming',
|
||||
Protocol: 'http',
|
||||
// If audio transcoding is needed, limit channels to number of physical audio channels
|
||||
|
|
|
@ -26,8 +26,13 @@ export async function serverAddress() {
|
|||
// Use servers specified in config.json
|
||||
const urls = await webSettings.getServers();
|
||||
|
||||
// Otherwise use computed base URL
|
||||
if (urls.length == 0) {
|
||||
if (urls.length === 0) {
|
||||
// Don't use app URL as server URL
|
||||
if (window.NativeShell) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
// Otherwise use computed base URL
|
||||
const index = window.location.href.toLowerCase().lastIndexOf('/web');
|
||||
if (index != -1) {
|
||||
urls.push(window.location.href.substring(0, index));
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
case 'Kodi':
|
||||
return baseUrl + 'kodi.svg';
|
||||
case 'Jellyfin Android':
|
||||
case 'AndroidTV':
|
||||
case 'Android TV':
|
||||
return baseUrl + 'android.svg';
|
||||
case 'Jellyfin Web':
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
import inputManager from './inputManager';
|
||||
import layoutManager from '../components/layoutManager';
|
||||
import appSettings from './settings/appSettings';
|
||||
|
||||
/**
|
||||
* Key name mapping.
|
||||
|
@ -160,7 +161,7 @@ function attachGamepadScript() {
|
|||
}
|
||||
|
||||
// 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 */
|
||||
if (navigator.getGamepads && appSettings.enableGamepad()) { /* eslint-disable-line compat/compat */
|
||||
window.addEventListener('gamepadconnected', attachGamepadScript);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@ import viewManager from '../components/viewManager/viewManager';
|
|||
import { appRouter } from '../components/appRouter';
|
||||
import { appHost } from '../components/apphost';
|
||||
import { playbackManager } from '../components/playback/playbackmanager';
|
||||
import SyncPlay from '../components/syncPlay/core';
|
||||
import groupSelectionMenu from '../components/syncPlay/ui/groupSelectionMenu';
|
||||
import browser from './browser';
|
||||
import globalize from './globalize';
|
||||
import imageHelper from './imagehelper';
|
||||
import { getMenuLinks } from '../scripts/settings/webSettings';
|
||||
import '../elements/emby-button/paper-icon-button-light';
|
||||
import 'material-design-icons-iconfont';
|
||||
import '../assets/css/scrollstyles.scss';
|
||||
|
@ -32,7 +32,7 @@ import Headroom from 'headroom.js';
|
|||
html += '</div>';
|
||||
html += '<div class="headerRight">';
|
||||
html += '<span class="headerSelectedPlayer"></span>';
|
||||
html += '<button is="paper-icon-button-light" class="headerSyncButton syncButton headerButton headerButtonRight hide"><span class="material-icons sync_disabled"></span></button>';
|
||||
html += '<button is="paper-icon-button-light" class="headerSyncButton syncButton headerButton headerButtonRight hide"><span class="material-icons groups"></span></button>';
|
||||
html += '<button is="paper-icon-button-light" class="headerAudioPlayerButton audioPlayerButton headerButton headerButtonRight hide"><span class="material-icons music_note"></span></button>';
|
||||
html += '<button is="paper-icon-button-light" class="headerCastButton castButton headerButton headerButtonRight hide"><span class="material-icons cast"></span></button>';
|
||||
html += '<button type="button" is="paper-icon-button-light" class="headerButton headerButtonRight headerSearchButton hide"><span class="material-icons search"></span></button>';
|
||||
|
@ -134,7 +134,7 @@ import Headroom from 'headroom.js';
|
|||
const policy = user.Policy ? user.Policy : user.localUser.Policy;
|
||||
|
||||
const apiClient = getCurrentApiClient();
|
||||
if (headerSyncButton && policy && policy.SyncPlayAccess !== 'None' && apiClient.isMinServerVersion('10.6.0')) {
|
||||
if (headerSyncButton && policy?.SyncPlayAccess !== 'None' && apiClient.isMinServerVersion('10.6.0')) {
|
||||
headerSyncButton.classList.remove('hide');
|
||||
}
|
||||
} else {
|
||||
|
@ -233,26 +233,6 @@ import Headroom from 'headroom.js';
|
|||
groupSelectionMenu.show(btn);
|
||||
}
|
||||
|
||||
function onSyncPlayEnabled(event, enabled) {
|
||||
const icon = headerSyncButton.querySelector('span');
|
||||
icon.classList.remove('sync', 'sync_disabled', 'sync_problem');
|
||||
if (enabled) {
|
||||
icon.classList.add('sync');
|
||||
} else {
|
||||
icon.classList.add('sync_disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function onSyncPlaySyncing(event, is_syncing) {
|
||||
const icon = headerSyncButton.querySelector('span');
|
||||
icon.classList.remove('sync', 'sync_disabled', 'sync_problem');
|
||||
if (is_syncing) {
|
||||
icon.classList.add('sync_problem');
|
||||
} else {
|
||||
icon.classList.add('sync');
|
||||
}
|
||||
}
|
||||
|
||||
function getItemHref(item, context) {
|
||||
return appRouter.getRouteUrl(item, {
|
||||
context: context
|
||||
|
@ -294,9 +274,11 @@ import Headroom from 'headroom.js';
|
|||
html += '<div style="height:.5em;"></div>';
|
||||
html += '<a is="emby-linkbutton" class="navMenuOption lnkMediaFolder" href="#!/home.html"><span class="material-icons navMenuOptionIcon home"></span><span class="navMenuOptionText">' + globalize.translate('Home') + '</span></a>';
|
||||
|
||||
// placeholder for custom menu links
|
||||
html += '<div class="customMenuOptions"></div>';
|
||||
|
||||
// libraries are added here
|
||||
html += '<div class="libraryMenuOptions">';
|
||||
html += '</div>';
|
||||
html += '<div class="libraryMenuOptions"></div>';
|
||||
|
||||
if (user.localUser && user.localUser.Policy.IsAdministrator) {
|
||||
html += '<div class="adminMenuOptions">';
|
||||
|
@ -430,12 +412,6 @@ import Headroom from 'headroom.js';
|
|||
pageIds: ['devicesPage', 'devicePage'],
|
||||
icon: 'devices'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('QuickConnect'),
|
||||
href: '#!/quickConnect.html',
|
||||
pageIds: ['quickConnectPage'],
|
||||
icon: 'tap_and_play'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('HeaderActivity'),
|
||||
href: '#!/serveractivity.html',
|
||||
|
@ -659,6 +635,32 @@ import Headroom from 'headroom.js';
|
|||
|
||||
const userId = Dashboard.getCurrentUserId();
|
||||
const apiClient = getCurrentApiClient();
|
||||
|
||||
const customMenuOptions = document.querySelector('.customMenuOptions');
|
||||
if (customMenuOptions) {
|
||||
getMenuLinks().then(links => {
|
||||
links.forEach(link => {
|
||||
const option = document.createElement('a');
|
||||
option.setAttribute('is', 'emby-linkbutton');
|
||||
option.className = 'navMenuOption lnkMediaFolder';
|
||||
option.rel = 'noopener noreferrer';
|
||||
option.target = '_blank';
|
||||
option.href = link.url;
|
||||
|
||||
const icon = document.createElement('span');
|
||||
icon.className = `material-icons navMenuOptionIcon ${link.icon || 'link'}`;
|
||||
option.appendChild(icon);
|
||||
|
||||
const label = document.createElement('span');
|
||||
label.className = 'navMenuOptionText';
|
||||
label.textContent = link.name;
|
||||
option.appendChild(label);
|
||||
|
||||
customMenuOptions.appendChild(option);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const libraryMenuOptions = document.querySelector('.libraryMenuOptions');
|
||||
|
||||
if (libraryMenuOptions) {
|
||||
|
@ -1023,9 +1025,6 @@ import Headroom from 'headroom.js';
|
|||
|
||||
Events.on(playbackManager, 'playerchange', updateCastIcon);
|
||||
|
||||
Events.on(SyncPlay.Manager, 'enabled', onSyncPlayEnabled);
|
||||
Events.on(SyncPlay.Manager, 'syncing', onSyncPlaySyncing);
|
||||
|
||||
loadNavDrawer();
|
||||
|
||||
const LibraryMenu = {
|
||||
|
|
|
@ -54,8 +54,8 @@ import dom from '../scripts/dom';
|
|||
|
||||
let lastPointerMoveData;
|
||||
function onPointerMove(e) {
|
||||
const eventX = e.screenX;
|
||||
const eventY = e.screenY;
|
||||
const eventX = e.screenX || e.clientX;
|
||||
const eventY = e.screenY || e.clientY;
|
||||
|
||||
// if coord don't exist how could it move
|
||||
if (typeof eventX === 'undefined' && typeof eventY === 'undefined') {
|
||||
|
|
|
@ -189,7 +189,7 @@ export default function (view) {
|
|||
reloadItems();
|
||||
});
|
||||
view.querySelector('.btnNewPlaylist').addEventListener('click', function () {
|
||||
import('playlistEditor').then(({default: playlistEditor}) => {
|
||||
import('../components/playlisteditor/playlisteditor').then(({default: playlistEditor}) => {
|
||||
const serverId = ApiClient.serverInfo().Id;
|
||||
new playlistEditor({
|
||||
items: [],
|
||||
|
|
|
@ -84,6 +84,13 @@ import { appRouter } from '../components/appRouter';
|
|||
controller: 'user/profile/index'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/mypreferencescontrols.html',
|
||||
path: 'user/controls/index.html',
|
||||
autoFocus: false,
|
||||
controller: 'user/controls/index'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
alias: '/mypreferencesdisplay.html',
|
||||
path: 'user/display/index.html',
|
||||
|
@ -304,7 +311,7 @@ import { appRouter } from '../components/appRouter';
|
|||
defineRoute({
|
||||
alias: '/search.html',
|
||||
path: 'search.html',
|
||||
controller: 'searchpage'
|
||||
pageComponent: 'SearchPage'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
|
@ -553,6 +560,11 @@ import { appRouter } from '../components/appRouter';
|
|||
serverRequest: true
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: '/dialog',
|
||||
dummyRoute: true
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: '',
|
||||
isDefaultRoute: true,
|
||||
|
|
|
@ -103,6 +103,8 @@ function centerOnFocusVertical(e) {
|
|||
|
||||
export const centerFocus = {
|
||||
on: function (element, horizontal) {
|
||||
element.setAttribute(`data-scroll-mode-${horizontal ? 'x' : 'y'}`, 'custom');
|
||||
|
||||
if (horizontal) {
|
||||
dom.addEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||
capture: true,
|
||||
|
@ -116,6 +118,8 @@ export const centerFocus = {
|
|||
}
|
||||
},
|
||||
off: function (element, horizontal) {
|
||||
element.removeAttribute(`data-scroll-mode-${horizontal ? 'x' : 'y'}`);
|
||||
|
||||
if (horizontal) {
|
||||
dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||
capture: true,
|
||||
|
|
|
@ -18,6 +18,19 @@ class AppSettings {
|
|||
return this.get('enableAutoLogin') !== 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'Enable Gamepad' state.
|
||||
* @param {boolean|undefined} val - Flag to enable 'Enable Gamepad' or undefined.
|
||||
* @return {boolean} 'Enable Gamepad' state.
|
||||
*/
|
||||
enableGamepad(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('enableGamepad', val.toString());
|
||||
}
|
||||
|
||||
return this.get('enableGamepad') === 'true';
|
||||
}
|
||||
|
||||
enableSystemExternalPlayers(val) {
|
||||
if (val !== undefined) {
|
||||
this.set('enableSystemExternalPlayers', val.toString());
|
||||
|
|
|
@ -169,6 +169,19 @@ export class UserSettings {
|
|||
return val !== 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'SetUsingLastTracks' state.
|
||||
* @param {boolean|undefined} val - Flag to enable 'SetUsingLastTracks' or undefined.
|
||||
* @return {boolean} 'SetUsingLastTracks' state.
|
||||
*/
|
||||
enableSetUsingLastTracks(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('enableSetUsingLastTracks', val.toString());
|
||||
}
|
||||
|
||||
return this.get('enableSetUsingLastTracks', false) !== 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'Theme Songs' state.
|
||||
* @param {boolean|undefined} val - Flag to enable 'Theme Songs' or undefined.
|
||||
|
@ -239,6 +252,32 @@ export class UserSettings {
|
|||
return val === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'disableCustomCss' state.
|
||||
* @param {boolean|undefined} val - Flag to enable 'disableCustomCss' or undefined.
|
||||
* @return {boolean} 'disableCustomCss' state.
|
||||
*/
|
||||
disableCustomCss(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('disableCustomCss', val.toString(), false);
|
||||
}
|
||||
|
||||
return this.get('disableCustomCss', false) === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set customCss.
|
||||
* @param {string|undefined} val - Language.
|
||||
* @return {string} Language.
|
||||
*/
|
||||
customCss(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('customCss', val.toString(), false);
|
||||
}
|
||||
|
||||
return this.get('customCss', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'Details Banner' state.
|
||||
* @param {boolean|undefined} val - Flag to enable 'Details Banner' or undefined.
|
||||
|
@ -253,6 +292,20 @@ export class UserSettings {
|
|||
return val !== 'false';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set 'Use Episode Images in Next Up and Continue Watching' state.
|
||||
* @param {string|boolean|undefined} val - Flag to enable 'Use Episode Images in Next Up and Continue Watching' or undefined.
|
||||
* @return {boolean} 'Use Episode Images in Next Up' state.
|
||||
*/
|
||||
useEpisodeImagesInNextUpAndResume(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('useEpisodeImagesInNextUpAndResume', val.toString(), true);
|
||||
}
|
||||
|
||||
val = this.get('useEpisodeImagesInNextUpAndResume', true);
|
||||
return val === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get or set language.
|
||||
* @param {string|undefined} val - Language.
|
||||
|
@ -488,12 +541,14 @@ export const allowedAudioChannels = currentSettings.allowedAudioChannels.bind(cu
|
|||
export const preferFmp4HlsContainer = currentSettings.preferFmp4HlsContainer.bind(currentSettings);
|
||||
export const enableCinemaMode = currentSettings.enableCinemaMode.bind(currentSettings);
|
||||
export const enableNextVideoInfoOverlay = currentSettings.enableNextVideoInfoOverlay.bind(currentSettings);
|
||||
export const enableSetUsingLastTracks = currentSettings.enableSetUsingLastTracks.bind(currentSettings);
|
||||
export const enableThemeSongs = currentSettings.enableThemeSongs.bind(currentSettings);
|
||||
export const enableThemeVideos = currentSettings.enableThemeVideos.bind(currentSettings);
|
||||
export const enableFastFadein = currentSettings.enableFastFadein.bind(currentSettings);
|
||||
export const enableBlurhash = currentSettings.enableBlurhash.bind(currentSettings);
|
||||
export const enableBackdrops = currentSettings.enableBackdrops.bind(currentSettings);
|
||||
export const detailsBanner = currentSettings.detailsBanner.bind(currentSettings);
|
||||
export const useEpisodeImagesInNextUpAndResume = currentSettings.useEpisodeImagesInNextUpAndResume.bind(currentSettings);
|
||||
export const language = currentSettings.language.bind(currentSettings);
|
||||
export const dateTimeLocale = currentSettings.dateTimeLocale.bind(currentSettings);
|
||||
export const chromecastVersion = currentSettings.chromecastVersion.bind(currentSettings);
|
||||
|
@ -511,3 +566,5 @@ export const getSubtitleAppearanceSettings = currentSettings.getSubtitleAppearan
|
|||
export const setSubtitleAppearanceSettings = currentSettings.setSubtitleAppearanceSettings.bind(currentSettings);
|
||||
export const setFilter = currentSettings.setFilter.bind(currentSettings);
|
||||
export const getFilter = currentSettings.getFilter.bind(currentSettings);
|
||||
export const customCss = currentSettings.customCss.bind(currentSettings);
|
||||
export const disableCustomCss = currentSettings.disableCustomCss.bind(currentSettings);
|
||||
|
|
|
@ -126,6 +126,18 @@ export function getThemes() {
|
|||
|
||||
export const getDefaultTheme = () => internalDefaultTheme;
|
||||
|
||||
export function getMenuLinks() {
|
||||
return getConfig().then(config => {
|
||||
if (!config.menuLinks) {
|
||||
console.error('web config is invalid, missing menuLinks:', config);
|
||||
}
|
||||
return config.menuLinks || [];
|
||||
}).catch(error => {
|
||||
console.log('cannot get web config:', error);
|
||||
return [];
|
||||
});
|
||||
}
|
||||
|
||||
export function getPlugins() {
|
||||
return getConfig().then(config => {
|
||||
if (!config.plugins) {
|
||||
|
|
|
@ -1,30 +1,40 @@
|
|||
// TODO: This seems like a good candidate for deprecation
|
||||
export default {
|
||||
enableFullscreen: function() {
|
||||
window.NativeShell?.enableFullscreen();
|
||||
if (window.NativeShell?.enableFullscreen) {
|
||||
window.NativeShell.enableFullscreen();
|
||||
}
|
||||
},
|
||||
disableFullscreen: function() {
|
||||
window.NativeShell?.disableFullscreen();
|
||||
if (window.NativeShell?.disableFullscreen) {
|
||||
window.NativeShell.disableFullscreen();
|
||||
}
|
||||
},
|
||||
openUrl: function(url, target) {
|
||||
if (window.NativeShell) {
|
||||
if (window.NativeShell?.openUrl) {
|
||||
window.NativeShell.openUrl(url, target);
|
||||
} else {
|
||||
window.open(url, target || '_blank');
|
||||
}
|
||||
},
|
||||
updateMediaSession(mediaInfo) {
|
||||
window.NativeShell?.updateMediaSession(mediaInfo);
|
||||
if (window.NativeShell?.updateMediaSession) {
|
||||
window.NativeShell.updateMediaSession(mediaInfo);
|
||||
}
|
||||
},
|
||||
hideMediaSession() {
|
||||
window.NativeShell?.hideMediaSession();
|
||||
if (window.NativeShell?.hideMediaSession) {
|
||||
window.NativeShell.hideMediaSession();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Notify the NativeShell about volume level changes.
|
||||
* Useful for e.g. remote playback.
|
||||
*/
|
||||
updateVolumeLevel(volume) {
|
||||
window.NativeShell?.updateVolumeLevel(volume);
|
||||
if (window.NativeShell?.updateVolumeLevel) {
|
||||
window.NativeShell.updateVolumeLevel(volume);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Download specified files with NativeShell if possible
|
||||
|
@ -32,7 +42,7 @@ export default {
|
|||
* @returns true on success
|
||||
*/
|
||||
downloadFiles(items) {
|
||||
if (window.NativeShell) {
|
||||
if (window.NativeShell?.downloadFile) {
|
||||
items.forEach(item => window.NativeShell.downloadFile(item));
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import { playbackManager } from '../components/playback/playbackmanager';
|
|||
import SyncPlayNoActivePlayer from '../components/syncPlay/ui/players/NoActivePlayer';
|
||||
import SyncPlayHtmlVideoPlayer from '../components/syncPlay/ui/players/HtmlVideoPlayer';
|
||||
import SyncPlayHtmlAudioPlayer from '../components/syncPlay/ui/players/HtmlAudioPlayer';
|
||||
import { currentSettings } from './settings/userSettings';
|
||||
import taskButton from '../scripts/taskbutton';
|
||||
|
||||
// TODO: Move this elsewhere
|
||||
|
@ -176,9 +177,11 @@ function initSyncPlay() {
|
|||
|
||||
// FIXME: Multiple apiClients?
|
||||
Events.on(ServerConnections, 'apiclientcreated', (e, newApiClient) => SyncPlay.Manager.init(newApiClient));
|
||||
Events.on(ServerConnections, 'localusersignedin', () => SyncPlay.Manager.updateApiClient(ServerConnections.currentApiClient()));
|
||||
Events.on(ServerConnections, 'localusersignedout', () => SyncPlay.Manager.updateApiClient(ServerConnections.currentApiClient()));
|
||||
}
|
||||
|
||||
function onAppReady() {
|
||||
async function onAppReady() {
|
||||
console.debug('begin onAppReady');
|
||||
|
||||
console.debug('onAppReady: loading dependencies');
|
||||
|
@ -221,27 +224,63 @@ function onAppReady() {
|
|||
|
||||
const apiClient = ServerConnections.currentApiClient();
|
||||
if (apiClient) {
|
||||
fetch(apiClient.getUrl('Branding/Css'))
|
||||
const updateStyle = (css) => {
|
||||
let style = document.querySelector('#cssBranding');
|
||||
if (!style) {
|
||||
// Inject the branding css as a dom element in body so it will take
|
||||
// precedence over other stylesheets
|
||||
style = document.createElement('style');
|
||||
style.id = 'cssBranding';
|
||||
document.body.appendChild(style);
|
||||
}
|
||||
style.textContent = css;
|
||||
};
|
||||
|
||||
const style = fetch(apiClient.getUrl('Branding/Css'))
|
||||
.then(function(response) {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.status + ' ' + response.statusText);
|
||||
}
|
||||
return response.text();
|
||||
})
|
||||
.then(function(css) {
|
||||
let style = document.querySelector('#cssBranding');
|
||||
if (!style) {
|
||||
// Inject the branding css as a dom element in body so it will take
|
||||
// precedence over other stylesheets
|
||||
style = document.createElement('style');
|
||||
style.id = 'cssBranding';
|
||||
document.body.appendChild(style);
|
||||
}
|
||||
style.textContent = css;
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.warn('Error applying custom css', err);
|
||||
});
|
||||
|
||||
const handleStyleChange = async () => {
|
||||
if (currentSettings.disableCustomCss()) {
|
||||
updateStyle('');
|
||||
} else {
|
||||
updateStyle(await style);
|
||||
}
|
||||
|
||||
const localCss = currentSettings.customCss();
|
||||
let localStyle = document.querySelector('#localCssBranding');
|
||||
if (localCss) {
|
||||
if (!localStyle) {
|
||||
// Inject the branding css as a dom element in body so it will take
|
||||
// precedence over other stylesheets
|
||||
localStyle = document.createElement('style');
|
||||
localStyle.id = 'localCssBranding';
|
||||
document.body.appendChild(localStyle);
|
||||
}
|
||||
localStyle.textContent = localCss;
|
||||
} else {
|
||||
if (localStyle) {
|
||||
localStyle.textContent = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Events.on(ServerConnections, 'localusersignedin', handleStyleChange);
|
||||
Events.on(ServerConnections, 'localusersignedout', handleStyleChange);
|
||||
Events.on(currentSettings, 'change', (e, prop) => {
|
||||
if (prop == 'disableCustomCss' || prop == 'customCss') {
|
||||
handleStyleChange();
|
||||
}
|
||||
});
|
||||
|
||||
style.then(updateStyle);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
27
src/scripts/stringUtils.js
Normal file
27
src/scripts/stringUtils.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
* Gets the value of a string as boolean.
|
||||
* @param {string} name The value as a string.
|
||||
* @param {boolean} defaultValue The default value if the string is invalid.
|
||||
* @returns {boolean} The value.
|
||||
*/
|
||||
export function toBoolean(value, defaultValue = false) {
|
||||
if (value !== 'true' && value !== 'false') {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value !== 'false';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of a string as float number.
|
||||
* @param {string} value The value as a string.
|
||||
* @param {number} defaultValue The default value if the string is invalid.
|
||||
* @returns {number} The value.
|
||||
*/
|
||||
export function toFloat(value, defaultValue = 0) {
|
||||
if (value === null || value === '' || isNaN(value)) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return parseFloat(value);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue