diff options
Diffstat (limited to 'src/js/plyr.js')
-rw-r--r-- | src/js/plyr.js | 306 |
1 files changed, 269 insertions, 37 deletions
diff --git a/src/js/plyr.js b/src/js/plyr.js index debbecfc..232c49a8 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -9,6 +9,7 @@ (function (api) { "use strict"; + /*global YT*/ // Globals var fullscreen, config; @@ -49,9 +50,9 @@ duration: ".player-duration" }, classes: { - video: "player-video", videoWrapper: "player-video-wrapper", - audio: "player-audio", + embedWrapper: "player-video-embed", + type: "player-{0}", stopped: "stopped", playing: "playing", muted: "muted", @@ -82,7 +83,7 @@ key: "plyr_volume" }, controls: ["restart", "rewind", "play", "fast-forward", "current-time", "duration", "mute", "volume", "captions", "fullscreen"], - onSetup: function() {}, + onSetup: function() {} }; // Build the default HTML @@ -331,6 +332,18 @@ return false; } + // Inject a script + function _injectScript(source) { + if(document.querySelectorAll("script[src='" + source + "']").length) { + return; + } + + var tag = document.createElement("script"); + tag.src = source; + var firstScriptTag = document.getElementsByTagName("script")[0]; + firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); + } + // Element exists in an array function _inArray(haystack, needle) { return Array.prototype.indexOf && (haystack.indexOf(needle) != -1); @@ -817,7 +830,7 @@ player.media.removeAttribute("controls"); // Add type class - _toggleClass(player.container, config.classes[player.type], true); + _toggleClass(player.container, config.classes.type.replace("{0}", player.type), true); // If there's no autoplay attribute, assume the video is stopped and add state class _toggleClass(player.container, config.classes.stopped, (player.media.getAttribute("autoplay") === null)); @@ -839,6 +852,11 @@ // Cache the container player.videoContainer = wrapper; } + + // YouTube + if(player.type == "youtube") { + _setupYouTube(player.media.getAttribute("data-video-id")); + } } // Autoplay @@ -847,6 +865,149 @@ } } + // Setup YouTube + function _setupYouTube(id) { + // Remove old containers + var containers = _getElements("[id^='youtube']"); + for (var i = containers.length - 1; i >= 0; i--) { + _remove(containers[i]); + } + + // Create the YouTube container + var container = document.createElement("div"); + container.setAttribute("id", "youtube-" + Math.floor(Math.random() * (10000))); + player.media.appendChild(container); + + // Add embed class for responsive + _toggleClass(player.media, config.classes.videoWrapper, true); + _toggleClass(player.media, config.classes.embedWrapper, true); + + if(typeof YT === "object") { + _YTReady(id, container); + } + else { + // Load the API + _injectScript("https://www.youtube.com/iframe_api"); + + // Setup callback for the API + window.onYouTubeIframeAPIReady = function () { _YTReady(id, container); } + } + } + + // Handle API ready + function _YTReady(id, container) { + _log("YouTube API Ready"); + + // Setup timers object + // We have to poll YouTube for updates + if(!("timer" in player)) { + player.timer = {}; + } + + // Setup instance + // https://developers.google.com/youtube/iframe_api_reference + player.embed = new YT.Player(container.id, { + videoId: id, + playerVars: { + autoplay: 0, + controls: 0, + vq: "hd720", + rel: 0, + showinfo: 0, + iv_load_policy: 3, + cc_lang_pref: "en", + wmode: "transparent", + modestbranding: 1 + }, + events: { + onReady: function(event) { + // Get the instance + 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 = (instance.getPlayerState() == 2); + player.media.currentTime = instance.getCurrentTime(); + player.media.muted = instance.isMuted(); + + // Trigger timeupdate + _triggerEvent(player.media, "timeupdate"); + + // Reset timer + window.clearInterval(player.timer.buffering); + + // Setup buffering + player.timer.buffering = window.setInterval(function() { + // Get loaded % from YouTube + player.media.buffered = instance.getVideoLoadedFraction(); + + // Trigger progress + _triggerEvent(player.media, "progress"); + + // Bail if we're at 100% + if(player.media.buffered === 1) { + window.clearInterval(player.timer.buffering); + } + }, 200); + + // Only setup controls once + if(!player.container.querySelectorAll(config.selectors.controls).length) { + _setupInterface(); + } + + // Display duration if available + if(config.displayDuration) { + _displayDuration(); + } + }, + onStateChange: function(event) { + // Get the instance + var instance = event.target; + + // Reset timer + window.clearInterval(player.timer.playing); + + // Handle events + // -1 Unstarted + // 0 Ended + // 1 Playing + // 2 Paused + // 3 Buffering + // 5 Video cued + switch(event.data) { + case 0: + player.media.paused = true; + _triggerEvent(player.media, "ended"); + break; + + case 1: + player.media.paused = false; + _triggerEvent(player.media, "play"); + + // Poll to get playback progress + player.timer.playing = window.setInterval(function() { + // Set the current time + player.media.currentTime = instance.getCurrentTime(); + + // Trigger timeupdate + _triggerEvent(player.media, "timeupdate"); + }, 200); + + break; + + case 2: + player.media.paused = true; + _triggerEvent(player.media, "pause"); + break; + } + } + } + }); + } + // Setup captions function _setupCaptions() { if(player.type === "video") { @@ -997,7 +1158,7 @@ // Setup fullscreen function _setupFullscreen() { - if(player.type === "video" && config.fullscreen.enabled) { + if(player.type != "audio" && config.fullscreen.enabled) { // Check for native support var nativeSupport = fullscreen.supportsFullScreen; @@ -1093,6 +1254,14 @@ } catch(e) {} + // YouTube + if(player.type == "youtube") { + player.embed.seekTo(player.media.currentTime); + + // Trigger timeupdate + _triggerEvent(player.media, "timeupdate"); + } + // Logging _log("Seeking to " + player.media.currentTime + " seconds"); @@ -1215,6 +1384,14 @@ // Set the player volume player.media.volume = parseFloat(volume / 10); + // YouTube + if(player.type == "youtube") { + player.embed.setVolume(player.media.volume * 100); + + // Trigger timeupdate + _triggerEvent(player.media, "volumechange"); + } + // Toggle muted state if(player.media.muted && volume > 0) { _toggleMute(); @@ -1230,6 +1407,14 @@ // Set mute on the player player.media.muted = muted; + + // YouTube + if(player.type === "youtube") { + player.embed[player.media.muted ? "mute" : "unMute"](); + + // Trigger timeupdate + _triggerEvent(player.media, "volumechange"); + } } // Update volume UI and storage @@ -1320,9 +1505,14 @@ value = (function() { var buffered = player.media.buffered; - if(buffered.length) { + // HTML5 + if(buffered && buffered.length) { return _getPercentage(buffered.end(0), player.media.duration); } + // YouTube returns between 0 and 1 + else if(typeof buffered == "number") { + return (buffered * 100); + } return 0; })(); @@ -1416,6 +1606,22 @@ // Update source // Sources are not checked for support so be careful function _parseSource(sources) { + // YouTube + if(player.type === "youtube" && typeof sources === "string") { + // Destroy YouTube instance + player.embed.destroy(); + + // Re-setup YouTube + // We don't use loadVideoBy[x] here since it has issues + _setupYouTube(sources); + + // Update times + _timeUpdate(); + + // Bail + return; + } + // Pause playback (webkit freaks out) _pause(); @@ -1537,10 +1743,7 @@ }); // Check for buffer progress - _on(player.media, "progress", _updateProgress); - - // Also check on start of playing - _on(player.media, "playing", _updateProgress); + _on(player.media, "progress playing", _updateProgress); // Handle native mute _on(player.media, "volumechange", _updateVolume); @@ -1572,6 +1775,8 @@ } // Destroy an instance + // Event listeners are removed when elements are removed + // 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) { @@ -1581,12 +1786,18 @@ // Reset container classname player.container.setAttribute("class", config.selectors.container.replace(".", "")); - // Event listeners are removed when elements are removed - // http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory + // Remove init flag + player.init = false; // Remove controls _remove(_getElement(config.selectors.controls)); + // YouTube + if(player.type === "youtube") { + player.embed.destroy(); + return; + } + // If video, we need to remove some more if(player.type === "video") { // Remove captions @@ -1603,9 +1814,6 @@ // 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); - - // Remove init flag - player.init = false; } // Setup a player @@ -1622,11 +1830,20 @@ player.browser = _browserSniff(); // Get the media element - player.media = player.container.querySelectorAll("audio, video")[0]; + player.media = player.container.querySelectorAll("audio, video, div")[0]; // Set media type - player.type = player.media.tagName.toLowerCase(); + var tagName = player.media.tagName.toLowerCase(); + switch(tagName) { + case "div": + player.type = player.media.getAttribute("data-type"); + break; + default: + player.type = tagName; + break; + } + // Check for full support player.supported = api.supported(player.type); @@ -1641,16 +1858,16 @@ // Setup media _setupMedia(); - // If there's full support - if(player.supported.full) { - // Inject custom controls - _injectControls(); - - // Find the elements - if(!_findElements()) { - return false; + // Setup interface + if(player.type == "video" || player.type == "audio") { + // Bail if no support + if(!player.supported.full) { + return; } + // Setup UI + _setupInterface(); + // Display duration if available if(config.displayDuration) { _displayDuration(); @@ -1658,23 +1875,33 @@ // Set up aria-label for Play button with the title option _setupAria(); + } - // Captions - _setupCaptions(); - - // Set volume - _setVolume(); - _updateVolume(); + // Successful setup + player.init = true; + } - // Setup fullscreen - _setupFullscreen(); + function _setupInterface() { + // Inject custom controls + _injectControls(); - // Listeners - _listeners(); + // Find the elements + if(!_findElements()) { + return false; } - // Successful setup - player.init = true; + // Captions + _setupCaptions(); + + // Set volume + _setVolume(); + _updateVolume(); + + // Setup fullscreen + _setupFullscreen(); + + // Listeners + _listeners(); } // Initialize instance @@ -1727,6 +1954,11 @@ full = (basic && !oldIE); break; + case "youtube": + basic = true; + full = !oldIE; + break; + default: basic = (audio && video); full = (basic && !oldIE); |