You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

439 lines
14 KiB

/*
* Video.js Hotkeys
* https://github.com/ctd1500/videojs-hotkeys
*
* Copyright (c) 2015 Chris Dougherty
* Licensed under the Apache-2.0 license.
*/
;(function(root, factory) {
if (typeof window !== 'undefined' && window.videojs) {
factory(window.videojs);
} else if (typeof define === 'function' && define.amd) {
define('videojs-hotkeys', ['video.js'], function (module) {
return factory(module.default || module);
});
} else if (typeof module !== 'undefined' && module.exports) {
module.exports = factory(require('video.js'));
}
}(this, function (videojs) {
"use strict";
if (typeof window !== 'undefined') {
window['videojs_hotkeys'] = { version: "0.2.27" };
}
var hotkeys = function(options) {
var player = this;
var pEl = player.el();
var doc = document;
var def_options = {
volumeStep: 0.1,
seekStep: 5,
enableMute: true,
enableVolumeScroll: true,
enableHoverScroll: false,
enableFullscreen: true,
enableNumbers: true,
enableJogStyle: false,
alwaysCaptureHotkeys: false,
captureDocumentHotkeys: false,
documentHotkeysFocusElementFilter: function () { return false },
enableModifiersForNumbers: true,
enableInactiveFocus: true,
skipInitialFocus: false,
playPauseKey: playPauseKey,
rewindKey: rewindKey,
forwardKey: forwardKey,
volumeUpKey: volumeUpKey,
volumeDownKey: volumeDownKey,
muteKey: muteKey,
fullscreenKey: fullscreenKey,
customKeys: {}
};
var cPlay = 1,
cRewind = 2,
cForward = 3,
cVolumeUp = 4,
cVolumeDown = 5,
cMute = 6,
cFullscreen = 7;
// Use built-in merge function from Video.js v5.0+ or v4.4.0+
var mergeOptions = videojs.mergeOptions || videojs.util.mergeOptions;
options = mergeOptions(def_options, options || {});
var volumeStep = options.volumeStep,
seekStep = options.seekStep,
enableMute = options.enableMute,
enableVolumeScroll = options.enableVolumeScroll,
enableHoverScroll = options.enableHoverScroll,
enableFull = options.enableFullscreen,
enableNumbers = options.enableNumbers,
enableJogStyle = options.enableJogStyle,
alwaysCaptureHotkeys = options.alwaysCaptureHotkeys,
captureDocumentHotkeys = options.captureDocumentHotkeys,
documentHotkeysFocusElementFilter = options.documentHotkeysFocusElementFilter,
enableModifiersForNumbers = options.enableModifiersForNumbers,
enableInactiveFocus = options.enableInactiveFocus,
skipInitialFocus = options.skipInitialFocus;
var videojsVer = videojs.VERSION;
// Set default player tabindex to handle keydown and doubleclick events
if (!pEl.hasAttribute('tabIndex')) {
pEl.setAttribute('tabIndex', '-1');
}
// Remove player outline to fix video performance issue
pEl.style.outline = "none";
if (alwaysCaptureHotkeys || !player.autoplay()) {
if (!skipInitialFocus) {
player.one('play', function() {
pEl.focus(); // Fixes the .vjs-big-play-button handing focus back to body instead of the player
});
}
}
if (enableInactiveFocus) {
player.on('userinactive', function() {
// When the control bar fades, re-apply focus to the player if last focus was a control button
var cancelFocusingPlayer = function() {
clearTimeout(focusingPlayerTimeout);
};
var focusingPlayerTimeout = setTimeout(function() {
player.off('useractive', cancelFocusingPlayer);
var activeElement = doc.activeElement;
var controlBar = pEl.querySelector('.vjs-control-bar');
if (activeElement && activeElement.parentElement == controlBar) {
pEl.focus();
}
}, 10);
player.one('useractive', cancelFocusingPlayer);
});
}
player.on('play', function() {
// Fix allowing the YouTube plugin to have hotkey support.
var ifblocker = pEl.querySelector('.iframeblocker');
if (ifblocker && ifblocker.style.display === '') {
ifblocker.style.display = "block";
ifblocker.style.bottom = "39px";
}
});
var keyDown = function keyDown(event) {
var ewhich = event.which, wasPlaying, seekTime;
var ePreventDefault = event.preventDefault.bind(event);
var duration = player.duration();
// When controls are disabled, hotkeys will be disabled as well
if (player.controls()) {
// Don't catch keys if any control buttons are focused, unless alwaysCaptureHotkeys is true
var activeEl = doc.activeElement;
if (
alwaysCaptureHotkeys ||
(captureDocumentHotkeys && documentHotkeysFocusElementFilter(activeEl)) ||
activeEl == pEl ||
activeEl == pEl.querySelector('.vjs-tech') ||
activeEl == pEl.querySelector('.vjs-control-bar') ||
activeEl == pEl.querySelector('.iframeblocker')
) {
switch (checkKeys(event, player)) {
// Spacebar toggles play/pause
case cPlay:
ePreventDefault();
if (alwaysCaptureHotkeys || captureDocumentHotkeys) {
// Prevent control activation with space
event.stopPropagation();
}
if (player.paused()) {
silencePromise(player.play());
} else {
player.pause();
}
break;
// Seeking with the left/right arrow keys
case cRewind: // Seek Backward
wasPlaying = !player.paused();
ePreventDefault();
if (wasPlaying) {
player.pause();
}
seekTime = player.currentTime() - seekStepD(event);
// The flash player tech will allow you to seek into negative
// numbers and break the seekbar, so try to prevent that.
if (seekTime <= 0) {
seekTime = 0;
}
player.currentTime(seekTime);
if (wasPlaying) {
silencePromise(player.play());
}
break;
case cForward: // Seek Forward
wasPlaying = !player.paused();
ePreventDefault();
if (wasPlaying) {
player.pause();
}
seekTime = player.currentTime() + seekStepD(event);
// Fixes the player not sending the end event if you
// try to seek past the duration on the seekbar.
if (seekTime >= duration) {
seekTime = wasPlaying ? duration - .001 : duration;
}
player.currentTime(seekTime);
if (wasPlaying) {
silencePromise(player.play());
}
break;
// Volume control with the up/down arrow keys
case cVolumeDown:
ePreventDefault();
if (!enableJogStyle) {
player.volume(player.volume() - volumeStep);
} else {
seekTime = player.currentTime() - 1;
if (player.currentTime() <= 1) {
seekTime = 0;
}
player.currentTime(seekTime);
}
break;
case cVolumeUp:
ePreventDefault();
if (!enableJogStyle) {
player.volume(player.volume() + volumeStep);
} else {
seekTime = player.currentTime() + 1;
if (seekTime >= duration) {
seekTime = duration;
}
player.currentTime(seekTime);
}
break;
// Toggle Mute with the M key
case cMute:
if (enableMute) {
player.muted(!player.muted());
}
break;
// Toggle Fullscreen with the F key
case cFullscreen:
if (enableFull) {
if (player.isFullscreen()) {
player.exitFullscreen();
} else {
player.requestFullscreen();
}
}
break;
default:
// Number keys from 0-9 skip to a percentage of the video. 0 is 0% and 9 is 90%
if ((ewhich > 47 && ewhich < 59) || (ewhich > 95 && ewhich < 106)) {
// Do not handle if enableModifiersForNumbers set to false and keys are Ctrl, Cmd or Alt
if (enableModifiersForNumbers || !(event.metaKey || event.ctrlKey || event.altKey)) {
if (enableNumbers) {
var sub = 48;
if (ewhich > 95) {
sub = 96;
}
var number = ewhich - sub;
ePreventDefault();
player.currentTime(player.duration() * number * 0.1);
}
}
}
// Handle any custom hotkeys
for (var customKey in options.customKeys) {
var customHotkey = options.customKeys[customKey];
// Check for well formed custom keys
if (customHotkey && customHotkey.key && customHotkey.handler) {
// Check if the custom key's condition matches
if (customHotkey.key(event)) {
ePreventDefault();
customHotkey.handler(player, options, event);
}
}
}
}
}
}
};
var doubleClick = function doubleClick(event) {
// Video.js added double-click fullscreen in 7.1.0
if (videojsVer != null && videojsVer <= "7.1.0") {
// When controls are disabled, hotkeys will be disabled as well
if (player.controls()) {
// Don't catch clicks if any control buttons are focused
var activeEl = event.relatedTarget || event.toElement || doc.activeElement;
if (activeEl == pEl ||
activeEl == pEl.querySelector('.vjs-tech') ||
activeEl == pEl.querySelector('.iframeblocker')) {
if (enableFull) {
if (player.isFullscreen()) {
player.exitFullscreen();
} else {
player.requestFullscreen();
}
}
}
}
}
};
var volumeHover = false;
var volumeSelector = pEl.querySelector('.vjs-volume-menu-button') || pEl.querySelector('.vjs-volume-panel');
if (volumeSelector != null) {
volumeSelector.onmouseover = function() { volumeHover = true; };
volumeSelector.onmouseout = function() { volumeHover = false; };
}
var mouseScroll = function mouseScroll(event) {
if (enableHoverScroll) {
// If we leave this undefined then it can match non-existent elements below
var activeEl = 0;
} else {
var activeEl = doc.activeElement;
}
// When controls are disabled, hotkeys will be disabled as well
if (player.controls()) {
if (alwaysCaptureHotkeys ||
activeEl == pEl ||
activeEl == pEl.querySelector('.vjs-tech') ||
activeEl == pEl.querySelector('.iframeblocker') ||
activeEl == pEl.querySelector('.vjs-control-bar') ||
volumeHover) {
if (enableVolumeScroll) {
event = window.event || event;
var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail)));
event.preventDefault();
if (delta == 1) {
player.volume(player.volume() + volumeStep);
} else if (delta == -1) {
player.volume(player.volume() - volumeStep);
}
}
}
}
};
var checkKeys = function checkKeys(e, player) {
// Allow some modularity in defining custom hotkeys
// Play/Pause check
if (options.playPauseKey(e, player)) {
return cPlay;
}
// Seek Backward check
if (options.rewindKey(e, player)) {
return cRewind;
}
// Seek Forward check
if (options.forwardKey(e, player)) {
return cForward;
}
// Volume Up check
if (options.volumeUpKey(e, player)) {
return cVolumeUp;
}
// Volume Down check
if (options.volumeDownKey(e, player)) {
return cVolumeDown;
}
// Mute check
if (options.muteKey(e, player)) {
return cMute;
}
// Fullscreen check
if (options.fullscreenKey(e, player)) {
return cFullscreen;
}
};
function playPauseKey(e) {
// Space bar or MediaPlayPause
return (e.which === 32 || e.which === 179);
}
function rewindKey(e) {
// Left Arrow or MediaRewind
return (e.which === 37 || e.which === 177);
}
function forwardKey(e) {
// Right Arrow or MediaForward
return (e.which === 39 || e.which === 176);
}
function volumeUpKey(e) {
// Up Arrow
return (e.which === 38);
}
function volumeDownKey(e) {
// Down Arrow
return (e.which === 40);
}
function muteKey(e) {
// M key
return (e.which === 77);
}
function fullscreenKey(e) {
// F key
return (e.which === 70);
}
function seekStepD(e) {
// SeekStep caller, returns an int, or a function returning an int
return (typeof seekStep === "function" ? seekStep(e) : seekStep);
}
function silencePromise(value) {
if (value != null && typeof value.then === 'function') {
value.then(null, function(e) {});
}
}
player.on('keydown', keyDown);
player.on('dblclick', doubleClick);
player.on('mousewheel', mouseScroll);
player.on("DOMMouseScroll", mouseScroll);
if (captureDocumentHotkeys) {
document.addEventListener('keydown', function (event) { keyDown(event) });
}
return this;
};
var registerPlugin = videojs.registerPlugin || videojs.plugin;
registerPlugin('hotkeys', hotkeys);
}));