diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/js/plyr.js | 321 | ||||
-rw-r--r-- | src/js/plyr.youtube.js | 32 | ||||
-rw-r--r-- | src/less/plyr.less | 98 | ||||
-rw-r--r-- | src/sass/plyr.scss | 96 |
4 files changed, 299 insertions, 248 deletions
diff --git a/src/js/plyr.js b/src/js/plyr.js index 41abe085..752f44de 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -866,124 +866,145 @@ // 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 div = document.createElement("div"); - div.setAttribute("id", "youtube" + Math.floor(Math.random() * (10000))); - player.media.appendChild(div); + 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); - // Load the API - _injectScript("https://www.youtube.com/iframe_api"); + 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() { - _log("YouTube API Ready"); + // 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 + // 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(div.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(); - - // Setup buffering - player.timer.buffering = window.setInterval(function() { - // Get loaded % from YouTube - player.media.buffered = instance.getVideoLoadedFraction(); - - // Trigger timeupdate - _triggerEvent(player.media, "progress"); - - // Bail if we're at 100% - if(player.media.buffered === 1) { - window.clearInterval(player.timer.buffering); - } - }, 200); + // 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"); - _setupInterface(); - }, - onStateChange: function(event) { - // Get the instance - var instance = event.target; - - // Reset timer - window.clearInterval(player.timer.playing); - - // Handle event - switch(event.data) { - // Unstarted - case -1: - break; - - // Ended - case 0: - player.media.paused = true; - _triggerEvent(player.media, "ended"); - break; - - // Playing - 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; - - // Paused - case 2: - player.media.paused = true; - _triggerEvent(player.media, "pause"); - break; - - // Buffering - case 3: - break; - - // Video cued - case 5: - break; + // 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 @@ -1586,12 +1607,17 @@ function _parseSource(sources) { // YouTube if(player.type === "youtube" && typeof sources === "string") { - if(sources.indexOf("http") === 0) { - player.embed.loadVideoByUrl(sources); - } - else { - player.embed.loadVideoById(sources); - } + // 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; } @@ -1748,6 +1774,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) { @@ -1757,12 +1785,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 @@ -1779,9 +1813,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 @@ -1828,24 +1859,14 @@ // Setup interface if(player.type == "video" || player.type == "audio") { - _setupInterface(); - } - - // Successful setup - player.init = true; - } - - function _setupInterface() { - // If there's full support - if(player.supported.full) { - // Inject custom controls - _injectControls(); - - // Find the elements - if(!_findElements()) { - return false; + // Bail if no support + if(!player.supported.full) { + return; } + // Setup UI + _setupInterface(); + // Display duration if available if(config.displayDuration) { _displayDuration(); @@ -1853,20 +1874,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; } + + // Captions + _setupCaptions(); + + // Set volume + _setVolume(); + _updateVolume(); + + // Setup fullscreen + _setupFullscreen(); + + // Listeners + _listeners(); } // Initialize instance @@ -1919,6 +1953,11 @@ full = (basic && !oldIE); break; + case "youtube": + basic = true; + full = !oldIE; + break; + default: basic = (audio && video); full = (basic && !oldIE); diff --git a/src/js/plyr.youtube.js b/src/js/plyr.youtube.js deleted file mode 100644 index 0b28460a..00000000 --- a/src/js/plyr.youtube.js +++ /dev/null @@ -1,32 +0,0 @@ -// ========================================================================== -// Plyr -// plyr.youtube.js v1.1.4 -// https://github.com/selz/plyr -// License: The MIT License (MIT) -// ========================================================================== - -(function (api) { - "use strict"; - - api.youtube = { - setup: function() { - console.log("Setup youtube"); - console.log(this); - - var player = this; - - // Find child <source> elements - var sources = player.media.querySelectorAll("source"); - - // Remove each - for (var i = sources.length - 1; i >= 0; i--) { - var source = sources[i]; - - if(source.type == "video/youtube") { - console.log(source.src); - } - } - } - }; - -}(this.plyr.plugins = this.plyr.plugins || {}));
\ No newline at end of file diff --git a/src/less/plyr.less b/src/less/plyr.less index b9bc8a30..75d94b0f 100644 --- a/src/less/plyr.less +++ b/src/less/plyr.less @@ -4,12 +4,14 @@ // Variables // ------------------------------- + // Colors @blue: #3498DB; -@gray-dark: #343f4a; -@gray: #565d64; -@gray-light: #cbd0d3; -@off-white: #d6dadd; +@gray-dark: #343F4A; +@gray: #565D64; +@gray-light: #6B7D86; +@gray-lighter: #CBD0D3; +@off-white: #D6DADD; // Font sizes @font-size-small: 14px; @@ -18,11 +20,10 @@ // Controls @control-spacing: 10px; -@controls-bg: @gray-dark; +@controls-bg: #fff; @control-bg-hover: @blue; -@control-color: @gray-light; -@control-color-inactive: @gray; -@control-color-hover: #fff; +.contrast-control-color(@controls-bg); +.contrast-control-color-hover(@control-bg-hover); // Tooltips @tooltip-bg: @controls-bg; @@ -40,7 +41,7 @@ // Volume @volume-track-height: 6px; -@volume-track-bg: @gray; +@volume-track-bg: darken(@controls-bg, 10%); @volume-thumb-height: (@volume-track-height * 2); @volume-thumb-width: (@volume-track-height * 2); @volume-thumb-bg: @control-color; @@ -50,18 +51,40 @@ @bp-control-split: 560px; // When controls split into left/right @bp-captions-large: 768px; // When captions jump to the larger font size -// Utility classes & mixins +// Animation +// --------------------------------------- + +@keyframes progress { + to { background-position: @progress-loading-size 0; } +} + +// Mixins // ------------------------------- -// Screen reader only -.sr-only { - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); - padding: 0 !important; - border: 0 !important; - height: 1px !important; - width: 1px !important; - overflow: hidden; + +// Contrast +.contrast-control-color(@color: "") when (lightness(@color) >= 65%) { + @control-color: @gray-light; +} +.contrast-control-color(@color: "") when (lightness(@color) < 65%) { + @control-color: @gray-lighter; +} +.contrast-control-color-hover(@color: "") when (lightness(@color) >= 65%) { + @control-color-hover: @gray; +} +.contrast-control-color-hover(@color: "") when (lightness(@color) < 65%) { + @control-color-hover: #fff; +} + +// Font smoothing +.font-smoothing(@mode: on) when (@mode = on) { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; +} +.font-smoothing(@mode: on) when (@mode = off) { + -moz-osx-font-smoothing: auto; + -webkit-font-smoothing: subpixel-antialiased; } + // Contain floats: nicolasgallagher.com/micro-clearfix-hack/ .clearfix() { zoom: 1; @@ -75,14 +98,7 @@ outline-offset: 0; } -// Animation -// --------------------------------------- -@keyframes progress { - to { background-position: @progress-loading-size 0; } -} - // <input type="range"> styling -// --------------------------------------- .volume-thumb() { height: @volume-thumb-height; width: @volume-thumb-width; @@ -109,15 +125,16 @@ border: 0; } -// Font smoothing -// --------------------------------------- -.font-smoothing(@mode: on) when (@mode = on) { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; -} -.font-smoothing(@mode: on) when (@mode = off) { - -moz-osx-font-smoothing: auto; - -webkit-font-smoothing: subpixel-antialiased; +// Screen reader only +// ------------------------------- +.sr-only { + position: absolute !important; + clip: rect(1px, 1px, 1px, 1px); + padding: 0 !important; + border: 0 !important; + height: 1px !important; + width: 1px !important; + overflow: hidden; } // Styles @@ -141,7 +158,8 @@ &-video-wrapper { position: relative; } - video { + video, + audio { width: 100%; height: auto; vertical-align: middle; @@ -199,6 +217,7 @@ background: @controls-bg; line-height: 1; text-align: center; + box-shadow: 0 1px 1px rgba(red(@gray-dark), green(@gray-dark), blue(@gray-dark), .2); // Layout &-right { @@ -222,7 +241,7 @@ margin: 0 2px; padding: (@control-spacing / 2) @control-spacing; - transition: background .3s ease; + transition: background .3s ease, color .3s ease, opacity .3s ease; border-radius: 3px; cursor: pointer; @@ -236,12 +255,13 @@ } input + label, .inverted:checked + label { - color: @control-color-inactive; + opacity: .5; } button, .inverted + label, input:checked + label { color: @control-color; + opacity: 1; } button { border: 0; @@ -256,6 +276,7 @@ [type="checkbox"] + label:hover { background: @control-bg-hover; color: @control-color-hover; + opacity: 1; } button:focus, input:focus + label { @@ -288,7 +309,6 @@ &::before { content: "\2044"; margin-right: @control-spacing; - color: darken(@control-color, 30%); } } } diff --git a/src/sass/plyr.scss b/src/sass/plyr.scss index bf027db0..011a5368 100644 --- a/src/sass/plyr.scss +++ b/src/sass/plyr.scss @@ -4,12 +4,14 @@ // Variables // ------------------------------- + // Colors $blue: #3498DB !default; -$gray-dark: #343f4a !default; -$gray: #565d64 !default; -$gray-light: #cbd0d3 !default; -$off-white: #d6dadd !default; +$gray-dark: #343F4A !default; +$gray: #565D64 !default; +$gray-light: #6B7D86 !default; +$gray-lighter: #CBD0D3 !default; +$off-white: #D6DADD !default; // Font sizes $font-size-small: 14px !default; @@ -18,11 +20,10 @@ $font-size-large: ceil(($font-size-base * 1.5)) !default; // Controls $control-spacing: 10px !default; -$controls-bg: $gray-dark !default; -$control-bg-hover: $blue !default; -$control-color: $gray-light !default; -$control-color-inactive: $gray !default; -$control-color-hover: #fff !default; +$controls-bg: #fff !default; +$control-bg-hover: @blue !default; +.contrast-control-color($controls-bg); +.contrast-control-color-hover($control-bg-hover); // Tooltips $tooltip-bg: $controls-bg !default; @@ -40,7 +41,7 @@ $progress-loading-bg: rgba(0,0,0, .15) !default; // Volume $volume-track-height: 6px !default; -$volume-track-bg: $gray !default; +$volume-track-bg: darken($controls-bg, 10%) !default; $volume-thumb-height: ($volume-track-height * 2) !default; $volume-thumb-width: ($volume-track-height * 2) !default; $volume-thumb-bg: $control-color !default; @@ -50,18 +51,40 @@ $volume-thumb-bg-focus: $control-bg-hover !default; $bp-control-split: 560px !default; // When controls split into left/right $bp-captions-large: 768px !default; // When captions jump to the larger font size -// Utility classes & mixins +// Mixins // ------------------------------- -// Screen reader only -.sr-only { - position: absolute !important; - clip: rect(1px, 1px, 1px, 1px); - padding: 0 !important; - border: 0 !important; - height: 1px !important; - width: 1px !important; - overflow: hidden; + +// Contrast +@mixin contrast-control-color($color: "") { + @if (lightness($color) >= 65%) { + $control-color: $gray-light; + } + @else if(lightness(@color) < 65%) { + $control-color: $gray-lighter; + } +} +@mixin contrast-control-color-hover($color: "") { + @if (lightness($color) >= 65%) { + $control-color-hover: $gray; + } + @else if (lightness($color) < 65%) { + $control-color-hover: #fff; + } } + +// Font smoothing +@mixin font-smoothing($mode: on) +{ + @if ($mode == 'on') { + -moz-osx-font-smoothing: grayscale; + -webkit-font-smoothing: antialiased; + } + @else if ($mode == 'off') { + -moz-osx-font-smoothing: auto; + -webkit-font-smoothing: subpixel-antialiased; + } +} + // Contain floats: nicolasgallagher.com/micro-clearfix-hack/ @mixin clearfix() { @@ -84,7 +107,6 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo } // <input type="range"> styling -// --------------------------------------- @mixin volume-thumb() { height: $volume-thumb-height; @@ -115,17 +137,16 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo border: 0; } -// Font smoothing -// --------------------------------------- -@mixin font-smoothing($mode: on) -{ - @if $mode == 'on' { - -moz-osx-font-smoothing: grayscale; - -webkit-font-smoothing: antialiased; - } @else if $mode == 'off' { - -moz-osx-font-smoothing: auto; - -webkit-font-smoothing: subpixel-antialiased; - } +// Screen reader only +// ------------------------------- +.sr-only { + position: absolute !important; + clip: rect(1px, 1px, 1px, 1px); + padding: 0 !important; + border: 0 !important; + height: 1px !important; + width: 1px !important; + overflow: hidden; } // Styles @@ -149,7 +170,8 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo &-video-wrapper { position: relative; } - video { + video, + audio { width: 100%; height: auto; vertical-align: middle; @@ -192,6 +214,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo background: $controls-bg; line-height: 1; text-align: center; + box-shadow: 0 1px 1px rgba(red($gray-dark), green($gray-dark), blue($gray-dark), .2); // Layout &-right { @@ -215,7 +238,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo margin: 0 2px; padding: ($control-spacing / 2) $control-spacing; - transition: background .3s ease; + background .3s ease, color .3s ease, opacity .3s ease; border-radius: 3px; cursor: pointer; @@ -229,12 +252,13 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo } input + label, .inverted:checked + label { - color: $control-color-inactive; + opacity: .5; } button, .inverted + label, input:checked + label { color: $control-color; + opacity: 1; } button { border: 0; @@ -249,6 +273,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo [type="checkbox"] + label:hover { background: $control-bg-hover; color: $control-color-hover; + opacity: 1; } button:focus, input:focus + label { @@ -281,7 +306,6 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fo &::before { content: "\2044"; margin-right: $control-spacing; - color: darken($control-color, 30%); } } } |