jellyfin-web/dashboard-ui/scripts/librarybrowser.js
2016-12-12 00:41:24 -05:00

924 lines
35 KiB
JavaScript

define(['appSettings', 'dom', 'browser', 'scrollStyles'], function (appSettings, dom, browser) {
'use strict';
function fadeInRight(elem) {
var pct = browser.mobile ? '4%' : '0.5%';
var keyframes = [
{ opacity: '0', transform: 'translate3d(' + pct + ', 0, 0)', offset: 0 },
{ opacity: '1', transform: 'none', offset: 1 }];
elem.animate(keyframes, {
duration: 160,
iterations: 1,
easing: 'ease-out'
});
}
var libraryBrowser = (function (window, document, screen) {
var pageSizeKey = 'pagesize_v4';
var libraryBrowser = {
getDefaultPageSize: function (key, defaultValue) {
return 100;
},
getSavedQueryKey: function (modifier) {
return window.location.href.split('#')[0] + (modifier || '');
},
loadSavedQueryValues: function (key, query) {
var values = appSettings.get(key + '_' + Dashboard.getCurrentUserId());
if (values) {
values = JSON.parse(values);
return Object.assign(query, values);
}
return query;
},
saveQueryValues: function (key, query) {
var values = {};
if (query.SortBy) {
values.SortBy = query.SortBy;
}
if (query.SortOrder) {
values.SortOrder = query.SortOrder;
}
try {
appSettings.set(key + '_' + Dashboard.getCurrentUserId(), JSON.stringify(values));
} catch (e) {
}
},
saveViewSetting: function (key, value) {
try {
appSettings.set(key + '_' + Dashboard.getCurrentUserId() + '_view', value);
} catch (e) {
}
},
getSavedView: function (key) {
var val = appSettings.get(key + '_' + Dashboard.getCurrentUserId() + '_view');
return val;
},
getSavedViewSetting: function (key) {
return new Promise(function (resolve, reject) {
var val = LibraryBrowser.getSavedView(key);
resolve(val);
});
},
allowSwipe: function (target) {
function allowSwipeOn(elem) {
if (dom.parentWithTag(elem, 'input')) {
return false;
}
if (elem.classList) {
return !elem.classList.contains('hiddenScrollX') && !elem.classList.contains('smoothScrollX') && !elem.classList.contains('animatedScrollX');
}
return true;
}
var parent = target;
while (parent != null) {
if (!allowSwipeOn(parent)) {
return false;
}
parent = parent.parentNode;
}
return true;
},
configureSwipeTabs: function (ownerpage, tabs) {
if (!browser.touch) {
return;
}
var pageCount = ownerpage.querySelectorAll('.pageTabContent').length;
var onSwipeLeft = function (e) {
if (LibraryBrowser.allowSwipe(e.target) && ownerpage.contains(e.target)) {
var selected = parseInt(tabs.selectedIndex() || '0');
if (selected < (pageCount - 1)) {
tabs.selectedIndex(selected + 1);
}
}
};
var onSwipeRight = function (e) {
if (LibraryBrowser.allowSwipe(e.target) && ownerpage.contains(e.target)) {
var selected = parseInt(tabs.selectedIndex() || '0');
if (selected > 0) {
tabs.selectedIndex(selected - 1);
}
}
};
require(['hammer-main'], function (hammertime) {
hammertime.on('swipeleft', onSwipeLeft);
hammertime.on('swiperight', onSwipeRight);
ownerpage.addEventListener('viewdestroy', function () {
hammertime.off('swipeleft', onSwipeLeft);
hammertime.off('swiperight', onSwipeRight);
});
});
},
configurePaperLibraryTabs: function (ownerpage, tabs, panels, animateTabs, enableSwipe) {
if (!browser.safari && enableSwipe !== false) {
LibraryBrowser.configureSwipeTabs(ownerpage, tabs);
}
ownerpage.addEventListener('viewbeforeshow', function () {
if (tabs.triggerBeforeTabChange && this.firstTabIndex == null) {
tabs.triggerBeforeTabChange();
}
});
ownerpage.addEventListener('viewshow', function () {
if (this.firstTabIndex) {
tabs.selectedIndex(this.firstTabIndex);
this.firstTabIndex = null;
} else {
tabs.triggerTabChange();
}
});
tabs.addEventListener('beforetabchange', function (e) {
if (e.detail.previousIndex != null) {
panels[e.detail.previousIndex].classList.remove('is-active');
}
var newPanel = panels[e.detail.selectedTabIndex];
if (e.detail.previousIndex != null && e.detail.previousIndex != e.detail.selectedTabIndex) {
if (newPanel.animate && (animateTabs || []).indexOf(e.detail.selectedTabIndex) != -1) {
fadeInRight(newPanel);
}
}
newPanel.classList.add('is-active');
});
},
showTab: function (url, index) {
var afterNavigate = function () {
document.removeEventListener('pageinit', afterNavigate);
if (window.location.href.toLowerCase().indexOf(url.toLowerCase()) != -1) {
this.firstTabIndex = index;
}
};
if (window.location.href.toLowerCase().indexOf(url.toLowerCase()) != -1) {
require(['viewManager'], function (viewManager) {
afterNavigate.call(viewManager.currentView());
});
} else {
pageClassOn('pageinit', 'page', afterNavigate);
Dashboard.navigate(url);
}
},
getArtistLinksHtml: function (artists, cssClass) {
var html = [];
for (var i = 0, length = artists.length; i < length; i++) {
var artist = artists[i];
var css = cssClass ? (' class="' + cssClass + '"') : '';
html.push('<a' + css + ' href="itemdetails.html?id=' + artist.Id + '">' + artist.Name + '</a>');
}
html = html.join(' / ');
return html;
},
playInExternalPlayer: function (id) {
Dashboard.loadExternalPlayer().then(function () {
ExternalPlayer.showMenu(id);
});
},
getHref: function (item, context, topParentId) {
if (!item) {
throw new Error('item cannot be null');
}
if (item.url) {
return item.url;
}
var url;
// Handle search hints
var id = item.Id || item.ItemId;
if (item.Type == "SeriesTimer") {
return "livetvseriestimer.html?id=" + id;
}
if (item.CollectionType == 'livetv') {
return 'livetv.html';
}
if (item.CollectionType == 'channels') {
return 'channels.html';
}
if (context != 'folders') {
if (item.CollectionType == 'movies') {
return 'movies.html?topParentId=' + item.Id;
}
if (item.CollectionType == 'boxsets') {
return 'itemlist.html?topParentId=' + item.Id + '&parentId=' + item.Id;
}
if (item.CollectionType == 'tvshows') {
return 'tv.html?topParentId=' + item.Id;
}
if (item.CollectionType == 'music') {
return 'music.html?topParentId=' + item.Id;
}
if (item.CollectionType == 'games') {
return id ? "itemlist.html?parentId=" + id : "#";
//return 'gamesrecommended.html?topParentId=' + item.Id;
}
if (item.CollectionType == 'playlists') {
return 'playlists.html?topParentId=' + item.Id;
}
if (item.CollectionType == 'photos') {
return 'photos.html?topParentId=' + item.Id;
}
}
else if (item.IsFolder) {
if (item.Type != "BoxSet" && item.Type != "Series") {
return id ? "itemlist.html?parentId=" + id : "#";
}
}
if (item.Type == 'CollectionFolder') {
return 'itemlist.html?topParentId=' + item.Id + '&parentId=' + item.Id;
}
if (item.Type == "PhotoAlbum") {
return "itemlist.html?context=photos&parentId=" + id;
}
if (item.Type == "Playlist") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "TvChannel") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "Channel") {
return "channelitems.html?id=" + id;
}
if ((item.IsFolder && item.SourceType == 'Channel') || item.Type == 'ChannelFolderItem') {
return "channelitems.html?id=" + item.ChannelId + '&folderId=' + item.Id;
}
if (item.Type == "Program") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "BoxSet") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "MusicAlbum") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "GameSystem") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "Genre") {
var type;
switch (context) {
case 'tvshows':
type = 'Series';
break;
case 'games':
type = 'Game';
break;
default:
type = 'Movie';
break;
}
url = "secondaryitems.html?type=" + type + "&genreId=" + id;
if (topParentId) {
url += "&parentId=" + topParentId;
}
return url;
}
if (item.Type == "MusicGenre") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "GameGenre") {
url = "secondaryitems.html?type=Game&genreId=" + id;
if (topParentId) {
url += "&parentId=" + topParentId;
}
return url;
}
if (item.Type == "Studio") {
var type;
switch (context) {
case 'tvshows':
type = 'Series';
break;
case 'games':
type = 'Game';
break;
default:
type = 'Movie';
break;
}
url = "secondaryitems.html?type=" + type + "&studioId=" + id;
if (topParentId) {
url += "&parentId=" + topParentId;
}
return url;
}
if (item.Type == "Person") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "Recording") {
return "itemdetails.html?id=" + id;
}
if (item.Type == "MusicArtist") {
return "itemdetails.html?id=" + id;
}
var contextSuffix = context ? ('&context=' + context) : '';
if (item.Type == "Series" || item.Type == "Season" || item.Type == "Episode") {
return "itemdetails.html?id=" + id + contextSuffix;
}
if (item.IsFolder) {
return id ? "itemlist.html?parentId=" + id : "#";
}
return "itemdetails.html?id=" + id;
},
getListItemInfo: function (elem) {
var elemWithAttributes = elem;
while (!elemWithAttributes.getAttribute('data-id')) {
elemWithAttributes = elemWithAttributes.parentNode;
}
var itemId = elemWithAttributes.getAttribute('data-id');
var index = elemWithAttributes.getAttribute('data-index');
var mediaType = elemWithAttributes.getAttribute('data-mediatype');
return {
id: itemId,
index: index,
mediaType: mediaType,
context: elemWithAttributes.getAttribute('data-context')
};
},
getFutureDateText: function (date) {
var weekday = [];
weekday[0] = Globalize.translate('OptionSunday');
weekday[1] = Globalize.translate('OptionMonday');
weekday[2] = Globalize.translate('OptionTuesday');
weekday[3] = Globalize.translate('OptionWednesday');
weekday[4] = Globalize.translate('OptionThursday');
weekday[5] = Globalize.translate('OptionFriday');
weekday[6] = Globalize.translate('OptionSaturday');
var day = weekday[date.getDay()];
date = date.toLocaleDateString();
if (date.toLowerCase().indexOf(day.toLowerCase()) == -1) {
return day + " " + date;
}
return date;
},
renderName: function (item, nameElem, linkToElement, context) {
require(['itemHelper'], function (itemHelper) {
var name = itemHelper.getDisplayName(item, {
includeParentInfo: false
});
if (linkToElement) {
nameElem.innerHTML = '<a class="detailPageParentLink" href="' + LibraryBrowser.getHref(item, context) + '">' + name + '</a>';
} else {
nameElem.innerHTML = name;
}
});
},
renderParentName: function (item, parentNameElem, context) {
var html = [];
var contextParam = context ? ('&context=' + context) : '';
if (item.AlbumArtists) {
html.push(LibraryBrowser.getArtistLinksHtml(item.AlbumArtists, "detailPageParentLink"));
} else if (item.ArtistItems && item.ArtistItems.length && item.Type == "MusicVideo") {
html.push(LibraryBrowser.getArtistLinksHtml(item.ArtistItems, "detailPageParentLink"));
} else if (item.SeriesName && item.Type == "Episode") {
html.push('<a class="detailPageParentLink" href="itemdetails.html?id=' + item.SeriesId + contextParam + '">' + item.SeriesName + '</a>');
}
if (item.SeriesName && item.Type == "Season") {
html.push('<a class="detailPageParentLink" href="itemdetails.html?id=' + item.SeriesId + contextParam + '">' + item.SeriesName + '</a>');
} else if (item.ParentIndexNumber != null && item.Type == "Episode") {
html.push('<a class="detailPageParentLink" href="itemdetails.html?id=' + item.SeasonId + contextParam + '">' + item.SeasonName + '</a>');
} else if (item.Album && item.Type == "Audio" && (item.AlbumId || item.ParentId)) {
html.push('<a class="detailPageParentLink" href="itemdetails.html?id=' + (item.AlbumId || item.ParentId) + contextParam + '">' + item.Album + '</a>');
} else if (item.Album && item.Type == "MusicVideo" && item.AlbumId) {
html.push('<a class="detailPageParentLink" href="itemdetails.html?id=' + item.AlbumId + contextParam + '">' + item.Album + '</a>');
} else if (item.Album) {
html.push(item.Album);
} else if (item.Type == 'Program' && item.IsSeries) {
html.push(item.Name);
}
if (html.length) {
parentNameElem.classList.remove('hide');
parentNameElem.innerHTML = html.join(' - ');
} else {
parentNameElem.classList.add('hide');
}
},
showLayoutMenu: function (button, currentLayout, views) {
var dispatchEvent = true;
if (!views) {
dispatchEvent = false;
// Add banner and list once all screens support them
views = button.getAttribute('data-layouts');
views = views ? views.split(',') : ['List', 'Poster', 'PosterCard', 'Thumb', 'ThumbCard'];
}
var menuItems = views.map(function (v) {
return {
name: Globalize.translate('Option' + v),
id: v,
selected: currentLayout == v
};
});
require(['actionsheet'], function (actionsheet) {
actionsheet.show({
items: menuItems,
positionTo: button,
callback: function (id) {
button.dispatchEvent(new CustomEvent('layoutchange', {
detail: {
viewStyle: id
},
bubbles: true,
cancelable: false
}));
if (!dispatchEvent) {
if (window.$) {
$(button).trigger('layoutchange', [id]);
}
}
}
});
});
},
getQueryPagingHtml: function (options) {
var startIndex = options.startIndex;
var limit = options.limit;
var totalRecordCount = options.totalRecordCount;
if (limit && options.updatePageSizeSetting !== false) {
try {
appSettings.set(options.pageSizeKey || pageSizeKey, limit);
} catch (e) {
}
}
var html = '';
var recordsEnd = Math.min(startIndex + limit, totalRecordCount);
// 20 is the minimum page size
var showControls = totalRecordCount > 20 || limit < totalRecordCount;
html += '<div class="listPaging">';
if (showControls) {
html += '<span style="vertical-align:middle;">';
var startAtDisplay = totalRecordCount ? startIndex + 1 : 0;
html += startAtDisplay + '-' + recordsEnd + ' of ' + totalRecordCount;
html += '</span>';
}
if (showControls || options.viewButton || options.filterButton || options.sortButton || options.addLayoutButton) {
html += '<div style="display:inline-block;">';
if (showControls) {
html += '<button is="paper-icon-button-light" class="btnPreviousPage autoSize" ' + (startIndex ? '' : 'disabled') + '><i class="md-icon">&#xE5C4;</i></button>';
html += '<button is="paper-icon-button-light" class="btnNextPage autoSize" ' + (startIndex + limit >= totalRecordCount ? 'disabled' : '') + '><i class="md-icon">arrow_forward</i></button>';
}
if (options.addLayoutButton) {
html += '<button is="paper-icon-button-light" title="' + Globalize.translate('ButtonSelectView') + '" class="btnChangeLayout autoSize" data-layouts="' + (options.layouts || '') + '" onclick="LibraryBrowser.showLayoutMenu(this, \'' + (options.currentLayout || '') + '\');"><i class="md-icon">view_comfy</i></button>';
}
if (options.sortButton) {
html += '<button is="paper-icon-button-light" class="btnSort autoSize" title="' + Globalize.translate('ButtonSort') + '"><i class="md-icon">sort_by_alpha</i></button>';
}
if (options.filterButton) {
html += '<button is="paper-icon-button-light" class="btnFilter autoSize" title="' + Globalize.translate('ButtonFilter') + '"><i class="md-icon">filter_list</i></button>';
}
html += '</div>';
if (showControls && options.showLimit) {
var id = "selectPageSize";
var pageSizes = options.pageSizes || [20, 50, 100, 200, 300, 400, 500];
var optionsHtml = pageSizes.map(function (val) {
if (limit == val) {
return '<option value="' + val + '" selected="selected">' + val + '</option>';
} else {
return '<option value="' + val + '">' + val + '</option>';
}
}).join('');
// Add styles to defeat jquery mobile
html += '<div class="pageSizeContainer"><label class="labelPageSize" for="' + id + '">' + Globalize.translate('LabelLimit') + '</label><select style="width:auto;" class="selectPageSize" id="' + id + '" data-inline="true" data-mini="true">' + optionsHtml + '</select></div>';
}
}
html += '</div>';
return html;
},
showSortMenu: function (options) {
require(['dialogHelper', 'emby-radio'], function (dialogHelper) {
var dlg = dialogHelper.createDialog({
removeOnClose: true,
modal: false,
entryAnimationDuration: 160,
exitAnimationDuration: 200
});
dlg.classList.add('ui-body-a');
dlg.classList.add('background-theme-a');
dlg.classList.add('formDialog');
var html = '';
html += '<div style="margin:0;padding:1.25em 1.5em 1.5em;">';
html += '<h2 style="margin:0 0 .5em;">';
html += Globalize.translate('HeaderSortBy');
html += '</h2>';
var i, length;
var isChecked;
html += '<div>';
for (i = 0, length = options.items.length; i < length; i++) {
var option = options.items[i];
var radioValue = option.id.replace(',', '_');
isChecked = (options.query.SortBy || '').replace(',', '_') == radioValue ? ' checked' : '';
html += '<label class="radio-label-block"><input type="radio" is="emby-radio" name="SortBy" data-id="' + option.id + '" value="' + radioValue + '" class="menuSortBy" ' + isChecked + ' /><span>' + option.name + '</span></label>';
}
html += '</div>';
html += '<h2 style="margin: 1em 0 .5em;">';
html += Globalize.translate('HeaderSortOrder');
html += '</h2>';
html += '<div>';
isChecked = options.query.SortOrder == 'Ascending' ? ' checked' : '';
html += '<label class="radio-label-block"><input type="radio" is="emby-radio" name="SortOrder" value="Ascending" class="menuSortOrder" ' + isChecked + ' /><span>' + Globalize.translate('OptionAscending') + '</span></label>';
isChecked = options.query.SortOrder == 'Descending' ? ' checked' : '';
html += '<label class="radio-label-block"><input type="radio" is="emby-radio" name="SortOrder" value="Descending" class="menuSortOrder" ' + isChecked + ' /><span>' + Globalize.translate('OptionDescending') + '</span></label>';
html += '</div>';
html += '</div>';
dlg.innerHTML = html;
dialogHelper.open(dlg);
function onSortByChange() {
var newValue = this.value;
if (this.checked) {
var changed = options.query.SortBy != newValue;
options.query.SortBy = newValue.replace('_', ',');
options.query.StartIndex = 0;
if (options.callback && changed) {
options.callback();
}
}
}
var sortBys = dlg.querySelectorAll('.menuSortBy');
for (i = 0, length = sortBys.length; i < length; i++) {
sortBys[i].addEventListener('change', onSortByChange);
}
function onSortOrderChange() {
var newValue = this.value;
if (this.checked) {
var changed = options.query.SortOrder != newValue;
options.query.SortOrder = newValue;
options.query.StartIndex = 0;
if (options.callback && changed) {
options.callback();
}
}
}
var sortOrders = dlg.querySelectorAll('.menuSortOrder');
for (i = 0, length = sortOrders.length; i < length; i++) {
sortOrders[i].addEventListener('change', onSortOrderChange);
}
});
},
renderDetailImage: function (elem, item, editable, preferThumb, imageLoader, indicators) {
var imageTags = item.ImageTags || {};
if (item.PrimaryImageTag) {
imageTags.Primary = item.PrimaryImageTag;
}
var html = '';
var url;
var shape = 'portrait';
var imageHeight = 360;
var detectRatio = false;
if (preferThumb && imageTags.Thumb) {
url = ApiClient.getScaledImageUrl(item.Id, {
type: "Thumb",
maxHeight: imageHeight,
tag: item.ImageTags.Thumb
});
shape = 'thumb';
}
else if (imageTags.Primary) {
url = ApiClient.getScaledImageUrl(item.Id, {
type: "Primary",
maxHeight: imageHeight,
tag: item.ImageTags.Primary
});
detectRatio = true;
}
else if (item.BackdropImageTags && item.BackdropImageTags.length) {
url = ApiClient.getScaledImageUrl(item.Id, {
type: "Backdrop",
maxHeight: imageHeight,
tag: item.BackdropImageTags[0]
});
shape = 'thumb';
}
else if (imageTags.Thumb) {
url = ApiClient.getScaledImageUrl(item.Id, {
type: "Thumb",
maxHeight: imageHeight,
tag: item.ImageTags.Thumb
});
shape = 'thumb';
}
else if (imageTags.Disc) {
url = ApiClient.getScaledImageUrl(item.Id, {
type: "Disc",
maxHeight: imageHeight,
tag: item.ImageTags.Disc
});
shape = 'square';
}
else if (item.AlbumId && item.AlbumPrimaryImageTag) {
url = ApiClient.getScaledImageUrl(item.AlbumId, {
type: "Primary",
maxHeight: imageHeight,
tag: item.AlbumPrimaryImageTag
});
shape = 'square';
}
else if (item.SeriesId && item.SeriesPrimaryImageTag) {
url = ApiClient.getScaledImageUrl(item.SeriesId, {
type: "Primary",
maxHeight: imageHeight,
tag: item.SeriesPrimaryImageTag
});
}
html += '<div style="position:relative;">';
if (editable) {
html += "<a class='itemDetailGalleryLink' href='#'>";
}
if (detectRatio && item.PrimaryImageAspectRatio) {
if (item.PrimaryImageAspectRatio >= 1.48) {
shape = 'thumb';
} else if (item.PrimaryImageAspectRatio >= .85 && item.PrimaryImageAspectRatio <= 1.34) {
shape = 'square';
}
}
html += "<img class='itemDetailImage lazy' src='css/images/empty.png' />";
if (editable) {
html += "</a>";
}
var progressHtml = item.IsFolder || !item.UserData ? '' : indicators.getProgressBarHtml(item);
html += '<div class="detailImageProgressContainer">';
if (progressHtml) {
html += progressHtml;
}
html += "</div>";
html += "</div>";
elem.innerHTML = html;
if (shape == 'thumb') {
elem.classList.add('thumbDetailImageContainer');
elem.classList.remove('portraitDetailImageContainer');
elem.classList.remove('squareDetailImageContainer');
}
else if (shape == 'square') {
elem.classList.remove('thumbDetailImageContainer');
elem.classList.remove('portraitDetailImageContainer');
elem.classList.add('squareDetailImageContainer');
} else {
elem.classList.remove('thumbDetailImageContainer');
elem.classList.add('portraitDetailImageContainer');
elem.classList.remove('squareDetailImageContainer');
}
if (url) {
var img = elem.querySelector('img');
img.onload = function () {
if (img.src.indexOf('empty.png') == -1) {
img.classList.add('loaded');
}
};
imageLoader.lazyImage(img, url);
}
},
renderDetailPageBackdrop: function (page, item, imageLoader) {
var screenWidth = screen.availWidth;
var imgUrl;
var hasbackdrop = false;
var itemBackdropElement = page.querySelector('#itemBackdrop');
if (item.BackdropImageTags && item.BackdropImageTags.length) {
imgUrl = ApiClient.getScaledImageUrl(item.Id, {
type: "Backdrop",
index: 0,
maxWidth: screenWidth,
tag: item.BackdropImageTags[0]
});
itemBackdropElement.classList.remove('noBackdrop');
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
hasbackdrop = true;
}
else if (item.ParentBackdropItemId && item.ParentBackdropImageTags && item.ParentBackdropImageTags.length) {
imgUrl = ApiClient.getScaledImageUrl(item.ParentBackdropItemId, {
type: 'Backdrop',
index: 0,
tag: item.ParentBackdropImageTags[0],
maxWidth: screenWidth
});
itemBackdropElement.classList.remove('noBackdrop');
imageLoader.lazyImage(itemBackdropElement, imgUrl, false);
hasbackdrop = true;
}
else {
itemBackdropElement.classList.add('noBackdrop');
itemBackdropElement.style.backgroundImage = '';
}
return hasbackdrop;
}
};
return libraryBrowser;
})(window, document, screen);
window.LibraryBrowser = libraryBrowser;
return libraryBrowser;
});