// Interfaces import infVideoApi from "../Player/Interfaces/infVideoApi"; // Plugins import { lory } from 'lory.js'; // Styles import './Scss/video-info.scss'; import './Scss/playlist.scss'; import arrow from "../Assets/arrow"; export default class PlaylistManager { private dmPlaylist: HTMLDivElement = null; private rootEl: HTMLDivElement = null; private videos: infVideoApi = null; private slides: HTMLLIElement[] = []; private slideActive: string = ''; private videoTitle: HTMLElement = null; private videoDesc: HTMLElement = null; public constructor(rootEl: HTMLDivElement, videos: infVideoApi, playnow?: boolean) { this.rootEl = rootEl; this.videos = videos; this.addEventListeners(); this.generateCarouselTag(); this.generateVideoInfo(); if (playnow) this.generateNowPlaying(); } private addEventListeners() { document.addEventListener('dm-video-end', ( e: Event) => { // @ts-ignore this.playNext(e.detail); }); } private generateNowPlaying() { const nowPlaying = document.createElement('div'); nowPlaying.className = 'dm__now-playing-status'; const statusText = document.createElement('div'); statusText.className = 'dm__now-playing-text'; statusText.innerText = 'Now playing: '; nowPlaying.appendChild(statusText); this.videoTitle = document.createElement('div'); this.videoTitle.className = 'dm__now-playing-title'; this.videoTitle.innerText = this.videos.list[0].title; nowPlaying.appendChild(this.videoTitle); this.dmPlaylist.insertAdjacentElement('afterbegin', nowPlaying); } private generateVideoInfo() { const videoInfo = document.createElement('div'); videoInfo.className = 'dm__video-info'; this.videoTitle = document.createElement('h3'); this.videoTitle.className = 'dm__video-title'; this.videoTitle.innerText = this.videos.list[0].title; this.videoDesc = document.createElement('div'); this.videoDesc.className = 'dm__video-description'; this.videoDesc.innerHTML = this.videos.list[0].description.replace( /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig, '$1' ); const collapseButton = document.createElement('button'); collapseButton.innerHTML = arrow; collapseButton.className = 'dm__collapse-button'; collapseButton.setAttribute('aria-label', 'Collapse video description'); collapseButton.addEventListener('click', e => { this.collapseDesc(this.videoDesc, collapseButton); }); videoInfo.appendChild(this.videoTitle); videoInfo.appendChild(this.videoDesc); videoInfo.appendChild(collapseButton); this.rootEl.insertAdjacentElement('afterbegin', videoInfo); } private collapseDesc(descEl: HTMLElement, buttonEl: HTMLButtonElement): void { if (descEl.classList.contains('dm__full-desc')) { descEl.classList.remove('dm__full-desc'); buttonEl.classList.remove('collapsed'); } else { descEl.classList.add('dm__full-desc'); buttonEl.classList.add('collapsed'); } } private generateCarouselTag() { // Create the playlist container first this.dmPlaylist = document.createElement('div'); this.dmPlaylist.className = 'dm-playlist'; const carouselPlugin = document.createElement('div'); carouselPlugin.className = 'slider js_slider'; // Track for Carousel const carouselTrack = document.createElement('div'); carouselTrack.className = 'frame js_frame'; // carouselTrack.setAttribute('data-glide-el', 'track'); // Slides const carouselSlides = document.createElement('ul'); carouselSlides.className = 'slides js_slides'; this.slideActive = this.videos.list[0].id; for(let i=0; i < this.videos.list.length; i++) { // Slide this.slides[i] = document.createElement('li'); this.slides[i].className = (i === 0) ? 'js_slide dm__playlist-slide dm__playlist-active' : 'js_slide dm__playlist-slide'; this.slides[i].setAttribute('data-id', this.videos.list[i].id); const slideWrapper = document.createElement('div'); slideWrapper.className = 'dm__slide-wrapper'; // Thumbnail const contThumbnail = document.createElement('figure'); contThumbnail.className = 'dm__playlist-cont-thumbnail'; const thumbnail = new Image(); thumbnail.className = 'dm__playlist-thumbnail'; thumbnail.src = this.videos.list[i].thumbnail_240_url; contThumbnail.appendChild(thumbnail); const duration = document.createElement('span'); duration.className = 'dm__playlist-duration'; duration.innerText = this.durationFormat(this.videos.list[i].duration); contThumbnail.appendChild(duration); slideWrapper.appendChild(contThumbnail); const thumbInfo = document.createElement('div'); thumbInfo.className = 'dm__playlist-info-container'; // Title const title = document.createElement('h5'); title.className = 'dm__playlist-title'; const text = document.createTextNode(this.videos.list[i].title); title.appendChild(text); thumbInfo.appendChild(title); const publisherName = document.createElement('p'); publisherName.className = 'dm__playlist-publisher-name'; const publisherAva = new Image(); publisherAva.className = 'dm__playlist-publisher-ava'; publisherAva.src = this.videos.list[i]['owner.avatar_25_url']; publisherName.appendChild(publisherAva); const name = document.createElement('span'); name.innerText = this.videos.list[i]['owner.screenname']; publisherName.appendChild(name); thumbInfo.appendChild(publisherName); slideWrapper.appendChild(thumbInfo); this.slides[i].appendChild(slideWrapper); // Listen to the slide this.slides[i].addEventListener('click', (e) => { // @ts-ignore this.toggleActive(e.target.dataset.id); }); carouselSlides.appendChild(this.slides[i]); } /** * Left Arrow */ const leftArrow = document.createElement('button'); leftArrow.className = 'js_prev prev'; // leftArrow.setAttribute('data-glide-dir', '<'); leftArrow.setAttribute('aria-label', 'Previous'); leftArrow.innerHTML = ''; /** * Right Arrow */ const rightArrow = document.createElement('button'); rightArrow.className = 'js_next next'; // rightArrow.setAttribute('data-glide-dir', '>'); rightArrow.setAttribute('aria-label', 'Next'); rightArrow.innerHTML = ''; /** * Append component to its container */ carouselTrack.appendChild(carouselSlides); carouselPlugin.appendChild(carouselTrack); carouselPlugin.appendChild(leftArrow); carouselPlugin.appendChild(rightArrow); this.dmPlaylist.appendChild(carouselPlugin); this.rootEl.appendChild(this.dmPlaylist); const self = this; /** * Execute the carousel */ lory(self.dmPlaylist, { slidesToScroll: 2, }); } private toggleActive(videoId: string): void { this.slideActive = videoId; for( let i=0; i < this.slides.length; i++) { this.slides[i].className = (this.slides[i].dataset.id === this.slideActive) ? 'glide__slide dm__playlist-slide dm__playlist-active' : 'glide__slide dm__playlist-slide'; } // Seek the video active if (this.videoTitle) { for (let i = 0; i < this.videos.list.length; i++) { if (videoId === this.videos.list[i].id) { this.videoTitle.innerText = this.videos.list[i].title; this.videoDesc.innerHTML = this.videos.list[i].description; break; } } } this.slideChanges(videoId); } private playNext(videoId: string): void { let slideActive = videoId; // Seek the video container first for( let i=0; i < this.slides.length; i++) { if (this.slides[i].dataset.id === videoId && i !== this.slides.length - 1) { slideActive = this.slides[i+1].dataset.id; break; } else if (i === this.slides.length - 1) { slideActive = this.slides[0].dataset.id; break; } } this.toggleActive(slideActive); } private slideChanges(videoId: string):void { const slideChanges = new CustomEvent("dm-slide-changes", {detail: videoId}); document.dispatchEvent(slideChanges); } private durationFormat(duration: number): string { return new Date(duration * 1000).toISOString().substr(14,5); } }