mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge branch 'master' into es6-subtitlesettings
This commit is contained in:
commit
2a30ed6461
222 changed files with 5287 additions and 2717 deletions
|
@ -53,45 +53,6 @@ define([], function () {
|
|||
return false;
|
||||
}
|
||||
|
||||
function isStyleSupported(prop, value) {
|
||||
|
||||
if (typeof window === 'undefined') {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If no value is supplied, use "inherit"
|
||||
value = arguments.length === 2 ? value : 'inherit';
|
||||
// Try the native standard method first
|
||||
if ('CSS' in window && 'supports' in window.CSS) {
|
||||
return window.CSS.supports(prop, value);
|
||||
}
|
||||
// Check Opera's native method
|
||||
if ('supportsCSS' in window) {
|
||||
return window.supportsCSS(prop, value);
|
||||
}
|
||||
|
||||
// need try/catch because it's failing on tizen
|
||||
|
||||
try {
|
||||
// Convert to camel-case for DOM interactions
|
||||
var camel = prop.replace(/-([a-z]|[0-9])/ig, function (all, letter) {
|
||||
return (letter + '').toUpperCase();
|
||||
});
|
||||
// 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;
|
||||
// Ensure both the property and value are
|
||||
// supported and return
|
||||
return support && (el.style[camel] !== '');
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function hasKeyboard(browser) {
|
||||
|
||||
if (browser.touch) {
|
||||
|
@ -283,10 +244,6 @@ define([], function () {
|
|||
browser.tv = isTv();
|
||||
browser.operaTv = browser.tv && userAgent.toLowerCase().indexOf('opr/') !== -1;
|
||||
|
||||
if (!isStyleSupported('display', 'flex')) {
|
||||
browser.noFlex = true;
|
||||
}
|
||||
|
||||
if (browser.mobile || browser.tv) {
|
||||
browser.slow = true;
|
||||
}
|
||||
|
|
54
src/scripts/deleteHelper.js
Normal file
54
src/scripts/deleteHelper.js
Normal file
|
@ -0,0 +1,54 @@
|
|||
import connectionManager from 'connectionManager';
|
||||
import confirm from 'confirm';
|
||||
import appRouter from 'appRouter';
|
||||
import globalize from 'globalize';
|
||||
|
||||
function alertText(options) {
|
||||
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
require(['alert'], function (alert) {
|
||||
alert(options).then(resolve, resolve);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deleteItem(options) {
|
||||
|
||||
const item = options.item;
|
||||
const parentId = item.SeasonId || item.SeriesId || item.ParentId;
|
||||
|
||||
let apiClient = connectionManager.getApiClient(item.ServerId);
|
||||
|
||||
return confirm({
|
||||
|
||||
title: globalize.translate('HeaderDeleteItem'),
|
||||
text: globalize.translate('ConfirmDeleteItem'),
|
||||
confirmText: globalize.translate('Delete'),
|
||||
primary: 'delete'
|
||||
|
||||
}).then(function () {
|
||||
|
||||
return apiClient.deleteItem(item.Id).then(function () {
|
||||
|
||||
if (options.navigate) {
|
||||
if (parentId) {
|
||||
appRouter.showItem(parentId, item.ServerId);
|
||||
} else {
|
||||
appRouter.goHome();
|
||||
}
|
||||
}
|
||||
}, function (err) {
|
||||
|
||||
let result = function () {
|
||||
return Promise.reject(err);
|
||||
};
|
||||
|
||||
return alertText(globalize.translate('ErrorDeletingItem')).then(result, result);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default {
|
||||
deleteItem: deleteItem
|
||||
};
|
14
src/scripts/fileDownloader.js
Normal file
14
src/scripts/fileDownloader.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
import multiDownload from 'multi-download';
|
||||
|
||||
export function download(items) {
|
||||
|
||||
if (window.NativeShell) {
|
||||
items.map(function (item) {
|
||||
window.NativeShell.downloadFile(item);
|
||||
});
|
||||
} else {
|
||||
multiDownload(items.map(function (item) {
|
||||
return item.url;
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', 'viewManager', 'libraryBrowser', 'appRouter', 'apphost', 'playbackManager', 'browser', 'globalize', 'scripts/imagehelper', 'paper-icon-button-light', 'material-icons', 'scrollStyles', 'flexStyles'], function (dom, layoutManager, inputManager, connectionManager, events, viewManager, libraryBrowser, appRouter, appHost, playbackManager, browser, globalize, imageHelper) {
|
||||
define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', 'viewManager', 'libraryBrowser', 'appRouter', 'apphost', 'playbackManager', 'syncPlayManager', 'groupSelectionMenu', 'browser', 'globalize', 'scripts/imagehelper', 'paper-icon-button-light', 'material-icons', 'scrollStyles', 'flexStyles'], function (dom, layoutManager, inputManager, connectionManager, events, viewManager, libraryBrowser, appRouter, appHost, playbackManager, syncPlayManager, groupSelectionMenu, browser, globalize, imageHelper) {
|
||||
'use strict';
|
||||
|
||||
function renderHeader() {
|
||||
|
@ -12,6 +12,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
|||
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="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>';
|
||||
|
@ -30,6 +31,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
|||
headerCastButton = skinHeader.querySelector('.headerCastButton');
|
||||
headerAudioPlayerButton = skinHeader.querySelector('.headerAudioPlayerButton');
|
||||
headerSearchButton = skinHeader.querySelector('.headerSearchButton');
|
||||
headerSyncButton = skinHeader.querySelector('.headerSyncButton');
|
||||
|
||||
lazyLoadViewMenuBarImages();
|
||||
bindMenuEvents();
|
||||
|
@ -84,9 +86,16 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
|||
if (!layoutManager.tv) {
|
||||
headerCastButton.classList.remove('hide');
|
||||
}
|
||||
|
||||
var policy = user.Policy ? user.Policy : user.localUser.Policy;
|
||||
|
||||
if (headerSyncButton && policy && policy.SyncPlayAccess !== 'None') {
|
||||
headerSyncButton.classList.remove('hide');
|
||||
}
|
||||
} else {
|
||||
headerHomeButton.classList.add('hide');
|
||||
headerCastButton.classList.add('hide');
|
||||
headerSyncButton.classList.add('hide');
|
||||
|
||||
if (headerSearchButton) {
|
||||
headerSearchButton.classList.add('hide');
|
||||
|
@ -147,6 +156,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
|||
}
|
||||
|
||||
headerAudioPlayerButton.addEventListener('click', showAudioPlayer);
|
||||
headerSyncButton.addEventListener('click', onSyncButtonClicked);
|
||||
|
||||
if (layoutManager.mobile) {
|
||||
initHeadRoom(skinHeader);
|
||||
|
@ -177,6 +187,31 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
|||
});
|
||||
}
|
||||
|
||||
function onSyncButtonClicked() {
|
||||
var btn = this;
|
||||
groupSelectionMenu.show(btn);
|
||||
}
|
||||
|
||||
function onSyncPlayEnabled(event, enabled) {
|
||||
var 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, syncMethod) {
|
||||
var 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
|
||||
|
@ -373,7 +408,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
|||
icon: 'live_tv'
|
||||
});
|
||||
links.push({
|
||||
name: globalize.translate('DVR'),
|
||||
name: globalize.translate('TabDVR'),
|
||||
href: 'livetvsettings.html',
|
||||
pageIds: ['liveTvSettingsPage'],
|
||||
icon: 'dvr'
|
||||
|
@ -799,6 +834,7 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
|||
var headerCastButton;
|
||||
var headerSearchButton;
|
||||
var headerAudioPlayerButton;
|
||||
var headerSyncButton;
|
||||
var enableLibraryNavDrawer = layoutManager.desktop;
|
||||
var skinHeader = document.querySelector('.skinHeader');
|
||||
var requiresUserRefresh = true;
|
||||
|
@ -931,6 +967,8 @@ define(['dom', 'layoutManager', 'inputManager', 'connectionManager', 'events', '
|
|||
updateUserInHeader();
|
||||
});
|
||||
events.on(playbackManager, 'playerchange', updateCastIcon);
|
||||
events.on(syncPlayManager, 'enabled', onSyncPlayEnabled);
|
||||
events.on(syncPlayManager, 'syncing', onSyncPlaySyncing);
|
||||
loadNavDrawer();
|
||||
return LibraryMenu;
|
||||
});
|
66
src/scripts/multiDownload.js
Normal file
66
src/scripts/multiDownload.js
Normal file
|
@ -0,0 +1,66 @@
|
|||
define(['browser'], function (browser) {
|
||||
'use strict';
|
||||
|
||||
function fallback(urls) {
|
||||
var i = 0;
|
||||
|
||||
(function createIframe() {
|
||||
var frame = document.createElement('iframe');
|
||||
frame.style.display = 'none';
|
||||
frame.src = urls[i++];
|
||||
document.documentElement.appendChild(frame);
|
||||
|
||||
// the download init has to be sequential otherwise IE only use the first
|
||||
var interval = setInterval(function () {
|
||||
if (frame.contentWindow.document.readyState === 'complete' || frame.contentWindow.document.readyState === 'interactive') {
|
||||
clearInterval(interval);
|
||||
|
||||
// Safari needs a timeout
|
||||
setTimeout(function () {
|
||||
frame.parentNode.removeChild(frame);
|
||||
}, 1000);
|
||||
|
||||
if (i < urls.length) {
|
||||
createIframe();
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
})();
|
||||
}
|
||||
|
||||
function sameDomain(url) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
|
||||
return location.hostname === a.hostname && location.protocol === a.protocol;
|
||||
}
|
||||
|
||||
function download(url) {
|
||||
var a = document.createElement('a');
|
||||
a.download = '';
|
||||
a.href = url;
|
||||
// firefox doesn't support `a.click()`...
|
||||
a.dispatchEvent(new MouseEvent('click'));
|
||||
}
|
||||
|
||||
return function (urls) {
|
||||
if (!urls) {
|
||||
throw new Error('`urls` required');
|
||||
}
|
||||
|
||||
if (typeof document.createElement('a').download === 'undefined') {
|
||||
return fallback(urls);
|
||||
}
|
||||
|
||||
var delay = 0;
|
||||
|
||||
urls.forEach(function (url) {
|
||||
// the download init has to be sequential for firefox if the urls are not on the same domain
|
||||
if (browser.firefox && !sameDomain(url)) {
|
||||
return setTimeout(download.bind(null, url), 100 * ++delay);
|
||||
}
|
||||
|
||||
download(url);
|
||||
});
|
||||
};
|
||||
});
|
|
@ -38,6 +38,14 @@ define([
|
|||
controller: 'auth/selectserver',
|
||||
type: 'selectserver'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/login.html',
|
||||
autoFocus: false,
|
||||
anonymous: true,
|
||||
startup: true,
|
||||
controller: 'auth/login',
|
||||
type: 'login'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/forgotpassword.html',
|
||||
anonymous: true,
|
||||
|
@ -52,12 +60,6 @@ define([
|
|||
controller: 'auth/forgotpasswordpin'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: '/addplugin.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/plugins/add'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/mypreferencesmenu.html',
|
||||
autoFocus: false,
|
||||
|
@ -129,19 +131,37 @@ define([
|
|||
path: '/dlnaprofile.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/dlna/dlnaprofile'
|
||||
controller: 'dashboard/dlna/profile'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/dlnaprofiles.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/dlna/dlnaprofiles'
|
||||
controller: 'dashboard/dlna/profiles'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/addplugin.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/plugins/add'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/library.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/mediaLibrary'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/librarydisplay.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/librarydisplay'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/dlnasettings.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/dlna/dlnasettings'
|
||||
controller: 'dashboard/dlna/settings'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/edititemmetadata.html',
|
||||
|
@ -154,6 +174,48 @@ define([
|
|||
roles: 'admin',
|
||||
controller: 'dashboard/encodingsettings'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/log.html',
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/logs'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/metadataimages.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/metadataImages'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/metadatanfo.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/metadatanfo'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/notificationsetting.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/notifications/notification'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/notificationsettings.html',
|
||||
controller: 'dashboard/notifications/notifications',
|
||||
autoFocus: false,
|
||||
roles: 'admin'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/playbackconfiguration.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/playback'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/availableplugins.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/plugins/available'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: '/home.html',
|
||||
autoFocus: false,
|
||||
|
@ -161,6 +223,10 @@ define([
|
|||
transition: 'fade',
|
||||
type: 'home'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/search.html',
|
||||
controller: 'searchpage'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/list.html',
|
||||
autoFocus: false,
|
||||
|
@ -169,22 +235,10 @@ define([
|
|||
});
|
||||
defineRoute({
|
||||
path: '/itemdetails.html',
|
||||
controller: 'itemdetailpage',
|
||||
controller: 'itemDetails',
|
||||
autoFocus: false,
|
||||
transition: 'fade'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/library.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/medialibrarypage'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/librarydisplay.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/librarydisplay'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/livetv.html',
|
||||
controller: 'livetv/livetvsuggested',
|
||||
|
@ -214,31 +268,6 @@ define([
|
|||
roles: 'admin',
|
||||
controller: 'livetvtuner'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/log.html',
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/logs'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/login.html',
|
||||
autoFocus: false,
|
||||
anonymous: true,
|
||||
startup: true,
|
||||
controller: 'auth/login',
|
||||
type: 'login'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/metadataimages.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/metadataimagespage'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/metadatanfo.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/metadatanfo'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/movies.html',
|
||||
autoFocus: false,
|
||||
|
@ -251,30 +280,6 @@ define([
|
|||
autoFocus: false,
|
||||
transition: 'fade'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/notificationsetting.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/notifications/notification'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/notificationsettings.html',
|
||||
controller: 'dashboard/notifications/notifications',
|
||||
autoFocus: false,
|
||||
roles: 'admin'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/playbackconfiguration.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/playbackconfiguration'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/availableplugins.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/plugins/available'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/installedplugins.html',
|
||||
autoFocus: false,
|
||||
|
@ -293,10 +298,6 @@ define([
|
|||
roles: 'admin',
|
||||
controller: 'dashboard/scheduledtasks/scheduledtasks'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/search.html',
|
||||
controller: 'searchpage'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/serveractivity.html',
|
||||
autoFocus: false,
|
||||
|
@ -313,7 +314,7 @@ define([
|
|||
path: '/streamingsettings.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'dashboard/streamingsettings'
|
||||
controller: 'dashboard/streaming'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/tv.html',
|
||||
|
@ -321,40 +322,41 @@ define([
|
|||
controller: 'shows/tvrecommended',
|
||||
transition: 'fade'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
path: '/useredit.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'useredit'
|
||||
controller: 'dashboard/users/useredit'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/userlibraryaccess.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'userlibraryaccess'
|
||||
controller: 'dashboard/users/userlibraryaccess'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/usernew.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'usernew'
|
||||
controller: 'dashboard/users/usernew'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/userparentalcontrol.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'userparentalcontrol'
|
||||
controller: 'dashboard/users/userparentalcontrol'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/userpassword.html',
|
||||
autoFocus: false,
|
||||
controller: 'userpasswordpage'
|
||||
controller: 'dashboard/users/userpasswordpage'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/userprofiles.html',
|
||||
autoFocus: false,
|
||||
roles: 'admin',
|
||||
controller: 'userprofilespage'
|
||||
controller: 'dashboard/users/userprofilespage'
|
||||
});
|
||||
|
||||
defineRoute({
|
||||
|
@ -373,7 +375,7 @@ define([
|
|||
path: '/wizardlibrary.html',
|
||||
autoFocus: false,
|
||||
anonymous: true,
|
||||
controller: 'medialibrarypage'
|
||||
controller: 'dashboard/mediaLibrary'
|
||||
});
|
||||
defineRoute({
|
||||
path: '/wizardsettings.html',
|
||||
|
|
137
src/scripts/scrollHelper.js
Normal file
137
src/scripts/scrollHelper.js
Normal file
|
@ -0,0 +1,137 @@
|
|||
define(['focusManager', 'dom', 'scrollStyles'], function (focusManager, dom) {
|
||||
'use strict';
|
||||
|
||||
function getBoundingClientRect(elem) {
|
||||
|
||||
// Support: BlackBerry 5, iOS 3 (original iPhone)
|
||||
// If we don't have gBCR, just use 0,0 rather than error
|
||||
if (elem.getBoundingClientRect) {
|
||||
return elem.getBoundingClientRect();
|
||||
} else {
|
||||
return { top: 0, left: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
function getPosition(scrollContainer, item, horizontal) {
|
||||
|
||||
var slideeOffset = getBoundingClientRect(scrollContainer);
|
||||
var itemOffset = getBoundingClientRect(item);
|
||||
|
||||
var offset = horizontal ? itemOffset.left - slideeOffset.left : itemOffset.top - slideeOffset.top;
|
||||
var size = horizontal ? itemOffset.width : itemOffset.height;
|
||||
if (!size && size !== 0) {
|
||||
size = item[horizontal ? 'offsetWidth' : 'offsetHeight'];
|
||||
}
|
||||
|
||||
var currentStart = horizontal ? scrollContainer.scrollLeft : scrollContainer.scrollTop;
|
||||
|
||||
offset += currentStart;
|
||||
|
||||
var frameSize = horizontal ? scrollContainer.offsetWidth : scrollContainer.offsetHeight;
|
||||
|
||||
var currentEnd = currentStart + frameSize;
|
||||
|
||||
var isVisible = offset >= currentStart && (offset + size) <= currentEnd;
|
||||
|
||||
return {
|
||||
start: offset,
|
||||
center: (offset - (frameSize / 2) + (size / 2)),
|
||||
end: offset - frameSize + size,
|
||||
size: size,
|
||||
isVisible: isVisible
|
||||
};
|
||||
}
|
||||
|
||||
function toCenter(container, elem, horizontal, skipWhenVisible) {
|
||||
var pos = getPosition(container, elem, horizontal);
|
||||
|
||||
if (skipWhenVisible && pos.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.scrollTo) {
|
||||
if (horizontal) {
|
||||
container.scrollTo(pos.center, 0);
|
||||
} else {
|
||||
container.scrollTo(0, pos.center);
|
||||
}
|
||||
} else {
|
||||
if (horizontal) {
|
||||
container.scrollLeft = Math.round(pos.center);
|
||||
} else {
|
||||
container.scrollTop = Math.round(pos.center);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toStart(container, elem, horizontal, skipWhenVisible) {
|
||||
var pos = getPosition(container, elem, horizontal);
|
||||
|
||||
if (skipWhenVisible && pos.isVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (container.scrollTo) {
|
||||
if (horizontal) {
|
||||
container.scrollTo(pos.start, 0);
|
||||
} else {
|
||||
container.scrollTo(0, pos.start);
|
||||
}
|
||||
} else {
|
||||
if (horizontal) {
|
||||
container.scrollLeft = Math.round(pos.start);
|
||||
} else {
|
||||
container.scrollTop = Math.round(pos.start);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function centerOnFocus(e, scrollSlider, horizontal) {
|
||||
var focused = focusManager.focusableParent(e.target);
|
||||
|
||||
if (focused) {
|
||||
toCenter(scrollSlider, focused, horizontal);
|
||||
}
|
||||
}
|
||||
|
||||
function centerOnFocusHorizontal(e) {
|
||||
centerOnFocus(e, this, true);
|
||||
}
|
||||
function centerOnFocusVertical(e) {
|
||||
centerOnFocus(e, this, false);
|
||||
}
|
||||
|
||||
return {
|
||||
getPosition: getPosition,
|
||||
centerFocus: {
|
||||
on: function (element, horizontal) {
|
||||
if (horizontal) {
|
||||
dom.addEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
} else {
|
||||
dom.addEventListener(element, 'focus', centerOnFocusVertical, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
},
|
||||
off: function (element, horizontal) {
|
||||
if (horizontal) {
|
||||
dom.removeEventListener(element, 'focus', centerOnFocusHorizontal, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
} else {
|
||||
dom.removeEventListener(element, 'focus', centerOnFocusVertical, {
|
||||
capture: true,
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
toCenter: toCenter,
|
||||
toStart: toStart
|
||||
};
|
||||
});
|
208
src/scripts/serverNotifications.js
Normal file
208
src/scripts/serverNotifications.js
Normal file
|
@ -0,0 +1,208 @@
|
|||
define(['connectionManager', 'playbackManager', 'syncPlayManager', 'events', 'inputManager', 'focusManager', 'appRouter'], function (connectionManager, playbackManager, syncPlayManager, events, inputManager, focusManager, appRouter) {
|
||||
'use strict';
|
||||
|
||||
var serverNotifications = {};
|
||||
|
||||
function notifyApp() {
|
||||
inputManager.notify();
|
||||
}
|
||||
|
||||
function displayMessage(cmd) {
|
||||
var args = cmd.Arguments;
|
||||
if (args.TimeoutMs) {
|
||||
require(['toast'], function (toast) {
|
||||
toast({ title: args.Header, text: args.Text });
|
||||
});
|
||||
} else {
|
||||
require(['alert'], function (alert) {
|
||||
alert({ title: args.Header, text: args.Text });
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function displayContent(cmd, apiClient) {
|
||||
if (!playbackManager.isPlayingLocally(['Video', 'Book'])) {
|
||||
appRouter.showItem(cmd.Arguments.ItemId, apiClient.serverId());
|
||||
}
|
||||
}
|
||||
|
||||
function playTrailers(apiClient, itemId) {
|
||||
apiClient.getItem(apiClient.getCurrentUserId(), itemId).then(function (item) {
|
||||
playbackManager.playTrailers(item);
|
||||
});
|
||||
}
|
||||
|
||||
function processGeneralCommand(cmd, apiClient) {
|
||||
console.debug('Received command: ' + cmd.Name);
|
||||
switch (cmd.Name) {
|
||||
case 'Select':
|
||||
inputManager.trigger('select');
|
||||
return;
|
||||
case 'Back':
|
||||
inputManager.trigger('back');
|
||||
return;
|
||||
case 'MoveUp':
|
||||
inputManager.trigger('up');
|
||||
return;
|
||||
case 'MoveDown':
|
||||
inputManager.trigger('down');
|
||||
return;
|
||||
case 'MoveLeft':
|
||||
inputManager.trigger('left');
|
||||
return;
|
||||
case 'MoveRight':
|
||||
inputManager.trigger('right');
|
||||
return;
|
||||
case 'PageUp':
|
||||
inputManager.trigger('pageup');
|
||||
return;
|
||||
case 'PageDown':
|
||||
inputManager.trigger('pagedown');
|
||||
return;
|
||||
case 'PlayTrailers':
|
||||
playTrailers(apiClient, cmd.Arguments.ItemId);
|
||||
break;
|
||||
case 'SetRepeatMode':
|
||||
playbackManager.setRepeatMode(cmd.Arguments.RepeatMode);
|
||||
break;
|
||||
case 'VolumeUp':
|
||||
inputManager.trigger('volumeup');
|
||||
return;
|
||||
case 'VolumeDown':
|
||||
inputManager.trigger('volumedown');
|
||||
return;
|
||||
case 'ChannelUp':
|
||||
inputManager.trigger('channelup');
|
||||
return;
|
||||
case 'ChannelDown':
|
||||
inputManager.trigger('channeldown');
|
||||
return;
|
||||
case 'Mute':
|
||||
inputManager.trigger('mute');
|
||||
return;
|
||||
case 'Unmute':
|
||||
inputManager.trigger('unmute');
|
||||
return;
|
||||
case 'ToggleMute':
|
||||
inputManager.trigger('togglemute');
|
||||
return;
|
||||
case 'SetVolume':
|
||||
notifyApp();
|
||||
playbackManager.setVolume(cmd.Arguments.Volume);
|
||||
break;
|
||||
case 'SetAudioStreamIndex':
|
||||
notifyApp();
|
||||
playbackManager.setAudioStreamIndex(parseInt(cmd.Arguments.Index));
|
||||
break;
|
||||
case 'SetSubtitleStreamIndex':
|
||||
notifyApp();
|
||||
playbackManager.setSubtitleStreamIndex(parseInt(cmd.Arguments.Index));
|
||||
break;
|
||||
case 'ToggleFullscreen':
|
||||
inputManager.trigger('togglefullscreen');
|
||||
return;
|
||||
case 'GoHome':
|
||||
inputManager.trigger('home');
|
||||
return;
|
||||
case 'GoToSettings':
|
||||
inputManager.trigger('settings');
|
||||
return;
|
||||
case 'DisplayContent':
|
||||
displayContent(cmd, apiClient);
|
||||
break;
|
||||
case 'GoToSearch':
|
||||
inputManager.trigger('search');
|
||||
return;
|
||||
case 'DisplayMessage':
|
||||
displayMessage(cmd);
|
||||
break;
|
||||
case 'ToggleOsd':
|
||||
// todo
|
||||
break;
|
||||
case 'ToggleContextMenu':
|
||||
// todo
|
||||
break;
|
||||
case 'TakeScreenShot':
|
||||
// todo
|
||||
break;
|
||||
case 'SendKey':
|
||||
// todo
|
||||
break;
|
||||
case 'SendString':
|
||||
// todo
|
||||
focusManager.sendText(cmd.Arguments.String);
|
||||
break;
|
||||
default:
|
||||
console.debug('processGeneralCommand does not recognize: ' + cmd.Name);
|
||||
break;
|
||||
}
|
||||
|
||||
notifyApp();
|
||||
}
|
||||
|
||||
function onMessageReceived(e, msg) {
|
||||
var apiClient = this;
|
||||
if (msg.MessageType === 'Play') {
|
||||
notifyApp();
|
||||
var serverId = apiClient.serverInfo().Id;
|
||||
if (msg.Data.PlayCommand === 'PlayNext') {
|
||||
playbackManager.queueNext({ ids: msg.Data.ItemIds, serverId: serverId });
|
||||
} else if (msg.Data.PlayCommand === 'PlayLast') {
|
||||
playbackManager.queue({ ids: msg.Data.ItemIds, serverId: serverId });
|
||||
} else {
|
||||
playbackManager.play({
|
||||
ids: msg.Data.ItemIds,
|
||||
startPositionTicks: msg.Data.StartPositionTicks,
|
||||
mediaSourceId: msg.Data.MediaSourceId,
|
||||
audioStreamIndex: msg.Data.AudioStreamIndex,
|
||||
subtitleStreamIndex: msg.Data.SubtitleStreamIndex,
|
||||
startIndex: msg.Data.StartIndex,
|
||||
serverId: serverId
|
||||
});
|
||||
}
|
||||
} else if (msg.MessageType === 'Playstate') {
|
||||
if (msg.Data.Command === 'Stop') {
|
||||
inputManager.trigger('stop');
|
||||
} else if (msg.Data.Command === 'Pause') {
|
||||
inputManager.trigger('pause');
|
||||
} else if (msg.Data.Command === 'Unpause') {
|
||||
inputManager.trigger('play');
|
||||
} else if (msg.Data.Command === 'PlayPause') {
|
||||
inputManager.trigger('playpause');
|
||||
} else if (msg.Data.Command === 'Seek') {
|
||||
playbackManager.seek(msg.Data.SeekPositionTicks);
|
||||
} else if (msg.Data.Command === 'NextTrack') {
|
||||
inputManager.trigger('next');
|
||||
} else if (msg.Data.Command === 'PreviousTrack') {
|
||||
inputManager.trigger('previous');
|
||||
} else {
|
||||
notifyApp();
|
||||
}
|
||||
} else if (msg.MessageType === 'GeneralCommand') {
|
||||
var cmd = msg.Data;
|
||||
processGeneralCommand(cmd, apiClient);
|
||||
} else if (msg.MessageType === 'UserDataChanged') {
|
||||
if (msg.Data.UserId === apiClient.getCurrentUserId()) {
|
||||
for (var i = 0, length = msg.Data.UserDataList.length; i < length; i++) {
|
||||
events.trigger(serverNotifications, 'UserDataChanged', [apiClient, msg.Data.UserDataList[i]]);
|
||||
}
|
||||
}
|
||||
} else if (msg.MessageType === 'SyncPlayCommand') {
|
||||
syncPlayManager.processCommand(msg.Data, apiClient);
|
||||
} else if (msg.MessageType === 'SyncPlayGroupUpdate') {
|
||||
syncPlayManager.processGroupUpdate(msg.Data, apiClient);
|
||||
} else {
|
||||
events.trigger(serverNotifications, msg.MessageType, [apiClient, msg.Data]);
|
||||
}
|
||||
}
|
||||
function bindEvents(apiClient) {
|
||||
events.off(apiClient, 'message', onMessageReceived);
|
||||
events.on(apiClient, 'message', onMessageReceived);
|
||||
}
|
||||
|
||||
connectionManager.getApiClients().forEach(bindEvents);
|
||||
events.on(connectionManager, 'apiclientcreated', function (e, newApiClient) {
|
||||
bindEvents(newApiClient);
|
||||
});
|
||||
return serverNotifications;
|
||||
});
|
|
@ -153,6 +153,14 @@ import events from 'events';
|
|||
return this.get('datetimelocale', false);
|
||||
}
|
||||
|
||||
export function chromecastVersion(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('chromecastVersion', val.toString());
|
||||
}
|
||||
|
||||
return this.get('chromecastVersion') || 'stable';
|
||||
}
|
||||
|
||||
export function skipBackLength(val) {
|
||||
if (val !== undefined) {
|
||||
return this.set('skipBackLength', val.toString());
|
||||
|
|
|
@ -2,7 +2,17 @@ let data;
|
|||
|
||||
function getConfig() {
|
||||
if (data) return Promise.resolve(data);
|
||||
return fetch('/config.json?nocache=' + new Date().getUTCMilliseconds()).then(function (response) {
|
||||
return fetch('/config.json?nocache=' + new Date().getUTCMilliseconds()).then(response => {
|
||||
data = response.json();
|
||||
return data;
|
||||
}).catch(error => {
|
||||
console.warn('web config file is missing so the template will be used');
|
||||
return getDefaultConfig();
|
||||
});
|
||||
}
|
||||
|
||||
function getDefaultConfig() {
|
||||
return fetch('/config.template.json').then(function (response) {
|
||||
data = response.json();
|
||||
return data;
|
||||
});
|
||||
|
|
24
src/scripts/shell.js
Normal file
24
src/scripts/shell.js
Normal file
|
@ -0,0 +1,24 @@
|
|||
define([], function () {
|
||||
'use strict';
|
||||
|
||||
return {
|
||||
openUrl: function (url, target) {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.openUrl(url, target);
|
||||
} else {
|
||||
window.open(url, target || '_blank');
|
||||
}
|
||||
|
||||
},
|
||||
enableFullscreen: function () {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.enableFullscreen();
|
||||
}
|
||||
},
|
||||
disableFullscreen: function () {
|
||||
if (window.NativeShell) {
|
||||
window.NativeShell.disableFullscreen();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -314,6 +314,13 @@ var AppInfo = {};
|
|||
return obj;
|
||||
}
|
||||
|
||||
function returnDefault(obj) {
|
||||
if (obj.default === null) {
|
||||
throw new Error('Object has no default!');
|
||||
}
|
||||
return obj.default;
|
||||
}
|
||||
|
||||
function getBowerPath() {
|
||||
return 'libraries';
|
||||
}
|
||||
|
@ -375,8 +382,8 @@ var AppInfo = {};
|
|||
|
||||
define('filesystem', [scriptsPath + '/filesystem'], returnFirstDependency);
|
||||
|
||||
define('lazyLoader', [componentsPath + '/lazyloader/lazyloader-intersectionobserver'], returnFirstDependency);
|
||||
define('shell', [componentsPath + '/shell'], returnFirstDependency);
|
||||
define('lazyLoader', [componentsPath + '/lazyLoader/lazyLoaderIntersectionObserver'], returnFirstDependency);
|
||||
define('shell', [scriptsPath + '/shell'], returnFirstDependency);
|
||||
|
||||
if ('registerElement' in document) {
|
||||
define('registerElement', []);
|
||||
|
@ -397,8 +404,8 @@ var AppInfo = {};
|
|||
define('prompt', [componentsPath + '/prompt/prompt'], returnFirstDependency);
|
||||
|
||||
define('loading', [componentsPath + '/loading/loading'], returnFirstDependency);
|
||||
define('multi-download', [componentsPath + '/multidownload'], returnFirstDependency);
|
||||
define('fileDownloader', [componentsPath + '/filedownloader'], returnFirstDependency);
|
||||
define('multi-download', [scriptsPath + '/multiDownload'], returnFirstDependency);
|
||||
define('fileDownloader', [scriptsPath + '/fileDownloader'], returnFirstDependency);
|
||||
|
||||
define('castSenderApiLoader', [componentsPath + '/castSenderApi'], returnFirstDependency);
|
||||
}
|
||||
|
@ -481,16 +488,16 @@ var AppInfo = {};
|
|||
var list = [
|
||||
'components/playback/playaccessvalidation',
|
||||
'components/playback/experimentalwarnings',
|
||||
'components/htmlaudioplayer/plugin',
|
||||
'components/htmlvideoplayer/plugin',
|
||||
'components/photoplayer/plugin',
|
||||
'components/htmlAudioPlayer/plugin',
|
||||
'components/htmlVideoPlayer/plugin',
|
||||
'components/photoPlayer/plugin',
|
||||
'components/youtubeplayer/plugin',
|
||||
'components/backdropscreensaver/plugin',
|
||||
'components/logoscreensaver/plugin'
|
||||
'components/backdropScreensaver/plugin',
|
||||
'components/logoScreensaver/plugin'
|
||||
];
|
||||
|
||||
if (appHost.supports('remotecontrol')) {
|
||||
list.push('components/sessionplayer');
|
||||
list.push('components/sessionPlayer');
|
||||
|
||||
if (browser.chrome || browser.opera) {
|
||||
list.push('components/chromecast/chromecastplayer');
|
||||
|
@ -532,16 +539,16 @@ var AppInfo = {};
|
|||
|
||||
window.Emby.Page = appRouter;
|
||||
|
||||
require(['emby-button', 'scripts/themeloader', 'libraryMenu', 'scripts/routes'], function () {
|
||||
require(['emby-button', 'scripts/themeLoader', 'libraryMenu', 'scripts/routes'], function () {
|
||||
Emby.Page.start({
|
||||
click: false,
|
||||
hashbang: true
|
||||
});
|
||||
|
||||
require(['components/thememediaplayer', 'scripts/autobackdrops']);
|
||||
require(['components/themeMediaPlayer', 'scripts/autoBackdrops']);
|
||||
|
||||
if (!browser.tv && !browser.xboxOne && !browser.ps4) {
|
||||
require(['components/nowplayingbar/nowplayingbar']);
|
||||
require(['components/nowPlayingBar/nowPlayingBar']);
|
||||
}
|
||||
|
||||
if (appHost.supports('remotecontrol')) {
|
||||
|
@ -630,23 +637,23 @@ var AppInfo = {};
|
|||
var scriptsPath = getScriptsPath();
|
||||
|
||||
var paths = {
|
||||
browserdeviceprofile: 'scripts/browserdeviceprofile',
|
||||
browserdeviceprofile: 'scripts/browserDeviceProfile',
|
||||
browser: 'scripts/browser',
|
||||
libraryBrowser: 'scripts/librarybrowser',
|
||||
libraryBrowser: 'scripts/libraryBrowser',
|
||||
inputManager: 'scripts/inputManager',
|
||||
datetime: 'scripts/datetime',
|
||||
globalize: 'scripts/globalize',
|
||||
dfnshelper: 'scripts/dfnshelper',
|
||||
libraryMenu: 'scripts/librarymenu',
|
||||
libraryMenu: 'scripts/libraryMenu',
|
||||
playlisteditor: componentsPath + '/playlisteditor/playlisteditor',
|
||||
medialibrarycreator: componentsPath + '/medialibrarycreator/medialibrarycreator',
|
||||
medialibraryeditor: componentsPath + '/medialibraryeditor/medialibraryeditor',
|
||||
imageoptionseditor: componentsPath + '/imageoptionseditor/imageoptionseditor',
|
||||
medialibrarycreator: componentsPath + '/mediaLibraryCreator/mediaLibraryCreator',
|
||||
medialibraryeditor: componentsPath + '/mediaLibraryEditor/mediaLibraryEditor',
|
||||
imageoptionseditor: componentsPath + '/imageOptionsEditor/imageOptionsEditor',
|
||||
apphost: componentsPath + '/apphost',
|
||||
visibleinviewport: bowerPath + '/visibleinviewport',
|
||||
qualityoptions: componentsPath + '/qualityoptions',
|
||||
qualityoptions: componentsPath + '/qualityOptions',
|
||||
focusManager: componentsPath + '/focusManager',
|
||||
itemHelper: componentsPath + '/itemhelper',
|
||||
itemHelper: componentsPath + '/itemHelper',
|
||||
itemShortcuts: componentsPath + '/shortcuts',
|
||||
playQueueManager: componentsPath + '/playback/playqueuemanager',
|
||||
nowPlayingHelper: componentsPath + '/playback/nowplayinghelper',
|
||||
|
@ -738,7 +745,7 @@ var AppInfo = {};
|
|||
|
||||
// there are several objects that need to be instantiated
|
||||
// TODO find a better way to do this
|
||||
define('appFooter', [componentsPath + '/appfooter/appfooter'], returnFirstDependency);
|
||||
define('appFooter', [componentsPath + '/appFooter/appFooter'], returnFirstDependency);
|
||||
define('appFooter-shared', ['appFooter'], createSharedAppFooter);
|
||||
|
||||
// TODO remove these libraries
|
||||
|
@ -773,23 +780,23 @@ var AppInfo = {};
|
|||
|
||||
define('chromecastHelper', [componentsPath + '/chromecast/chromecasthelpers'], returnFirstDependency);
|
||||
define('mediaSession', [componentsPath + '/playback/mediasession'], returnFirstDependency);
|
||||
define('actionsheet', [componentsPath + '/actionsheet/actionsheet'], returnFirstDependency);
|
||||
define('tunerPicker', [componentsPath + '/tunerpicker'], returnFirstDependency);
|
||||
define('actionsheet', [componentsPath + '/actionSheet/actionSheet'], returnFirstDependency);
|
||||
define('tunerPicker', [componentsPath + '/tunerPicker'], returnFirstDependency);
|
||||
define('mainTabsManager', [componentsPath + '/maintabsmanager'], returnFirstDependency);
|
||||
define('imageLoader', [componentsPath + '/images/imageLoader'], returnFirstDependency);
|
||||
define('directorybrowser', [componentsPath + '/directorybrowser/directorybrowser'], returnFirstDependency);
|
||||
define('metadataEditor', [componentsPath + '/metadataeditor/metadataeditor'], returnFirstDependency);
|
||||
define('personEditor', [componentsPath + '/metadataeditor/personeditor'], returnFirstDependency);
|
||||
define('metadataEditor', [componentsPath + '/metadataEditor/metadataEditor'], returnFirstDependency);
|
||||
define('personEditor', [componentsPath + '/metadataEditor/personEditor'], returnFirstDependency);
|
||||
define('playerSelectionMenu', [componentsPath + '/playback/playerSelectionMenu'], returnFirstDependency);
|
||||
define('playerSettingsMenu', [componentsPath + '/playback/playersettingsmenu'], returnFirstDependency);
|
||||
define('playMethodHelper', [componentsPath + '/playback/playmethodhelper'], returnFirstDependency);
|
||||
define('brightnessOsd', [componentsPath + '/playback/brightnessosd'], returnFirstDependency);
|
||||
define('alphaNumericShortcuts', [scriptsPath + '/alphanumericshortcuts'], returnFirstDependency);
|
||||
define('multiSelect', [componentsPath + '/multiselect/multiselect'], returnFirstDependency);
|
||||
define('alphaPicker', [componentsPath + '/alphapicker/alphapicker'], returnFirstDependency);
|
||||
define('multiSelect', [componentsPath + '/multiSelect/multiSelect'], returnFirstDependency);
|
||||
define('alphaPicker', [componentsPath + '/alphaPicker/alphaPicker'], returnFirstDependency);
|
||||
define('tabbedView', [componentsPath + '/tabbedview/tabbedview'], returnFirstDependency);
|
||||
define('itemsTab', [componentsPath + '/tabbedview/itemstab'], returnFirstDependency);
|
||||
define('collectionEditor', [componentsPath + '/collectioneditor/collectioneditor'], returnFirstDependency);
|
||||
define('collectionEditor', [componentsPath + '/collectionEditor/collectionEditor'], returnFirstDependency);
|
||||
define('serverRestartDialog', [componentsPath + '/serverRestartDialog'], returnFirstDependency);
|
||||
define('playlistEditor', [componentsPath + '/playlisteditor/playlisteditor'], returnFirstDependency);
|
||||
define('recordingCreator', [componentsPath + '/recordingcreator/recordingcreator'], returnFirstDependency);
|
||||
|
@ -803,9 +810,9 @@ var AppInfo = {};
|
|||
define('itemIdentifier', [componentsPath + '/itemidentifier/itemidentifier'], returnFirstDependency);
|
||||
define('itemMediaInfo', [componentsPath + '/itemMediaInfo/itemMediaInfo'], returnFirstDependency);
|
||||
define('mediaInfo', [componentsPath + '/mediainfo/mediainfo'], returnFirstDependency);
|
||||
define('itemContextMenu', [componentsPath + '/itemcontextmenu'], returnFirstDependency);
|
||||
define('itemContextMenu', [componentsPath + '/itemContextMenu'], returnFirstDependency);
|
||||
define('imageEditor', [componentsPath + '/imageeditor/imageeditor'], returnFirstDependency);
|
||||
define('imageDownloader', [componentsPath + '/imagedownloader/imagedownloader'], returnFirstDependency);
|
||||
define('imageDownloader', [componentsPath + '/imageDownloader/imageDownloader'], returnFirstDependency);
|
||||
define('dom', [scriptsPath + '/dom'], returnFirstDependency);
|
||||
define('playerStats', [componentsPath + '/playerstats/playerstats'], returnFirstDependency);
|
||||
define('searchFields', [componentsPath + '/search/searchfields'], returnFirstDependency);
|
||||
|
@ -814,10 +821,14 @@ var AppInfo = {};
|
|||
define('subtitleAppearanceHelper', [componentsPath + '/subtitlesettings/subtitleappearancehelper'], returnFirstDependency);
|
||||
define('subtitleSettings', [componentsPath + '/subtitlesettings/subtitlesettings'], returnFirstDependency);
|
||||
define('settingsHelper', [componentsPath + '/settingshelper'], returnFirstDependency);
|
||||
define('displaySettings', [componentsPath + '/displaysettings/displaysettings'], returnFirstDependency);
|
||||
define('playbackSettings', [componentsPath + '/playbacksettings/playbacksettings'], returnFirstDependency);
|
||||
define('homescreenSettings', [componentsPath + '/homescreensettings/homescreensettings'], returnFirstDependency);
|
||||
define('displaySettings', [componentsPath + '/displaySettings/displaySettings'], returnFirstDependency);
|
||||
define('playbackSettings', [componentsPath + '/playbackSettings/playbackSettings'], returnFirstDependency);
|
||||
define('homescreenSettings', [componentsPath + '/homeScreenSettings/homeScreenSettings'], returnFirstDependency);
|
||||
define('playbackManager', [componentsPath + '/playback/playbackmanager'], getPlaybackManager);
|
||||
define('timeSyncManager', [componentsPath + '/syncplay/timeSyncManager'], returnDefault);
|
||||
define('groupSelectionMenu', [componentsPath + '/syncplay/groupSelectionMenu'], returnFirstDependency);
|
||||
define('syncPlayManager', [componentsPath + '/syncplay/syncPlayManager'], returnDefault);
|
||||
define('playbackPermissionManager', [componentsPath + '/syncplay/playbackPermissionManager'], returnDefault);
|
||||
define('layoutManager', [componentsPath + '/layoutManager', 'apphost'], getLayoutManager);
|
||||
define('homeSections', [componentsPath + '/homesections/homesections'], returnFirstDependency);
|
||||
define('playMenu', [componentsPath + '/playmenu'], returnFirstDependency);
|
||||
|
@ -827,10 +838,10 @@ var AppInfo = {};
|
|||
define('cardBuilder', [componentsPath + '/cardbuilder/cardBuilder'], returnFirstDependency);
|
||||
define('peoplecardbuilder', [componentsPath + '/cardbuilder/peoplecardbuilder'], returnFirstDependency);
|
||||
define('chaptercardbuilder', [componentsPath + '/cardbuilder/chaptercardbuilder'], returnFirstDependency);
|
||||
define('deleteHelper', [componentsPath + '/deletehelper'], returnFirstDependency);
|
||||
define('deleteHelper', [scriptsPath + '/deleteHelper'], returnFirstDependency);
|
||||
define('tvguide', [componentsPath + '/guide/guide'], returnFirstDependency);
|
||||
define('guide-settings-dialog', [componentsPath + '/guide/guide-settings'], returnFirstDependency);
|
||||
define('loadingDialog', [componentsPath + '/loadingdialog/loadingdialog'], returnFirstDependency);
|
||||
define('loadingDialog', [componentsPath + '/loadingDialog/loadingDialog'], returnFirstDependency);
|
||||
define('viewManager', [componentsPath + '/viewManager/viewManager'], function (viewManager) {
|
||||
window.ViewManager = viewManager;
|
||||
viewManager.dispatchPageEvents(true);
|
||||
|
@ -844,18 +855,17 @@ var AppInfo = {};
|
|||
define('viewSettings', [componentsPath + '/viewsettings/viewsettings'], returnFirstDependency);
|
||||
define('filterMenu', [componentsPath + '/filtermenu/filtermenu'], returnFirstDependency);
|
||||
define('sortMenu', [componentsPath + '/sortmenu/sortmenu'], returnFirstDependency);
|
||||
define('idb', [componentsPath + '/idb'], returnFirstDependency);
|
||||
define('sanitizefilename', [componentsPath + '/sanitizefilename'], returnFirstDependency);
|
||||
define('sanitizefilename', [componentsPath + '/sanitizeFilename'], returnFirstDependency);
|
||||
define('toast', [componentsPath + '/toast/toast'], returnFirstDependency);
|
||||
define('scrollHelper', [componentsPath + '/scrollhelper'], returnFirstDependency);
|
||||
define('touchHelper', [componentsPath + '/touchhelper'], returnFirstDependency);
|
||||
define('imageUploader', [componentsPath + '/imageuploader/imageuploader'], returnFirstDependency);
|
||||
define('scrollHelper', [scriptsPath + '/scrollHelper'], returnFirstDependency);
|
||||
define('touchHelper', [scriptsPath + '/touchHelper'], returnFirstDependency);
|
||||
define('imageUploader', [componentsPath + '/imageUploader/imageUploader'], returnFirstDependency);
|
||||
define('htmlMediaHelper', [componentsPath + '/htmlMediaHelper'], returnFirstDependency);
|
||||
define('viewContainer', [componentsPath + '/viewContainer'], returnFirstDependency);
|
||||
define('dialogHelper', [componentsPath + '/dialogHelper/dialogHelper'], returnFirstDependency);
|
||||
define('serverNotifications', [componentsPath + '/serverNotifications'], returnFirstDependency);
|
||||
define('serverNotifications', [scriptsPath + '/serverNotifications'], returnFirstDependency);
|
||||
define('skinManager', [componentsPath + '/skinManager'], returnFirstDependency);
|
||||
define('keyboardnavigation', [scriptsPath + '/keyboardnavigation'], returnFirstDependency);
|
||||
define('keyboardnavigation', [scriptsPath + '/keyboardNavigation'], returnFirstDependency);
|
||||
define('mouseManager', [scriptsPath + '/mouseManager'], returnFirstDependency);
|
||||
define('scrollManager', [componentsPath + '/scrollManager'], returnFirstDependency);
|
||||
define('autoFocuser', [componentsPath + '/autoFocuser'], returnFirstDependency);
|
||||
|
|
171
src/scripts/touchHelper.js
Normal file
171
src/scripts/touchHelper.js
Normal file
|
@ -0,0 +1,171 @@
|
|||
define(['dom', 'events'], function (dom, events) {
|
||||
'use strict';
|
||||
|
||||
function getTouches(e) {
|
||||
|
||||
return e.changedTouches || e.targetTouches || e.touches;
|
||||
}
|
||||
|
||||
function TouchHelper(elem, options) {
|
||||
|
||||
options = options || {};
|
||||
var touchTarget;
|
||||
var touchStartX;
|
||||
var touchStartY;
|
||||
var lastDeltaX;
|
||||
var lastDeltaY;
|
||||
var thresholdYMet;
|
||||
var self = this;
|
||||
|
||||
var swipeXThreshold = options.swipeXThreshold || 50;
|
||||
var swipeYThreshold = options.swipeYThreshold || 50;
|
||||
var swipeXMaxY = 30;
|
||||
|
||||
var excludeTagNames = options.ignoreTagNames || [];
|
||||
|
||||
var touchStart = function (e) {
|
||||
|
||||
var touch = getTouches(e)[0];
|
||||
touchTarget = null;
|
||||
touchStartX = 0;
|
||||
touchStartY = 0;
|
||||
lastDeltaX = null;
|
||||
lastDeltaY = null;
|
||||
thresholdYMet = false;
|
||||
|
||||
if (touch) {
|
||||
|
||||
var currentTouchTarget = touch.target;
|
||||
|
||||
if (dom.parentWithTag(currentTouchTarget, excludeTagNames)) {
|
||||
return;
|
||||
}
|
||||
|
||||
touchTarget = currentTouchTarget;
|
||||
touchStartX = touch.clientX;
|
||||
touchStartY = touch.clientY;
|
||||
}
|
||||
};
|
||||
|
||||
var touchEnd = function (e) {
|
||||
|
||||
var isTouchMove = e.type === 'touchmove';
|
||||
|
||||
if (touchTarget) {
|
||||
var touch = getTouches(e)[0];
|
||||
|
||||
var deltaX;
|
||||
var deltaY;
|
||||
|
||||
var clientX;
|
||||
var clientY;
|
||||
|
||||
if (touch) {
|
||||
clientX = touch.clientX || 0;
|
||||
clientY = touch.clientY || 0;
|
||||
deltaX = clientX - (touchStartX || 0);
|
||||
deltaY = clientY - (touchStartY || 0);
|
||||
} else {
|
||||
deltaX = 0;
|
||||
deltaY = 0;
|
||||
}
|
||||
|
||||
var currentDeltaX = lastDeltaX == null ? deltaX : (deltaX - lastDeltaX);
|
||||
var currentDeltaY = lastDeltaY == null ? deltaY : (deltaY - lastDeltaY);
|
||||
|
||||
lastDeltaX = deltaX;
|
||||
lastDeltaY = deltaY;
|
||||
|
||||
if (deltaX > swipeXThreshold && Math.abs(deltaY) < swipeXMaxY) {
|
||||
events.trigger(self, 'swiperight', [touchTarget]);
|
||||
} else if (deltaX < (0 - swipeXThreshold) && Math.abs(deltaY) < swipeXMaxY) {
|
||||
events.trigger(self, 'swipeleft', [touchTarget]);
|
||||
} else if ((deltaY < (0 - swipeYThreshold) || thresholdYMet) && Math.abs(deltaX) < swipeXMaxY) {
|
||||
|
||||
thresholdYMet = true;
|
||||
|
||||
events.trigger(self, 'swipeup', [touchTarget, {
|
||||
deltaY: deltaY,
|
||||
deltaX: deltaX,
|
||||
clientX: clientX,
|
||||
clientY: clientY,
|
||||
currentDeltaX: currentDeltaX,
|
||||
currentDeltaY: currentDeltaY
|
||||
}]);
|
||||
} else if ((deltaY > swipeYThreshold || thresholdYMet) && Math.abs(deltaX) < swipeXMaxY) {
|
||||
thresholdYMet = true;
|
||||
|
||||
events.trigger(self, 'swipedown', [touchTarget, {
|
||||
deltaY: deltaY,
|
||||
deltaX: deltaX,
|
||||
clientX: clientX,
|
||||
clientY: clientY,
|
||||
currentDeltaX: currentDeltaX,
|
||||
currentDeltaY: currentDeltaY
|
||||
}]);
|
||||
}
|
||||
|
||||
if (isTouchMove && options.preventDefaultOnMove) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
if (!isTouchMove) {
|
||||
touchTarget = null;
|
||||
touchStartX = 0;
|
||||
touchStartY = 0;
|
||||
lastDeltaX = null;
|
||||
lastDeltaY = null;
|
||||
thresholdYMet = false;
|
||||
}
|
||||
};
|
||||
|
||||
this.touchStart = touchStart;
|
||||
this.touchEnd = touchEnd;
|
||||
|
||||
dom.addEventListener(elem, 'touchstart', touchStart, {
|
||||
passive: true
|
||||
});
|
||||
if (options.triggerOnMove) {
|
||||
dom.addEventListener(elem, 'touchmove', touchEnd, {
|
||||
passive: !options.preventDefaultOnMove
|
||||
});
|
||||
}
|
||||
dom.addEventListener(elem, 'touchend', touchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.addEventListener(elem, 'touchcancel', touchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
|
||||
TouchHelper.prototype.destroy = function () {
|
||||
|
||||
var elem = this.elem;
|
||||
|
||||
if (elem) {
|
||||
var touchStart = this.touchStart;
|
||||
var touchEnd = this.touchEnd;
|
||||
|
||||
dom.removeEventListener(elem, 'touchstart', touchStart, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(elem, 'touchmove', touchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(elem, 'touchend', touchEnd, {
|
||||
passive: true
|
||||
});
|
||||
dom.removeEventListener(elem, 'touchcancel', touchEnd, {
|
||||
passive: true
|
||||
});
|
||||
}
|
||||
|
||||
this.touchStart = null;
|
||||
this.touchEnd = null;
|
||||
|
||||
this.elem = null;
|
||||
};
|
||||
|
||||
return TouchHelper;
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue