diff --git a/.eslintrc.yml b/.eslintrc.yml
index 4bc22fc1d4..377716d53c 100644
--- a/.eslintrc.yml
+++ b/.eslintrc.yml
@@ -3,6 +3,12 @@ env:
browser: true
amd: true
+parserOptions:
+ ecmaVersion: 6
+ sourceType: module
+ ecmaFeatures:
+ impliedStrict: true
+
globals:
# New browser globals
DataView: readonly
diff --git a/package.json b/package.json
index 9d26a02319..2de78b821c 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
"devDependencies": {
"@babel/core": "^7.8.6",
"@babel/polyfill": "^7.8.7",
+ "@babel/plugin-transform-modules-amd": "^7.8.3",
"@babel/preset-env": "^7.8.6",
"autoprefixer": "^9.7.4",
"babel-loader": "^8.0.6",
@@ -69,9 +70,11 @@
"whatwg-fetch": "^3.0.0"
},
"babel": {
- "presets": [
- "@babel/preset-env"
- ]
+ "presets": ["@babel/preset-env"],
+ "overrides": [{
+ "test": ["src/components/cardbuilder/cardBuilder.js"],
+ "plugins": ["@babel/plugin-transform-modules-amd"]
+ }]
},
"browserslist": [
"last 2 Firefox versions",
diff --git a/src/components/cardbuilder/cardBuilder.js b/src/components/cardbuilder/cardBuilder.js
index 7f562f1fd0..9d86bc9d7c 100644
--- a/src/components/cardbuilder/cardBuilder.js
+++ b/src/components/cardbuilder/cardBuilder.js
@@ -1,10 +1,36 @@
-define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusManager', 'indicators', 'globalize', 'layoutManager', 'apphost', 'dom', 'browser', 'playbackManager', 'itemShortcuts', 'scripts/imagehelper', 'css!./card', 'paper-icon-button-light', 'programStyles'],
- function (datetime, imageLoader, connectionManager, itemHelper, focusManager, indicators, globalize, layoutManager, appHost, dom, browser, playbackManager, itemShortcuts, imageHelper) {
- 'use strict';
+/* eslint-disable indent */
- var enableFocusTransform = !browser.slow && !browser.edge;
+/**
+ * Module for building cards from item data.
+ * @module components/cardBuilder/cardBuilder
+ */
- function getCardsHtml(items, options) {
+import datetime from 'datetime';
+import imageLoader from 'imageLoader';
+import connectionManager from 'connectionManager';
+import itemHelper from 'itemHelper';
+import focusManager from 'focusManager';
+import indicators from 'indicators';
+import globalize from 'globalize';
+import layoutManager from 'layoutManager';
+import dom from 'dom';
+import browser from 'browser';
+import playbackManager from 'playbackManager';
+import itemShortcuts from 'itemShortcuts';
+import imageHelper from 'scripts/imagehelper';
+import 'css!./card';
+import 'paper-icon-button-light';
+import 'programStyles';
+
+ const enableFocusTransform = !browser.slow && !browser.edge;
+
+ /**
+ * Generate the HTML markup for cards for a set of items.
+ * @param items - The items used to generate cards.
+ * @param options - The options of the cards.
+ * @returns {string} The HTML markup for the cards.
+ */
+ export function getCardsHtml(items, options) {
if (arguments.length === 1) {
options = arguments[0];
items = options.items;
@@ -13,6 +39,13 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
return buildCardsHtmlInternal(items, options);
}
+ /**
+ * Computes the number of posters per row.
+ * @param {string} shape - Shape of the cards.
+ * @param {number} screenWidth - Width of the screen.
+ * @param {boolean} isOrientationLandscape - Flag for the orientation of the screen.
+ * @returns {number} Number of cards per row for an itemsContainer.
+ */
function getPostersPerRow(shape, screenWidth, isOrientationLandscape) {
switch (shape) {
case 'portrait':
@@ -217,10 +250,15 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
}
+ /**
+ * Checks if the window is resizable.
+ * @param {number} windowWidth - Width of the device's screen.
+ * @returns {boolean} - Result of the check.
+ */
function isResizable(windowWidth) {
- var screen = window.screen;
+ const screen = window.screen;
if (screen) {
- var screenWidth = screen.availWidth;
+ const screenWidth = screen.availWidth;
if ((screenWidth - windowWidth) > 20) {
return true;
@@ -230,22 +268,31 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
return false;
}
+ /**
+ * Gets the width of a card's image according to the shape and amount of cards per row.
+ * @param {string} shape - Shape of the card.
+ * @param {number} screenWidth - Width of the screen.
+ * @param {boolean} isOrientationLandscape - Flag for the orientation of the screen.
+ * @returns {number} Width of the image for a card.
+ */
function getImageWidth(shape, screenWidth, isOrientationLandscape) {
- var imagesPerRow = getPostersPerRow(shape, screenWidth, isOrientationLandscape);
- var shapeWidth = Math.round(screenWidth / imagesPerRow) * 2;
-
- return shapeWidth;
+ const imagesPerRow = getPostersPerRow(shape, screenWidth, isOrientationLandscape);
+ return Math.round(screenWidth / imagesPerRow) * 2;
}
+ /**
+ * Normalizes the options for a card.
+ * @param {Object} items - A set of items.
+ * @param {Object} options - Options for handling the items.
+ */
function setCardData(items, options) {
-
options.shape = options.shape || "auto";
- var primaryImageAspectRatio = imageLoader.getPrimaryImageAspectRatio(items);
+ const primaryImageAspectRatio = imageLoader.getPrimaryImageAspectRatio(items);
- if (options.shape === 'auto' || options.shape === 'autohome' || options.shape === 'autooverflow' || options.shape === 'autoVertical') {
+ if (['auto', 'autohome', 'autooverflow', 'autoVertical'].includes(options.shape)) {
- var requestedShape = options.shape;
+ const requestedShape = options.shape;
options.shape = null;
if (primaryImageAspectRatio) {
@@ -283,11 +330,11 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
if (!options.width) {
- var screenWidth = dom.getWindowSize().innerWidth;
- var screenHeight = dom.getWindowSize().innerHeight;
+ let screenWidth = dom.getWindowSize().innerWidth;
+ const screenHeight = dom.getWindowSize().innerHeight;
if (isResizable(screenWidth)) {
- var roundScreenTo = 100;
+ const roundScreenTo = 100;
screenWidth = Math.floor(screenWidth / roundScreenTo) * roundScreenTo;
}
@@ -295,9 +342,14 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
}
+ /**
+ * Generates the internal HTML markup for cards.
+ * @param {Object} items - Items for which to generate the markup.
+ * @param {Object} options - Options for generating the markup.
+ * @returns {string} The internal HTML markup of the cards.
+ */
function buildCardsHtmlInternal(items, options) {
-
- var isVertical;
+ let isVertical = false;
if (options.shape === 'autoVertical') {
isVertical = true;
@@ -305,24 +357,21 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
setCardData(items, options);
- var html = '';
- var itemsInRow = 0;
+ let html = '';
+ let itemsInRow = 0;
- var currentIndexValue;
- var hasOpenRow;
- var hasOpenSection;
+ let currentIndexValue;
+ let hasOpenRow;
+ let hasOpenSection;
- var sectionTitleTagName = options.sectionTitleTagName || 'div';
- var apiClient;
- var lastServerId;
+ let sectionTitleTagName = options.sectionTitleTagName || 'div';
+ let apiClient;
+ let lastServerId;
- var i;
- var length;
+ for (let i = 0; i < items.length; i++) {
- for (i = 0, length = items.length; i < length; i++) {
-
- var item = items[i];
- var serverId = item.ServerId || options.serverId;
+ let item = items[i];
+ let serverId = item.ServerId || options.serverId;
if (serverId !== lastServerId) {
lastServerId = serverId;
@@ -330,14 +379,14 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
if (options.indexBy) {
- var newIndexValue = '';
+ let newIndexValue = '';
if (options.indexBy === 'PremiereDate') {
if (item.PremiereDate) {
try {
newIndexValue = datetime.toLocaleDateString(datetime.parseISO8601Date(item.PremiereDate), { weekday: 'long', month: 'long', day: 'numeric' });
- } catch (err) {
- console.error('error parsing timestamp for premiere date');
+ } catch (error) {
+ console.error('error parsing timestamp for premiere date', error);
}
}
} else if (options.indexBy === 'ProductionYear') {
@@ -412,21 +461,15 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
}
- var cardFooterHtml = '';
- for (i = 0, length = (options.lines || 0); i < length; i++) {
-
- if (i === 0) {
- cardFooterHtml += '
';
- } else {
- cardFooterHtml += '
';
- }
- }
-
return html;
}
+ /**
+ * Computes the aspect ratio for a card given its shape.
+ * @param {string} shape - Shape for which to get the aspect ratio.
+ * @returns {null|number} Ratio of the shape.
+ */
function getDesiredAspect(shape) {
-
if (shape) {
shape = shape.toLowerCase();
if (shape.indexOf('portrait') !== -1) {
@@ -445,18 +488,23 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
return null;
}
+ /** Get the URL of the card's image.
+ * @param {Object} item - Item for which to generate a card.
+ * @param {Object} apiClient - API client object.
+ * @param {Object} options - Options of the card.
+ * @param {string} shape - Shape of the desired image.
+ * @returns {Object} Object representing the URL of the card's image.
+ */
function getCardImageUrl(item, apiClient, options, shape) {
+ item = item.ProgramInfo || item;
- var imageItem = item.ProgramInfo || item;
- item = imageItem;
-
- var width = options.width;
- var height = null;
- var primaryImageAspectRatio = item.PrimaryImageAspectRatio;
- var forceName = false;
- var imgUrl = null;
- var coverImage = false;
- var uiAspect = null;
+ const width = options.width;
+ let height = null;
+ const primaryImageAspectRatio = item.PrimaryImageAspectRatio;
+ let forceName = false;
+ let imgUrl = null;
+ let coverImage = false;
+ let uiAspect = null;
if (options.preferThumb && item.ImageTags && item.ImageTags.Thumb) {
@@ -663,21 +711,32 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
};
}
+ /**
+ * Generates a random integer in a given range.
+ * @param {number} min - Minimum of the range.
+ * @param {number} max - Maximum of the range.
+ * @returns {number} Randomly generated number.
+ */
function getRandomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
- var numRandomColors = 5;
+ /**
+ * Generates an index used to select the default color of a card based on a string.
+ * @param {string} str - String to use for generating the index.
+ * @returns {number} Index of the color.
+ */
function getDefaultColorIndex(str) {
+ const numRandomColors = 5;
if (str) {
- var charIndex = Math.floor(str.length / 2);
- var character = String(str.substr(charIndex, 1).charCodeAt());
- var sum = 0;
- for (var i = 0; i < character.length; i++) {
+ const charIndex = Math.floor(str.length / 2);
+ const character = String(str.substr(charIndex, 1).charCodeAt());
+ let sum = 0;
+ for (let i = 0; i < character.length; i++) {
sum += parseInt(character.charAt(i));
}
- var index = String(sum).substr(-1);
+ let index = String(sum).substr(-1);
return (index % numRandomColors) + 1;
} else {
@@ -685,18 +744,26 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
}
+ /**
+ * Generates the HTML markup for a card's text.
+ * @param {Array} lines - Array containing the text lines.
+ * @param {string} cssClass - Base CSS class to use for the lines.
+ * @param {boolean} forceLines - Flag to force the rendering of all lines.
+ * @param {boolean} isOuterFooter - Flag to mark the text lines as outer footer.
+ * @param {string} cardLayout - DEPRECATED
+ * @param {boolean} addRightMargin - Flag to add a right margin to the text.
+ * @param {number} maxLines - Maximum number of lines to render.
+ * @returns {string} HTML markup for the card's text.
+ */
function getCardTextLines(lines, cssClass, forceLines, isOuterFooter, cardLayout, addRightMargin, maxLines) {
+ let html = '';
- var html = '';
+ let valid = 0;
- var valid = 0;
- var i;
- var length;
+ for (let i = 0; i < lines.length; i++) {
- for (i = 0, length = lines.length; i < length; i++) {
-
- var currentCssClass = cssClass;
- var text = lines[i];
+ let currentCssClass = cssClass;
+ let text = lines[i];
if (valid > 0 && isOuterFooter) {
currentCssClass += ' cardText-secondary';
@@ -722,9 +789,9 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
if (forceLines) {
- length = maxLines || Math.min(lines.length, maxLines || lines.length);
+ let linesLength = maxLines || Math.min(lines.length, maxLines || lines.length);
- while (valid < length) {
+ while (valid < linesLength) {
html += "
";
valid++;
}
@@ -733,17 +800,29 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
return html;
}
+ /**
+ * Determines if the item is live TV.
+ * @param {Object} item - Item to use for the check.
+ * @returns {boolean} Flag showing if the item is live TV.
+ */
function isUsingLiveTvNaming(item) {
return item.Type === 'Program' || item.Type === 'Timer' || item.Type === 'Recording';
}
+ /**
+ * Returns the air time text for the item based on the given times.
+ * @param {object} item - Item used to generate the air time text.
+ * @param {string} showAirDateTime - ISO8601 date for the start of the show.
+ * @param {string} showAirEndTime - ISO8601 date for the end of the show.
+ * @returns {string} The air time text for the item based on the given dates.
+ */
function getAirTimeText(item, showAirDateTime, showAirEndTime) {
+ let airTimeText = '';
- var airTimeText = '';
if (item.StartDate) {
try {
- var date = datetime.parseISO8601Date(item.StartDate);
+ let date = datetime.parseISO8601Date(item.StartDate);
if (showAirDateTime) {
airTimeText += datetime.toLocaleDateString(date, { weekday: 'short', month: 'short', day: 'numeric' }) + ' ';
@@ -763,15 +842,29 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
return airTimeText;
}
+ /**
+ * Generates the HTML markup for the card's footer text.
+ * @param {Object} item - Item used to generate the footer text.
+ * @param {Object} apiClient - API client instance.
+ * @param {Object} options - Options used to generate the footer text.
+ * @param {string} showTitle - Flag to show the title in the footer.
+ * @param {boolean} forceName - Flag to force showing the name of the item.
+ * @param {boolean} overlayText - Flag to show overlay text.
+ * @param {Object} imgUrl - Object representing the card's image URL.
+ * @param {string} footerClass - CSS classes of the footer element.
+ * @param {string} progressHtml - HTML markup of the progress bar element.
+ * @param {string} logoUrl - URL of the logo for the item.
+ * @param {boolean} isOuterFooter - Flag to mark the text as outer footer.
+ * @returns {string} HTML markup of the card's footer text element.
+ */
function getCardFooterText(item, apiClient, options, showTitle, forceName, overlayText, imgUrl, footerClass, progressHtml, logoUrl, isOuterFooter) {
-
- var html = '';
+ let html = '';
if (logoUrl) {
html += '';
}
- var showOtherText = isOuterFooter ? !overlayText : overlayText;
+ const showOtherText = isOuterFooter ? !overlayText : overlayText;
if (isOuterFooter && options.cardLayout && layoutManager.mobile) {
@@ -780,12 +873,12 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
}
- var cssClass = options.centerText ? "cardText cardTextCentered" : "cardText";
- var serverId = item.ServerId || options.serverId;
+ const cssClass = options.centerText ? "cardText cardTextCentered" : "cardText";
+ const serverId = item.ServerId || options.serverId;
- var lines = [];
- var parentTitleUnderneath = item.Type === 'MusicAlbum' || item.Type === 'Audio' || item.Type === 'MusicVideo';
- var titleAdded;
+ let lines = [];
+ const parentTitleUnderneath = item.Type === 'MusicAlbum' || item.Type === 'Audio' || item.Type === 'MusicVideo';
+ let titleAdded;
if (showOtherText) {
if ((options.showParentTitle || options.showParentTitleOrTitle) && !parentTitleUnderneath) {
@@ -814,7 +907,7 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
} else {
- var parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || "";
+ const parentTitle = item.SeriesName || item.Series || item.Album || item.AlbumArtist || "";
if (parentTitle || showTitle) {
lines.push(parentTitle);
@@ -824,14 +917,14 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
}
- var showMediaTitle = (showTitle && !titleAdded) || (options.showParentTitleOrTitle && !lines.length);
+ let showMediaTitle = (showTitle && !titleAdded) || (options.showParentTitleOrTitle && !lines.length);
if (!showMediaTitle && !titleAdded && (showTitle || forceName)) {
showMediaTitle = true;
}
if (showMediaTitle) {
- var name = options.showTitle === 'auto' && !item.IsFolder && item.MediaType === 'Photo' ? '' : itemHelper.getDisplayName(item, {
+ const name = options.showTitle === 'auto' && !item.IsFolder && item.MediaType === 'Photo' ? '' : itemHelper.getDisplayName(item, {
includeParentInfo: options.includeParentInfoInTitle
});
@@ -858,22 +951,18 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
}
if (options.showItemCounts) {
-
- var itemCountHtml = getItemCountsHtml(options, item);
-
- lines.push(itemCountHtml);
+ lines.push(getItemCountsHtml(options, item));
}
if (options.textLines) {
- var additionalLines = options.textLines(item);
- for (var i = 0, length = additionalLines.length; i < length; i++) {
+ const additionalLines = options.textLines(item);
+ for (let i = 0; i < additionalLines.length; i++) {
lines.push(additionalLines[i]);
}
}
if (options.showSongCount) {
-
- var songLine = '';
+ let songLine = '';
if (item.SongCount) {
songLine = item.SongCount === 1 ?
@@ -911,7 +1000,7 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
} else {
if (item.EndDate && item.ProductionYear) {
- var endYear = datetime.parseISO8601Date(item.EndDate).getFullYear();
+ const endYear = datetime.parseISO8601Date(item.EndDate).getFullYear();
lines.push(item.ProductionYear + ((endYear === item.ProductionYear) ? '' : (' - ' + endYear)));
} else {
lines.push(item.ProductionYear || '');
@@ -1006,7 +1095,7 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
lines = [];
}
- var addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile;
+ const addRightTextMargin = isOuterFooter && options.cardLayout && !options.centerText && options.cardFooterAside !== 'none' && layoutManager.mobile;
html += getCardTextLines(lines, cssClass, !options.overlayText, isOuterFooter, options.cardLayout, addRightTextMargin, options.lines);
@@ -1027,8 +1116,14 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
return html;
}
+ /**
+ * Generates the HTML markup for the action button.
+ * @param {Object} item - Item used to generate the action button.
+ * @param {string} text - Text of the action button.
+ * @param {string} serverId - ID of the server.
+ * @returns {string} HTML markup of the action button.
+ */
function getTextActionButton(item, text, serverId) {
-
if (!text) {
text = itemHelper.getDisplayName(item);
}
@@ -1037,18 +1132,22 @@ define(['datetime', 'imageLoader', 'connectionManager', 'itemHelper', 'focusMana
return text;
}
- var html = '';
}
- var cardScalableClass = 'cardScalable';
+ let cardScalableClass = 'cardScalable';
cardImageContainerOpen = '