';
}
let outerCardFooter = '';
if (!overlayText && !footerOverlayed) {
footerCssClass = options.cardLayout ? 'cardFooter' : 'cardFooter cardFooter-transparent';
if (logoUrl) {
footerCssClass += ' cardFooter-withlogo';
}
if (!options.cardLayout) {
logoUrl = null;
}
outerCardFooter = getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerCssClass, progressHtml, logoUrl, true);
}
if (outerCardFooter && !options.cardLayout) {
cardBoxClass += ' cardBox-bottompadded';
}
let overlayButtons = '';
if (layoutManager.mobile) {
let overlayPlayButton = options.overlayPlayButton;
if (overlayPlayButton == null && !options.overlayMoreButton && !options.overlayInfoButton && !options.cardLayout) {
overlayPlayButton = item.MediaType === 'Video';
}
const btnCssClass = 'cardOverlayButton cardOverlayButton-br itemAction';
if (options.centerPlayButton) {
overlayButtons += ``;
}
if (overlayPlayButton && !item.IsPlaceHolder && (item.LocationType !== 'Virtual' || !item.MediaType || item.Type === 'Program') && item.Type !== 'Person') {
overlayButtons += ``;
}
if (options.overlayMoreButton) {
overlayButtons += ``;
}
}
if (options.showChildCountIndicator && item.ChildCount) {
className += ' groupedCard';
}
// cardBox can be it's own separate element if an outer footer is ever needed
let cardImageContainerOpen;
let cardImageContainerClose = '';
let cardBoxClose = '';
let cardScalableClose = '';
const cardContentClass = 'cardContent';
let blurhashAttrib = '';
if (blurhash && blurhash.length > 0) {
blurhashAttrib = 'data-blurhash="' + blurhash + '"';
}
if (layoutManager.tv) {
// Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('
') : ('
');
cardImageContainerClose = '
';
} else {
const cardImageContainerAriaLabelAttribute = ` aria-label="${item.Name}"`;
const url = appRouter.getRouteUrl(item);
// Don't use the IMG tag with safari because it puts a white border around it
cardImageContainerOpen = imgUrl ? ('') : ('');
cardImageContainerClose = '';
}
const cardScalableClass = 'cardScalable';
let cardPadderIcon = '';
// TV Channel logos are transparent so skip the placeholder to avoid overlapping
if (imgUrl && item.Type !== 'TvChannel') {
cardPadderIcon = getDefaultText(item, {
// Always use an icon
defaultCardImageIcon: 'folder',
...options
});
}
cardImageContainerOpen = `
';
const url = appRouter.getRouteUrl(item);
html += '';
const btnCssClass = 'cardOverlayButton cardOverlayButton-hover itemAction paper-icon-button-light';
if (playbackManager.canPlay(item)) {
html += '';
}
html += '
';
const userData = item.UserData || {};
if (itemHelper.canMarkPlayed(item)) {
/* eslint-disable-next-line @babel/no-unused-expressions */
import('../../elements/emby-playstatebutton/emby-playstatebutton');
html += '';
}
if (itemHelper.canRate(item)) {
const likes = userData.Likes == null ? '' : userData.Likes;
/* eslint-disable-next-line @babel/no-unused-expressions */
import('../../elements/emby-ratingbutton/emby-ratingbutton');
html += '';
}
html += ``;
html += '
';
html += '
';
return html;
}
/**
* Generates the text or icon used for default card backgrounds.
* @param {object} item - Item used to generate the card overlay.
* @param {object} options - Options used to generate the card overlay.
* @returns {string} HTML markup of the card overlay.
*/
export function getDefaultText(item, options) {
if (item.CollectionType) {
return '';
}
switch (item.Type) {
case 'MusicAlbum':
return '';
case 'MusicArtist':
case 'Person':
return '';
case 'Audio':
return '';
case 'Movie':
return '';
case 'Episode':
case 'Series':
return '';
case 'Program':
return '';
case 'Book':
return '';
case 'Folder':
return '';
case 'BoxSet':
return '';
case 'Playlist':
return '';
case 'Photo':
return '';
case 'PhotoAlbum':
return '';
}
if (options?.defaultCardImageIcon) {
return '';
}
const defaultName = isUsingLiveTvNaming(item) ? item.Name : itemHelper.getDisplayName(item);
return '
' + escapeHtml(defaultName) + '
';
}
/**
* Builds a set of cards and inserts them into the page.
* @param {Array} items - Array of items used to build the cards.
* @param {options} options - Options of the cards to build.
*/
export function buildCards(items, options) {
// Abort if the container has been disposed
if (!document.body.contains(options.itemsContainer)) {
return;
}
if (options.parentContainer) {
if (items.length) {
options.parentContainer.classList.remove('hide');
} else {
options.parentContainer.classList.add('hide');
return;
}
}
const html = buildCardsHtmlInternal(items, options);
if (html) {
if (options.itemsContainer.cardBuilderHtml !== html) {
options.itemsContainer.innerHTML = html;
if (items.length < 50) {
options.itemsContainer.cardBuilderHtml = html;
} else {
options.itemsContainer.cardBuilderHtml = null;
}
}
imageLoader.lazyChildren(options.itemsContainer);
} else {
options.itemsContainer.innerHTML = html;
options.itemsContainer.cardBuilderHtml = null;
}
if (options.autoFocus) {
focusManager.autoFocus(options.itemsContainer, true);
}
}
/**
* Ensures the indicators for a card exist and creates them if they don't exist.
* @param {HTMLDivElement} card - DOM element of the card.
* @param {HTMLDivElement} indicatorsElem - DOM element of the indicators.
* @returns {HTMLDivElement} - DOM element of the indicators.
*/
function ensureIndicators(card, indicatorsElem) {
if (indicatorsElem) {
return indicatorsElem;
}
indicatorsElem = card.querySelector('.cardIndicators');
if (!indicatorsElem) {
const cardImageContainer = card.querySelector('.cardImageContainer');
indicatorsElem = document.createElement('div');
indicatorsElem.classList.add('cardIndicators');
cardImageContainer.appendChild(indicatorsElem);
}
return indicatorsElem;
}
/**
* Adds user data to the card such as progress indicators and played status.
* @param {HTMLDivElement} card - DOM element of the card.
* @param {Object} userData - User data to apply to the card.
*/
function updateUserData(card, userData) {
const type = card.getAttribute('data-type');
const enableCountIndicator = type === 'Series' || type === 'BoxSet' || type === 'Season';
let indicatorsElem = null;
let playedIndicator = null;
let countIndicator = null;
let itemProgressBar = null;
if (userData.Played) {
playedIndicator = card.querySelector('.playedIndicator');
if (!playedIndicator) {
playedIndicator = document.createElement('div');
playedIndicator.classList.add('playedIndicator');
playedIndicator.classList.add('indicator');
indicatorsElem = ensureIndicators(card, indicatorsElem);
indicatorsElem.appendChild(playedIndicator);
}
playedIndicator.innerHTML = '';
} else {
playedIndicator = card.querySelector('.playedIndicator');
if (playedIndicator) {
playedIndicator.parentNode.removeChild(playedIndicator);
}
}
if (userData.UnplayedItemCount) {
countIndicator = card.querySelector('.countIndicator');
if (!countIndicator) {
countIndicator = document.createElement('div');
countIndicator.classList.add('countIndicator');
indicatorsElem = ensureIndicators(card, indicatorsElem);
indicatorsElem.appendChild(countIndicator);
}
countIndicator.innerHTML = userData.UnplayedItemCount;
} else if (enableCountIndicator) {
countIndicator = card.querySelector('.countIndicator');
if (countIndicator) {
countIndicator.parentNode.removeChild(countIndicator);
}
}
const progressHtml = indicators.getProgressBarHtml({
Type: type,
UserData: userData,
MediaType: 'Video'
});
if (progressHtml) {
itemProgressBar = card.querySelector('.itemProgressBar');
if (!itemProgressBar) {
itemProgressBar = document.createElement('div');
itemProgressBar.classList.add('itemProgressBar');
let innerCardFooter = card.querySelector('.innerCardFooter');
if (!innerCardFooter) {
innerCardFooter = document.createElement('div');
innerCardFooter.classList.add('innerCardFooter');
const cardImageContainer = card.querySelector('.cardImageContainer');
cardImageContainer.appendChild(innerCardFooter);
}
innerCardFooter.appendChild(itemProgressBar);
}
itemProgressBar.innerHTML = progressHtml;
} else {
itemProgressBar = card.querySelector('.itemProgressBar');
if (itemProgressBar) {
itemProgressBar.parentNode.removeChild(itemProgressBar);
}
}
}
/**
* Handles when user data has changed.
* @param {Object} userData - User data to apply to the card.
* @param {HTMLElement} scope - DOM element to use as a scope when selecting cards.
*/
export function onUserDataChanged(userData, scope) {
const cards = (scope || document.body).querySelectorAll('.card-withuserdata[data-id="' + userData.ItemId + '"]');
for (let i = 0, length = cards.length; i < length; i++) {
updateUserData(cards[i], userData);
}
}
/**
* Handles when a timer has been created.
* @param {string} programId - ID of the program.
* @param {string} newTimerId - ID of the new timer.
* @param {HTMLElement} itemsContainer - DOM element of the itemsContainer.
*/
export function onTimerCreated(programId, newTimerId, itemsContainer) {
const cells = itemsContainer.querySelectorAll('.card[data-id="' + programId + '"]');
for (let i = 0, length = cells.length; i < length; i++) {
const cell = cells[i];
const icon = cell.querySelector('.timerIndicator');
if (!icon) {
const indicatorsElem = ensureIndicators(cell);
indicatorsElem.insertAdjacentHTML('beforeend', '');
}
cell.setAttribute('data-timerid', newTimerId);
}
}
/**
* Handles when a timer has been cancelled.
* @param {string} timerId - ID of the cancelled timer.
* @param {HTMLElement} itemsContainer - DOM element of the itemsContainer.
*/
export function onTimerCancelled(timerId, itemsContainer) {
const cells = itemsContainer.querySelectorAll('.card[data-timerid="' + timerId + '"]');
for (let i = 0; i < cells.length; i++) {
const cell = cells[i];
const icon = cell.querySelector('.timerIndicator');
if (icon) {
icon.parentNode.removeChild(icon);
}
cell.removeAttribute('data-timerid');
}
}
/**
* Handles when a series timer has been cancelled.
* @param {string} cancelledTimerId - ID of the cancelled timer.
* @param {HTMLElement} itemsContainer - DOM element of the itemsContainer.
*/
export function onSeriesTimerCancelled(cancelledTimerId, itemsContainer) {
const cells = itemsContainer.querySelectorAll('.card[data-seriestimerid="' + cancelledTimerId + '"]');
for (let i = 0; i < cells.length; i++) {
const cell = cells[i];
const icon = cell.querySelector('.timerIndicator');
if (icon) {
icon.parentNode.removeChild(icon);
}
cell.removeAttribute('data-seriestimerid');
}
}
/* eslint-enable indent */
export default {
getCardsHtml: getCardsHtml,
getDefaultBackgroundClass: getDefaultBackgroundClass,
getDefaultText: getDefaultText,
buildCards: buildCards,
onUserDataChanged: onUserDataChanged,
onTimerCreated: onTimerCreated,
onTimerCancelled: onTimerCancelled,
onSeriesTimerCancelled: onSeriesTimerCancelled
};