1
0
mirror of https://github.com/videojs/video.js.git synced 2024-12-27 02:43:45 +02:00

feat: add hotkeys support ("m", "f", "k", and Space) (#5571)

Extend keyboard support for the SeekBar, and pass unhandled keydown events from components back to the player.
Switch from raw keycodes to the keycode module.

Using `userActions.hotkeys`, which can either be a function to match the hotkeys plugin, or an object with properties like `fullscreenKey`, see the documentation for more info.

This is currently off by default, we will consider turning it on by default in the future, see #5765.

Fixes #4048, fixes #3022.
This commit is contained in:
Owen Edwards 2019-01-25 11:10:29 -08:00 committed by Gary Katsevman
parent 9786d8a29f
commit 61053bf674
19 changed files with 2507 additions and 3183 deletions

View File

@ -35,6 +35,11 @@
* [sources](#sources)
* [techCanOverridePoster](#techcanoverrideposter)
* [techOrder](#techorder)
* [userActions](#useractions)
* [userActions.hotkeys](#useractions.hotkeys)
* [userActions.hotkeys.fullscreenKey](#useractions.hotkeys.fullscreenkey)
* [userActions.hotkeys.muteKey](#useractions.hotkeys.mutekey)
* [userActions.hotkeys.playPauseKey](#useractions.hotkeys.playpausekey)
* [vtt.js](#vttjs)
* [Component Options](#component-options)
* [children](#children-1)
@ -365,7 +370,82 @@ This can be useful when multiple techs are used and each has to set their own po
> Type: `Array`, Default: `['html5']`
Defines the order in which Video.js techs are preferred. By default, this means that the `Html5` tech is preferred. Other regisetered techs will be added after this tech in the order in which they are registered.
Defines the order in which Video.js techs are preferred. By default, this means that the `Html5` tech is preferred. Other registered techs will be added after this tech in the order in which they are registered.
### `userActions`
> Type: `Object`
### `userActions.hotkeys`
> Type: `boolean|function|object`
Controls how player-wide hotkeys operate. If set to `false`, or `undefined`, hotkeys are disabled. If set to `true` or an object (to allow definitions of `fullscreenKey` etc. below), hotkeys are enabled as described below. To override the default hotkey handling, set `userActions.hotkeys` to a function which accepts a `keydown` event:
```js
var player = videojs('my-player', {
userActions: {
hotkeys: function(event) {
// `this` is the player in this context
// `x` key = pause
if (event.which === 88) {
this.pause();
}
// `y` key = play
if (event.which === 89) {
this.play();
}
}
}
});
```
Default hotkey handling is:
| Key | Action | Enabled by |
| :-: | ------ | ---------- |
| `f` | toggle fullscreen | only enabled if a Fullscreen button is present in the Control Bar
| `m` | toggle mute | always enabled, even if no Control Bar is present
| `k` | toggle play/pause | always enabled, even if no Control Bar is present
| `Space` | toggle play/pause | always enabled, even if no Control Bar is present
Note that the `Space` key activates controls such as buttons and menus if that control has keyboard focus. The other hotkeys work regardless of which
control in the player has focus.
### `userActions.hotkeys.fullscreenKey`
> Type: `function`
Override the fullscreen key definition. If this is set, the function receives the `keydown` event; if the function returns `true`, then the fullscreen toggle action is performed.
```js
var player = videojs('my-player', {
userActions: {
hotkeys: {
muteKey: function(event) {
// disable mute key
},
fullscreenKey: function(event) {
// override fullscreen to trigger when pressing the v key
return (event.which === 86);
}
}
}
});
```
### `userActions.hotkeys.muteKey`
> Type: `function`
Override the mute key definition. If this is set, the function receives the `keydown` event; if the function returns `true`, then the mute toggle action is performed.
### `userActions.hotkeys.playPauseKey`
> Type: `function`
Override the play/pause key definition. If this is set, the function receives the `keydown` event; if the function returns `true`, then the play/pause toggle action is performed.
### `vtt.js`

5145
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -81,6 +81,7 @@
"@babel/runtime": "^7.2.0",
"@videojs/http-streaming": "1.8.0",
"global": "4.3.2",
"keycode": "^2.2.0",
"safe-json-parse": "4.0.0",
"tsml": "1.0.1",
"videojs-font": "3.1.0",

View File

@ -90,7 +90,8 @@ const externals = {
'mux.js/lib/mp4',
'mux.js/lib/tools/ts-inspector.js',
'mux.js/lib/mp4/probe',
'aes-decrypter'
'aes-decrypter',
'keycode'
]),
test: Object.keys(globals.test).concat([
])

View File

@ -0,0 +1,158 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Video.js Hotkeys - Sandbox</title>
<link href="../dist/video-js.css" rel="stylesheet" type="text/css">
<script src="../dist/video.js"></script>
<style>
.hotkeys-function { background: #FF6961; }
.hotkeys-override { background: #77DD77; }
.hotkeys-normal { background: #AEC6CF; }
.video-js {
height: 150px;
width: 300px;
}
.wrapper {
display: grid;
margin: 0 auto;
grid-gap: 10px;
grid-template-columns: 300px 300px 300px;
}
.panel > p:first-child {
border-bottom: black 1px solid;
}
</style>
</head>
<body>
<h1>Video.js Hotkeys</h1>
<p>All the various ways to configure hotkeys.</p>
<div class="wrapper">
<div class="panel hotkeys-normal">
<p>Default (no) hotkeys</p>
<video-js
id="vid0"
controls
preload="auto"
poster="//vjs.zencdn.net/v/oceans.png">
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
<source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg">
<track kind="captions" src="../docs/examples/shared/example-captions.vtt" srclang="en" label="English">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video-js>
</div>
<div class="panel hotkeys-normal">
<p>Disable hotkeys</p>
<video-js
id="vid1"
controls
preload="auto"
poster="//vjs.zencdn.net/v/oceans.png">
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
<source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg">
<track kind="captions" src="../docs/examples/shared/example-captions.vtt" srclang="en" label="English">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video-js>
</div>
<div class="panel hotkeys-normal">
<p>Enable default hotkeys (k = play/pause, m = mute/unmute, f = fullscreen)</p>
<video-js
id="vid12"
controls
preload="auto"
poster="//vjs.zencdn.net/v/oceans.png">
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
<source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg">
<track kind="captions" src="../docs/examples/shared/example-captions.vtt" srclang="en" label="English">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video-js>
</div>
<div class="panel hotkeys-function">
<p>Custom hotkey function (x = pause, y = play)</p>
<video-js
id="vid2"
controls
preload="auto"
poster="//vjs.zencdn.net/v/oceans.png">
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
<source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg">
<track kind="captions" src="../docs/examples/shared/example-captions.vtt" srclang="en" label="English">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video-js>
</div>
<div class="panel hotkeys-override">
<p>Customize specific hotkeys (z = play/pause, v = fullscreen)</p>
<video-js
id="vid3"
controls
preload="auto"
poster="//vjs.zencdn.net/v/oceans.png">
<source src="//vjs.zencdn.net/v/oceans.mp4" type="video/mp4">
<source src="//vjs.zencdn.net/v/oceans.webm" type="video/webm">
<source src="//vjs.zencdn.net/v/oceans.ogv" type="video/ogg">
<track kind="captions" src="../docs/examples/shared/example-captions.vtt" srclang="en" label="English">
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that <a href="http://videojs.com/html5-video-support/" target="_blank">supports HTML5 video</a></p>
</video-js>
</div>
</div>
<script>
var player0 = videojs('vid0', {});
// Note that we support both explicitly disbling and explicitly enabling Hotkeys
// because one day the default may change from disabled to enabled.
var player1 = videojs('vid1', {
userActions: {
hotkeys: false
}
});
var player12 = videojs('vid12', {
userActions: {
hotkeys: true
}
});
var player2 = videojs('vid2', {
userActions: {
hotkeys: function(event) {
// `this` is the player in this context
// `x` key = pause
if (event.which === 88) {
this.pause();
}
// `y` key = play
if (event.which === 89) {
this.play();
}
}
}
});
var player3 = videojs('vid3', {
userActions: {
hotkeys: {
playPauseKey: function(event) {
// override play/pause to trigger when pressing the z key
return (event.which === 90);
},
muteKey: function(event) {
// disable mute key
},
fullscreenKey: function(event) {
// override fullscreen to trigger when pressing the v key
return (event.which === 86);
}
}
}
});
</script>
</body>
</html>

View File

@ -47,6 +47,8 @@ class BigPlayButton extends Button {
// exit early if clicked via the mouse
if (this.mouseused_ && event.clientX && event.clientY) {
silencePromise(playPromise);
// call handleFocus manually to get hotkeys working
this.player_.handleFocus({});
return;
}

View File

@ -5,6 +5,7 @@ import ClickableComponent from './clickable-component.js';
import Component from './component';
import log from './utils/log.js';
import {assign} from './utils/obj';
import keycode from 'keycode';
/**
* Base class for all buttons.
@ -104,14 +105,12 @@ class Button extends ClickableComponent {
* @listens keydown
*/
handleKeyPress(event) {
// Ignore Space or Enter key operation, which is handled by the browser for a button.
if (!(keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter'))) {
// Ignore Space (32) or Enter (13) key operation, which is handled by the browser for a button.
if (event.which === 32 || event.which === 13) {
return;
// Pass keypress handling up for unsupported keys
super.handleKeyPress(event);
}
// Pass keypress handling up for unsupported keys
super.handleKeyPress(event);
}
}

View File

@ -8,6 +8,7 @@ import * as Fn from './utils/fn.js';
import log from './utils/log.js';
import document from 'global/document';
import {assign} from './utils/obj';
import keycode from 'keycode';
/**
* Clickable Component which is clickable or keyboard actionable,
@ -224,12 +225,11 @@ class ClickableComponent extends Component {
* @listens keydown
*/
handleKeyPress(event) {
// Support Space (32) or Enter (13) key operation to fire a click event
if (event.which === 32 || event.which === 13) {
// Support Space or Enter key operation to fire a click event
if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
event.preventDefault();
this.trigger('click');
} else if (super.handleKeyPress) {
} else {
// Pass keypress handling up for unsupported keys
super.handleKeyPress(event);

View File

@ -36,6 +36,21 @@ class CloseButton extends Button {
return `vjs-close-button ${super.buildCSSClass()}`;
}
/**
* This gets called when a `CloseButton` has focus and `keydown` is triggered via a key
* press.
*
* @param {EventTarget~Event} event
* The event that caused this function to get called.
*
* @listens keydown
*/
handleKeyPress(event) {
// Override the default `Button` behavior, and don't pass the keypress event
// up to the player because this button is part of a `ModalDialog`, which
// doesn't pass keypresses to the player either.
}
/**
* This gets called when a `CloseButton` gets clicked. See
* {@link ClickableComponent#handleClick} for more information on when this will be

View File

@ -1077,6 +1077,19 @@ class Component {
this.el_.blur();
}
/**
* When this Component receives a keydown event which it does not process,
* it passes the event to the Player for handling.
*
* @param {EventTarget~Event} event
* The `keydown` event that caused this function to be called.
*/
handleKeyPress(event) {
if (this.player_) {
this.player_.handleKeyPress(event);
}
}
/**
* Emit a 'tap' events when touch event support gets detected. This gets used to
* support toggling the controls through a tap on the video. They get enabled

View File

@ -8,6 +8,7 @@ import * as Dom from '../../utils/dom.js';
import * as Fn from '../../utils/fn.js';
import formatTime from '../../utils/format-time.js';
import {silencePromise} from '../../utils/promise';
import keycode from 'keycode';
import './load-progress-bar.js';
import './play-progress-bar.js';
@ -16,6 +17,9 @@ import './mouse-time-display.js';
// The number of seconds the `step*` functions move the timeline.
const STEP_SECONDS = 5;
// The multiplier of STEP_SECONDS that PgUp/PgDown move the timeline.
const PAGE_KEY_MULTIPLIER = 12;
// The interval at which the bar should update as it progresses.
const UPDATE_REFRESH_INTERVAL = 30;
@ -362,8 +366,15 @@ class SeekBar extends Slider {
}
/**
* Called when this SeekBar has focus and a key gets pressed down. By
* default it will call `this.handleAction` when the key is space or enter.
* Called when this SeekBar has focus and a key gets pressed down.
* Supports the following keys:
*
* Space or Enter key fire a click event
* Home key moves to start of the timeline
* End key moves to end of the timeline
* Digit "0" through "9" keys move to 0%, 10% ... 80%, 90% of the timeline
* PageDown key moves back a larger step than ArrowDown
* PageUp key moves forward a large step
*
* @param {EventTarget~Event} event
* The `keydown` event that caused this function to be called.
@ -371,13 +382,27 @@ class SeekBar extends Slider {
* @listens keydown
*/
handleKeyPress(event) {
// Support Space (32) or Enter (13) key operation to fire a click event
if (event.which === 32 || event.which === 13) {
if (keycode.isEventKey(event, 'Space') || keycode.isEventKey(event, 'Enter')) {
event.preventDefault();
this.handleAction(event);
} else if (super.handleKeyPress) {
} else if (keycode.isEventKey(event, 'Home')) {
event.preventDefault();
this.player_.currentTime(0);
} else if (keycode.isEventKey(event, 'End')) {
event.preventDefault();
this.player_.currentTime(this.player_.duration());
} else if (/^[0-9]$/.test(keycode(event))) {
event.preventDefault();
const gotoFraction = (keycode.codes[keycode(event)] - keycode.codes['0']) * 10.0 / 100.0;
this.player_.currentTime(this.player_.duration() * gotoFraction);
} else if (keycode.isEventKey(event, 'PgDn')) {
event.preventDefault();
this.player_.currentTime(this.player_.currentTime() - (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
} else if (keycode.isEventKey(event, 'PgUp')) {
event.preventDefault();
this.player_.currentTime(this.player_.currentTime() + (STEP_SECONDS * PAGE_KEY_MULTIPLIER));
} else {
// Pass keypress handling up for unsupported keys
super.handleKeyPress(event);
}

View File

@ -10,6 +10,7 @@ import * as Events from '../utils/events.js';
import toTitleCase from '../utils/to-title-case.js';
import { IS_IOS } from '../utils/browser.js';
import document from 'global/document';
import keycode from 'keycode';
/**
* A `MenuButton` class for any popup {@link Menu}.
@ -282,24 +283,28 @@ class MenuButton extends Component {
* @listens keydown
*/
handleKeyPress(event) {
// Escape (27) key or Tab (9) key unpress the 'button'
if (event.which === 27 || event.which === 9) {
// Escape or Tab unpress the 'button'
if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
if (this.buttonPressed_) {
this.unpressButton();
}
// Don't preventDefault for Tab key - we still want to lose focus
if (event.which !== 9) {
if (!keycode.isEventKey(event, 'Tab')) {
event.preventDefault();
// Set focus back to the menu button's button
this.menuButton_.el_.focus();
this.menuButton_.focus();
}
// Enter (13) or Up (38) key or Down (40) key press the 'button'
} else if (event.which === 13 || event.which === 38 || event.which === 40) {
// Up Arrow or Down Arrow also 'press' the button to open the menu
} else if (keycode.isEventKey(event, 'Up') || keycode.isEventKey(event, 'Down')) {
if (!this.buttonPressed_) {
this.pressButton();
event.preventDefault();
this.pressButton();
}
} else {
// NOTE: This is a special case where we don't pass unhandled
// keypress events up to the Component handler, because it is
// just entending the keypress handling of the actual `Button`
// in the `MenuButton` which already passes unused keys up.
}
}
@ -313,18 +318,22 @@ class MenuButton extends Component {
* @listens keydown
*/
handleSubmenuKeyPress(event) {
// Escape (27) key or Tab (9) key unpress the 'button'
if (event.which === 27 || event.which === 9) {
// Escape or Tab unpress the 'button'
if (keycode.isEventKey(event, 'Esc') || keycode.isEventKey(event, 'Tab')) {
if (this.buttonPressed_) {
this.unpressButton();
}
// Don't preventDefault for Tab key - we still want to lose focus
if (event.which !== 9) {
if (!keycode.isEventKey(event, 'Tab')) {
event.preventDefault();
// Set focus back to the menu button's button
this.menuButton_.el_.focus();
this.menuButton_.focus();
}
} else {
// NOTE: This is a special case where we don't pass unhandled
// keypress events up to the Component handler, because it is
// just entending the keypress handling of the `MenuItem`
// in the `Menu` which already passes unused keys up.
}
}

View File

@ -4,6 +4,8 @@
import ClickableComponent from '../clickable-component.js';
import Component from '../component.js';
import {assign} from '../utils/obj';
import {MenuKeys} from './menu-keys.js';
import keycode from 'keycode';
/**
* The component for a menu item. `<li>`
@ -68,6 +70,22 @@ class MenuItem extends ClickableComponent {
}, props), attrs);
}
/**
* Ignore keys which are used by the menu, but pass any other ones up. See
* {@link ClickableComponent#handleKeyPress} for instances where this is called.
*
* @param {EventTarget~Event} event
* The `keydown` event that caused this function to be called.
*
* @listens keydown
*/
handleKeyPress(event) {
if (!MenuKeys.some((key) => keycode.isEventKey(event, key))) {
// Pass keypress handling up for unused keys
super.handleKeyPress(event);
}
}
/**
* Any click on a `MenuItem` puts it into the selected state.
* See {@link ClickableComponent#handleClick} for instances where this is called.

19
src/js/menu/menu-keys.js Normal file
View File

@ -0,0 +1,19 @@
/**
* @file menu-keys.js
*/
/**
* All keys used for operation of a menu (`MenuButton`, `Menu`, and `MenuItem`)
* Note that 'Enter' and 'Space' are not included here (otherwise they would
* prevent the `MenuButton` and `MenuItem` from being keyboard-clickable)
* @typedef MenuKeys
* @array
*/
export const MenuKeys = [
'Tab',
'Esc',
'Up',
'Down',
'Right',
'Left'
];

View File

@ -6,6 +6,7 @@ import document from 'global/document';
import * as Dom from '../utils/dom.js';
import * as Fn from '../utils/fn.js';
import * as Events from '../utils/events.js';
import keycode from 'keycode';
/**
* The Menu component is used to build popup menus, including subtitle and
@ -132,14 +133,20 @@ class Menu extends Component {
*/
handleKeyPress(event) {
// Left and Down Arrows
if (event.which === 37 || event.which === 40) {
if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
event.preventDefault();
this.stepForward();
// Up and Right Arrows
} else if (event.which === 38 || event.which === 39) {
} else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
event.preventDefault();
this.stepBack();
} else {
// NOTE: This is a special case where we don't pass unhandled
// keypress events up to the Component handler, because this
// is just adding a keypress handler on top of the MenuItem's
// existing keypress handler, which already handles passing keypress
// events up.
}
}

View File

@ -6,9 +6,9 @@ import * as Fn from './utils/fn';
import Component from './component';
import window from 'global/window';
import document from 'global/document';
import keycode from 'keycode';
const MODAL_CLASS_NAME = 'vjs-modal-dialog';
const ESC = 27;
/**
* The `ModalDialog` displays over the video and its controls, which blocks
@ -119,13 +119,13 @@ class ModalDialog extends Component {
* Handles `keydown` events on the document, looking for ESC, which closes
* the modal.
*
* @param {EventTarget~Event} e
* @param {EventTarget~Event} event
* The keypress that triggered this event.
*
* @listens keydown
*/
handleKeyPress(e) {
if (e.which === ESC && this.closeable()) {
handleKeyPress(event) {
if (keycode.isEventKey(event, 'Escape') && this.closeable()) {
this.close();
}
}
@ -466,7 +466,7 @@ class ModalDialog extends Component {
*/
handleKeyDown(event) {
// exit early if it isn't a tab key
if (event.which !== 9) {
if (!keycode.isEventKey(event, 'Tab')) {
return;
}

View File

@ -34,6 +34,7 @@ import * as middleware from './tech/middleware.js';
import {ALL as TRACK_TYPES} from './tracks/track-types';
import filterSource from './utils/filter-source';
import {getMimetype, findMimetype} from './utils/mimetypes';
import keycode from 'keycode';
// The following imports are used only to ensure that the corresponding modules
// are always included in the video.js package. Importing the modules will
@ -519,6 +520,8 @@ class Player extends Component {
this.reportUserActivity();
this.one('play', this.listenForUserActivity_);
this.on('focus', this.handleFocus);
this.on('blur', this.handleBlur);
this.on('stageclick', this.handleStageClick_);
this.breakpoints(this.options_.breakpoints);
@ -2675,7 +2678,7 @@ class Player extends Component {
* Event to check for key press
*/
fullWindowOnEscKey(event) {
if (event.keyCode === 27) {
if (keycode.isEventKey(event, 'Esc')) {
if (this.isFullscreen() === true) {
this.exitFullscreen();
} else {
@ -2708,6 +2711,111 @@ class Player extends Component {
this.trigger('exitFullWindow');
}
/**
* This gets called when a `Player` gains focus via a `focus` event.
* Turns on listening for `keydown` events. When they happen it
* calls `this.handleKeyPress`.
*
* @param {EventTarget~Event} event
* The `focus` event that caused this function to be called.
*
* @listens focus
*/
handleFocus(event) {
// call off first to make sure we don't keep adding keydown handlers
Events.off(document, 'keydown', Fn.bind(this, this.handleKeyPress));
Events.on(document, 'keydown', Fn.bind(this, this.handleKeyPress));
}
/**
* Called when a `Player` loses focus. Turns off the listener for
* `keydown` events. Which Stops `this.handleKeyPress` from getting called.
*
* @param {EventTarget~Event} event
* The `blur` event that caused this function to be called.
*
* @listens blur
*/
handleBlur(event) {
Events.off(document, 'keydown', Fn.bind(this, this.handleKeyPress));
}
/**
* Called when this Player has focus and a key gets pressed down, or when
* any Component of this player receives a key press that it doesn't handle.
* This allows player-wide hotkeys (either as defined below, or optionally
* by an external function).
*
* @param {EventTarget~Event} event
* The `keydown` event that caused this function to be called.
*
* @listens keydown
*/
handleKeyPress(event) {
if (this.options_.userActions && this.options_.userActions.hotkeys && (this.options_.userActions.hotkeys !== false)) {
if (typeof this.options_.userActions.hotkeys === 'function') {
this.options_.userActions.hotkeys.call(this, event);
} else {
this.handleHotkeys(event);
}
}
}
/**
* Called when this Player receives a hotkey keydown event.
* Supported player-wide hotkeys are:
*
* f - toggle fullscreen
* m - toggle mute
* k or Space - toggle play/pause
*
* @param {EventTarget~Event} event
* The `keydown` event that caused this function to be called.
*/
handleHotkeys(event) {
const hotkeys = this.options_.userActions ? this.options_.userActions.hotkeys : {};
// set fullscreenKey, muteKey, playPauseKey from `hotkeys`, use defaults if not set
const {
fullscreenKey = keydownEvent => keycode.isEventKey(keydownEvent, 'f'),
muteKey = keydownEvent => keycode.isEventKey(keydownEvent, 'm'),
playPauseKey = keydownEvent => (keycode.isEventKey(keydownEvent, 'k') || keycode.isEventKey(keydownEvent, 'Space'))
} = hotkeys;
if (fullscreenKey.call(this, event)) {
event.preventDefault();
const FSToggle = Component.getComponent('FullscreenToggle');
if (document[FullscreenApi.fullscreenEnabled] !== false) {
FSToggle.prototype.handleClick.call(this);
}
} else if (muteKey.call(this, event)) {
event.preventDefault();
const MuteToggle = Component.getComponent('MuteToggle');
MuteToggle.prototype.handleClick.call(this);
} else if (playPauseKey.call(this, event)) {
event.preventDefault();
const PlayToggle = Component.getComponent('PlayToggle');
PlayToggle.prototype.handleClick.call(this);
}
}
/**
* Check whether the player can play a given mimetype
*

View File

@ -117,6 +117,9 @@ class PosterImage extends ClickableComponent {
} else {
this.player_.pause();
}
// call handleFocus manually to get hotkeys working
this.player_.handleFocus({});
}
}

View File

@ -5,6 +5,7 @@ import Component from '../component.js';
import * as Dom from '../utils/dom.js';
import {assign} from '../utils/obj';
import {IS_CHROME} from '../utils/browser.js';
import keycode from 'keycode';
/**
* The base functionality for a slider. Can be vertical or horizontal.
@ -316,14 +317,18 @@ class Slider extends Component {
*/
handleKeyPress(event) {
// Left and Down Arrows
if (event.which === 37 || event.which === 40) {
if (keycode.isEventKey(event, 'Left') || keycode.isEventKey(event, 'Down')) {
event.preventDefault();
this.stepBack();
// Up and Right Arrows
} else if (event.which === 38 || event.which === 39) {
} else if (keycode.isEventKey(event, 'Right') || keycode.isEventKey(event, 'Up')) {
event.preventDefault();
this.stepForward();
} else {
// Pass keypress handling up for unsupported keys
super.handleKeyPress(event);
}
}