main/VideoPlaylist.js

import EmbedPlayer from './classes/EmbedPlayer';
import StickyVideo from './classes/StickyVideo';
import Messenger from './Messenger';

/**
 * Video playlist functionality.
 *
 * @class VideoPlaylist
 * @prop {object} states - Set of classes that serve as flags for playlist states.
 * @prop {object} selectors - Set of selectors that can be used to select playlist features.
 */

const VideoPlaylist = {
	states: {
		playing: 'c-posts__item--playing',
	},

	selectors: {
		main: '.l-playlist',
		mainHeadline: '.l-playlist__main .l-playlist__headline',
		itemLink: '.l-playlist__link',
		orderedItem: '[data-playlist-index]',
		player: '.c-video',
	},

	/**
	 * Initializes video playlists.
	 * Listens for playlist clicks to initiate video player.
	 *
	 * @method init
	 */
	init() {
		const $targets = document.querySelectorAll( this.selectors.main );

		[].forEach.call( $targets, ( $target ) => {
			const $links = $target.querySelectorAll( this.selectors.itemLink );
			[].forEach.call( $links, ( $link ) => {
				$link.addEventListener( 'click', evt => this.handleItemClick( evt, $target, $link ) );
			});
		});

		Messenger.registerReceiver( 'gnca_video', this );
	},

	/**
	 * Playlist item click handler. Loads selected video into player if it is not there already.
	 * Removes selected video from the playlist.
	 *
	 * @method handleItemClick
	 * @param {event} event - click event.
	 * @param {HTMLElement} $playlist - DOM element of the playlist.
	 * @param {object} data - selected video data.
	 */
	handleItemClick( event, $playlist, $link ) {
		event.preventDefault();

		const data = $link.dataset;
		const $player = $playlist.querySelector( this.selectors.player );

		if ( data.videoCurrent ) {
			// Video player title clicked, trigger playback
			if ( 'true' !== $player.dataset.displayinlineInit ) {
				this.startVideo( $player );
			}
		} else {
			// Playlist item clicked, swap out video and trigger playback
			data.headline = $link.getAttribute( 'title' );
			this.swapVideo( $player, data );
			this.updatePlaylist( $playlist, data.videoId );
		}
	},

	/**
	 * Swap out current playlist video with a new video and restart player.
	 *
	 * @method swapVideo
	 * @param {HTMLElement} $player - DOM element of the player.
	 * @param {object} data - selected video data.
	 */
	swapVideo( $player, data ) {
		let embedPlayer = false;

		if ( 'true' === $player.dataset.displayinlineInit ) {
			embedPlayer = EmbedPlayer.getPlayer( data.videoPlayerId );
			if ( embedPlayer ) {
				const $video = StickyVideo.getVideo( data.videoPlayerId );
				StickyVideo.detachCurrentVideo();
				embedPlayer.reset( $video );
			}
		}

		/* eslint-disable no-param-reassign */
		$player.dataset.displayinlineInit = '';
		$player.dataset.displayinline = data.embedUrl;
		$player.dataset.displayinlineVideoId = data.videoId;
		/* eslint-enable no-param-reassign */

		this.startVideo( $player );
	},

	/**
	 * Start video
	 *
	 * @method startVideo
	 * @param {HTMLElement} $player - DOM element of the player.
	 */
	startVideo( $player ) {
		const embedPlayer = new EmbedPlayer( $player );
		embedPlayer.start();
	},

	/**
	 * Set playing state for selected playlist item.
	 * Reorder playlist so that current item sits at the top,
	 * or below a live stream video if one exists.
	 *
	 * @method updatePlaylist
	 * @param {HTMLElement} $playlist - DOM element of the playlist.
	 * @param {string} videoId - selected video ID
	 */
	updatePlaylist( $playlist, videoId ) {
		const $currentItem = $playlist.querySelector( `.${this.states.playing}` );

		if ( $currentItem ) {
			// Now playing state already set, bail.
			if ( $currentItem.querySelector( this.selectors.itemLink ).dataset.videoId === videoId ) {
				return;
			}
			$currentItem.classList.remove( this.states.playing );
		}

		const $selectedLink = $playlist.querySelector( `[data-video-id="${videoId}"]` );
		if ( $selectedLink ) {
			const $selectedItem = $selectedLink.parentNode;
			$selectedItem.classList.add( this.states.playing );
		}
	},

	/**
	 * Checks to see if play event has fired for current active carousel video.
	 * If so, grabs current playlist index and passes along to update now playing flag.
	 *
	 * @method receiveMessage
	 * @param {object} data - postMessage data object.
	 */
	receiveMessage( data ) {
		if ( data.status && data.iframeId && 'undefined' !== typeof data.playlistIndex ) {
			if ( 'playlistItem' === data.status ) {
				const { iframeId: playerId } = data;
				let { videoPostId } = data;
				const $playlist = document.querySelector( `#${this.getId( playerId )}` );
				if ( $playlist ) {
					// No post ID sent via window postMessage, grap ID from player
					// grap ID from the player
					if ( ! videoPostId ) {
						const $player = $playlist.querySelector( this.selectors.player );
						videoPostId = $player.dataset.displayinlineVideoId;
					}
					this.updatePlaylist( $playlist, videoPostId );
				}
			}
		}
	},

	/**
	 * Get video playlist id
	 *
	 * @static
	 * @method getId
	 * @param {sting} playerId
	 * @return {string} The corresponding ID for that video playlist.
	 */
	getId( playerId ) {
		return `${playerId}-playlist`;
	},
};

export default VideoPlaylist;