From 79e664ca13dc631c300bcc52c2aa62e07af3eb70 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 21 Mar 2020 23:45:23 +0100 Subject: [PATCH 1/6] Refactor slideshow.js --- src/components/slideshow/slideshow.js | 236 +++++--------------------- 1 file changed, 44 insertions(+), 192 deletions(-) diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index 4d426f2484..cb8f0442b2 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -1,8 +1,7 @@ -define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'loading', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost, loading) { +define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost) { 'use strict'; function getImageUrl(item, options, apiClient) { - options = options || {}; options.type = options.type || "Primary"; @@ -28,7 +27,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function getBackdropImageUrl(item, options, apiClient) { - options = options || {}; options.type = options.type || "Backdrop"; @@ -46,19 +44,14 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f return null; } - function getImgUrl(item, original) { - + function getImgUrl(item) { var apiClient = connectionManager.getApiClient(item.ServerId); var imageOptions = {}; - if (!original) { - imageOptions.maxWidth = screen.availWidth; - } if (item.BackdropImageTags && item.BackdropImageTags.length) { return getBackdropImageUrl(item, imageOptions, apiClient); } else { - - if (item.MediaType === 'Photo' && original) { + if (item.MediaType === 'Photo') { return apiClient.getItemDownloadUrl(item.Id); } imageOptions.type = "Primary"; @@ -67,14 +60,12 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function getIcon(icon, cssClass, canFocus, autoFocus) { - var tabIndex = canFocus ? '' : ' tabindex="-1"'; autoFocus = autoFocus ? ' autofocus' : ''; return ''; } function setUserScalable(scalable) { - try { appHost.setUserScalable(scalable); } catch (err) { @@ -88,9 +79,8 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f var swiperInstance; var dlg; var currentTimeout; - var currentIntervalMs; var currentOptions; - var currentIndex; + var _osdOpen = false; // small hack since this is not possible anyway if (browser.chromecast) { @@ -98,6 +88,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function createElements(options) { + currentOptions = options; dlg = dialogHelper.createDialog({ exitAnimationDuration: options.interactive ? 400 : 800, @@ -113,10 +104,8 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f var html = ''; if (options.interactive) { - var actionButtonsOnTop = layoutManager.mobile; - html += '
'; html += '
'; html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false); @@ -137,7 +126,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f if (!actionButtonsOnTop) { html += '
'; - html += getIcon('pause', 'btnSlideshowPause slideshowButton', true, true); + html += getIcon('play_arrow', 'btnSlideshowPause slideshowButton', true, true); if (appHost.supports('filedownload')) { html += getIcon('file_download', 'btnDownload slideshowButton', true); } @@ -148,8 +137,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f html += '
'; } - html += '
'; - } else { html += '

'; } @@ -158,11 +145,8 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f if (options.interactive) { dlg.querySelector('.btnSlideshowExit').addEventListener('click', function (e) { - dialogHelper.close(dlg); }); - dlg.querySelector('.btnSlideshowNext').addEventListener('click', nextImage); - dlg.querySelector('.btnSlideshowPrevious').addEventListener('click', previousImage); var btnPause = dlg.querySelector('.btnSlideshowPause'); if (btnPause) { @@ -201,25 +185,23 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f function onAutoplayStart() { var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i'); if (btnSlideshowPause) { - btnSlideshowPause.classList.remove("play_arrow"); - btnSlideshowPause.classList.add("pause"); + btnSlideshowPause.classList.replace("play_arrow", "pause"); } } function onAutoplayStop() { var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i'); if (btnSlideshowPause) { - btnSlideshowPause.classList.remove("pause"); - btnSlideshowPause.classList.add("play_arrow"); + btnSlideshowPause.classList.replace("pause", "play_arrow"); } } function loadSwiper(dlg) { - + var slides; if (currentOptions.slides) { - dlg.querySelector('.swiper-wrapper').innerHTML = currentOptions.slides.map(getSwiperSlideHtmlFromSlide).join(''); + slides = currentOptions.slides; } else { - dlg.querySelector('.swiper-wrapper').innerHTML = currentOptions.items.map(getSwiperSlideHtmlFromItem).join(''); + slides = currentOptions.items; } require(['swiper'], function (Swiper) { @@ -227,33 +209,43 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f swiperInstance = new Swiper(dlg.querySelector('.slideshowSwiperContainer'), { // Optional parameters direction: 'horizontal', - loop: options.loop !== false, - autoplay: { - delay: options.interval || 8000 + loop: false, + autoplay: false, + keyboard: { + enabled: true }, - // Disable preloading of all images - preloadImages: false, - // Enable lazy loading - lazy: true, - loadPrevNext: true, - disableOnInteraction: false, + preloadImages: true, + slidesPerView: 1, + slidesPerColumn: 1, initialSlide: options.startIndex || 0, - speed: 240 + speed: 240, + navigation: { + nextEl: '.btnSlideshowNext', + prevEl: '.btnSlideshowPrevious' + }, + virtual: { + slides: slides, + cache: true, + renderSlide: getSwiperSlideHtml, + addSlidesBefore: 1, + addSlidesAfter: 1 + } }); swiperInstance.on('autoplayStart', onAutoplayStart); swiperInstance.on('autoplayStop', onAutoplayStop); - - if (layoutManager.mobile) { - pause(); - } else { - play(); - } }); } - function getSwiperSlideHtmlFromItem(item) { + function getSwiperSlideHtml(item, index) { + if (currentOptions.slides) { + return getSwiperSlideHtmlFromSlide(item); + } else { + return getSwiperSlideHtmlFromItem(item); + } + } + function getSwiperSlideHtmlFromItem(item) { return getSwiperSlideHtmlFromSlide({ imageUrl: getImgUrl(item), originalImage: getImgUrl(item, true), @@ -265,10 +257,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function getSwiperSlideHtmlFromSlide(item) { - var html = ''; - html += '
'; - html += ''; + html += '
'; + html += '
'; + html += ''; + html += '
'; if (item.title || item.subtitle) { html += '
'; html += '
'; @@ -290,42 +283,14 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f return html; } - function previousImage() { - if (swiperInstance) { - swiperInstance.slidePrev(); - } else { - stopInterval(); - showNextImage(currentIndex - 1); - } - } - - function nextImage() { - if (swiperInstance) { - - if (options.loop === false) { - - if (swiperInstance.activeIndex >= swiperInstance.slides.length - 1) { - dialogHelper.close(dlg); - return; - } - } - - swiperInstance.slideNext(); - } else { - stopInterval(); - showNextImage(currentIndex + 1); - } - } - function getCurrentImageInfo() { - if (swiperInstance) { var slide = document.querySelector('.swiper-slide-active'); if (slide) { return { url: slide.getAttribute('data-original'), - shareUrl: slide.getAttribute('data-imageurl'), + shareUrl: slide.getAttribute('data-original'), itemId: slide.getAttribute('data-itemid'), serverId: slide.getAttribute('data-serverid') }; @@ -337,7 +302,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function download() { - var imageInfo = getCurrentImageInfo(); require(['fileDownloader'], function (fileDownloader) { @@ -346,7 +310,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function share() { - var imageInfo = getCurrentImageInfo(); navigator.share({ @@ -376,7 +339,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function onDialogClosed() { - var swiper = swiperInstance; if (swiper) { swiper.destroy(true, true); @@ -387,31 +349,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); } - function startInterval(options) { - - currentOptions = options; - - stopInterval(); - createElements(options); - - if (!options.interactive) { - currentIntervalMs = options.interval || 11000; - showNextImage(options.startIndex || 0, true); - } - } - - var _osdOpen = false; - - function isOsdOpen() { - return _osdOpen; - } - function getOsdBottom() { return dlg.querySelector('.slideshowBottomBar'); } function showOsd() { - var bottom = getOsdBottom(); if (bottom) { slideUpToShow(bottom); @@ -420,7 +362,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function hideOsd() { - var bottom = getOsdBottom(); if (bottom) { slideDownToHide(bottom); @@ -442,7 +383,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function slideUpToShow(elem) { - if (!elem.classList.contains('hide')) { return; } @@ -471,7 +411,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function slideDownToHide(elem) { - if (elem.classList.contains('hide')) { return; } @@ -529,23 +468,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } function onInputCommand(e) { - switch (e.detail.command) { - - case 'left': - if (!isOsdOpen()) { - e.preventDefault(); - e.stopPropagation(); - previousImage(); - } - break; - case 'right': - if (!isOsdOpen()) { - e.preventDefault(); - e.stopPropagation(); - nextImage(); - } - break; case 'up': case 'down': case 'select': @@ -561,76 +484,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } } - function showNextImage(index, skipPreload) { - - index = Math.max(0, index); - if (index >= currentOptions.items.length) { - index = 0; - } - currentIndex = index; - - var options = currentOptions; - var items = options.items; - var item = items[index]; - var imgUrl = getImgUrl(item); - - var onSrcLoaded = function () { - var cardImageContainer = dlg.querySelector('.slideshowImage'); - - var newCardImageContainer = document.createElement('div'); - newCardImageContainer.className = cardImageContainer.className; - - if (options.cover) { - newCardImageContainer.classList.add('slideshowImage-cover'); - } - - newCardImageContainer.style.backgroundImage = "url('" + imgUrl + "')"; - newCardImageContainer.classList.add('hide'); - cardImageContainer.parentNode.appendChild(newCardImageContainer); - - if (options.showTitle) { - dlg.querySelector('.slideshowImageText').innerHTML = item.Name; - } else { - dlg.querySelector('.slideshowImageText').innerHTML = ''; - } - - newCardImageContainer.classList.remove('hide'); - var onAnimationFinished = function () { - - var parentNode = cardImageContainer.parentNode; - if (parentNode) { - parentNode.removeChild(cardImageContainer); - } - }; - - if (newCardImageContainer.animate) { - - var keyframes = [ - { opacity: '0', offset: 0 }, - { opacity: '1', offset: 1 } - ]; - var timing = { duration: 1200, iterations: 1 }; - newCardImageContainer.animate(keyframes, timing).onfinish = onAnimationFinished; - } else { - onAnimationFinished(); - } - - stopInterval(); - currentTimeout = setTimeout(function () { - showNextImage(index + 1, true); - - }, currentIntervalMs); - }; - - if (!skipPreload) { - var img = new Image(); - img.onload = onSrcLoaded; - img.src = imgUrl; - } else { - onSrcLoaded(); - } - } - function stopInterval() { if (currentTimeout) { clearTimeout(currentTimeout); @@ -639,11 +492,10 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } self.show = function () { - startInterval(options); + createElements(options); }; self.hide = function () { - var dialog = dlg; if (dialog) { From 18d1305ead762f01202ab6e6d59c590ef95ecce4 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sun, 22 Mar 2020 15:34:34 +0100 Subject: [PATCH 2/6] Add JSDoc comments --- src/components/slideshow/slideshow.js | 255 ++++++++++++++++++-------- 1 file changed, 182 insertions(+), 73 deletions(-) diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index cb8f0442b2..f8aae5c716 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -1,6 +1,17 @@ +/** + * Image viewer component + * @module components/slideshow/slideshow + */ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'focusManager', 'browser', 'apphost', 'css!./style', 'material-icons', 'paper-icon-button-light'], function (dialogHelper, inputManager, connectionManager, layoutManager, focusManager, browser, appHost) { 'use strict'; + /** + * Retrieves an item's image URL from the API. + * @param {object|string} item - Item used to generate the image URL. + * @param {object} options - Options of the image. + * @param {object} apiClient - API client instance used to retrieve the image. + * @returns {null|string} URL of the item's image. + */ function getImageUrl(item, options, apiClient) { options = options || {}; options.type = options.type || "Primary"; @@ -10,7 +21,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } if (item.ImageTags && item.ImageTags[options.type]) { - options.tag = item.ImageTags[options.type]; return apiClient.getScaledImageUrl(item.Id, options); } @@ -26,6 +36,13 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f return null; } + /** + * Retrieves a backdrop's image URL from the API. + * @param {object} item - Item used to generate the image URL. + * @param {object} options - Options of the image. + * @param {object} apiClient - API client instance used to retrieve the image. + * @returns {null|string} URL of the item's backdrop. + */ function getBackdropImageUrl(item, options, apiClient) { options = options || {}; options.type = options.type || "Backdrop"; @@ -44,6 +61,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f return null; } + /** + * Dispatches a request for an item's image to its respective handler. + * @param {object} item - Item used to generate the image URL. + * @returns {string} URL of the item's image. + */ function getImgUrl(item) { var apiClient = connectionManager.getApiClient(item.ServerId); var imageOptions = {}; @@ -59,12 +81,24 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } } + /** + * Generates a button using the specified icon, classes and properties. + * @param {string} icon - Name of the material icon on the button + * @param {string} cssClass - CSS classes to assign to the button + * @param {boolean} canFocus - Flag to set the tabindex attribute on the button to -1. + * @param {boolean} autoFocus - Flag to set the autofocus attribute on the button. + * @returns {string} The HTML markup of the button. + */ function getIcon(icon, cssClass, canFocus, autoFocus) { var tabIndex = canFocus ? '' : ' tabindex="-1"'; autoFocus = autoFocus ? ' autofocus' : ''; return ''; } + /** + * Sets the viewport meta tag to enable or disable scaling by the user. + * @param {boolean} scalable - Flag to set the scalability of the viewport. + */ function setUserScalable(scalable) { try { appHost.setUserScalable(scalable); @@ -74,23 +108,31 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } return function (options) { - var self = this; + /** Initialized instance of Swiper. */ var swiperInstance; - var dlg; - var currentTimeout; + /** Initialized instance of the dialog containing the Swiper instance. */ + var dialog; + /** Options of the slideshow components */ var currentOptions; + /** ID of the timeout used to hide the OSD. */ + var hideTimeout; + /** Last coordinates of the mouse pointer. */ + var lastMouseMoveData; + /** Visibility status of the OSD. */ var _osdOpen = false; - // small hack since this is not possible anyway - if (browser.chromecast) { - options.interactive = false; - } + // Use autoplay on Chromecast since it is non-interactive. + options.interactive = !browser.chromecast; + /** + * Creates the HTML markup for the dialog and the OSD. + * @param {Object} options - Options used to create the dialog and slideshow. + */ function createElements(options) { currentOptions = options; - dlg = dialogHelper.createDialog({ + dialog = dialogHelper.createDialog({ exitAnimationDuration: options.interactive ? 400 : 800, size: 'fullscreen', autoFocus: false, @@ -99,7 +141,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f removeOnClose: true }); - dlg.classList.add('slideshowDialog'); + dialog.classList.add('slideshowDialog'); var html = ''; @@ -141,24 +183,24 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f html += '

'; } - dlg.innerHTML = html; + dialog.innerHTML = html; if (options.interactive) { - dlg.querySelector('.btnSlideshowExit').addEventListener('click', function (e) { - dialogHelper.close(dlg); + dialog.querySelector('.btnSlideshowExit').addEventListener('click', function (e) { + dialogHelper.close(dialog); }); - var btnPause = dlg.querySelector('.btnSlideshowPause'); + var btnPause = dialog.querySelector('.btnSlideshowPause'); if (btnPause) { btnPause.addEventListener('click', playPause); } - var btnDownload = dlg.querySelector('.btnDownload'); + var btnDownload = dialog.querySelector('.btnDownload'); if (btnDownload) { btnDownload.addEventListener('click', download); } - var btnShare = dlg.querySelector('.btnShare'); + var btnShare = dialog.querySelector('.btnShare'); if (btnShare) { btnShare.addEventListener('click', share); } @@ -166,37 +208,46 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f setUserScalable(true); - dialogHelper.open(dlg).then(function () { - + dialogHelper.open(dialog).then(function () { setUserScalable(false); - stopInterval(); }); inputManager.on(window, onInputCommand); document.addEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); - dlg.addEventListener('close', onDialogClosed); + dialog.addEventListener('close', onDialogClosed); if (options.interactive) { - loadSwiper(dlg); + loadSwiper(dialog, options); } } + /** + * Handles OSD changes when the autoplay is started. + */ function onAutoplayStart() { - var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i'); + var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause i'); if (btnSlideshowPause) { btnSlideshowPause.classList.replace("play_arrow", "pause"); } } + /** + * Handles OSD changes when the autoplay is stopped. + */ function onAutoplayStop() { - var btnSlideshowPause = dlg.querySelector('.btnSlideshowPause i'); + var btnSlideshowPause = dialog.querySelector('.btnSlideshowPause i'); if (btnSlideshowPause) { btnSlideshowPause.classList.replace("pause", "play_arrow"); } } - function loadSwiper(dlg) { + /** + * Initializes the Swiper instance and binds the relevant events. + * @param {HTMLElement} dialog - Element containing the dialog. + * @param {Object} options - Options used to initialize the Swiper instance. + */ + function loadSwiper(dialog, options) { var slides; if (currentOptions.slides) { slides = currentOptions.slides; @@ -205,12 +256,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } require(['swiper'], function (Swiper) { - - swiperInstance = new Swiper(dlg.querySelector('.slideshowSwiperContainer'), { + swiperInstance = new Swiper(dialog.querySelector('.slideshowSwiperContainer'), { // Optional parameters direction: 'horizontal', loop: false, - autoplay: false, + autoplay: options.interactive ? false : true, keyboard: { enabled: true }, @@ -237,6 +287,12 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f }); } + /** + * Renders the HTML markup of a slide for an item or a slide. + * @param {Object} item - The item used to render the slide. + * @param {number} index - The index of the item in the Swiper instance. + * @returns {string} The HTML markup of the slide. + */ function getSwiperSlideHtml(item, index) { if (currentOptions.slides) { return getSwiperSlideHtmlFromSlide(item); @@ -245,6 +301,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } } + /** + * Renders the HTML markup of a slide for an item. + * @param {Object} item - Item used to generate the slide. + * @returns {string} The HTML markup of the slide. + */ function getSwiperSlideHtmlFromItem(item) { return getSwiperSlideHtmlFromSlide({ imageUrl: getImgUrl(item), @@ -256,6 +317,11 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f }); } + /** + * Renders the HTML markup of a slide for a slide object. + * @param {Object} item - Slide object used to generate the slide. + * @returns {string} The HTML markup of the slide. + */ function getSwiperSlideHtmlFromSlide(item) { var html = ''; html += '
'; @@ -283,6 +349,10 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f return html; } + /** + * Fetches the information of the currently displayed slide. + * @returns {null|{itemId: string, shareUrl: string, serverId: string, url: string}} Object containing the information of the currently displayed slide. + */ function getCurrentImageInfo() { if (swiperInstance) { var slide = document.querySelector('.swiper-slide-active'); @@ -301,6 +371,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } } + /** + * Starts a download for the currently displayed slide. + */ function download() { var imageInfo = getCurrentImageInfo(); @@ -309,6 +382,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f }); } + /** + * Shares the currently displayed slide using the browser's built-in sharing feature. + */ function share() { var imageInfo = getCurrentImageInfo(); @@ -317,20 +393,29 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f }); } + /** + * Starts the autoplay feature of the Swiper instance. + */ function play() { if (swiperInstance.autoplay) { swiperInstance.autoplay.start(); } } + /** + * Pauses the autoplay feature of the Swiper instance; + */ function pause() { if (swiperInstance.autoplay) { swiperInstance.autoplay.stop(); } } + /** + * Toggles the autoplay feature of the Swiper instance. + */ function playPause() { - var paused = !dlg.querySelector('.btnSlideshowPause i').classList.contains("pause"); + var paused = !dialog.querySelector('.btnSlideshowPause i').classList.contains("pause"); if (paused) { play(); } else { @@ -338,6 +423,9 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } } + /** + * Closes the dialog and destroys the Swiper instance. + */ function onDialogClosed() { var swiper = swiperInstance; if (swiper) { @@ -349,32 +437,38 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f document.removeEventListener((window.PointerEvent ? 'pointermove' : 'mousemove'), onPointerMove); } - function getOsdBottom() { - return dlg.querySelector('.slideshowBottomBar'); - } - + /** + * Shows the OSD. + */ function showOsd() { - var bottom = getOsdBottom(); + var bottom = dialog.querySelector('.slideshowBottomBar'); if (bottom) { slideUpToShow(bottom); startHideTimer(); } } + /** + * Hides the OSD. + */ function hideOsd() { - var bottom = getOsdBottom(); + var bottom = dialog.querySelector('.slideshowBottomBar'); if (bottom) { slideDownToHide(bottom); } } - var hideTimeout; - + /** + * Starts the timer used to automatically hide the OSD. + */ function startHideTimer() { stopHideTimer(); - hideTimeout = setTimeout(hideOsd, 4000); + hideTimeout = setTimeout(hideOsd, 3000); } + /** + * Stops the timer used to automatically hide the OSD. + */ function stopHideTimer() { if (hideTimeout) { clearTimeout(hideTimeout); @@ -382,69 +476,76 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } } - function slideUpToShow(elem) { - if (!elem.classList.contains('hide')) { + /** + * Shows the OSD by sliding it into view. + * @param {HTMLElement} element - Element containing the OSD. + */ + function slideUpToShow(element) { + if (!element.classList.contains('hide')) { return; } _osdOpen = true; - elem.classList.remove('hide'); + element.classList.remove('hide'); var onFinish = function () { - focusManager.focus(elem.querySelector('.btnSlideshowPause')); + focusManager.focus(element.querySelector('.btnSlideshowPause')); }; - if (!elem.animate) { + if (!element.animate) { onFinish(); return; } requestAnimationFrame(function () { - var keyframes = [ - { transform: 'translate3d(0,' + elem.offsetHeight + 'px,0)', opacity: '.3', offset: 0 }, + { transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 0 }, { transform: 'translate3d(0,0,0)', opacity: '1', offset: 1 } ]; var timing = { duration: 300, iterations: 1, easing: 'ease-out' }; - elem.animate(keyframes, timing).onfinish = onFinish; + element.animate(keyframes, timing).onfinish = onFinish; }); } - function slideDownToHide(elem) { - if (elem.classList.contains('hide')) { + /** + * Hides the OSD by sliding it out of view. + * @param {HTMLElement} element - Element containing the OSD. + */ + function slideDownToHide(element) { + if (element.classList.contains('hide')) { return; } var onFinish = function () { - elem.classList.add('hide'); + element.classList.add('hide'); _osdOpen = false; }; - if (!elem.animate) { + if (!element.animate) { onFinish(); return; } requestAnimationFrame(function () { - var keyframes = [ { transform: 'translate3d(0,0,0)', opacity: '1', offset: 0 }, - { transform: 'translate3d(0,' + elem.offsetHeight + 'px,0)', opacity: '.3', offset: 1 } + { transform: 'translate3d(0,' + element.offsetHeight + 'px,0)', opacity: '.3', offset: 1 } ]; var timing = { duration: 300, iterations: 1, easing: 'ease-out' }; - elem.animate(keyframes, timing).onfinish = onFinish; + element.animate(keyframes, timing).onfinish = onFinish; }); } - var lastMouseMoveData; - - function onPointerMove(e) { - - var pointerType = e.pointerType || (layoutManager.mobile ? 'touch' : 'mouse'); + /** + * Shows the OSD when moving the mouse pointer or touching the screen. + * @param {Event} event - Pointer movement event. + */ + function onPointerMove(event) { + var pointerType = event.pointerType || (layoutManager.mobile ? 'touch' : 'mouse'); if (pointerType === 'mouse') { - var eventX = e.screenX || 0; - var eventY = e.screenY || 0; + var eventX = event.screenX || 0; + var eventY = event.screenY || 0; var obj = lastMouseMoveData; if (!obj) { @@ -467,38 +568,46 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f } } - function onInputCommand(e) { - switch (e.detail.command) { + /** + * Dispatches keyboard inputs to their proper handlers. + * @param {Event} event - Keyboard input event. + */ + function onInputCommand(event) { + switch (event.detail.command) { case 'up': case 'down': case 'select': case 'menu': case 'info': - case 'play': - case 'playpause': - case 'pause': showOsd(); break; + case 'play': + play(); + break; + case 'pause': + pause(); + break; + case 'playpause': + playPause(); + break; default: break; } } - function stopInterval() { - if (currentTimeout) { - clearTimeout(currentTimeout); - currentTimeout = null; - } - } - + /** + * Shows the slideshow component. + */ self.show = function () { createElements(options); }; + /** + * Hides the slideshow element. + */ self.hide = function () { - var dialog = dlg; + var dialog = dialog; if (dialog) { - dialogHelper.close(dialog); } }; From 71f12dc36017186654e8311b4fd739075a4d6d61 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 4 Apr 2020 00:22:31 +0200 Subject: [PATCH 3/6] Correct TV mode slideshow --- src/components/slideshow/slideshow.js | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index f8aae5c716..e0eaeb8e62 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -145,10 +145,13 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f var html = ''; - if (options.interactive) { + html += '
'; + + if (options.interactive && !layoutManager.tv) { + console.warn(layoutManager.tv); + console.warn("Interactive"); var actionButtonsOnTop = layoutManager.mobile; - html += '
'; html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false); html += getIcon('keyboard_arrow_right', 'btnSlideshowNext slideshowButton hide-mouse-idle-tv', false); @@ -185,7 +188,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f dialog.innerHTML = html; - if (options.interactive) { + if (options.interactive && !layoutManager.tv) { dialog.querySelector('.btnSlideshowExit').addEventListener('click', function (e) { dialogHelper.close(dialog); }); @@ -217,9 +220,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f dialog.addEventListener('close', onDialogClosed); - if (options.interactive) { - loadSwiper(dialog, options); - } + loadSwiper(dialog, options); } /** @@ -257,10 +258,10 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f require(['swiper'], function (Swiper) { swiperInstance = new Swiper(dialog.querySelector('.slideshowSwiperContainer'), { - // Optional parameters direction: 'horizontal', + // Loop is disabled due to the virtual slides option not supporting it. loop: false, - autoplay: options.interactive ? false : true, + autoplay: !options.interactive, keyboard: { enabled: true }, @@ -273,6 +274,7 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f nextEl: '.btnSlideshowNext', prevEl: '.btnSlideshowPrevious' }, + // Virtual slides reduce memory consumption for large libraries while allowing preloading of images; virtual: { slides: slides, cache: true, From 6cf140894403308161473e938e6eb40a9a8cd776 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 4 Apr 2020 00:23:29 +0200 Subject: [PATCH 4/6] Fix lint issue in slideshow --- src/components/slideshow/slideshow.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index e0eaeb8e62..e637b93701 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -152,7 +152,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f console.warn("Interactive"); var actionButtonsOnTop = layoutManager.mobile; - html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false); html += getIcon('keyboard_arrow_right', 'btnSlideshowNext slideshowButton hide-mouse-idle-tv', false); From 03f5be20adbcf63e27918956ac92fc5414429b70 Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 4 Apr 2020 01:14:11 +0200 Subject: [PATCH 5/6] Remove console logs in slideshow --- src/components/slideshow/slideshow.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/slideshow/slideshow.js b/src/components/slideshow/slideshow.js index e637b93701..26dc303def 100644 --- a/src/components/slideshow/slideshow.js +++ b/src/components/slideshow/slideshow.js @@ -148,8 +148,6 @@ define(['dialogHelper', 'inputManager', 'connectionManager', 'layoutManager', 'f html += '
'; if (options.interactive && !layoutManager.tv) { - console.warn(layoutManager.tv); - console.warn("Interactive"); var actionButtonsOnTop = layoutManager.mobile; html += getIcon('keyboard_arrow_left', 'btnSlideshowPrevious slideshowButton hide-mouse-idle-tv', false); From 88bc6d4e915069fe79f9f5677d10cfdc7cb72dfb Mon Sep 17 00:00:00 2001 From: MrTimscampi Date: Sat, 4 Apr 2020 16:00:38 +0200 Subject: [PATCH 6/6] Add Classlist.js polyfill --- package.json | 1 + src/bundle.js | 6 ++++++ src/scripts/site.js | 4 +++- yarn.lock | 4 ++++ 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c0ff158ec9..2f34ea2302 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ }, "dependencies": { "alameda": "^1.4.0", + "classlist.js": "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz", "core-js": "^3.6.4", "document-register-element": "^1.14.3", "flv.js": "^1.5.0", diff --git a/src/bundle.js b/src/bundle.js index ba5f74b163..a8d872d4e3 100644 --- a/src/bundle.js +++ b/src/bundle.js @@ -112,3 +112,9 @@ var polyfill = require("@babel/polyfill/dist/polyfill"); _define("polyfill", function () { return polyfill; }); + +// domtokenlist-shim +var classlist = require("classlist.js"); +_define("classlist-polyfill", function () { + return classlist; +}); diff --git a/src/scripts/site.js b/src/scripts/site.js index 513d0c0754..824d8d0ca0 100644 --- a/src/scripts/site.js +++ b/src/scripts/site.js @@ -694,7 +694,8 @@ var AppInfo = {}; "material-icons", "jellyfin-noto", "page", - "polyfill" + "polyfill", + "classlist-polyfill" ] }, urlArgs: urlArgs, @@ -703,6 +704,7 @@ var AppInfo = {}; }); require(["polyfill"]); + require(["classlist-polyfill"]); // Expose jQuery globally require(["jQuery"], function(jQuery) { diff --git a/yarn.lock b/yarn.lock index 2cfa8f3e8b..eed2aa6b85 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2320,6 +2320,10 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +"classlist.js@https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz": + version "1.2.20180112" + resolved "https://github.com/eligrey/classList.js/archive/1.2.20180112.tar.gz#9c7ab3ccdbde5c940f6f26f8fc59bfb6bc813bc4" + clean-css@4.2.x, clean-css@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.3.tgz#507b5de7d97b48ee53d84adb0160ff6216380f78"