UNPKG

vlitejs

Version:

vLitejs is a fast and lightweight Javascript library for customizing HTML5 and Youtube video players in Javascript with a minimalist theme

664 lines (570 loc) 20.4 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: player.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: player.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>// Import SVG icons import svgBigPlay from '../svg/big-play.svg'; import svgPlay from '../svg/play.svg'; import svgPause from '../svg/pause.svg'; import svgVolumeHigh from '../svg/volume-high.svg'; import svgVolumeMute from '../svg/volume-mute.svg'; import svgFullscreen from '../svg/fullscreen.svg'; import svgFullscreenExit from '../svg/fullscreen-exit.svg'; /** * vLite Player * @module vLite/Player */ export default class Player { /** * Instanciate the constructor * @constructor * @param {String|Object} selector CSS selector or query selector * @param {Object} options Player options * @param {Function} callback Callback function executed when the player is ready */ constructor({ selector, options, callback }) { this.callback = callback; this.isFullScreen = false; this.isPaused = null; this.player = selector; this.touchSupport = this.isTouch(); this.skinDisabled = false; this.delayAutoHide = 3000; let customOptions = {}; const DEFAULT_OPTIONS = { autoplay: false, controls: true, playPause: true, timeline: true, time: true, volume: true, fullscreen: true, poster: null, bigPlay: true, autoHide: false, nativeControlsForTouch: false, playsinline: true }; // Check if options have gone through DOM with data attribute if (this.player.hasAttribute('data-options')) { // Check if there is a conflict with the constructor options if (options !== undefined) { console.warn(`[vLite] - Option passed in '${selector}' by data attribute is priority over object in constructor.`); } customOptions = JSON.parse(this.player.getAttribute('data-options')); } else { // No conflict, we can use options in the constructor customOptions = options; } this.options = Object.assign({}, DEFAULT_OPTIONS, customOptions) // Keep player native control and disable custom skin if (this.options.nativeControlsForTouch) { this.skinDisabled = true; this.player.setAttribute('controls', 'controls'); this.options.controls = false; } // Add play inline attribute if (this.options.playsinline) { this.player.setAttribute('playsinline', true); this.player.setAttribute('webkit-playsinline', true); } // Check fullscreen support API on different browsers and cached prefixs this.supportFullScreen = this.constructor.checkSupportFullScreen(); this.buildPlayer(); this.bindEvents(); } /** * Build the DOM of the player */ buildPlayer() { // Create a wrapper for each player const wrapper = document.createElement('div'); wrapper.setAttribute('class', 'vl-wrapper-vlite vl-first-start vl-paused vl-loading'); wrapper.setAttribute('tabindex', 0); this.player.parentNode.insertBefore(wrapper, this.player); wrapper.appendChild(this.player); this.wrapperPlayer = this.player.parentNode; this.player.classList.add('vl-toggle-play-pause-js'); if (this.skinDisabled) { this.wrapperPlayer.classList.add('vl-force-controls'); } const cssstylePoster = this.options.poster !== null ? `background-image: url(${this.options.poster});` : ""; const htmlControls = `${!this.options.nativeControlsForTouch ? `&lt;div class="vl-overlay-video vl-toggle-play-pause-js"> ${!this.touchSupport ? `&lt;div class="vl-overlay-left vl-fast-forward-js" data-direction="left">&lt;/div> &lt;div class="vl-overlay-right vl-fast-forward-js" data-direction="right">&lt;/div>` : ``} &lt;/div>` : ``} &lt;div class="vl-wrapper-loader"> &lt;div class="vl-loader"> &lt;div class="vl-loader-bounce-1">&lt;/div> &lt;div class="vl-loader-bounce-2">&lt;/div> &lt;div class="vl-loader-bounce-3">&lt;/div> &lt;/div> &lt;/div> &lt;div class="vl-poster vl-toggle-play-pause-js vl-active" style="${cssstylePoster}">&lt;/div> ${this.options.bigPlay ? `&lt;div class="vl-big-play-button vl-toggle-play-pause-js"> &lt;span class="vl-player-icon vl-icon-big-play">${svgBigPlay}&lt;/span> &lt;/div>` : ``} ${this.options.controls ? `&lt;div class="vl-control-bar"> ${this.options.timeline ? `&lt;div class="vl-progress-bar"> &lt;div class="vl-progress-seek">&lt;/div> &lt;input type="range" class="vl-progress-input" min="0" max="100" step="0.01" value="0" orient="horizontal" /> &lt;/div>` : ``} &lt;div class="vl-control-bar-inner"> ${this.options.playPause ? `&lt;div class="vl-play-pause-button vl-toggle-play-pause-js"> &lt;span class="vl-player-icon vl-icon-play">${svgPlay}&lt;/span> &lt;span class="vl-player-icon vl-icon-pause">${svgPause}&lt;/span> &lt;/div>` : ``} ${this.options.time ? `&lt;div class="vl-time"> &lt;span class="vl-current-time">00:00&lt;/span>&amp;nbsp;/&amp;nbsp;&lt;span class="vl-duration">&lt;/span> &lt;/div>` : ``} ${this.options.volume ? `&lt;div class="vl-volume"> &lt;span class="vl-player-icon vl-icon-volume-high">${svgVolumeHigh}&lt;/span> &lt;span class="vl-player-icon vl-icon-volume-mute">${svgVolumeMute}&lt;/span> &lt;/div>` : ``} ${this.options.fullscreen ? `&lt;div class="vl-fullscreen"> &lt;span class="vl-player-icon vl-icon-fullscreen">${svgFullscreen}&lt;/span> &lt;span class="vl-player-icon vl-icon-shrink">${svgFullscreenExit}&lt;/span> &lt;/div>` : ``} &lt;/div> &lt;/div>` : `` }`; wrapper.insertAdjacentHTML('beforeend', htmlControls); } /** * Create event listeners * All listeners are created on class properties to facilitate the deletion of events */ bindEvents() { if (this.options.controls &amp;&amp; this.options.timeline) { // Create progress bar event listener this.onChangeProgressBar = e => { this.onProgressChanged(e); }; this.wrapperPlayer.querySelector('.vl-progress-input').addEventListener('change', this.onChangeProgressBar, false); } // Create play/pause button event listener this.onClickTogglePlayPause = e => { e.preventDefault(); this.togglePlayPause(); }; const playPauseButtons = this.wrapperPlayer.querySelectorAll('.vl-toggle-play-pause-js'); playPauseButtons.forEach(button => { button.addEventListener('click', this.onClickTogglePlayPause, false); }); // Create double click to fast-forward video current time (only on desktop, mobile doesn't support event) if (!this.touchSupport) { this.onDblclickFastForward = e => { e.preventDefault(); this.fastForward(e); }; const fastForwardButtons = [...this.wrapperPlayer.querySelectorAll('.vl-fast-forward-js')] fastForwardButtons.forEach(button => { button.addEventListener('dblclick', this.onDblclickFastForward, false); }); } if (this.options.controls &amp;&amp; this.options.volume) { // Create volume button event listener this.onCLickToggleVolume = e => { e.preventDefault(); this.toggleVolume(); }; this.wrapperPlayer.querySelector('.vl-volume').addEventListener('click', this.onCLickToggleVolume, false); } if (this.options.controls &amp;&amp; this.options.fullscreen) { // Create fullscreen button event listener this.onClickToggleFullscreen = e => { e.preventDefault(); this.toggleFullscreen(); }; this.wrapperPlayer.querySelector('.vl-fullscreen').addEventListener('click', this.onClickToggleFullscreen, false); // Create double click event to trigger fullscreen change this.onDblclickVideo = e => { e.preventDefault(); // Prevent double click to fast-forward video current time if (e.target.classList.contains('vl-fast-forward-js')) return; this.toggleFullscreen(); }; this.wrapperPlayer.querySelector('.vl-overlay-video').addEventListener('dblclick', this.onDblclickVideo, false); } if (this.options.controls) { this.onKeyupEvent = e => { this.onKeyup(e); }; this.wrapperPlayer.addEventListener('keyup', this.onKeyupEvent, false); this.onMousemoveEvent = e => { this.onMousemove(e); }; this.wrapperPlayer.addEventListener('mousemove', this.onMousemoveEvent, false); } // Create fullscreen button event listener // Detect fullscreen change, particulary util for esc key because state is not updated // More information on MDN : https://developer.mozilla.org/en-US/docs/Web/API/Fullscreen_API this.onChangeFullScreen = e => { if (!document[this.supportFullScreen.isFullScreen] &amp;&amp; this.isFullScreen) { this.exitFullscreen(e.target); } }; window.addEventListener(this.supportFullScreen.changeEvent, this.onChangeFullScreen, false); } /** * Function executed when the player is ready */ playerIsReady() { this.loading(false); // Execute the constructor callback if (typeof this.callback === 'function') { this.callback(this); } // If player has autoplay option, play now if (this.options.autoplay) { this.togglePlayPause(); } } /** * Update the loader status * @param {Boolean} state Status of the loader */ loading(state) { if (state) { this.wrapperPlayer.classList.add('vl-loading'); } else { this.wrapperPlayer.classList.remove('vl-loading'); } } /** * Update player duration */ updateDuration() { this.wrapperPlayer.querySelector('.vl-duration').innerHTML = this.constructor.formatVideoTime(this.getDuration()); } /** * Function executed when is video is ended */ onVideoEnded() { this.wrapperPlayer.classList.replace('vl-playing', 'vl-paused'); this.wrapperPlayer.classList.add('vl-first-start'); this.wrapperPlayer.querySelector('.vl-poster').classList.add('vl-active'); if (this.options.constrols) { this.wrapperPlayer.querySelector('.vl-progress-seek').style.width = '0%'; this.wrapperPlayer.querySelector('.vl-progress-input').setAttribute('value', 0); this.wrapperPlayer.querySelector('.vl-current-time').innerHTML = '00:00'; } } /** * Function executed to toggle the video status (play, pause) */ togglePlayPause() { if (this.wrapperPlayer.classList.contains('vl-paused')) { this.play(); } else { this.pause(); } } /** * Trigger the video fast forward (front and rear) * @param {Object} e Event listener datas */ fastForward(e) { if (e.target.getAttribute('data-direction') === 'left') { this.seekTo(this.getCurrentTime() - 10); } else { this.seekTo(this.getCurrentTime() + 10); } } /** * Play the video */ play() { if (this.wrapperPlayer.classList.contains('vl-first-start')) { this.wrapperPlayer.classList.remove('vl-first-start'); this.wrapperPlayer.querySelector('.vl-poster').classList.remove('vl-active'); } this.methodPlay(); this.isPaused = false; this.afterPlayPause(); } /** * Pause the video */ pause() { this.methodPause(); this.isPaused = true; this.afterPlayPause(); } /** * Function executed after the play or pause method */ afterPlayPause() { if (this.isPaused) { this.wrapperPlayer.classList.replace('vl-playing', 'vl-paused'); } else { this.wrapperPlayer.classList.replace('vl-paused', 'vl-playing'); } if (this.options.autoHide &amp;&amp; this.options.controls) { if (this.isPaused) { this.wrapperPlayer.querySelector('.vl-control-bar').classList.remove('hidden'); } else { this.timerAutoHide = setTimeout(() => { this.wrapperPlayer.querySelector('.vl-control-bar').classList.add('hidden'); }, this.delayAutoHide); } } } /** * Toggle the volume on the video */ toggleVolume() { const volumeButton = this.wrapperPlayer.querySelector('.vl-volume'); if (volumeButton.classList.contains('vl-muted')) { this.unMute(); } else { this.mute(); } } /** * Mute the volume on the video */ mute() { this.methodMute(); this.wrapperPlayer.querySelector('.vl-volume').classList.add('vl-muted'); } /** * Toggle the volume on the video */ unMute() { this.methodUnMute(); this.wrapperPlayer.querySelector('.vl-volume').classList.remove('vl-muted'); } /** * Update the current time of the video * @param {Float|Integer} newTime New current time of the video */ seekTo(newTime) { this.setCurrentTime(newTime); } /** * Toggle the fullscreen of the video */ toggleFullscreen() { if (this.isFullScreen) { this.exitFullscreen(); } else { this.requestFullscreen(); } } /** * Check fullscreen support API on different browsers and cached prefixs */ static checkSupportFullScreen() { let prefixs = ['', 'moz', 'webkit', 'ms', 'o']; let lengthPrefixs = prefixs.length; let requestFn; let cancelFn; let changeEvent; let isFullScreen; if (document.cancelFullscreen !== undefined) { requestFn = 'requestFullscreen'; cancelFn = 'exitFullscreen'; changeEvent = 'fullscreenchange'; } else { while (lengthPrefixs--) { if ((prefixs[lengthPrefixs] !== 'moz' || document.mozFullScreenEnabled) &amp;&amp; document[prefixs[lengthPrefixs] + 'CancelFullScreen'] !== undefined) { requestFn = prefixs[lengthPrefixs] + 'RequestFullScreen'; cancelFn = prefixs[lengthPrefixs] + 'CancelFullScreen'; changeEvent = prefixs[lengthPrefixs] + 'fullscreenchange'; isFullScreen = prefixs[lengthPrefixs] === 'webkit' ? prefixs[lengthPrefixs] + 'IsFullScreen' : prefixs[lengthPrefixs] + 'FullScreen'; } } } const fullscreen = { requestFn: requestFn, cancelFn: cancelFn, changeEvent: changeEvent, isFullScreen: isFullScreen }; return requestFn ? fullscreen : false; } /** * Request fullscreen after user action */ requestFullscreen() { const { requestFn } = this.supportFullScreen; if (this.player[requestFn]) { //Request fullscreen on parentNode player, to display custom controls this.player.parentNode[requestFn](); this.isFullScreen = true; this.wrapperPlayer.classList.add('vl-fullscreen-display'); this.wrapperPlayer.querySelector('.vl-fullscreen').classList.add('vl-exit'); } } /** * Exit fullscreen after user action */ exitFullscreen() { const { cancelFn } = this.supportFullScreen; if (document[cancelFn]) { document[cancelFn](); this.wrapperPlayer.classList.remove('vl-fullscreen-display'); this.wrapperPlayer.querySelector('.vl-fullscreen').classList.remove('vl-exit'); this.isFullScreen = false; } } /** * Function executed on keyup event listener * Toggle the video on spacebar press * @param {Object} e Event listener datas */ onKeyup(e) { if (e.keyCode === 32) { this.togglePlayPause(); } } /** * Function executed on mousemove event listener * Toggle controls display on mousemove event */ onMousemove() { if (this.isPaused === false &amp;&amp; this.options.autoHide &amp;&amp; this.options.controls) { this.wrapperPlayer.querySelector('.vl-control-bar').classList.remove('hidden'); clearTimeout(this.timerAutoHide); this.timerAutoHide = setTimeout(() => { this.wrapperPlayer.querySelector('.vl-control-bar').classList.add('hidden'); }, this.delayAutoHide); } } /** * Update current time displaying in the control bar and the width of the progress bar */ updateCurrentTime() { const currentTime = Math.round(this.getCurrentTime()); const duration = this.getDuration(); const width = (currentTime * 100) / duration; const timeElement = this.wrapperPlayer.querySelector('.vl-current-time'); this.wrapperPlayer.querySelector('.vl-progress-seek').style.width = `${width}%`; if (timeElement !== null) { timeElement.innerHTML = this.constructor.formatVideoTime(currentTime); } } /** * Unbind event listeners */ unBindEvents() { const playPauseButtons = [...this.wrapperPlayer.querySelectorAll('.vl-toggle-play-pause-js')] playPauseButtons.forEach(button => { button.removeEventListener('click', this.onClickTogglePlayPause); }); this.onClickTogglePlayPause = null; if (!this.touchSupport) { const fastForwardButtons = [...this.wrapperPlayer.querySelectorAll('.vl-fast-forward-js')] fastForwardButtons.forEach(button => { button.removeEventListener('dblclick', this.onDblclickFastForward); }); this.onDblclickFastForward = null; } if (this.options.controls &amp;&amp; this.options.timeline) { this.wrapperPlayer.querySelector('.vl-progress-input').removeEventListener('change', this.onChangeProgressBar, false); this.onChangeProgressBar = null; } if (this.options.controls &amp;&amp; this.options.volume) { this.wrapperPlayer.querySelector('.vl-volume').removeEventListener('click', this.onCLickToggleVolume); this.onCLickToggleVolume = null; } if (this.options.controls) { this.wrapperPlayer.removeEventListener('keyup', this.onKeyupEvent); this.wrapperPlayer.removeEventListener('mousemove', this.onMousemoveEvent); this.onKeyupEvent = null; this.onMousemoveEvent = null; } if (this.options.controls &amp;&amp; this.options.fullscreen) { this.wrapperPlayer.querySelector('.vl-fullscreen').removeEventListener('click', this.onClickToggleFullscreen); this.wrapperPlayer.querySelector('.vl-overlay-video').removeEventListener('dblclick', this.onDblclickVideo); this.onClickToggleFullscreen = null; this.onDblclickVideo = null; } window.removeEventListener(this.supportFullScreen.changeEvent, this.onChangeFullScreen); } /** * Destroy the player * Remove event listeners, player instance and player HTML */ destroy() { this.pause(); this.unBindEvents(); if (typeof this.unBindSpecificEvents === 'function') { this.unBindSpecificEvents(); } if (typeof this.removeInstance === 'function') { this.removeInstance(); } this.wrapperPlayer.remove(); } /** * Check if browser support touch event * @returns {Boolean} Touch event support */ isTouch() { return 'ontouchstart' in window || (window.DocumentTouch &amp;&amp; document instanceof window.DocumentTouch); } /** * Convert video time second to 00:00 display * @param {Float|Integer} time Current time */ static formatVideoTime(time) { const ms = time * 1000; const min = (ms / 1000 / 60) &lt;&lt; 0; const sec = (ms / 1000) % 60 &lt;&lt; 0; let timeInString = ''; timeInString += min &lt; 10 ? '0' : ''; timeInString += min + ':'; timeInString += sec &lt; 10 ? '0' : ''; timeInString += sec; return timeInString; } } </code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Home</a></h2><h3>Modules</h3><ul><li><a href="module-vLite_entrypoint.html">vLite/entrypoint</a></li><li><a href="module-vLite_Player.html">vLite/Player</a></li><li><a href="module-vLite_Player_PlayerHtml5.html">vLite/Player/PlayerHtml5</a></li><li><a href="module-vLite_Player_PlayerYoutube.html">vLite/Player/PlayerYoutube</a></li></ul><h3>Classes</h3><ul><li><a href="module-vLite_entrypoint.html">vLite/entrypoint</a></li><li><a href="module-vLite_Player.html">vLite/Player</a></li><li><a href="module-vLite_Player_PlayerHtml5.html">vLite/Player/PlayerHtml5</a></li><li><a href="module-vLite_Player_PlayerYoutube.html">vLite/Player/PlayerYoutube</a></li></ul><h3>Global</h3><ul><li><a href="global.html#vLitejs">vLitejs</a></li></ul> </nav> <br class="clear"> <footer> Documentation generated by <a href="https://github.com/jsdoc/jsdoc">JSDoc 3.6.3</a> on Tue Nov 19 2019 00:24:53 GMT+0100 (CET) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>