diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/js/plyr.js | 869 | ||||
-rw-r--r-- | src/less/plyr.less | 58 |
2 files changed, 471 insertions, 456 deletions
diff --git a/src/js/plyr.js b/src/js/plyr.js index ed4352e0..5e22c680 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -27,41 +27,41 @@ displayDuration: true, iconPrefix: 'icon', selectors: { - container: '.player', - controls: '.player-controls', - labels: '[data-player] .sr-only, label .sr-only', + container: '.plyr', + controls: '.plyr-controls', + labels: '[data-plyr] .sr-only, label .sr-only', buttons: { - seek: '[data-player="seek"]', - play: '[data-player="play"]', - pause: '[data-player="pause"]', - restart: '[data-player="restart"]', - rewind: '[data-player="rewind"]', - forward: '[data-player="fast-forward"]', - mute: '[data-player="mute"]', - volume: '[data-player="volume"]', - captions: '[data-player="captions"]', - fullscreen: '[data-player="fullscreen"]' + seek: '[data-plyr="seek"]', + play: '[data-plyr="play"]', + pause: '[data-plyr="pause"]', + restart: '[data-plyr="restart"]', + rewind: '[data-plyr="rewind"]', + forward: '[data-plyr="fast-forward"]', + mute: '[data-plyr="mute"]', + volume: '[data-plyr="volume"]', + captions: '[data-plyr="captions"]', + fullscreen: '[data-plyr="fullscreen"]' }, progress: { - container: '.player-progress', - buffer: '.player-progress-buffer', - played: '.player-progress-played' + container: '.plyr-progress', + buffer: '.plyr-progress-buffer', + played: '.plyr-progress-played' }, - captions: '.player-captions', - currentTime: '.player-current-time', - duration: '.player-duration' + captions: '.plyr-captions', + currentTime: '.plyr-current-time', + duration: '.plyr-duration' }, classes: { - videoWrapper: 'player-video-wrapper', - embedWrapper: 'player-video-embed', - type: 'player-{0}', + videoWrapper: 'plyr-video-wrapper', + embedWrapper: 'plyr-video-embed', + type: 'plyr-{0}', stopped: 'stopped', playing: 'playing', muted: 'muted', loading: 'loading', - tooltip: 'player-tooltip', + tooltip: 'plyr-tooltip', hidden: 'sr-only', - hover: 'player-hover', + hover: 'plyr-hover', captions: { enabled: 'captions-enabled', active: 'captions-active' @@ -103,6 +103,14 @@ types: { embed: ['youtube','vimeo'], html5: ['video', 'audio'] + }, + urls: { + vimeo: { + api: 'https://cdn.rawgit.com/vimeo/player-api/master/javascript/froogaloop.js' + }, + youtube: { + api: 'https://www.youtube.com/iframe_api' + } } }; @@ -110,23 +118,23 @@ function _buildControls() { // Open and add the progress and seek elements var html = [ - '<div class="player-controls">', - '<div class="player-progress">', + '<div class="plyr-controls">', + '<div class="plyr-progress">', '<label for="seek{id}" class="sr-only">Seek</label>', - '<input id="seek{id}" class="player-progress-seek" type="range" min="0" max="100" step="0.5" value="0" data-player="seek">', - '<progress class="player-progress-played" max="100" value="0">', + '<input id="seek{id}" class="plyr-progress-seek" type="range" min="0" max="100" step="0.5" value="0" data-plyr="seek">', + '<progress class="plyr-progress-played" max="100" value="0">', '<span>0</span>% ' + config.i18n.played, '</progress>', - '<progress class="player-progress-buffer" max="100" value="0">', + '<progress class="plyr-progress-buffer" max="100" value="0">', '<span>0</span>% ' + config.i18n.buffered, '</progress>', '</div>', - '<span class="player-controls-left">']; + '<span class="plyr-controls-left">']; // Restart button if (_inArray(config.controls, 'restart')) { html.push( - '<button type="button" data-player="restart">', + '<button type="button" data-plyr="restart">', '<svg><use xlink:href="#' + config.iconPrefix + '-restart" /></svg>', '<span class="sr-only">' + config.i18n.restart + '</span>', '</button>' @@ -136,7 +144,7 @@ // Rewind button if (_inArray(config.controls, 'rewind')) { html.push( - '<button type="button" data-player="rewind">', + '<button type="button" data-plyr="rewind">', '<svg><use xlink:href="#' + config.iconPrefix + '-rewind" /></svg>', '<span class="sr-only">' + config.i18n.rewind + '</span>', '</button>' @@ -146,11 +154,11 @@ // Play/pause button if (_inArray(config.controls, 'play')) { html.push( - '<button type="button" data-player="play">', + '<button type="button" data-plyr="play">', '<svg><use xlink:href="#' + config.iconPrefix + '-play" /></svg>', '<span class="sr-only">' + config.i18n.play + '</span>', '</button>', - '<button type="button" data-player="pause">', + '<button type="button" data-plyr="pause">', '<svg><use xlink:href="#' + config.iconPrefix + '-pause" /></svg>', '<span class="sr-only">' + config.i18n.pause + '</span>', '</button>' @@ -160,7 +168,7 @@ // Fast forward button if (_inArray(config.controls, 'fast-forward')) { html.push( - '<button type="button" data-player="fast-forward">', + '<button type="button" data-plyr="fast-forward">', '<svg><use xlink:href="#' + config.iconPrefix + '-fast-forward" /></svg>', '<span class="sr-only">' + config.i18n.forward + '</span>', '</button>' @@ -170,9 +178,9 @@ // Media current time display if (_inArray(config.controls, 'current-time')) { html.push( - '<span class="player-time">', + '<span class="plyr-time">', '<span class="sr-only">' + config.i18n.currentTime + '</span>', - '<span class="player-current-time">00:00</span>', + '<span class="plyr-current-time">00:00</span>', '</span>' ); } @@ -180,9 +188,9 @@ // Media duration display if (_inArray(config.controls, 'duration')) { html.push( - '<span class="player-time">', + '<span class="plyr-time">', '<span class="sr-only">' + config.i18n.duration + '</span>', - '<span class="player-duration">00:00</span>', + '<span class="plyr-duration">00:00</span>', '</span>' ); } @@ -190,13 +198,13 @@ // Close left controls html.push( '</span>', - '<span class="player-controls-right">' + '<span class="plyr-controls-right">' ); // Toggle mute button if (_inArray(config.controls, 'mute')) { html.push( - '<button type="button" data-player="mute">', + '<button type="button" data-plyr="mute">', '<svg class="icon-muted"><use xlink:href="#' + config.iconPrefix + '-muted" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-volume" /></svg>', '<span class="sr-only">' + config.i18n.toggleMute + '</span>', @@ -208,14 +216,14 @@ if (_inArray(config.controls, 'volume')) { html.push( '<label for="volume{id}" class="sr-only">' + config.i18n.volume + '</label>', - '<input id="volume{id}" class="player-volume" type="range" min="0" max="10" value="5" data-player="volume">' + '<input id="volume{id}" class="plyr-volume" type="range" min="0" max="10" value="5" data-plyr="volume">' ); } // Toggle captions button if (_inArray(config.controls, 'captions')) { html.push( - '<button type="button" data-player="captions">', + '<button type="button" data-plyr="captions">', '<svg class="icon-captions-on"><use xlink:href="#' + config.iconPrefix + '-captions-on" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-captions-off" /></svg>', '<span class="sr-only">' + config.i18n.toggleCaptions + '</span>', @@ -226,7 +234,7 @@ // Toggle fullscreen button if (_inArray(config.controls, 'fullscreen')) { html.push( - '<button type="button" data-player="fullscreen">', + '<button type="button" data-plyr="fullscreen">', '<svg class="icon-exit-fullscreen"><use xlink:href="#' + config.iconPrefix + '-exit-fullscreen" /></svg>', '<svg><use xlink:href="#' + config.iconPrefix + '-enter-fullscreen" /></svg>', '<span class="sr-only">' + config.i18n.toggleFullscreen + '</span>', @@ -322,12 +330,12 @@ // Check for mime type support against a player instance // Credits: http://diveintohtml5.info/everything.html - // Related: http://www.leanbackplayer.com/test/h5mt.html - function _supportMime(player, mimeType) { - var media = player.media; + // Related: http://www.leanbackplyr.com/test/h5mt.html + function _supportMime(plyr, mimeType) { + var media = plyr.media; // Only check video types for video players - if (player.type == 'video') { + if (plyr.type == 'video') { // Check type switch (mimeType) { case 'video/webm': return !!(media.canPlayType && media.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/no/, '')); @@ -337,7 +345,7 @@ } // Only check audio types for audio players - else if (player.type == 'audio') { + else if (plyr.type == 'audio') { // Check type switch (mimeType) { case 'audio/mpeg': return !!(media.canPlayType && media.canPlayType('audio/mpeg;').replace(/no/, '')); @@ -636,73 +644,73 @@ // Player instance function Plyr(container) { - var player = this; - player.container = container; + var plyr = this; + plyr.container = container; // Captions functions // Seek the manual caption time and update UI function _seekManualCaptions(time) { // If it's not video, or we're using textTracks, bail. - if (player.usingTextTracks || player.type !== 'video' || !player.supported.full) { + if (plyr.usingTextTracks || plyr.type !== 'video' || !plyr.supported.full) { return; } // Reset subcount - player.subcount = 0; + plyr.subcount = 0; // Check time is a number, if not use currentTime // IE has a bug where currentTime doesn't go to 0 // https://twitter.com/Sam_Potts/status/573715746506731521 - time = typeof time === 'number' ? time : player.media.currentTime; + time = typeof time === 'number' ? time : plyr.media.currentTime; // If there's no subs available, bail - if(!player.captions[player.subcount]) { + if(!plyr.captions[plyr.subcount]) { return; } - while (_timecodeMax(player.captions[player.subcount][0]) < time.toFixed(1)) { - player.subcount++; - if (player.subcount > player.captions.length-1) { - player.subcount = player.captions.length-1; + while (_timecodeMax(plyr.captions[plyr.subcount][0]) < time.toFixed(1)) { + plyr.subcount++; + if (plyr.subcount > plyr.captions.length-1) { + plyr.subcount = plyr.captions.length-1; break; } } // Check if the next caption is in the current time range - if (player.media.currentTime.toFixed(1) >= _timecodeMin(player.captions[player.subcount][0]) && - player.media.currentTime.toFixed(1) <= _timecodeMax(player.captions[player.subcount][0])) { - player.currentCaption = player.captions[player.subcount][1]; + if (plyr.media.currentTime.toFixed(1) >= _timecodeMin(plyr.captions[plyr.subcount][0]) && + plyr.media.currentTime.toFixed(1) <= _timecodeMax(plyr.captions[plyr.subcount][0])) { + plyr.currentCaption = plyr.captions[plyr.subcount][1]; // Trim caption text - var content = player.currentCaption.trim(); + var content = plyr.currentCaption.trim(); // Render the caption (only if changed) - if (player.captionsContainer.innerHTML != content) { + if (plyr.captionsContainer.innerHTML != content) { // Empty caption // Otherwise NVDA reads it twice - player.captionsContainer.innerHTML = ''; + plyr.captionsContainer.innerHTML = ''; // Set new caption text - player.captionsContainer.innerHTML = content; + plyr.captionsContainer.innerHTML = content; } } else { - player.captionsContainer.innerHTML = ''; + plyr.captionsContainer.innerHTML = ''; } } // Display captions container and button (for initialization) function _showCaptions() { // If there's no caption toggle, bail - if (!player.buttons.captions) { + if (!plyr.buttons.captions) { return; } - _toggleClass(player.container, config.classes.captions.enabled, true); + _toggleClass(plyr.container, config.classes.captions.enabled, true); if (config.captions.defaultActive) { - _toggleClass(player.container, config.classes.captions.active, true); - _toggleState(player.buttons.captions, true); + _toggleClass(plyr.container, config.classes.captions.active, true); + _toggleState(plyr.buttons.captions, true); } } @@ -734,7 +742,7 @@ // Find all elements function _getElements(selector) { - return player.container.querySelectorAll(selector); + return plyr.container.querySelectorAll(selector); } // Find a single element @@ -772,7 +780,7 @@ html = _replaceAll(html, '{id}', Math.floor(Math.random() * (10000))); // Inject into the container - player.container.insertAdjacentHTML('beforeend', html); + plyr.container.insertAdjacentHTML('beforeend', html); // Setup tooltips if (config.tooltips) { @@ -790,44 +798,44 @@ // Find the UI controls and store references function _findElements() { try { - player.controls = _getElement(config.selectors.controls); + plyr.controls = _getElement(config.selectors.controls); // Buttons - player.buttons = {}; - player.buttons.seek = _getElement(config.selectors.buttons.seek); - player.buttons.play = _getElement(config.selectors.buttons.play); - player.buttons.pause = _getElement(config.selectors.buttons.pause); - player.buttons.restart = _getElement(config.selectors.buttons.restart); - player.buttons.rewind = _getElement(config.selectors.buttons.rewind); - player.buttons.forward = _getElement(config.selectors.buttons.forward); - player.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen); + plyr.buttons = {}; + plyr.buttons.seek = _getElement(config.selectors.buttons.seek); + plyr.buttons.play = _getElement(config.selectors.buttons.play); + plyr.buttons.pause = _getElement(config.selectors.buttons.pause); + plyr.buttons.restart = _getElement(config.selectors.buttons.restart); + plyr.buttons.rewind = _getElement(config.selectors.buttons.rewind); + plyr.buttons.forward = _getElement(config.selectors.buttons.forward); + plyr.buttons.fullscreen = _getElement(config.selectors.buttons.fullscreen); // Inputs - player.buttons.mute = _getElement(config.selectors.buttons.mute); - player.buttons.captions = _getElement(config.selectors.buttons.captions); - player.checkboxes = _getElements('[type="checkbox"]'); + plyr.buttons.mute = _getElement(config.selectors.buttons.mute); + plyr.buttons.captions = _getElement(config.selectors.buttons.captions); + plyr.checkboxes = _getElements('[type="checkbox"]'); // Progress - player.progress = {}; - player.progress.container = _getElement(config.selectors.progress.container); + plyr.progress = {}; + plyr.progress.container = _getElement(config.selectors.progress.container); // Progress - Buffering - player.progress.buffer = {}; - player.progress.buffer.bar = _getElement(config.selectors.progress.buffer); - player.progress.buffer.text = player.progress.buffer.bar && player.progress.buffer.bar.getElementsByTagName('span')[0]; + plyr.progress.buffer = {}; + plyr.progress.buffer.bar = _getElement(config.selectors.progress.buffer); + plyr.progress.buffer.text = plyr.progress.buffer.bar && plyr.progress.buffer.bar.getElementsByTagName('span')[0]; // Progress - Played - player.progress.played = {}; - player.progress.played.bar = _getElement(config.selectors.progress.played); - player.progress.played.text = player.progress.played.bar && player.progress.played.bar.getElementsByTagName('span')[0]; + plyr.progress.played = {}; + plyr.progress.played.bar = _getElement(config.selectors.progress.played); + plyr.progress.played.text = plyr.progress.played.bar && plyr.progress.played.bar.getElementsByTagName('span')[0]; // Volume - player.volume = _getElement(config.selectors.buttons.volume); + plyr.volume = _getElement(config.selectors.buttons.volume); // Timing - player.duration = _getElement(config.selectors.duration); - player.currentTime = _getElement(config.selectors.currentTime); - player.seekTime = _getElements(config.selectors.seekTime); + plyr.duration = _getElement(config.selectors.duration); + plyr.currentTime = _getElement(config.selectors.currentTime); + plyr.seekTime = _getElements(config.selectors.seekTime); return true; } @@ -835,7 +843,7 @@ _log('It looks like there\'s a problem with your controls html. Bailing.', true); // Restore native video controls - player.media.setAttribute('controls', ''); + plyr.media.setAttribute('controls', ''); return false; } @@ -844,64 +852,64 @@ // Setup aria attribute for play function _setupPlayAria() { // If there's no play button, bail - if (!player.buttons.play) { + if (!plyr.buttons.play) { return; } // Find the current text - var label = player.buttons.play.innerText || config.i18n.play; + var label = plyr.buttons.play.innerText || config.i18n.play; // If there's a media title set, use that for the label if (typeof(config.title) !== 'undefined' && config.title.length) { label += ', ' + config.title; } - player.buttons.play.setAttribute('aria-label', label); + plyr.buttons.play.setAttribute('aria-label', label); } // Setup media function _setupMedia() { // If there's no media, bail - if (!player.media) { + if (!plyr.media) { _log('No audio or video element found!', true); return false; } - if (player.supported.full) { + if (plyr.supported.full) { // Remove native video controls - player.media.removeAttribute('controls'); + plyr.media.removeAttribute('controls'); // Add type class - _toggleClass(player.container, config.classes.type.replace('{0}', player.type), true); + _toggleClass(plyr.container, config.classes.type.replace('{0}', plyr.type), true); // If there's no autoplay attribute, assume the video is stopped and add state class - _toggleClass(player.container, config.classes.stopped, config.autoplay); + _toggleClass(plyr.container, config.classes.stopped, config.autoplay); // Add iOS class - if (player.browser.ios) { - _toggleClass(player.container, 'ios', true); + if (plyr.browser.ios) { + _toggleClass(plyr.container, 'ios', true); } // Inject the player wrapper - if (player.type === 'video') { + if (plyr.type === 'video') { // Create the wrapper div var wrapper = document.createElement('div'); wrapper.setAttribute('class', config.classes.videoWrapper); // Wrap the video in a container - _wrap(player.media, wrapper); + _wrap(plyr.media, wrapper); // Cache the container - player.videoContainer = wrapper; + plyr.videoContainer = wrapper; } } // Embeds - if (_inArray(config.types.embed, player.type)) { - _setupEmbed(player.embedId, player.type); + if (_inArray(config.types.embed, plyr.type)) { + _setupEmbed(plyr.embedId, plyr.type); // Clean up - player.embedId = null; + plyr.embedId = null; } else { // Autoplay @@ -914,10 +922,10 @@ // Setup YouTube/Vimeo function _setupEmbed(videoId) { var container = document.createElement('div'), - id = player.type + '-' + Math.floor(Math.random() * (10000)); + id = plyr.type + '-' + Math.floor(Math.random() * (10000)); // Remove old containers - var containers = _getElements('[id^="' + player.type + '-"]'); + var containers = _getElements('[id^="' + plyr.type + '-"]'); for (var i = containers.length - 1; i >= 0; i--) { _remove(containers[i]); } @@ -926,13 +934,13 @@ container.setAttribute('id', id); // Add embed class for responsive - _toggleClass(player.media, config.classes.videoWrapper, true); - _toggleClass(player.media, config.classes.embedWrapper, true); + _toggleClass(plyr.media, config.classes.videoWrapper, true); + _toggleClass(plyr.media, config.classes.embedWrapper, true); // YouTube - if (player.type === 'youtube') { + if (plyr.type === 'youtube') { // Create the YouTube container - player.media.appendChild(container); + plyr.media.appendChild(container); // Setup API if (typeof YT === 'object') { @@ -940,14 +948,14 @@ } else { // Load the API - _injectScript('https://www.youtube.com/iframe_api'); + _injectScript(config.urls.youtube.api); // Setup callback for the API window.onYouTubeIframeAPIReady = function () { _youTubeReady(videoId, container); }; } } // Vimeo - else if (player.type === 'vimeo') { + else if (plyr.type === 'vimeo') { // Inject the iframe var iframe = document.createElement('iframe'); @@ -964,7 +972,7 @@ 'frameborder': 0 }); container.appendChild(iframe); - player.media.appendChild(container); + plyr.media.appendChild(container); // Setup API if (typeof Froogaloop === 'function') { @@ -972,7 +980,7 @@ } else { // Load the API - _injectScript('https://rawgit.com/vimeo/player-api/master/javascript/froogaloop.js'); + _injectScript(config.urls.vimeo.api); // Wait for fragaloop load var timer = window.setInterval(function() { @@ -989,9 +997,9 @@ // When embeds are ready function _embedReady() { // Inject and update UI - if (player.supported.full) { + if (plyr.supported.full) { // Only setup controls once - if (!player.container.querySelectorAll(config.selectors.controls).length) { + if (!plyr.container.querySelectorAll(config.selectors.controls).length) { _setupInterface(); } } @@ -1005,17 +1013,17 @@ function _youTubeReady(videoId, container) { // Setup timers object // We have to poll YouTube for updates - if (!('timer' in player)) { - player.timer = {}; + if (!('timer' in plyr)) { + plyr.timer = {}; } // Setup instance // https://developers.google.com/youtube/iframe_api_reference - player.embed = new YT.Player(container.id, { + plyr.embed = new YT.Player(container.id, { videoId: videoId, playerVars: { autoplay: (config.autoplay ? 1 : 0), - controls: (player.supported.full ? 0 : 1), + controls: (plyr.supported.full ? 0 : 1), rel: 0, showinfo: 0, iv_load_policy: 3, @@ -1031,31 +1039,31 @@ var instance = event.target; // Create a faux HTML5 API using the YouTube API - player.media.play = function() { instance.playVideo(); }; - player.media.pause = function() { instance.pauseVideo(); }; - player.media.stop = function() { instance.stopVideo(); }; - player.media.duration = instance.getDuration(); - player.media.paused = !config.autoplay; - player.media.currentTime = instance.getCurrentTime(); - player.media.muted = instance.isMuted(); + plyr.media.play = function() { instance.playVideo(); }; + plyr.media.pause = function() { instance.pauseVideo(); }; + plyr.media.stop = function() { instance.stopVideo(); }; + plyr.media.duration = instance.getDuration(); + plyr.media.paused = !config.autoplay; + plyr.media.currentTime = instance.getCurrentTime(); + plyr.media.muted = instance.isMuted(); // Trigger timeupdate - _triggerEvent(player.media, 'timeupdate'); + _triggerEvent(plyr.media, 'timeupdate'); // Reset timer - window.clearInterval(player.timer.buffering); + window.clearInterval(plyr.timer.buffering); // Setup buffering - player.timer.buffering = window.setInterval(function() { + plyr.timer.buffering = window.setInterval(function() { // Get loaded % from YouTube - player.media.buffered = instance.getVideoLoadedFraction(); + plyr.media.buffered = instance.getVideoLoadedFraction(); // Trigger progress - _triggerEvent(player.media, 'progress'); + _triggerEvent(plyr.media, 'progress'); // Bail if we're at 100% - if (player.media.buffered === 1) { - window.clearInterval(player.timer.buffering); + if (plyr.media.buffered === 1) { + window.clearInterval(plyr.timer.buffering); } }, 200); @@ -1072,7 +1080,7 @@ var instance = event.target; // Reset timer - window.clearInterval(player.timer.playing); + window.clearInterval(plyr.timer.playing); // Handle events // -1 Unstarted @@ -1083,28 +1091,28 @@ // 5 Video cued switch (event.data) { case 0: - player.media.paused = true; - _triggerEvent(player.media, 'ended'); + plyr.media.paused = true; + _triggerEvent(plyr.media, 'ended'); break; case 1: - player.media.paused = false; - _triggerEvent(player.media, 'play'); + plyr.media.paused = false; + _triggerEvent(plyr.media, 'play'); // Poll to get playback progress - player.timer.playing = window.setInterval(function() { + plyr.timer.playing = window.setInterval(function() { // Set the current time - player.media.currentTime = instance.getCurrentTime(); + plyr.media.currentTime = instance.getCurrentTime(); // Trigger timeupdate - _triggerEvent(player.media, 'timeupdate'); + _triggerEvent(plyr.media, 'timeupdate'); }, 200); break; case 2: - player.media.paused = true; - _triggerEvent(player.media, 'pause'); + plyr.media.paused = true; + _triggerEvent(plyr.media, 'pause'); } } } @@ -1115,94 +1123,92 @@ function _vimeoReady() { /* jshint validthis: true */ // Get the frame with fragaloop lib - player.embed = $f(this); + plyr.embed = $f(this); // Setup on ready - player.embed.addEvent('ready', function() { + plyr.embed.addEvent('ready', function() { // Create a faux HTML5 API using the Vimeo API - player.media.play = function() { player.embed.api('play'); }; - player.media.pause = function() { player.embed.api('pause'); }; - player.media.stop = function() { player.embed.api('stop') }; - player.media.paused = !config.autoplay; - player.media.currentTime = 0; + plyr.media.play = function() { plyr.embed.api('play'); }; + plyr.media.pause = function() { plyr.embed.api('pause'); }; + plyr.media.stop = function() { plyr.embed.api('stop') }; + plyr.media.paused = !config.autoplay; + plyr.media.currentTime = 0; // Update UI _embedReady(); - player.embed.api('getCurrentTime', function (value) { - player.media.currentTime = value; + plyr.embed.api('getCurrentTime', function (value) { + plyr.media.currentTime = value; // Trigger timeupdate - _triggerEvent(player.media, 'timeupdate'); + _triggerEvent(plyr.media, 'timeupdate'); }); - player.embed.api('getDuration', function(value) { - player.media.duration = value; + plyr.embed.api('getDuration', function(value) { + plyr.media.duration = value; // Display duration if available - if (player.supported.full && config.displayDuration) { + if (plyr.supported.full && config.displayDuration) { _displayDuration(); } }); - player.embed.addEvent('play', function() { - console.log('play'); - player.media.paused = false; - _triggerEvent(player.media, 'play'); + plyr.embed.addEvent('play', function() { + plyr.media.paused = false; + _triggerEvent(plyr.media, 'play'); }); - player.embed.addEvent('pause', function() { - console.log('pause'); - player.media.paused = true; - _triggerEvent(player.media, 'pause'); + plyr.embed.addEvent('pause', function() { + plyr.media.paused = true; + _triggerEvent(plyr.media, 'pause'); }); - player.embed.addEvent('playProgress', function(data) { - player.media.currentTime = data.seconds; - _triggerEvent(player.media, 'timeupdate'); + plyr.embed.addEvent('playProgress', function(data) { + plyr.media.currentTime = data.seconds; + _triggerEvent(plyr.media, 'timeupdate'); }); - player.embed.addEvent('loadProgress', function(data) { - player.media.buffered = data.percent; - _triggerEvent(player.media, 'progress'); + plyr.embed.addEvent('loadProgress', function(data) { + plyr.media.buffered = data.percent; + _triggerEvent(plyr.media, 'progress'); }); - player.embed.addEvent('finish', function() { - player.media.paused = true; - _triggerEvent(player.media, 'ended'); + plyr.embed.addEvent('finish', function() { + plyr.media.paused = true; + _triggerEvent(plyr.media, 'ended'); }); /*// Always seek to 0 - player.embed.api('seekTo', 0); + plyr.embed.api('seekTo', 0); // Prevent autoplay if needed (seek will play) if (!config.autoplay) { - player.embed.api('pause'); + plyr.embed.api('pause'); }*/ }); } // Setup captions function _setupCaptions() { - if (player.type === 'video') { + if (plyr.type === 'video') { // Inject the container if(!_getElement(config.selectors.captions)) { - player.videoContainer.insertAdjacentHTML('afterbegin', '<div class="' + _getClassname(config.selectors.captions) + '"><span></span></div>'); + plyr.videoContainer.insertAdjacentHTML('afterbegin', '<div class="' + _getClassname(config.selectors.captions) + '"><span></span></div>'); } // Cache selector - player.captionsContainer = _getElement(config.selectors.captions).querySelector('span'); + plyr.captionsContainer = _getElement(config.selectors.captions).querySelector('span'); // Determine if HTML5 textTracks is supported - player.usingTextTracks = false; - if (player.media.textTracks) { - player.usingTextTracks = true; + plyr.usingTextTracks = false; + if (plyr.media.textTracks) { + plyr.usingTextTracks = true; } // Get URL of caption file if exists var captionSrc = '', kind, - children = player.media.childNodes; + children = plyr.media.childNodes; for (var i = 0; i < children.length; i++) { if (children[i].nodeName.toLowerCase() === 'track') { @@ -1214,9 +1220,9 @@ } // Record if caption file exists or not - player.captionExists = true; + plyr.captionExists = true; if (captionSrc === '') { - player.captionExists = false; + plyr.captionExists = false; _log('No caption track found.'); } else { @@ -1224,36 +1230,36 @@ } // If no caption file exists, hide container for caption text - if (!player.captionExists) { - _toggleClass(player.container, config.classes.captions.enabled); + if (!plyr.captionExists) { + _toggleClass(plyr.container, config.classes.captions.enabled); } // If caption file exists, process captions else { // Turn off native caption rendering to avoid double captions // This doesn't seem to work in Safari 7+, so the <track> elements are removed from the dom below - var tracks = player.media.textTracks; + var tracks = plyr.media.textTracks; for (var x = 0; x < tracks.length; x++) { tracks[x].mode = 'hidden'; } // Enable UI - _showCaptions(player); + _showCaptions(plyr); // Disable unsupported browsers than report false positive - if ((player.browser.name === 'IE' && player.browser.version >= 10) || - (player.browser.name === 'Firefox' && player.browser.version >= 31) || - (player.browser.name === 'Chrome' && player.browser.version >= 43) || - (player.browser.name === 'Safari' && player.browser.version >= 7)) { + if ((plyr.browser.name === 'IE' && plyr.browser.version >= 10) || + (plyr.browser.name === 'Firefox' && plyr.browser.version >= 31) || + (plyr.browser.name === 'Chrome' && plyr.browser.version >= 43) || + (plyr.browser.name === 'Safari' && plyr.browser.version >= 7)) { // Debugging _log('Detected unsupported browser for HTML5 captions. Using fallback.'); // Set to false so skips to 'manual' captioning - player.usingTextTracks = false; + plyr.usingTextTracks = false; } // Rendering caption tracks // Native support required - http://caniuse.com/webvtt - if (player.usingTextTracks) { + if (plyr.usingTextTracks) { _log('TextTracks supported.'); for (var y = 0; y < tracks.length; y++) { @@ -1262,11 +1268,11 @@ if (track.kind === 'captions' || track.kind === 'subtitles') { _on(track, 'cuechange', function() { // Clear container - player.captionsContainer.innerHTML = ''; + plyr.captionsContainer.innerHTML = ''; // Display a cue, if there is one if (this.activeCues[0] && this.activeCues[0].hasOwnProperty('text')) { - player.captionsContainer.appendChild(this.activeCues[0].getCueAsHTML().trim()); + plyr.captionsContainer.appendChild(this.activeCues[0].getCueAsHTML().trim()); } }); } @@ -1277,8 +1283,8 @@ _log('TextTracks not supported so rendering captions manually.'); // Render captions from array at appropriate time - player.currentCaption = ''; - player.captions = []; + plyr.currentCaption = ''; + plyr.captions = []; if (captionSrc !== '') { // Create XMLHttpRequest Object @@ -1295,12 +1301,12 @@ for (var r = 0; r < records.length; r++) { record = records[r]; - player.captions[r] = []; - player.captions[r] = record.split('\n'); + plyr.captions[r] = []; + plyr.captions[r] = record.split('\n'); } // Remove first element ('VTT') - player.captions.shift(); + plyr.captions.shift(); _log('Successfully loaded the caption file via AJAX.'); } @@ -1317,15 +1323,15 @@ } // If Safari 7+, removing track from DOM [see 'turn off native caption rendering' above] - if (player.browser.name === 'Safari' && player.browser.version >= 7) { + if (plyr.browser.name === 'Safari' && plyr.browser.version >= 7) { _log('Safari 7+ detected; removing track from DOM.'); // Find all <track> elements - tracks = player.media.getElementsByTagName('track'); + tracks = plyr.media.getElementsByTagName('track'); // Loop through and remove one by one for (var t = 0; t < tracks.length; t++) { - player.media.removeChild(tracks[t]); + plyr.media.removeChild(tracks[t]); } } } @@ -1334,7 +1340,7 @@ // Setup fullscreen function _setupFullscreen() { - if (player.type != 'audio' && config.fullscreen.enabled) { + if (plyr.type != 'audio' && config.fullscreen.enabled) { // Check for native support var nativeSupport = fullscreen.supportsFullScreen; @@ -1342,30 +1348,30 @@ _log((nativeSupport ? 'Native' : 'Fallback') + ' fullscreen enabled.'); // Add styling hook - _toggleClass(player.container, config.classes.fullscreen.enabled, true); + _toggleClass(plyr.container, config.classes.fullscreen.enabled, true); } else { _log('Fullscreen not supported and fallback disabled.'); } // Toggle state - _toggleState(player.buttons.fullscreen, false); + _toggleState(plyr.buttons.fullscreen, false); // Set control hide class hook if (config.fullscreen.hideControls) { - _toggleClass(player.container, config.classes.fullscreen.hideControls, true); + _toggleClass(plyr.container, config.classes.fullscreen.hideControls, true); } } } // Play media function _play() { - player.media.play(); + plyr.media.play(); } // Pause media function _pause() { - player.media.pause(); + plyr.media.pause(); } // Toggle playback @@ -1380,7 +1386,7 @@ } // True toggle else { - player.media[player.media.paused ? 'play' : 'pause'](); + plyr.media[plyr.media.paused ? 'play' : 'pause'](); } } @@ -1390,7 +1396,7 @@ if (typeof seekTime !== 'number') { seekTime = config.seekTime; } - _seek(player.media.currentTime - seekTime); + _seek(plyr.media.currentTime - seekTime); } // Fast forward @@ -1399,14 +1405,14 @@ if (typeof seekTime !== 'number') { seekTime = config.seekTime; } - _seek(player.media.currentTime + seekTime); + _seek(plyr.media.currentTime + seekTime); } // Seek to time // The input parameter can be an event or a number function _seek(input) { var targetTime = 0, - paused = player.media.paused; + paused = plyr.media.paused; // Explicit position if (typeof input === 'number') { @@ -1416,38 +1422,38 @@ else if (typeof input === 'object' && (input.type === 'input' || input.type === 'change')) { // It's the seek slider // Seek to the selected time - targetTime = ((input.target.value / input.target.max) * player.media.duration); + targetTime = ((input.target.value / input.target.max) * plyr.media.duration); } // Normalise targetTime if (targetTime < 0) { targetTime = 0; } - else if (targetTime > player.media.duration) { - targetTime = player.media.duration; + else if (targetTime > plyr.media.duration) { + targetTime = plyr.media.duration; } // Set the current time // Try/catch incase the media isn't set and we're calling seek() from source() and IE moans try { - player.media.currentTime = targetTime.toFixed(1); + plyr.media.currentTime = targetTime.toFixed(1); } catch(e) {} // Trigger timeupdate for embed and restore pause state - if ('embed' in player) { + if ('embed' in plyr) { // YouTube - if (player.type === 'youtube') { - player.embed.seekTo(targetTime); + if (plyr.type === 'youtube') { + plyr.embed.seekTo(targetTime); } // Vimeo - if (player.type === 'vimeo') { - player.embed.api('seekTo', targetTime); + if (plyr.type === 'vimeo') { + plyr.embed.api('seekTo', targetTime); } // Trigger timeupdate - _triggerEvent(player.media, 'timeupdate'); + _triggerEvent(plyr.media, 'timeupdate'); if (paused) { _pause(); @@ -1455,7 +1461,7 @@ } // Logging - _log('Seeking to ' + player.media.currentTime + ' seconds'); + _log('Seeking to ' + plyr.media.currentTime + ' seconds'); // Special handling for 'manual' captions _seekManualCaptions(targetTime); @@ -1463,8 +1469,8 @@ // Check playing state function _checkPlaying() { - _toggleClass(player.container, config.classes.playing, !player.media.paused); - _toggleClass(player.container, config.classes.stopped, player.media.paused); + _toggleClass(plyr.container, config.classes.playing, !plyr.media.paused); + _toggleClass(plyr.container, config.classes.stopped, plyr.media.paused); } // Toggle fullscreen @@ -1474,13 +1480,13 @@ // If it's a fullscreen change event, it's probably a native close if (event && event.type === fullscreen.fullScreenEventName) { - player.isFullscreen = fullscreen.isFullScreen(player.container); + plyr.isFullscreen = fullscreen.isFullScreen(plyr.container); } // If there's native support, use it else if (nativeSupport) { // Request fullscreen - if (!fullscreen.isFullScreen(player.container)) { - fullscreen.requestFullScreen(player.container); + if (!fullscreen.isFullScreen(plyr.container)) { + fullscreen.requestFullScreen(plyr.container); } // Bail from fullscreen else { @@ -1488,14 +1494,14 @@ } // Check if we're actually full screen (it could fail) - player.isFullscreen = fullscreen.isFullScreen(player.container); + plyr.isFullscreen = fullscreen.isFullScreen(plyr.container); } else { // Otherwise, it's a simple toggle - player.isFullscreen = !player.isFullscreen; + plyr.isFullscreen = !plyr.isFullscreen; // Bind/unbind escape key - if (player.isFullscreen) { + if (plyr.isFullscreen) { _on(document, 'keyup', _handleEscapeFullscreen); document.body.style.overflow = 'hidden'; } @@ -1506,10 +1512,10 @@ } // Set class hook - _toggleClass(player.container, config.classes.fullscreen.active, player.isFullscreen); + _toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen); // Set button state - _toggleState(player.buttons.fullscreen, player.isFullscreen); + _toggleState(plyr.buttons.fullscreen, plyr.isFullscreen); // Toggle controls visibility based on mouse movement and location var hoverTimer, isMouseOver = false; @@ -1517,7 +1523,7 @@ // Show the player controls function _showControls() { // Set shown class - _toggleClass(player.container, config.classes.hover, true); + _toggleClass(plyr.container, config.classes.hover, true); // Clear timer every movement window.clearTimeout(hoverTimer); @@ -1525,7 +1531,7 @@ // If the mouse is not over the controls, set a timeout to hide them if (!isMouseOver) { hoverTimer = window.setTimeout(function() { - _toggleClass(player.container, config.classes.hover, false); + _toggleClass(plyr.container, config.classes.hover, false); }, 2000); } } @@ -1537,20 +1543,20 @@ if (config.fullscreen.hideControls) { // Hide on entering full screen - _toggleClass(player.controls, config.classes.hover, false); + _toggleClass(plyr.controls, config.classes.hover, false); // Keep an eye on the mouse location in relation to controls - _toggleHandler(player.controls, 'mouseenter mouseleave', _setMouseOver, player.isFullscreen); + _toggleHandler(plyr.controls, 'mouseenter mouseleave', _setMouseOver, plyr.isFullscreen); // Show the controls on mouse move - _toggleHandler(player.container, 'mousemove', _showControls, player.isFullscreen); + _toggleHandler(plyr.container, 'mousemove', _showControls, plyr.isFullscreen); } } // Bail from faux-fullscreen function _handleEscapeFullscreen(event) { // If it's a keypress and not escape, bail - if ((event.which || event.charCode || event.keyCode) === 27 && player.isFullscreen) { + if ((event.which || event.charCode || event.keyCode) === 27 && plyr.isFullscreen) { _toggleFullscreen(); } } @@ -1559,21 +1565,21 @@ function _toggleMute(muted) { // If the method is called without parameter, toggle based on current value if (typeof muted !== 'boolean') { - muted = !player.media.muted; + muted = !plyr.media.muted; } // Set button state - _toggleState(player.buttons.mute, muted); + _toggleState(plyr.buttons.mute, muted); // Set mute on the player - player.media.muted = muted; + plyr.media.muted = muted; // YouTube - if (player.type === 'youtube') { - player.embed[player.media.muted ? 'mute' : 'unMute'](); + if (plyr.type === 'youtube') { + plyr.embed[plyr.media.muted ? 'mute' : 'unMute'](); // Trigger timeupdate - _triggerEvent(player.media, 'volumechange'); + _triggerEvent(plyr.media, 'volumechange'); } } @@ -1599,28 +1605,28 @@ } // Set the player volume - player.media.volume = parseFloat(volume / 10); + plyr.media.volume = parseFloat(volume / 10); // Store in config config.volume = volume; // YouTube - if (player.type === 'youtube') { - player.embed.setVolume(player.media.volume * 100); + if (plyr.type === 'youtube') { + plyr.embed.setVolume(plyr.media.volume * 100); } // Vimeo - if (player.type === 'vimeo') { - player.embed.api('setVolume', player.media.volume); + if (plyr.type === 'vimeo') { + plyr.embed.api('setVolume', plyr.media.volume); } // Trigger volumechange for embeds - if ('embed' in player) { - _triggerEvent(player.media, 'volumechange'); + if ('embed' in plyr) { + _triggerEvent(plyr.media, 'volumechange'); } // Toggle muted state - if (player.media.muted && volume > 0) { + if (plyr.media.muted && volume > 0) { _toggleMute(); } } @@ -1628,11 +1634,11 @@ // Update volume UI and storage function _updateVolume() { // Get the current volume - var volume = player.media.muted ? 0 : (player.media.volume * 10); + var volume = plyr.media.muted ? 0 : (plyr.media.volume * 10); // Update the <input type="range"> if present - if (player.supported.full && player.volume) { - player.volume.value = volume; + if (plyr.supported.full && plyr.volume) { + plyr.volume.value = volume; } // Store the volume in storage @@ -1641,31 +1647,31 @@ } // Toggle class if muted - _toggleClass(player.container, config.classes.muted, (volume === 0)); + _toggleClass(plyr.container, config.classes.muted, (volume === 0)); // Update checkbox for mute state - if (player.supported.full && player.buttons.mute) { - _toggleState(player.buttons.mute, (volume === 0)); + if (plyr.supported.full && plyr.buttons.mute) { + _toggleState(plyr.buttons.mute, (volume === 0)); } } // Toggle captions function _toggleCaptions(show) { // If there's no full support, or there's no caption toggle - if (!player.supported.full || !player.buttons.captions) { + if (!plyr.supported.full || !plyr.buttons.captions) { return; } // If the method is called without parameter, toggle based on current value if (typeof show !== 'boolean') { - show = (player.container.className.indexOf(config.classes.captions.active) === -1); + show = (plyr.container.className.indexOf(config.classes.captions.active) === -1); } // Toggle state - _toggleState(player.buttons.captions, show); + _toggleState(plyr.buttons.captions, show); // Add class hook - _toggleClass(player.container, config.classes.captions.active, show); + _toggleClass(plyr.container, config.classes.captions.active, show); } // Check if media is loading @@ -1673,18 +1679,18 @@ var loading = (event.type === 'waiting'); // Clear timer - clearTimeout(player.loadingTimer); + clearTimeout(plyr.loadingTimer); // Timer to prevent flicker when seeking - player.loadingTimer = setTimeout(function() { - _toggleClass(player.container, config.classes.loading, loading); + plyr.loadingTimer = setTimeout(function() { + _toggleClass(plyr.container, config.classes.loading, loading); }, (loading ? 250 : 0)); } // Update <progress> elements function _updateProgress(event) { - var progress = player.progress.played.bar, - text = player.progress.played.text, + var progress = plyr.progress.played.bar, + text = plyr.progress.played.text, value = 0; if (event) { @@ -1692,11 +1698,11 @@ // Video playing case 'timeupdate': case 'seeking': - value = _getPercentage(player.media.currentTime, player.media.duration); + value = _getPercentage(plyr.media.currentTime, plyr.media.duration); // Set seek range value only if it's a 'natural' time event - if (event.type == 'timeupdate' && player.buttons.seek) { - player.buttons.seek.value = value; + if (event.type == 'timeupdate' && plyr.buttons.seek) { + plyr.buttons.seek.value = value; } break; @@ -1711,14 +1717,14 @@ // Check buffer status case 'playing': case 'progress': - progress = player.progress.buffer.bar; - text = player.progress.buffer.text; + progress = plyr.progress.buffer.bar; + text = plyr.progress.buffer.text; value = (function() { - var buffered = player.media.buffered; + var buffered = plyr.media.buffered; // HTML5 if (buffered && buffered.length) { - return _getPercentage(buffered.end(0), player.media.duration); + return _getPercentage(buffered.end(0), plyr.media.duration); } // YouTube returns between 0 and 1 else if (typeof buffered === 'number') { @@ -1751,66 +1757,53 @@ time = 0; } - player.secs = parseInt(time % 60); - player.mins = parseInt((time / 60) % 60); - player.hours = parseInt(((time / 60) / 60) % 60); + plyr.secs = parseInt(time % 60); + plyr.mins = parseInt((time / 60) % 60); + plyr.hours = parseInt(((time / 60) / 60) % 60); // Do we need to display hours? - var displayHours = (parseInt(((player.media.duration / 60) / 60) % 60) > 0); + var displayHours = (parseInt(((plyr.media.duration / 60) / 60) % 60) > 0); // Ensure it's two digits. For example, 03 rather than 3. - player.secs = ('0' + player.secs).slice(-2); - player.mins = ('0' + player.mins).slice(-2); + plyr.secs = ('0' + plyr.secs).slice(-2); + plyr.mins = ('0' + plyr.mins).slice(-2); // Render - element.innerHTML = (displayHours ? player.hours + ':' : '') + player.mins + ':' + player.secs; + element.innerHTML = (displayHours ? plyr.hours + ':' : '') + plyr.mins + ':' + plyr.secs; } // Show the duration on metadataloaded function _displayDuration() { - var duration = player.media.duration || 0; + var duration = plyr.media.duration || 0; // If there's only one time display, display duration there - if (!player.duration && config.displayDuration && player.media.paused) { - _updateTimeDisplay(duration, player.currentTime); + if (!plyr.duration && config.displayDuration && plyr.media.paused) { + _updateTimeDisplay(duration, plyr.currentTime); } // If there's a duration element, update content - if (player.duration) { - _updateTimeDisplay(duration, player.duration); + if (plyr.duration) { + _updateTimeDisplay(duration, plyr.duration); } } // Handle time change event function _timeUpdate(event) { // Duration - _updateTimeDisplay(player.media.currentTime, player.currentTime); + _updateTimeDisplay(plyr.media.currentTime, plyr.currentTime); // Playing progress _updateProgress(event); } - // Add a source element - function _addSource(attributes) { - _insertElement('source', player.media, attributes); - } - - // Add a source element - function _addTracks(tracks) { - for (var i = tracks.length - 1; i >= 0; i--) { - _insertElement('track', player.media, tracks[i]); + // Add elements to HTML5 media (source, tracks, etc) + function _insertChildElements(type, attributes) { + if(typeof attributes === 'string') { + _insertElement(type, plyr.media, { src: attributes }); } - } - - // Add sources to HTML5 media - function _addSources(sources) { - // Set new sources - if(typeof sources === 'string') { - _addSource({ src: sources }); - } - else if(sources.constructor === Array) { - for (var i = sources.length - 1; i >= 0; i--) { - _addSource(sources[i]); + else if(attributes.constructor === Array) { + for (var i = attributes.length - 1; i >= 0; i--) { + _insertElement(type, plyr.media, attributes[i]); } } } @@ -1826,77 +1819,77 @@ _pause(); // Clean up YouTube stuff - if(player.type === 'youtube') { + if(plyr.type === 'youtube') { // Destroy the embed instance - player.embed.destroy(); + plyr.embed.destroy(); // Clear timer - window.clearInterval(player.timer.buffering); - window.clearInterval(player.timer.playing); + window.clearInterval(plyr.timer.buffering); + window.clearInterval(plyr.timer.playing); } - else if (player.type === 'video') { + else if (plyr.type === 'video') { // Remove video wrapper - _remove(player.videoContainer); + _remove(plyr.videoContainer); } // Remove the old media - _remove(player.media); + _remove(plyr.media); // Set the new type - if('type' in source && source.type !== player.type) { - player.type = source.type; + if('type' in source && source.type !== plyr.type) { + plyr.type = source.type; } // Create new markup - switch(player.type) { + switch(plyr.type) { case 'video': - player.media = document.createElement('video'); + plyr.media = document.createElement('video'); break; case 'audio': - player.media = document.createElement('audio'); + plyr.media = document.createElement('audio'); break; case 'youtube': case 'vimeo': - player.media = document.createElement('div'); - player.embedId = source.sources; + plyr.media = document.createElement('div'); + plyr.embedId = source.sources; break; } // Inject the new element - _prependChild(player.container, player.media); + _prependChild(plyr.container, plyr.media); // Set attributes for audio video - if(_inArray(config.types.html5, player.type)) { + if(_inArray(config.types.html5, plyr.type)) { if(config.crossorigin) { - player.media.setAttribute('crossorigin', ''); + plyr.media.setAttribute('crossorigin', ''); } if (config.autoplay) { - player.media.setAttribute('autoplay', ''); + plyr.media.setAttribute('autoplay', ''); } if ('poster' in source) { - player.media.setAttribute('poster', source.poster); + plyr.media.setAttribute('poster', source.poster); } if (config.loop) { - player.media.setAttribute('loop', ''); + plyr.media.setAttribute('loop', ''); } } // Classname reset - player.container.className = _getClassname(config.selectors.container); + plyr.container.className = plyr.originalClassName; // Autoplay the new source? config.autoplay = (source.autoplay || config.autoplay); // Set media id for embeds - if(_inArray(config.types.embed, player.type)) { - player.embedId = source.sources; + if(_inArray(config.types.embed, plyr.type)) { + plyr.embedId = source.sources; } - // Set new sources and tracks for html5 - if(_inArray(config.types.html5, player.type)) { - _addSources(source.sources); + // Set new sources for html5 + if(_inArray(config.types.html5, plyr.type)) { + _insertChildElements('source', source.sources); } // Set up from scratch @@ -1906,13 +1899,13 @@ _mediaUpdated(); // HTML5 stuff - if(_inArray(config.types.html5, player.type)) { + if(_inArray(config.types.html5, plyr.type)) { // Set volume _setVolume(); _updateVolume(); // UI updates - if(player.supported.full) { + if(plyr.supported.full) { // Reset time display _timeUpdate(); @@ -1922,14 +1915,14 @@ // Setup captions if('tracks' in source) { - _addTracks(source.tracks); + _insertChildElements('track', source.tracks); // Captions _setupCaptions(); } // Load HTML5 sources - player.media.load(); + plyr.media.load(); // Play if autoplay attribute is present if (config.autoplay) { @@ -1945,15 +1938,15 @@ // Update poster function _updatePoster(source) { - if (player.type === 'video') { - player.media.setAttribute('poster', source); + if (plyr.type === 'video') { + plyr.media.setAttribute('poster', source); } } // Listen for events function _listeners() { // IE doesn't support input event, so we fallback to change - var inputEvent = (player.browser.name == 'IE' ? 'change' : 'input'); + var inputEvent = (plyr.browser.name == 'IE' ? 'change' : 'input'); // Detect tab focus function checkFocus() { @@ -1964,8 +1957,8 @@ else if (document.querySelector) { focused = document.querySelector(':focus'); } - for (var button in player.buttons) { - var element = player.buttons[button]; + for (var button in plyr.buttons) { + var element = plyr.buttons[button]; _toggleClass(element, 'tab-focus', (element === focused)); } @@ -1977,8 +1970,8 @@ checkFocus(); } }); - for (var button in player.buttons) { - var element = player.buttons[button]; + for (var button in plyr.buttons) { + var element = plyr.buttons[button]; _on(element, 'blur', function() { _toggleClass(element, 'tab-focus', false); @@ -1986,39 +1979,39 @@ } // Play - _on(player.buttons.play, 'click', function() { + _on(plyr.buttons.play, 'click', function() { _play(); - setTimeout(function() { player.buttons.pause.focus(); }, 100); + setTimeout(function() { plyr.buttons.pause.focus(); }, 100); }); // Pause - _on(player.buttons.pause, 'click', function() { + _on(plyr.buttons.pause, 'click', function() { _pause(); - setTimeout(function() { player.buttons.play.focus(); }, 100); + setTimeout(function() { plyr.buttons.play.focus(); }, 100); }); // Restart - _on(player.buttons.restart, 'click', _seek); + _on(plyr.buttons.restart, 'click', _seek); // Rewind - _on(player.buttons.rewind, 'click', _rewind); + _on(plyr.buttons.rewind, 'click', _rewind); // Fast forward - _on(player.buttons.forward, 'click', _forward); + _on(plyr.buttons.forward, 'click', _forward); // Seek - _on(player.buttons.seek, inputEvent, _seek); + _on(plyr.buttons.seek, inputEvent, _seek); // Set volume - _on(player.volume, inputEvent, function() { + _on(plyr.volume, inputEvent, function() { _setVolume(this.value); }); // Mute - _on(player.buttons.mute, 'click', _toggleMute); + _on(plyr.buttons.mute, 'click', _toggleMute); // Fullscreen - _on(player.buttons.fullscreen, 'click', _toggleFullscreen); + _on(plyr.buttons.fullscreen, 'click', _toggleFullscreen); // Handle user exiting fullscreen by escaping etc if (fullscreen.supportsFullScreen) { @@ -2026,22 +2019,22 @@ } // Time change on media - _on(player.media, 'timeupdate seeking', _timeUpdate); + _on(plyr.media, 'timeupdate seeking', _timeUpdate); // Update manual captions - _on(player.media, 'timeupdate', _seekManualCaptions); + _on(plyr.media, 'timeupdate', _seekManualCaptions); // Display duration - _on(player.media, 'loadedmetadata', _displayDuration); + _on(plyr.media, 'loadedmetadata', _displayDuration); // Captions - _on(player.buttons.captions, 'click', _toggleCaptions); + _on(plyr.buttons.captions, 'click', _toggleCaptions); // Handle the media finishing - _on(player.media, 'ended', function() { + _on(plyr.media, 'ended', function() { // Clear - if (player.type === 'video') { - player.captionsContainer.innerHTML = ''; + if (plyr.type === 'video') { + plyr.captionsContainer.innerHTML = ''; } // Reset UI @@ -2049,29 +2042,29 @@ }); // Check for buffer progress - _on(player.media, 'progress playing', _updateProgress); + _on(plyr.media, 'progress playing', _updateProgress); // Handle native mute - _on(player.media, 'volumechange', _updateVolume); + _on(plyr.media, 'volumechange', _updateVolume); // Handle native play/pause - _on(player.media, 'play pause', _checkPlaying); + _on(plyr.media, 'play pause', _checkPlaying); // Loading - _on(player.media, 'waiting canplay seeked', _checkLoading); + _on(plyr.media, 'waiting canplay seeked', _checkLoading); // Click video - if (player.type === 'video' && config.click) { - _on(player.videoContainer, 'click', function() { - if (player.media.paused) { - _triggerEvent(player.buttons.play, 'click'); + if (plyr.type === 'video' && config.click) { + _on(plyr.videoContainer, 'click', function() { + if (plyr.media.paused) { + _triggerEvent(plyr.buttons.play, 'click'); } - else if (player.media.ended) { + else if (plyr.media.ended) { _seek(); - _triggerEvent(player.buttons.play, 'click'); + _triggerEvent(plyr.buttons.play, 'click'); } else { - _triggerEvent(player.buttons.pause, 'click'); + _triggerEvent(plyr.buttons.pause, 'click'); } }); } @@ -2082,47 +2075,47 @@ // http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory function _destroy() { // Bail if the element is not initialized - if (!player.init) { + if (!plyr.init) { return null; } // Reset container classname - player.container.setAttribute('class', _getClassname(config.selectors.container)); + plyr.container.setAttribute('class', _getClassname(config.selectors.container)); // Remove init flag - player.init = false; + plyr.init = false; // Remove controls _remove(_getElement(config.selectors.controls)); // YouTube - if (player.type === 'youtube') { - player.embed.destroy(); + if (plyr.type === 'youtube') { + plyr.embed.destroy(); return; } // If video, we need to remove some more - if (player.type === 'video') { + if (plyr.type === 'video') { // Remove captions _remove(_getElement(config.selectors.captions)); // Remove video wrapper - _unwrap(player.videoContainer); + _unwrap(plyr.videoContainer); } // Restore native video controls - player.media.setAttribute('controls', ''); + plyr.media.setAttribute('controls', ''); // Clone the media element to remove listeners // http://stackoverflow.com/questions/19469881/javascript-remove-all-event-listeners-of-specific-type - var clone = player.media.cloneNode(true); - player.media.parentNode.replaceChild(clone, player.media); + var clone = plyr.media.cloneNode(true); + plyr.media.parentNode.replaceChild(clone, plyr.media); } // Setup a player function _init() { // Bail if the element is initialized - if (player.init) { + if (plyr.init) { return null; } @@ -2130,49 +2123,52 @@ fullscreen = _fullscreen(); // Sniff out the browser - player.browser = _browserSniff(); + plyr.browser = _browserSniff(); // Get the media element - player.media = player.container.querySelectorAll('audio, video, div')[0]; + plyr.media = plyr.container.querySelectorAll('audio, video, div')[0]; + + // Get original classname + plyr.originalClassName = plyr.container.className; // Set media type based on tag or data attribute // Supported: video, audio, vimeo, youtube - var tagName = player.media.tagName.toLowerCase(); + var tagName = plyr.media.tagName.toLowerCase(); if (tagName === 'div') { - player.type = player.media.getAttribute('data-type'); - player.embedId = player.media.getAttribute('data-video-id'); + plyr.type = plyr.media.getAttribute('data-type'); + plyr.embedId = plyr.media.getAttribute('data-video-id'); // Clean up - player.media.removeAttribute('data-type'); - player.media.removeAttribute('data-video-id'); + plyr.media.removeAttribute('data-type'); + plyr.media.removeAttribute('data-video-id'); } else { - player.type = tagName; - config.crossorigin = (player.media.getAttribute('crossorigin') !== null); - config.autoplay = (config.autoplay || (player.media.getAttribute('autoplay') !== null)); - config.loop = (config.loop || (player.media.getAttribute('loop') !== null)); + plyr.type = tagName; + config.crossorigin = (plyr.media.getAttribute('crossorigin') !== null); + config.autoplay = (config.autoplay || (plyr.media.getAttribute('autoplay') !== null)); + config.loop = (config.loop || (plyr.media.getAttribute('loop') !== null)); } // Check for full support - player.supported = api.supported(player.type); + plyr.supported = api.supported(plyr.type); // If no native support, bail - if (!player.supported.basic) { + if (!plyr.supported.basic) { return false; } // Debug info - _log(player.browser.name + ' ' + player.browser.version); + _log(plyr.browser.name + ' ' + plyr.browser.version); // Setup media _setupMedia(); // Setup interface - if (player.type == 'video' || player.type == 'audio') { + if (plyr.type == 'video' || plyr.type == 'audio') { // Bail if no support - if (!player.supported.full) { + if (!plyr.supported.full) { // Successful setup - player.init = true; + plyr.init = true; // Don't inject controls if no full support return; @@ -2191,7 +2187,7 @@ } // Successful setup - player.init = true; + plyr.init = true; } function _setupInterface() { @@ -2226,12 +2222,12 @@ _init(); // If init failed, return an empty object - if (!player.init) { + if (!plyr.init) { return {}; } return { - media: player.media, + media: plyr.media, play: _play, pause: _pause, restart: _seek, @@ -2245,8 +2241,8 @@ toggleMute: _toggleMute, toggleCaptions: _toggleCaptions, toggleFullscreen: _toggleFullscreen, - isFullscreen: function() { return player.isFullscreen || false; }, - support: function(mimeType) { return _supportMime(player, mimeType); }, + isFullscreen: function() { return plyr.isFullscreen || false; }, + support: function(mimeType) { return _supportMime(plyr, mimeType); }, destroy: _destroy, restore: _init }; @@ -2290,7 +2286,30 @@ }; // Expose setup function - api.setup = function(options) { + api.setup = function(elements, options) { + // Get the players + var instances = []; + + // Select the elements + // Assume elements is a NodeList by default + if(typeof elements === 'string') { + elements = document.querySelectorAll(elements); + } + // Single HTMLElement passed + else if(elements instanceof HTMLElement) { + elements = [elements]; + } + // No selector passed, possibly options as first argument + else if (!(elements instanceof NodeList) && typeof elements !== 'string') { + // If options are the first argument + if(typeof options === 'undefined' && typeof elements === 'object') { + options = elements; + } + + // Use default selector + elements = document.querySelectorAll(defaults.selectors.container); + } + // Extend the default options with user specified config = _extend(defaults, options); @@ -2300,10 +2319,6 @@ return false; } - // Get the players - var elements = document.querySelectorAll(config.selectors.container), - players = []; - // Create a player instance for each element for (var i = elements.length - 1; i >= 0; i--) { // Get the current element @@ -2324,10 +2339,10 @@ } // Add to return array even if it's already setup - players.push(element.plyr); + instances.push(element.plyr); } - return players; + return instances; }; }(this.plyr = this.plyr || {})); diff --git a/src/less/plyr.less b/src/less/plyr.less index ad393c18..72217a88 100644 --- a/src/less/plyr.less +++ b/src/less/plyr.less @@ -144,7 +144,7 @@ // Styles // ------------------------------- // Base -.player { +.plyr { position: relative; max-width: 100%; min-width: 290px; @@ -227,7 +227,7 @@ font-size: @font-size-captions-large; } - // Player controls + // Plyr controls &-controls { .clearfix(); .font-smoothing(); @@ -294,8 +294,8 @@ display: none; } - // Player time - .player-time { + // plyr time + .plyr-time { display: inline-block; vertical-align: middle; margin-left: @control-spacing; @@ -306,7 +306,7 @@ } // Media duration hidden on small screens - .player-time + .player-time { + .plyr-time + .plyr-time { display: none; @media (min-width: @bp-control-split) { @@ -358,16 +358,16 @@ border-width: 0 1px 1px 0; } } - button:hover .player-tooltip, - button.tab-focus:focus .player-tooltip { + button:hover .plyr-tooltip, + button.tab-focus:focus .plyr-tooltip { opacity: 1; transform: translate(-50%, 0) scale(1); } - button:hover .player-tooltip { + button:hover .plyr-tooltip { z-index: 3; } - // Player progress + // Playback progress // <progress> element &-progress { position: absolute; @@ -466,7 +466,7 @@ } // Loading state - &.loading .player-progress-buffer { + &.loading .plyr-progress-buffer { animation: progress 1s linear infinite; background-size: @progress-loading-size @progress-loading-size; background-repeat: repeat-x; @@ -484,11 +484,11 @@ } // States - &-controls [data-player='pause'], - &.playing .player-controls [data-player='play'] { + &-controls [data-plyr='pause'], + &.playing .plyr-controls [data-plyr='play'] { display: none; } - &.playing .player-controls [data-player='pause'] { + &.playing .plyr-controls [data-plyr='pause'] { display: inline-block; } @@ -560,7 +560,7 @@ // It's not supported to change volume using JavaScript: // https://developer.apple.com/library/safari/documentation/AudioVideo/Conceptual/Using_HTML5_Audio_Video/Device-SpecificConsiderations/Device-SpecificConsiderations.html &.ios &-volume, - &.ios [data-player='mute'], + &.ios [data-plyr='mute'], &-audio.ios &-controls-right { display: none; } @@ -571,10 +571,10 @@ // Audio specific styles // Position the progress within the container - &-audio .player-controls { + &-audio .plyr-controls { padding-top: (@control-spacing * 2); } - &-audio .player-progress { + &-audio .plyr-progress { bottom: auto; top: 0; background: @off-white; @@ -596,11 +596,11 @@ video { height: 100%; } - .player-video-wrapper { + .plyr-video-wrapper { height: 100%; width: 100%; } - .player-controls { + .plyr-controls { position: absolute; bottom: 0; left: 0; @@ -609,22 +609,22 @@ // Hide controls when playing in full screen &.fullscreen-hide-controls.playing { - .player-controls { + .plyr-controls { transform: translateY(100%) translateY(@control-spacing / 2); transition: transform .3s .2s ease; } - &.player-hover .player-controls { + &.plyr-hover .plyr-controls { transform: translateY(0); } - .player-captions { + .plyr-captions { bottom: (@control-spacing / 2); transition: bottom .3s .2s ease; } } // Captions - .player-captions, - &.fullscreen-hide-controls.playing.player-hover .player-captions { + .plyr-captions, + &.fullscreen-hide-controls.playing.plyr-hover .plyr-captions { top: auto; bottom: 90px; @@ -636,8 +636,8 @@ // Change icons on state change &.fullscreen-active .icon-exit-fullscreen, - &.muted .player-controls .icon-muted, - &.captions-active .player-controls .icon-captions-on { + &.muted .plyr-controls .icon-muted, + &.captions-active .plyr-controls .icon-captions-on { display: block; & + svg { @@ -646,12 +646,12 @@ } // Some options are hidden by default - [data-player='captions'], - [data-player='fullscreen'] { + [data-plyr='captions'], + [data-plyr='fullscreen'] { display: none; } - &.captions-enabled [data-player='captions'], - &.fullscreen-enabled [data-player='fullscreen'] { + &.captions-enabled [data-plyr='captions'], + &.fullscreen-enabled [data-plyr='fullscreen'] { display: inline-block; } }
\ No newline at end of file |