mirror of
https://github.com/jellyfin/jellyfin-web
synced 2025-03-30 19:56:21 +00:00
Merge remote-tracking branch 'upstream/master' into metadata-editor
This commit is contained in:
commit
9f14bde2cb
117 changed files with 560 additions and 563 deletions
|
@ -50,7 +50,7 @@ define(['dialogHelper', 'datetime', 'globalize', 'emby-select', 'paper-icon-butt
|
|||
show: function (options) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'components/accessschedule/accessschedule.template.html', true);
|
||||
xhr.open('GET', 'components/accessSchedule/accessSchedule.template.html', true);
|
||||
|
||||
xhr.onload = function (e) {
|
||||
var template = this.response;
|
|
@ -3,7 +3,7 @@ import layoutManager from 'layoutManager';
|
|||
import globalize from 'globalize';
|
||||
import dom from 'dom';
|
||||
import 'emby-button';
|
||||
import 'css!./actionsheet';
|
||||
import 'css!./actionSheet';
|
||||
import 'material-icons';
|
||||
import 'scrollStyles';
|
||||
import 'listViewStyle';
|
|
@ -1,4 +1,4 @@
|
|||
define(['browser', 'css!./appfooter'], function (browser) {
|
||||
define(['browser', 'css!./appFooter'], function (browser) {
|
||||
'use strict';
|
||||
|
||||
function render(options) {
|
|
@ -54,7 +54,13 @@ define(['appSettings', 'userSettings', 'playbackManager', 'connectionManager', '
|
|||
|
||||
// production version registered with google
|
||||
// replace this value if you want to test changes on another instance
|
||||
var applicationID = 'F007D354';
|
||||
var applicationStable = 'F007D354';
|
||||
var applicationNightly = '6F511C87';
|
||||
|
||||
var applicationID = applicationStable;
|
||||
if (userSettings.chromecastVersion === 'nightly') {
|
||||
applicationID = applicationNightly;
|
||||
}
|
||||
|
||||
var messageNamespace = 'urn:x-cast:com.connectsdk';
|
||||
|
||||
|
|
|
@ -1,54 +0,0 @@
|
|||
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
|
||||
};
|
|
@ -269,7 +269,7 @@ define(['require', 'browser', 'layoutManager', 'appSettings', 'pluginManager', '
|
|||
}
|
||||
|
||||
function embed(options, self) {
|
||||
require(['text!./displaysettings.template.html'], function (template) {
|
||||
require(['text!./displaySettings.template.html'], function (template) {
|
||||
options.element.innerHTML = globalize.translateDocument(template, 'core');
|
||||
options.element.querySelector('form').addEventListener('submit', onSubmit.bind(self));
|
||||
if (options.enableSaveButton) {
|
|
@ -1,14 +0,0 @@
|
|||
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;
|
||||
}));
|
||||
}
|
||||
}
|
|
@ -470,7 +470,7 @@ define(['require', 'apphost', 'layoutManager', 'focusManager', 'globalize', 'loa
|
|||
|
||||
function embed(options, self) {
|
||||
|
||||
require(['text!./homescreensettings.template.html'], function (template) {
|
||||
require(['text!./homeScreenSettings.template.html'], function (template) {
|
||||
|
||||
for (var i = 1; i <= numConfigurableSections; i++) {
|
||||
template = template.replace('{section' + i + 'label}', globalize.translate('LabelHomeScreenSectionValue', i));
|
|
@ -320,7 +320,7 @@ define(['dom', 'loading', 'apphost', 'dialogHelper', 'connectionManager', 'image
|
|||
function showEditor(itemId, serverId, itemType) {
|
||||
loading.show();
|
||||
|
||||
require(['text!./imagedownloader.template.html'], function (template) {
|
||||
require(['text!./imageDownloader.template.html'], function (template) {
|
||||
|
||||
var apiClient = connectionManager.getApiClient(serverId);
|
||||
|
|
@ -82,7 +82,7 @@ define(['globalize', 'dom', 'dialogHelper', 'emby-checkbox', 'emby-select', 'emb
|
|||
this.show = function (itemType, options, availableOptions) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'components/imageoptionseditor/imageoptionseditor.template.html', true);
|
||||
xhr.open('GET', 'components/imageOptionsEditor/imageOptionsEditor.template.html', true);
|
||||
|
||||
xhr.onload = function (e) {
|
||||
var template = this.response;
|
|
@ -125,7 +125,7 @@ define(['dialogHelper', 'connectionManager', 'dom', 'loading', 'scrollHelper', '
|
|||
|
||||
options = options || {};
|
||||
|
||||
require(['text!./imageuploader.template.html'], function (template) {
|
||||
require(['text!./imageUploader.template.html'], function (template) {
|
||||
|
||||
currentItemId = options.itemId;
|
||||
currentServerId = options.serverId;
|
|
@ -1,38 +0,0 @@
|
|||
define(['dom'], function (dom) {
|
||||
'use strict';
|
||||
|
||||
function loadImage(elem, url) {
|
||||
|
||||
if (!elem) {
|
||||
return Promise.reject('elem cannot be null');
|
||||
}
|
||||
|
||||
if (elem.tagName !== 'IMG') {
|
||||
|
||||
elem.style.backgroundImage = "url('" + url + "')";
|
||||
return Promise.resolve();
|
||||
|
||||
//return loadImageIntoImg(document.createElement('img'), url).then(function () {
|
||||
// elem.style.backgroundImage = "url('" + url + "')";
|
||||
// return Promise.resolve();
|
||||
//});
|
||||
|
||||
}
|
||||
return loadImageIntoImg(elem, url);
|
||||
}
|
||||
|
||||
function loadImageIntoImg(elem, url) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
dom.addEventListener(elem, 'load', resolve, {
|
||||
once: true
|
||||
});
|
||||
elem.setAttribute('src', url);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
loadImage: loadImage
|
||||
};
|
||||
|
||||
});
|
|
@ -148,7 +148,7 @@ export function getTypeIndicator(item) {
|
|||
};
|
||||
|
||||
const icon = iconT[item.Type];
|
||||
return icon ? '<div class="indicator videoIndicator"><span class="material-icons indicatorIcon '+ icon +'"></span></div>' : '';
|
||||
return icon ? '<div class="indicator videoIndicator"><span class="material-icons indicatorIcon ' + icon + '"></span></div>' : '';
|
||||
}
|
||||
|
||||
export function getMissingIndicator(item) {
|
||||
|
|
|
@ -182,7 +182,7 @@ define(['loading', 'dialogHelper', 'dom', 'jQuery', 'components/libraryoptionsed
|
|||
currentResolve = resolve;
|
||||
hasChanges = false;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'components/medialibrarycreator/medialibrarycreator.template.html', true);
|
||||
xhr.open('GET', 'components/mediaLibraryCreator/mediaLibraryCreator.template.html', true);
|
||||
|
||||
xhr.onload = function (e) {
|
||||
var template = this.response;
|
|
@ -199,7 +199,7 @@ define(['jQuery', 'loading', 'dialogHelper', 'dom', 'components/libraryoptionsed
|
|||
currentDeferred = deferred;
|
||||
hasChanges = false;
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', 'components/medialibraryeditor/medialibraryeditor.template.html', true);
|
||||
xhr.open('GET', 'components/mediaLibraryEditor/mediaLibraryEditor.template.html', true);
|
||||
|
||||
xhr.onload = function (e) {
|
||||
var template = this.response;
|
|
@ -1026,7 +1026,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
|
|||
function show(itemId, serverId, resolve, reject) {
|
||||
loading.show();
|
||||
|
||||
require(['text!./metadataeditor.template.html'], function (template) {
|
||||
require(['text!./metadataEditor.template.html'], function (template) {
|
||||
|
||||
var dialogOptions = {
|
||||
removeOnClose: true,
|
||||
|
@ -1083,7 +1083,7 @@ define(['itemHelper', 'dom', 'layoutManager', 'dialogHelper', 'datetime', 'loadi
|
|||
|
||||
loading.show();
|
||||
|
||||
require(['text!./metadataeditor.template.html'], function (template) {
|
||||
require(['text!./metadataEditor.template.html'], function (template) {
|
||||
|
||||
elem.innerHTML = globalize.translateDocument(template, 'core');
|
||||
|
|
@ -11,7 +11,7 @@ define(['dialogHelper', 'layoutManager', 'globalize', 'require', 'paper-icon-but
|
|||
function show(person) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
require(['text!./personeditor.template.html'], function (template) {
|
||||
require(['text!./personEditor.template.html'], function (template) {
|
||||
|
||||
var dialogOptions = {
|
||||
removeOnClose: true,
|
|
@ -1,4 +1,4 @@
|
|||
define(['browser', 'appStorage', 'apphost', 'loading', 'connectionManager', 'globalize', 'appRouter', 'dom', 'css!./multiselect'], function (browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) {
|
||||
define(['browser', 'appStorage', 'apphost', 'loading', 'connectionManager', 'globalize', 'appRouter', 'dom', 'css!./multiSelect'], function (browser, appStorage, appHost, loading, connectionManager, globalize, appRouter, dom) {
|
||||
'use strict';
|
||||
|
||||
var selectedItems = [];
|
|
@ -1,66 +0,0 @@
|
|||
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);
|
||||
});
|
||||
};
|
||||
});
|
|
@ -244,7 +244,7 @@ define(['require', 'datetime', 'itemHelper', 'events', 'browser', 'imageLoader',
|
|||
|
||||
return new Promise(function (resolve, reject) {
|
||||
|
||||
require(['appFooter-shared', 'itemShortcuts', 'css!./nowplayingbar.css', 'emby-slider'], function (appfooter, itemShortcuts) {
|
||||
require(['appFooter-shared', 'itemShortcuts', 'css!./nowPlayingBar.css', 'emby-slider'], function (appfooter, itemShortcuts) {
|
||||
|
||||
var parentContainer = appfooter.element;
|
||||
nowPlayingBarElement = parentContainer.querySelector('.nowPlayingBar');
|
|
@ -7,11 +7,13 @@ define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'glob
|
|||
return stream.Type === 'Video';
|
||||
})[0];
|
||||
var videoWidth = videoStream ? videoStream.Width : null;
|
||||
var videoHeight = videoStream ? videoStream.Height : null;
|
||||
|
||||
var options = qualityoptions.getVideoQualityOptions({
|
||||
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
|
||||
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
|
||||
videoWidth: videoWidth,
|
||||
videoHeight: videoHeight,
|
||||
enableAuto: true
|
||||
});
|
||||
|
||||
|
@ -91,11 +93,13 @@ define(['connectionManager', 'actionsheet', 'datetime', 'playbackManager', 'glob
|
|||
})[0];
|
||||
|
||||
var videoWidth = videoStream ? videoStream.Width : null;
|
||||
var videoHeight = videoStream ? videoStream.Height : null;
|
||||
|
||||
var options = qualityoptions.getVideoQualityOptions({
|
||||
currentMaxBitrate: playbackManager.getMaxStreamingBitrate(player),
|
||||
isAutomaticBitrateEnabled: playbackManager.enableAutomaticBitrateDetection(player),
|
||||
videoWidth: videoWidth,
|
||||
videoHeight: videoHeight,
|
||||
enableAuto: true
|
||||
});
|
||||
|
||||
|
|
|
@ -204,6 +204,9 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
|
|||
|
||||
fillChromecastQuality(context.querySelector('.selectChromecastVideoQuality'));
|
||||
|
||||
var selectChromecastVersion = context.querySelector('.selectChromecastVersion');
|
||||
selectChromecastVersion.value = userSettings.chromecastVersion();
|
||||
|
||||
var selectSkipForwardLength = context.querySelector('.selectSkipForwardLength');
|
||||
fillSkipLengths(selectSkipForwardLength);
|
||||
selectSkipForwardLength.value = userSettings.skipForwardLength();
|
||||
|
@ -234,6 +237,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
|
|||
userSettingsInstance.enableCinemaMode(context.querySelector('.chkEnableCinemaMode').checked);
|
||||
|
||||
userSettingsInstance.enableNextVideoInfoOverlay(context.querySelector('.chkEnableNextVideoOverlay').checked);
|
||||
userSettingsInstance.chromecastVersion(context.querySelector('.selectChromecastVersion').value);
|
||||
userSettingsInstance.skipForwardLength(context.querySelector('.selectSkipForwardLength').value);
|
||||
userSettingsInstance.skipBackLength(context.querySelector('.selectSkipBackLength').value);
|
||||
|
||||
|
@ -285,7 +289,7 @@ define(['require', 'browser', 'appSettings', 'apphost', 'focusManager', 'quality
|
|||
|
||||
function embed(options, self) {
|
||||
|
||||
require(['text!./playbacksettings.template.html'], function (template) {
|
||||
require(['text!./playbackSettings.template.html'], function (template) {
|
||||
|
||||
options.element.innerHTML = globalize.translateDocument(template, 'core');
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
<form style="margin: 0 auto;">
|
||||
|
||||
<div class="verticalSection verticalSection-extrabottompadding">
|
||||
<h2 class="sectionTitle">
|
||||
${HeaderAudioSettings}
|
||||
</h2>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" id="selectAudioLanguage" label="${LabelAudioLanguagePreference}"></select>
|
||||
</div>
|
||||
|
||||
<label class="checkboxContainer">
|
||||
<input type="checkbox" is="emby-checkbox" class="chkPlayDefaultAudioTrack" />
|
||||
<span>${LabelPlayDefaultAudioTrack}</span>
|
||||
|
@ -18,12 +19,15 @@
|
|||
<h2 class="sectionTitle">
|
||||
${HeaderVideoQuality}
|
||||
</h2>
|
||||
|
||||
<div class="selectContainer fldVideoInNetworkQuality hide">
|
||||
<select is="emby-select" class="selectVideoInNetworkQuality" label="${LabelHomeNetworkQuality}"></select>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer fldVideoInternetQuality hide">
|
||||
<select is="emby-select" class="selectVideoInternetQuality" label="${LabelInternetQuality}"></select>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer fldChromecastQuality hide">
|
||||
<select is="emby-select" class="selectChromecastVideoQuality" label="${LabelMaxChromecastBitrate}"></select>
|
||||
</div>
|
||||
|
@ -33,6 +37,7 @@
|
|||
<h2>
|
||||
${HeaderMusicQuality}
|
||||
</h2>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" class="selectMusicInternetQuality" label="${LabelInternetQuality}"></select>
|
||||
</div>
|
||||
|
@ -43,6 +48,7 @@
|
|||
<h2 class="sectionTitle">
|
||||
${TabAdvanced}
|
||||
</h2>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription cinemaModeOptions">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkEnableCinemaMode" />
|
||||
|
@ -50,12 +56,14 @@
|
|||
</label>
|
||||
<div class="fieldDescription checkboxFieldDescription">${CinemaModeConfigurationHelp}</div>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer fldEpisodeAutoPlay hide">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkEpisodeAutoPlay" />
|
||||
<span>${PlayNextEpisodeAutomatically}</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkboxContainer checkboxContainer-withDescription fldEnableNextVideoOverlay hide">
|
||||
<label>
|
||||
<input type="checkbox" is="emby-checkbox" class="chkEnableNextVideoOverlay" />
|
||||
|
@ -74,6 +82,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" class="selectChromecastVersion" label="${LabelChromecastVersion}">
|
||||
<option value="stable">${LabelStable}</option>
|
||||
<option value="nightly">${LabelNightly}</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="selectContainer">
|
||||
<select is="emby-select" class="selectSkipForwardLength" label="${LabelSkipForwardLength}"></select>
|
||||
</div>
|
|
@ -5,6 +5,13 @@ define(['globalize'], function (globalize) {
|
|||
|
||||
var maxStreamingBitrate = options.currentMaxBitrate;
|
||||
var videoWidth = options.videoWidth;
|
||||
var videoHeight = options.videoHeight;
|
||||
|
||||
// If the aspect ratio is less than 16/9 (1.77), set the width as if it were pillarboxed.
|
||||
// 4:3 1440x1080 -> 1920x1080
|
||||
if (videoWidth / videoHeight < 16 / 9) {
|
||||
videoWidth = videoHeight * (16 / 9);
|
||||
}
|
||||
|
||||
var maxAllowedWidth = videoWidth || 4096;
|
||||
//var maxAllowedHeight = videoHeight || 2304;
|
|
@ -140,7 +140,7 @@ define(['browser', 'datetime', 'backdrop', 'libraryBrowser', 'listView', 'imageL
|
|||
}
|
||||
if (item.SeriesName != null) {
|
||||
var seriesName = item.SeriesName;
|
||||
if (item.SeriesId !=null) {
|
||||
if (item.SeriesId != null) {
|
||||
context.querySelector('.nowPlayingSerie').innerHTML = '<a class="button-link emby-button" is="emby-linkbutton" href="itemdetails.html?id=' + item.SeriesId + `&serverId=${nowPlayingServerId}">${seriesName}</a>`;
|
||||
} else {
|
||||
context.querySelector('.nowPlayingSerie').innerHTML = seriesName;
|
||||
|
|
|
@ -92,7 +92,7 @@ import layoutManager from 'layoutManager';
|
|||
* @return {number} Eased value in range [0, 1].
|
||||
*/
|
||||
function ease(t) {
|
||||
return t*(2 - t); // easeOutQuad === ease-out
|
||||
return t * (2 - t); // easeOutQuad === ease-out
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -402,8 +402,8 @@ import layoutManager from 'layoutManager';
|
|||
|
||||
k = ease(k);
|
||||
|
||||
const x = ox + dx*k;
|
||||
const y = oy + dy*k;
|
||||
const x = ox + dx * k;
|
||||
const y = oy + dy * k;
|
||||
|
||||
builtinScroll(xScroller, x, yScroller, y, false);
|
||||
|
||||
|
|
|
@ -1,137 +0,0 @@
|
|||
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
|
||||
};
|
||||
});
|
|
@ -1,204 +0,0 @@
|
|||
define(['connectionManager', 'playbackManager', 'events', 'inputManager', 'focusManager', 'appRouter'], function (connectionManager, playbackManager, 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 {
|
||||
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;
|
||||
});
|
|
@ -1,24 +0,0 @@
|
|||
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();
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
|
@ -1,171 +0,0 @@
|
|||
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