Keyboard shortcuts
This commit is contained in:
parent
bea513f5dd
commit
92b9e3400b
@ -25,6 +25,7 @@ And some other changes and bug fixes:
|
||||
- Sprite is only loaded once (fixes #259)
|
||||
- Fixes for Vimeo post message bugs on source change or destroy (fixes #318)
|
||||
- Save caption state in storage (fixes #311)
|
||||
- Added keyboard shortcuts to the current focused player (with option to disable) (fixes #309)
|
||||
|
||||
## v1.8.12
|
||||
- Vimeo keyboard focus fix (Fixes #317)
|
||||
|
2
dist/plyr.css
vendored
2
dist/plyr.css
vendored
File diff suppressed because one or more lines are too long
4
dist/plyr.js
vendored
4
dist/plyr.js
vendored
File diff suppressed because one or more lines are too long
297
src/js/plyr.js
297
src/js/plyr.js
@ -1,6 +1,6 @@
|
||||
// ==========================================================================
|
||||
// Plyr
|
||||
// plyr.js v1.8.12
|
||||
// plyr.js v1.9.0
|
||||
// https://github.com/selz/plyr
|
||||
// License: The MIT License (MIT)
|
||||
// ==========================================================================
|
||||
@ -48,6 +48,7 @@
|
||||
hideControls: true,
|
||||
showPosterOnEnd: false,
|
||||
disableContextMenu: true,
|
||||
keyboardShorcuts: true,
|
||||
tooltips: {
|
||||
controls: false,
|
||||
seek: true
|
||||
@ -481,11 +482,11 @@
|
||||
}
|
||||
|
||||
// Unbind event
|
||||
function _off(element, events, callback, useCapture) {
|
||||
/*function _off(element, events, callback, useCapture) {
|
||||
if (element) {
|
||||
_toggleListener(element, events, callback, false, useCapture);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
// Trigger event
|
||||
function _triggerEvent(element, eventName, bubbles, properties) {
|
||||
@ -1155,8 +1156,8 @@
|
||||
|
||||
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;
|
||||
if (plyr.subcount > plyr.captions.length - 1) {
|
||||
plyr.subcount = plyr.captions.length - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1394,8 +1395,11 @@
|
||||
var label = config.i18n.play;
|
||||
|
||||
// If there's a media title set, use that for the label
|
||||
if (!_is.undefined(config.title) && config.title.length) {
|
||||
if (_is.string(config.title) && config.title.length) {
|
||||
label += ', ' + config.title;
|
||||
|
||||
// Set container label
|
||||
plyr.container.setAttribute('aria-label', config.title);
|
||||
}
|
||||
|
||||
// If there's a play button, set label
|
||||
@ -1614,9 +1618,10 @@
|
||||
|
||||
// When embeds are ready
|
||||
function _embedReady() {
|
||||
// Setup the UI if full support
|
||||
// Setup the UI and call ready if full support
|
||||
if (plyr.supported.full) {
|
||||
_setupInterface();
|
||||
_ready();
|
||||
}
|
||||
|
||||
// Set title
|
||||
@ -1674,6 +1679,11 @@
|
||||
// Set title
|
||||
config.title = instance.getVideoData().title;
|
||||
|
||||
// Set the tabindex
|
||||
if (plyr.supported.full) {
|
||||
plyr.media.querySelector('iframe').setAttribute('tabindex', '-1');
|
||||
}
|
||||
|
||||
// Update UI
|
||||
_embedReady();
|
||||
|
||||
@ -1817,8 +1827,6 @@
|
||||
if(_is.htmlElement(plyr.embed.element) && plyr.supported.full) {
|
||||
plyr.embed.element.setAttribute('tabindex', '-1');
|
||||
}
|
||||
|
||||
//console.log(plyr.embed);
|
||||
});
|
||||
|
||||
plyr.embed.on('play', function() {
|
||||
@ -1950,18 +1958,21 @@
|
||||
|
||||
// Toggle playback
|
||||
function _togglePlay(toggle) {
|
||||
// True toggle
|
||||
if (!_is.boolean(toggle)) {
|
||||
toggle = plyr.media.paused;
|
||||
}
|
||||
|
||||
// Play
|
||||
if (toggle === true) {
|
||||
if (toggle) {
|
||||
_play();
|
||||
}
|
||||
// Pause
|
||||
else if (toggle === false) {
|
||||
else {
|
||||
_pause();
|
||||
}
|
||||
// True toggle
|
||||
else {
|
||||
plyr.media[plyr.media.paused ? 'play' : 'pause']();
|
||||
}
|
||||
|
||||
return toggle;
|
||||
}
|
||||
|
||||
// Rewind
|
||||
@ -2097,54 +2108,44 @@
|
||||
// Check for native support
|
||||
var nativeSupport = fullscreen.supportsFullScreen;
|
||||
|
||||
// If it's a fullscreen change event, it's probably a native close
|
||||
if (event && event.type === fullscreen.fullScreenEventName) {
|
||||
plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
|
||||
}
|
||||
// If there's native support, use it
|
||||
else if (nativeSupport) {
|
||||
// Request fullscreen
|
||||
if (!fullscreen.isFullScreen(plyr.container)) {
|
||||
// Save scroll position
|
||||
_saveScrollPosition();
|
||||
|
||||
// Request full screen
|
||||
fullscreen.requestFullScreen(plyr.container);
|
||||
}
|
||||
// Bail from fullscreen
|
||||
else {
|
||||
fullscreen.cancelFullScreen();
|
||||
|
||||
if (nativeSupport) {
|
||||
// If it's a fullscreen change event, update the UI
|
||||
if (event && event.type === fullscreen.fullScreenEventName) {
|
||||
plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
|
||||
}
|
||||
// Else it's a user request to enter or exit
|
||||
else {
|
||||
// Request fullscreen
|
||||
if (!fullscreen.isFullScreen(plyr.container)) {
|
||||
// Save scroll position
|
||||
_saveScrollPosition();
|
||||
|
||||
// Check if we're actually full screen (it could fail)
|
||||
plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
|
||||
// Request full screen
|
||||
fullscreen.requestFullScreen(plyr.container);
|
||||
}
|
||||
// Bail from fullscreen
|
||||
else {
|
||||
fullscreen.cancelFullScreen();
|
||||
}
|
||||
|
||||
// Check if we're actually full screen (it could fail)
|
||||
// plyr.isFullscreen = fullscreen.isFullScreen(plyr.container);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Otherwise, it's a simple toggle
|
||||
plyr.isFullscreen = !plyr.isFullscreen;
|
||||
|
||||
// Bind/unbind escape key
|
||||
if (plyr.isFullscreen) {
|
||||
_on(document, 'keyup', _handleEscapeFullscreen);
|
||||
document.body.style.overflow = 'hidden';
|
||||
}
|
||||
else {
|
||||
_off(document, 'keyup', _handleEscapeFullscreen);
|
||||
document.body.style.overflow = '';
|
||||
}
|
||||
document.body.style.overflow = plyr.isFullscreen ? 'hidden' : '';
|
||||
}
|
||||
|
||||
// Set class hook
|
||||
_toggleClass(plyr.container, config.classes.fullscreen.active, plyr.isFullscreen);
|
||||
|
||||
// Trap focus
|
||||
if (plyr.isFullscreen) {
|
||||
plyr.container.setAttribute('tabindex', '-1');
|
||||
}
|
||||
else {
|
||||
plyr.container.removeAttribute('tabindex');
|
||||
}
|
||||
|
||||
// Trap focus
|
||||
_focusTrap(plyr.isFullscreen);
|
||||
|
||||
@ -2162,14 +2163,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Bail from faux-fullscreen
|
||||
function _handleEscapeFullscreen(event) {
|
||||
// If it's a keypress and not escape, bail
|
||||
if ((event.which || event.charCode || event.keyCode) === 27 && plyr.isFullscreen) {
|
||||
_toggleFullscreen();
|
||||
}
|
||||
}
|
||||
|
||||
// Mute
|
||||
function _toggleMute(muted) {
|
||||
// If the method is called without parameter, toggle based on current value
|
||||
@ -2263,17 +2256,25 @@
|
||||
}
|
||||
|
||||
// Increase volume
|
||||
function _increaseVolume() {
|
||||
function _increaseVolume(step) {
|
||||
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
|
||||
|
||||
_setVolume(volume + (config.volumeStep / 5));
|
||||
if (!_is.number(step)) {
|
||||
step = config.volumeStep;
|
||||
}
|
||||
|
||||
_setVolume(volume + step);
|
||||
}
|
||||
|
||||
// Decrease volume
|
||||
function _decreaseVolume() {
|
||||
function _decreaseVolume(step) {
|
||||
var volume = plyr.media.muted ? 0 : (plyr.media.volume * config.volumeMax);
|
||||
|
||||
_setVolume(volume - (config.volumeStep / 5));
|
||||
if (!_is.number(step)) {
|
||||
step = config.volumeStep;
|
||||
}
|
||||
|
||||
_setVolume(volume - step);
|
||||
}
|
||||
|
||||
// Update volume UI and storage
|
||||
@ -2689,6 +2690,9 @@
|
||||
// Cancel current network requests
|
||||
_cancelRequests();
|
||||
|
||||
// Remove ready class hook
|
||||
_toggleClass(plyr.container, config.classes.ready, false);
|
||||
|
||||
// Setup new source
|
||||
function setup() {
|
||||
// Remove embed object
|
||||
@ -2794,10 +2798,14 @@
|
||||
|
||||
// Display duration if available
|
||||
_displayDuration();
|
||||
|
||||
// Call ready
|
||||
_ready();
|
||||
}
|
||||
// If embed but not fully supported, setupInterface now
|
||||
// If embed but not fully supported, setupInterface and call ready now
|
||||
else if (_inArray(config.types.embed, plyr.type) && !plyr.supported.full) {
|
||||
_setupInterface();
|
||||
_ready();
|
||||
}
|
||||
|
||||
// Set aria title and iframe title
|
||||
@ -2823,16 +2831,8 @@
|
||||
var inputEvent = (plyr.browser.isIE ? 'change' : 'input');
|
||||
|
||||
// Click play/pause helper
|
||||
function _togglePlay() {
|
||||
var play = plyr.media.paused;
|
||||
|
||||
// Toggle playback
|
||||
if (play) {
|
||||
_play();
|
||||
}
|
||||
else {
|
||||
_pause();
|
||||
}
|
||||
function togglePlay() {
|
||||
var play = _togglePlay();
|
||||
|
||||
// Determine which buttons
|
||||
var trigger = plyr.buttons[play ? 'play' : 'pause'],
|
||||
@ -2861,16 +2861,27 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Detect tab focus
|
||||
function checkFocus() {
|
||||
// Get the focused element
|
||||
function getFocusElement() {
|
||||
var focused = document.activeElement;
|
||||
|
||||
if (!focused || focused === document.body) {
|
||||
focused = null;
|
||||
}
|
||||
else if (document.querySelector) {
|
||||
else {
|
||||
focused = document.querySelector(':focus');
|
||||
}
|
||||
|
||||
return focused;
|
||||
}
|
||||
|
||||
// Get the key code for an event
|
||||
function getKeyCode(event) {
|
||||
return event.keyCode ? event.keyCode : event.which;
|
||||
}
|
||||
|
||||
// Detect tab focus
|
||||
function checkTabFocus(focused) {
|
||||
for (var button in plyr.buttons) {
|
||||
var element = plyr.buttons[button];
|
||||
|
||||
@ -2885,11 +2896,67 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Keyboard shortcuts
|
||||
if (config.keyboardShorcuts) {
|
||||
//var held = false;
|
||||
|
||||
_on(plyr.container, 'keyup keydown', function(event) {
|
||||
var code = getKeyCode(event),
|
||||
down = event.type === 'keydown',
|
||||
first = true,
|
||||
timer;
|
||||
|
||||
function handleKey() {
|
||||
switch(code) {
|
||||
// Space and K key
|
||||
case 32:
|
||||
case 75: if (first) { _togglePlay(); } break;
|
||||
// Arrow up
|
||||
case 38: _increaseVolume(); break;
|
||||
// Arrow down
|
||||
case 40: _decreaseVolume(); break;
|
||||
// M key
|
||||
case 77: if (first) { _toggleMute(); } break;
|
||||
// Arrow forward
|
||||
case 39: _forward(); break;
|
||||
// Arrow back
|
||||
case 37: _rewind(); break;
|
||||
// F key
|
||||
case 70: if (first) { _toggleFullscreen(); } break;
|
||||
// C key
|
||||
case 67: if (first) { _toggleCaptions(); } break;
|
||||
}
|
||||
|
||||
// Escape is handle natively when in full screen
|
||||
// So we only need to worry about non native
|
||||
if (!fullscreen.supportsFullScreen && plyr.isFullscreen && code === 27) {
|
||||
_toggleFullscreen();
|
||||
}
|
||||
|
||||
// First run completed
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (down) {
|
||||
handleKey();
|
||||
|
||||
// If a key is held for 200ms, run again
|
||||
// Handy for volume and skip
|
||||
timer = setTimeout(handleKey, 200);
|
||||
}
|
||||
else {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Focus/tab management
|
||||
_on(window, 'keyup', function(event) {
|
||||
var code = (event.keyCode ? event.keyCode : event.which);
|
||||
var code = getKeyCode(event),
|
||||
focused = getFocusElement();
|
||||
|
||||
if (code === 9) {
|
||||
checkFocus();
|
||||
checkTabFocus(focused);
|
||||
}
|
||||
});
|
||||
_on(document.body, 'click', function() {
|
||||
@ -2904,10 +2971,10 @@
|
||||
}
|
||||
|
||||
// Play
|
||||
_proxyListener(plyr.buttons.play, 'click', config.listeners.play, _togglePlay);
|
||||
_proxyListener(plyr.buttons.play, 'click', config.listeners.play, togglePlay);
|
||||
|
||||
// Pause
|
||||
_proxyListener(plyr.buttons.pause, 'click', config.listeners.pause, _togglePlay);
|
||||
_proxyListener(plyr.buttons.pause, 'click', config.listeners.pause, togglePlay);
|
||||
|
||||
// Restart
|
||||
_proxyListener(plyr.buttons.restart, 'click', config.listeners.restart, _seek);
|
||||
@ -2968,25 +3035,26 @@
|
||||
|
||||
// Detect "natural" scroll - suppored on OS X Safari only
|
||||
// Other browsers on OS X will be inverted until support improves
|
||||
var inverted = event.webkitDirectionInvertedFromDevice;
|
||||
var inverted = event.webkitDirectionInvertedFromDevice,
|
||||
step = (config.volumeStep / 5);
|
||||
|
||||
// Scroll down (or up on natural) to decrease
|
||||
if (event.deltaY < 0 || event.deltaX > 0) {
|
||||
if (inverted) {
|
||||
_decreaseVolume();
|
||||
_decreaseVolume(step);
|
||||
}
|
||||
else {
|
||||
_increaseVolume();
|
||||
_increaseVolume(step);
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll up (or down on natural) to increase
|
||||
if (event.deltaY > 0 || event.deltaX < 0) {
|
||||
if (inverted) {
|
||||
_increaseVolume();
|
||||
_increaseVolume(step);
|
||||
}
|
||||
else {
|
||||
_decreaseVolume();
|
||||
_decreaseVolume(step);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -3231,6 +3299,9 @@
|
||||
// Wrap media
|
||||
plyr.container = _wrap(media, document.createElement('div'));
|
||||
|
||||
// Allow focus to be captured
|
||||
plyr.container.setAttribute('tabindex', 0);
|
||||
|
||||
// Add style hook
|
||||
_toggleStyleHook();
|
||||
|
||||
@ -3252,16 +3323,21 @@
|
||||
if (config.autoplay) {
|
||||
_play();
|
||||
}
|
||||
|
||||
// Call ready
|
||||
_ready();
|
||||
}
|
||||
// If embed but not fully supported, setupInterface now (to avoid flash of controls)
|
||||
// If embed but not fully supported, setupInterface (to avoid flash of controls) and call ready now
|
||||
else if (_inArray(config.types.embed, plyr.type) && !plyr.supported.full) {
|
||||
_setupInterface();
|
||||
_ready();
|
||||
}
|
||||
|
||||
// Successful setup
|
||||
plyr.init = true;
|
||||
}
|
||||
|
||||
// Setup the UI
|
||||
function _setupInterface() {
|
||||
// Don't setup interface if no support
|
||||
if (!plyr.supported.full) {
|
||||
@ -3321,23 +3397,9 @@
|
||||
|
||||
// Display duration
|
||||
_displayDuration();
|
||||
|
||||
// Ready event
|
||||
_triggerEvent(plyr.container, 'ready', true);
|
||||
|
||||
// Class
|
||||
_toggleClass(plyr.container, config.classes.ready, true);
|
||||
}
|
||||
|
||||
// Initialize instance
|
||||
_init();
|
||||
|
||||
// If init failed, return an empty object
|
||||
if (!plyr.init) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return {
|
||||
var api = {
|
||||
getOriginal: function() { return original; },
|
||||
getContainer: function() { return plyr.container },
|
||||
getEmbed: function() { return plyr.embed; },
|
||||
@ -3365,6 +3427,31 @@
|
||||
destroy: _destroy,
|
||||
getCurrentTime: function() { return media.currentTime; }
|
||||
};
|
||||
|
||||
// Everything done
|
||||
function _ready() {
|
||||
// Ready event
|
||||
_triggerEvent(plyr.container, 'ready', true);
|
||||
|
||||
// Set class hook on media element
|
||||
_toggleClass(plyr.media, defaults.classes.setup, true);
|
||||
|
||||
// Set container class for ready
|
||||
_toggleClass(plyr.container, config.classes.ready, true);
|
||||
|
||||
// Store a refernce to instance
|
||||
plyr.media.plyr = api;
|
||||
}
|
||||
|
||||
// Initialize instance
|
||||
_init();
|
||||
|
||||
// If init failed, return null
|
||||
if (!plyr.init) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return api;
|
||||
}
|
||||
|
||||
// Load a sprite
|
||||
@ -3544,14 +3631,6 @@
|
||||
return;
|
||||
}
|
||||
|
||||
// Set plyr to false if setup failed
|
||||
// Maybe we remove this and add a .get() function to get the instance
|
||||
// If passed a media element or container?
|
||||
media.plyr = instance;
|
||||
|
||||
// Set class hook
|
||||
_toggleClass(media, defaults.classes.setup, true);
|
||||
|
||||
// Listen for events if debugging
|
||||
if (config.debug) {
|
||||
var events = config.events.concat(['setup', 'ready', 'statechange', 'enterfullscreen', 'exitfullscreen', 'captionsenabled', 'captionsdisabled']);
|
||||
|
@ -40,6 +40,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Focus
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
// Media elements
|
||||
video,
|
||||
audio {
|
||||
|
@ -12,7 +12,7 @@
|
||||
@plyr-color-main: #3498db;
|
||||
|
||||
// Font
|
||||
@plyr-font-family: 'San Francisco', -apple-system, BlinkMacSystemFont, '.SFNSText-Regular', Avenir, 'Avenir Next', 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif;
|
||||
@plyr-font-family: Avenir, 'Avenir Next', 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif;
|
||||
@plyr-font-size-small: 14px;
|
||||
@plyr-font-size-base: 16px;
|
||||
|
||||
|
@ -40,6 +40,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Focus
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
// Media elements
|
||||
video,
|
||||
audio {
|
||||
|
@ -13,7 +13,7 @@ $plyr-sr-only-important: true !default;
|
||||
$plyr-color-main: #3498db !default;
|
||||
|
||||
// Font sizes
|
||||
$plyr-font-family: 'San Francisco', -apple-system, BlinkMacSystemFont, '.SFNSText-Regular', Avenir, 'Avenir Next', 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif !default;
|
||||
$plyr-font-family: Avenir, 'Avenir Next', 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif !default;
|
||||
$plyr-font-size-small: 14px !default;
|
||||
$plyr-font-size-base: 16px !default;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user