forked from midou/invidious
414 lines
13 KiB
JavaScript
414 lines
13 KiB
JavaScript
/*
|
|
* 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.22" };
|
|
}
|
|
|
|
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: true,
|
|
enableFullscreen: true,
|
|
enableNumbers: true,
|
|
enableJogStyle: false,
|
|
alwaysCaptureHotkeys: 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,
|
|
enableModifiersForNumbers = options.enableModifiersForNumbers,
|
|
enableInactiveFocus = options.enableInactiveFocus,
|
|
skipInitialFocus = options.skipInitialFocus;
|
|
|
|
// 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;
|
|
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 ||
|
|
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) {
|
|
// Prevent control activation with space
|
|
event.stopPropagation();
|
|
}
|
|
|
|
if (player.paused()) {
|
|
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) {
|
|
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) {
|
|
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) {
|
|
// 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');
|
|
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);
|
|
}
|
|
|
|
player.on('keydown', keyDown);
|
|
player.on('dblclick', doubleClick);
|
|
player.on('mousewheel', mouseScroll);
|
|
player.on("DOMMouseScroll", mouseScroll);
|
|
|
|
return this;
|
|
};
|
|
|
|
var registerPlugin = videojs.registerPlugin || videojs.plugin;
|
|
registerPlugin('hotkeys', hotkeys);
|
|
}));
|