diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/js/plyr.js | 145 | ||||
-rw-r--r-- | src/less/plyr.less | 106 | ||||
-rw-r--r-- | src/sass/plyr.scss | 110 |
3 files changed, 181 insertions, 180 deletions
diff --git a/src/js/plyr.js b/src/js/plyr.js index 782e6397..a94d67ed 100644 --- a/src/js/plyr.js +++ b/src/js/plyr.js @@ -1,6 +1,6 @@ // ========================================================================== // Plyr -// plyr.js v1.2.6 +// plyr.js v1.3.0 // https://github.com/selz/plyr // License: The MIT License (MIT) // ========================================================================== @@ -83,7 +83,21 @@ key: "plyr_volume" }, controls: ["restart", "rewind", "play", "fast-forward", "current-time", "duration", "mute", "volume", "captions", "fullscreen"], - onSetup: function() {} + i18n: { + restart: "Restart", + rewind: "Rewind {seektime} secs", + play: "Play", + pause: "Pause", + forward: "Forward {seektime} secs", + played: "played", + buffered: "buffered", + currentTime: "Current time", + duration: "Duration", + volume: "Volume", + toggleMute: "Toggle Mute", + toggleCaptions: "Toggle Captions", + toggleFullscreen: "Toggle Fullscreen" + } }; // Build the default HTML @@ -95,10 +109,10 @@ "<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'>", - "<span>0</span>% played", + "<span>0</span>% " + config.i18n.played, "</progress>", "<progress class='player-progress-buffer' max='100' value='0'>", - "<span>0</span>% buffered", + "<span>0</span>% " + config.i18n.buffered, "</progress>", "</div>", "<span class='player-controls-left'>"]; @@ -108,7 +122,7 @@ html.push( "<button type='button' data-player='restart'>", "<svg><use xlink:href='#" + config.iconPrefix + "-restart'></use></svg>", - "<span class='sr-only'>Restart</span>", + "<span class='sr-only'>" + config.i18n.restart + "</span>", "</button>" ); } @@ -118,7 +132,7 @@ html.push( "<button type='button' data-player='rewind'>", "<svg><use xlink:href='#" + config.iconPrefix + "-rewind'></use></svg>", - "<span class='sr-only'>Rewind {seektime} secs</span>", + "<span class='sr-only'>" + config.i18n.rewind + "</span>", "</button>" ); } @@ -128,11 +142,11 @@ html.push( "<button type='button' data-player='play'>", "<svg><use xlink:href='#" + config.iconPrefix + "-play'></use></svg>", - "<span class='sr-only'>Play</span>", + "<span class='sr-only'>" + config.i18n.play + "</span>", "</button>", "<button type='button' data-player='pause'>", "<svg><use xlink:href='#" + config.iconPrefix + "-pause'></use></svg>", - "<span class='sr-only'>Pause</span>", + "<span class='sr-only'>" + config.i18n.pause + "</span>", "</button>" ); } @@ -142,7 +156,7 @@ html.push( "<button type='button' data-player='fast-forward'>", "<svg><use xlink:href='#" + config.iconPrefix + "-fast-forward'></use></svg>", - "<span class='sr-only'>Forward {seektime} secs</span>", + "<span class='sr-only'>" + config.i18n.forward + "</span>", "</button>" ); } @@ -151,7 +165,7 @@ if(_inArray(config.controls, "current-time")) { html.push( "<span class='player-time'>", - "<span class='sr-only'>Current time</span>", + "<span class='sr-only'>" + config.i18n.currentTime + "</span>", "<span class='player-current-time'>00:00</span>", "</span>" ); @@ -161,7 +175,7 @@ if(_inArray(config.controls, "duration")) { html.push( "<span class='player-time'>", - "<span class='sr-only'>Duration</span>", + "<span class='sr-only'>" + config.i18n.duration + "</span>", "<span class='player-duration'>00:00</span>", "</span>" ); @@ -176,19 +190,18 @@ // Toggle mute button if(_inArray(config.controls, "mute")) { html.push( - "<input class='inverted sr-only' id='mute{id}' type='checkbox' data-player='mute'>", - "<label id='mute{id}' for='mute{id}'>", + "<button type='button' data-player='mute'>", "<svg class='icon-muted'><use xlink:href='#" + config.iconPrefix + "-muted'></use></svg>", "<svg><use xlink:href='#" + config.iconPrefix + "-volume'></use></svg>", - "<span class='sr-only'>Toggle Mute</span>", - "</label>" + "<span class='sr-only'>" + config.i18n.toggleMute + "</span>", + "</button>" ); } // Volume range control if(_inArray(config.controls, "volume")) { html.push( - "<label for='volume{id}' class='sr-only'>Volume</label>", + "<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'>" ); } @@ -196,12 +209,11 @@ // Toggle captions button if(_inArray(config.controls, "captions")) { html.push( - "<input class='sr-only' id='captions{id}' type='checkbox' data-player='captions'>", - "<label for='captions{id}'>", + "<button type='button' data-player='captions'>", "<svg class='icon-captions-on'><use xlink:href='#" + config.iconPrefix + "-captions-on'></use></svg>", "<svg><use xlink:href='#" + config.iconPrefix + "-captions-off'></use></svg>", - "<span class='sr-only'>Toggle Captions</span>", - "</label>" + "<span class='sr-only'>" + config.i18n.toggleCaptions + "</span>", + "</button>" ); } @@ -211,7 +223,7 @@ "<button type='button' data-player='fullscreen'>", "<svg class='icon-exit-fullscreen'><use xlink:href='#" + config.iconPrefix + "-exit-fullscreen'></use></svg>", "<svg><use xlink:href='#" + config.iconPrefix + "-enter-fullscreen'></use></svg>", - "<span class='sr-only'>Toggle Fullscreen</span>", + "<span class='sr-only'>" + config.i18n.toggleFullscreen + "</span>", "</button>" ); } @@ -478,18 +490,15 @@ element.dispatchEvent(fauxEvent); } - // Toggle checkbox - function _toggleCheckbox(event) { - // Only listen for return key - if(event.keyCode && event.keyCode != 13) { - return true; - } - - // Toggle the checkbox - event.target.checked = !event.target.checked; - - // Trigger change event - _triggerEvent(event.target, "change"); + // Toggle aria-pressed state on a toggle button + function _toggleState(target, state) { + // Get state + state = (typeof state === "boolean" ? state : !target.getAttribute("aria-pressed")); + + // Set the attribute on target + target.setAttribute("aria-pressed", state); + + return state; } // Get percentage @@ -637,7 +646,7 @@ player.currentCaption = player.captions[player.subcount][1]; // Render the caption - player.captionsContainer.innerHTML = player.currentCaption; + player.captionsContainer.innerHTML = player.currentCaption.trim(); } else { // Clear the caption @@ -656,7 +665,7 @@ if (config.captions.defaultActive) { _toggleClass(player.container, config.classes.captions.active, true); - player.buttons.captions.checked = true; + _toggleState(player.buttons.captions, true); } } @@ -795,15 +804,15 @@ } } - // Setup aria attributes - function _setupAria() { + // Setup aria attribute for play + function _setupPlayAria() { // If there's no play button, bail if(!player.buttons.play) { return; } // Find the current text - var label = player.buttons.play.innerText || "Play"; + var label = player.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) { @@ -913,10 +922,10 @@ cc_lang_pref: "en", wmode: "transparent", modestbranding: 1, - disablekb: 1 + disablekb: 1 }, events: { - 'onReady': function(event) { + "onReady": function(event) { // Get the instance var instance = event.target; @@ -925,7 +934,7 @@ 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.paused = true; player.media.currentTime = instance.getCurrentTime(); player.media.muted = instance.isMuted(); @@ -961,7 +970,7 @@ } } }, - 'onStateChange': function(event) { + "onStateChange": function(event) { // Get the instance var instance = event.target; @@ -1010,10 +1019,10 @@ function _setupCaptions() { if(player.type === "video") { // Inject the container - player.videoContainer.insertAdjacentHTML("afterbegin", "<div class='" + config.selectors.captions.replace(".", "") + "'></div>"); + player.videoContainer.insertAdjacentHTML("afterbegin", "<div class='" + config.selectors.captions.replace(".", "") + "' aria-live='assertive'><span></span></div>"); // Cache selector - player.captionsContainer = _getElement(config.selectors.captions); + player.captionsContainer = _getElement(config.selectors.captions).querySelector("span"); // Determine if HTML5 textTracks is supported player.usingTextTracks = false; @@ -1088,7 +1097,7 @@ // Display a cue, if there is one if (this.activeCues[0] && this.activeCues[0].hasOwnProperty("text")) { - player.captionsContainer.appendChild(this.activeCues[0].getCueAsHTML()); + player.captionsContainer.appendChild(this.activeCues[0].getCueAsHTML().trim()); } }); } @@ -1170,6 +1179,9 @@ _log("Fullscreen not supported and fallback disabled."); } + // Toggle state + _toggleState(player.buttons.fullscreen, false); + // Set control hide class hook if(config.fullscreen.hideControls) { _toggleClass(player.container, config.classes.fullscreen.hideControls, true); @@ -1224,7 +1236,8 @@ // Seek to time // The input parameter can be an event or a number function _seek(input) { - var targetTime = 0; + var targetTime = 0, + paused = player.media.paused; // Explicit position if (typeof input === "number") { @@ -1256,6 +1269,10 @@ if(player.type == "youtube") { player.embed.seekTo(targetTime); + if(paused) { + _pause(); + } + // Trigger timeupdate _triggerEvent(player.media, "timeupdate"); } @@ -1313,6 +1330,9 @@ // Set class hook _toggleClass(player.container, config.classes.fullscreen.active, player.isFullscreen); + + // Set button state + _toggleState(player.buttons.fullscreen, player.isFullscreen); // Toggle controls visibility based on mouse movement and location var hoverTimer, isMouseOver = false; @@ -1399,10 +1419,13 @@ // Mute function _toggleMute(muted) { // If the method is called without parameter, toggle based on current value - if(typeof muted === "undefined") { + if(typeof muted !== "boolean") { muted = !player.media.muted; } + // Set button state + _toggleState(player.buttons.mute, muted); + // Set mute on the player player.media.muted = muted; @@ -1435,7 +1458,7 @@ // Update checkbox for mute state if(player.supported.full && player.buttons.mute) { - player.buttons.mute.checked = (volume === 0); + _toggleState(player.buttons.mute, (volume === 0)); } } @@ -1447,11 +1470,14 @@ } // If the method is called without parameter, toggle based on current value - if(typeof show === "undefined") { + if(typeof show !== "boolean") { show = (player.container.className.indexOf(config.classes.captions.active) === -1); - player.buttons.captions.checked = show; } + // Toggle state + _toggleState(player.buttons.captions, show); + + // Add class hook _toggleClass(player.container, config.classes.captions.active, show); } @@ -1731,9 +1757,7 @@ }); // Mute - _on(player.buttons.mute, "change", function() { - _toggleMute(this.checked); - }); + _on(player.buttons.mute, "click", _toggleMute); // Fullscreen _on(player.buttons.fullscreen, "click", _toggleFullscreen); @@ -1753,9 +1777,7 @@ _on(player.media, "loadedmetadata", _displayDuration); // Captions - _on(player.buttons.captions, "change", function() { - _toggleCaptions(this.checked); - }); + _on(player.buttons.captions, "click", _toggleCaptions); // Handle the media finishing _on(player.media, "ended", function() { @@ -1780,9 +1802,6 @@ // Loading _on(player.media, "waiting canplay seeked", _checkLoading); - // Toggle checkboxes on return key (as they look like buttons) - _on(player.checkboxes, "keyup", _toggleCheckbox); - // Click video if(player.type === "video" && config.click) { _on(player.videoContainer, "click", function() { @@ -1900,7 +1919,7 @@ } // Set up aria-label for Play button with the title option - _setupAria(); + _setupPlayAria(); } // Successful setup @@ -2026,7 +2045,9 @@ element.plyr = (Object.keys(instance).length ? instance : false); // Callback - config.onSetup.apply(element.plyr); + if(typeof config.onSetup === "function") { + config.onSetup.apply(element.plyr); + } } // Add to return array even if it's already setup @@ -2036,4 +2057,4 @@ return players; } -}(this.plyr = this.plyr || {})); +}(this.plyr = this.plyr || {}));
\ No newline at end of file diff --git a/src/less/plyr.less b/src/less/plyr.less index 740a7ae7..a008fd21 100644 --- a/src/less/plyr.less +++ b/src/less/plyr.less @@ -17,7 +17,11 @@ // Font sizes @font-size-small: 14px; @font-size-base: 16px; -@font-size-large: ceil((@font-size-base * 1.5)); + +// Captions +@font-size-captions-base: ceil(@font-size-base * 1.25); +@font-size-captions-medium: ceil(@font-size-base * 1.5); +@font-size-captions-large: (@font-size-base * 2); // Controls @control-spacing: 10px; @@ -189,26 +193,31 @@ bottom: 0; left: 0; width: 100%; - padding: 20px; - min-height: 2.5em; + padding: (@control-spacing * 2) (@control-spacing * 2) (@control-spacing * 3); color: #fff; - font-size: @font-size-base; - font-weight: 600; - text-shadow: - -1px -1px 0 @gray, - 1px -1px 0 @gray, - -1px 1px 0 @gray, - 1px 1px 0 @gray; + font-size: @font-size-captions-base; text-align: center; .font-smoothing(); + span { + border-radius: 2px; + padding: 3px 10px; + background: rgba(0,0,0, .9); + } + span:empty { + display: none; + } + @media (min-width: @bp-captions-large) { - font-size: @font-size-large; + font-size: @font-size-captions-medium; } } &.captions-active &-captions { display: block; } + &.fullscreen-active &-captions { + font-size: @font-size-captions-large; + } // Player controls &-controls { @@ -236,17 +245,20 @@ } } - input + label, + // Buttons button { display: inline-block; vertical-align: middle; margin: 0 2px; padding: (@control-spacing / 2) @control-spacing; - - transition: background .3s ease, color .3s ease, opacity .3s ease; + overflow: hidden; + border: 0; + background: transparent; border-radius: 3px; cursor: pointer; - + color: @control-color; + transition: background .3s ease, color .3s ease, opacity .3s ease; + svg { width: 18px; height: 18px; @@ -254,41 +266,27 @@ fill: currentColor; transition: fill .3s ease; } - } - input + label, - .inverted:checked + label { - opacity: .5; - } - button, - .inverted + label, - input:checked + label { - color: @control-color; - opacity: 1; - } - button { - border: 0; - background: transparent; - overflow: hidden; - } - // Specificity for overriding .inverted - button:focus, - button:hover, - [type="checkbox"]:focus + label, - [type="checkbox"] + label:hover { - background: @control-bg-hover; - color: @control-color-hover; - opacity: 1; - } - button:focus, - input:focus + label { - outline: 0; + // Hover and tab focus + &.tab-focus, + &:hover { + background: @control-bg-hover; + color: @control-color-hover; + } + // Default focus + &:focus { + outline: 0; + } } + + // Hide toggle icons by default .icon-exit-fullscreen, .icon-muted, .icon-captions-on { display: none; } + + // Player time .player-time { display: inline-block; vertical-align: middle; @@ -309,7 +307,7 @@ // Add a slash in before &::before { - content: "\2044"; + content: '\2044'; margin-right: @control-spacing; } } @@ -338,7 +336,7 @@ // Arrow &::after { - content: ""; + content: ''; position: absolute; z-index: 1; top: 100%; @@ -352,14 +350,11 @@ border-width: 0 1px 1px 0; } } - label:hover .player-tooltip, - input.tab-focus:focus + label .player-tooltip, button:hover .player-tooltip, button.tab-focus:focus .player-tooltip { opacity: 1; transform: translate(-50%, 0) scale(1); } - label:hover .player-tooltip, button:hover .player-tooltip { z-index: 3; } @@ -377,7 +372,7 @@ &-buffer[value], &-played[value], - &-seek[type=range] { + &-seek[type='range'] { position: absolute; left: 0; top: 0; @@ -417,7 +412,7 @@ // Seek control // <input[type='range']> element // Specificity is for bootstrap compatibility - &-seek[type=range] { + &-seek[type='range'] { z-index: 4; cursor: pointer; outline: 0; @@ -492,7 +487,7 @@ // Volume control // <input[type='range']> element // Specificity is for bootstrap compatibility - &-volume[type="range"] { + &-volume[type='range'] { display: inline-block; vertical-align: middle; -webkit-appearance: none; @@ -558,7 +553,6 @@ // 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-player='mute'] + label, &-audio.ios &-controls-right { display: none; } @@ -645,15 +639,11 @@ // Some options are hidden by default [data-player='captions'], - [data-player='captions'] + label, - [data-player='fullscreen'], - [data-player='fullscreen'] + label { + [data-player='fullscreen'] { display: none; } &.captions-enabled [data-player='captions'], - &.captions-enabled [data-player='captions'] + label, - &.fullscreen-enabled [data-player='fullscreen'], - &.fullscreen-enabled [data-player='fullscreen'] + label { + &.fullscreen-enabled [data-player='fullscreen'] { display: inline-block; } }
\ No newline at end of file diff --git a/src/sass/plyr.scss b/src/sass/plyr.scss index a4dab010..8009b0d7 100644 --- a/src/sass/plyr.scss +++ b/src/sass/plyr.scss @@ -17,7 +17,11 @@ $off-white: #D6DADD !default; // Font sizes $font-size-small: 14px !default; $font-size-base: 16px !default; -$font-size-large: ceil(($font-size-base * 1.5)) !default; + +// Captions +$font-size-captions-base: ceil(@font-size-base * 1.25) !default; +$font-size-captions-medium: ceil(@font-size-base * 1.5) !default; +$font-size-captions-large: (@font-size-base * 2) !default; // Controls $control-spacing: 10px !default; @@ -89,7 +93,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger { zoom: 1; &:before, - &:after { content: ""; display: table; } + &:after { content: ''; display: table; } &:after { clear: both; } } // Tab focus styles @@ -192,26 +196,31 @@ $bp-captions-large: 768px !default; // When captions jump to the larger bottom: 0; left: 0; width: 100%; - padding: 20px; - min-height: 2.5em; + padding: ($control-spacing * 2) ($control-spacing * 2) ($control-spacing * 3); color: #fff; - font-size: $font-size-base; - font-weight: 600; - text-shadow: - -1px -1px 0 $gray, - 1px -1px 0 $gray, - -1px 1px 0 $gray, - 1px 1px 0 $gray; + font-size: $font-size-captions-base; text-align: center; @include font-smoothing(); - @media (min-width: $bp-captions-large) { - font-size: $font-size-large; + span { + border-radius: 2px; + padding: 3px 10px; + background: rgba(0,0,0, .9); + } + span:empty { + display: none; + } + + @media (min-width: @bp-captions-large) { + font-size: $font-size-captions-medium; } } &.captions-active &-captions { display: block; } + &.fullscreen-active &-captions { + font-size: $font-size-captions-large; + } // Player controls &-controls { @@ -239,17 +248,20 @@ $bp-captions-large: 768px !default; // When captions jump to the larger } } - input + label, + // Buttons button { display: inline-block; vertical-align: middle; margin: 0 2px; padding: ($control-spacing / 2) $control-spacing; - - transition: background .3s ease, color .3s ease, opacity .3s ease; + overflow: hidden; + border: 0; + background: transparent; border-radius: 3px; cursor: pointer; - + color: $control-color; + transition: background .3s ease, color .3s ease, opacity .3s ease; + svg { width: 18px; height: 18px; @@ -257,41 +269,27 @@ $bp-captions-large: 768px !default; // When captions jump to the larger fill: currentColor; transition: fill .3s ease; } - } - input + label, - .inverted:checked + label { - opacity: .5; - } - button, - .inverted + label, - input:checked + label { - color: $control-color; - opacity: 1; - } - button { - border: 0; - background: transparent; - overflow: hidden; - } - // Specificity for overriding .inverted - button:focus, - button:hover, - [type="checkbox"]:focus + label, - [type="checkbox"] + label:hover { - background: $control-bg-hover; - color: $control-color-hover; - opacity: 1; - } - button:focus, - input:focus + label { - outline: 0; + // Hover and tab focus + &.tab-focus, + &:hover { + background: $control-bg-hover; + color: $control-color-hover; + } + // Default focus + &:focus { + outline: 0; + } } + + // Hide toggle icons by default .icon-exit-fullscreen, .icon-muted, .icon-captions-on { display: none; } + + // Time display .player-time { display: inline-block; vertical-align: middle; @@ -312,7 +310,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger // Add a slash in before &::before { - content: "\2044"; + content: '\2044'; margin-right: $control-spacing; } } @@ -339,7 +337,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger transition: transform .2s .1s ease, opacity .2s .1s ease; &::after { - content: ""; + content: ''; display: block; position: absolute; left: 50%; @@ -353,14 +351,11 @@ $bp-captions-large: 768px !default; // When captions jump to the larger border-color: $controls-bg transparent transparent; } } - label:hover .player-tooltip, - input:focus + label .player-tooltip, button:hover .player-tooltip, button:focus .player-tooltip { opacity: 1; transform: translate(-50%, 0) scale(1); } - label:hover .player-tooltip, button:hover .player-tooltip { z-index: 3; } @@ -378,7 +373,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger &-buffer[value], &-played[value], - &-seek[type=range] { + &-seek[type='range'] { position: absolute; left: 0; top: 0; @@ -418,7 +413,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger // Seek control // <input[type='range']> element // Specificity is for bootstrap compatibility - &-seek[type=range] { + &-seek[type='range'] { z-index: 4; cursor: pointer; outline: 0; @@ -493,7 +488,7 @@ $bp-captions-large: 768px !default; // When captions jump to the larger // Volume control // <input[type='range']> element // Specificity is for bootstrap compatibility - &-volume[type=range] { + &-volume[type='range'] { display: inline-block; vertical-align: middle; -webkit-appearance: none; @@ -559,7 +554,6 @@ $bp-captions-large: 768px !default; // When captions jump to the larger // 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-player='mute'] + label, &-audio.ios &-controls-right { display: none; } @@ -646,15 +640,11 @@ $bp-captions-large: 768px !default; // When captions jump to the larger // Some options are hidden by default [data-player='captions'], - [data-player='captions'] + label, - [data-player='fullscreen'], - [data-player='fullscreen'] + label { + [data-player='fullscreen'] { display: none; } &.captions-enabled [data-player='captions'], - &.captions-enabled [data-player='captions'] + label, - &.fullscreen-enabled [data-player='fullscreen'], - &.fullscreen-enabled [data-player='fullscreen'] + label { + &.fullscreen-enabled [data-player='fullscreen'] { display: inline-block; } } |