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); } };