diff options
Diffstat (limited to 'assets/js/player.js')
| -rw-r--r-- | assets/js/player.js | 332 |
1 files changed, 269 insertions, 63 deletions
diff --git a/assets/js/player.js b/assets/js/player.js index 25cbb18b..4a61258c 100644 --- a/assets/js/player.js +++ b/assets/js/player.js @@ -38,69 +38,7 @@ var shareOptions = { embedCode: "<iframe id='ivplayer' type='text/html' width='640' height='360' src='" + embed_url + "' frameborder='0'></iframe>" } -var player = videojs('player', options, function () { - this.hotkeys({ - volumeStep: 0.1, - seekStep: 5, - enableModifiersForNumbers: false, - enableHoverScroll: true, - customKeys: { - // Toggle play with K Key - play: { - key: function (e) { - return e.which === 75; - }, - handler: function (player, options, e) { - if (player.paused()) { - player.play(); - } else { - player.pause(); - } - } - }, - // Go backward 10 seconds - backward: { - key: function (e) { - return e.which === 74; - }, - handler: function (player, options, e) { - player.currentTime(player.currentTime() - 10); - } - }, - // Go forward 10 seconds - forward: { - key: function (e) { - return e.which === 76; - }, - handler: function (player, options, e) { - player.currentTime(player.currentTime() + 10); - } - }, - // Increase speed - increase_speed: { - key: function (e) { - return (e.which === 190 && e.shiftKey); - }, - handler: function (player, _, e) { - size = options.playbackRates.length; - index = options.playbackRates.indexOf(player.playbackRate()); - player.playbackRate(options.playbackRates[(index + 1) % size]); - } - }, - // Decrease speed - decrease_speed: { - key: function (e) { - return (e.which === 188 && e.shiftKey); - }, - handler: function (player, _, e) { - size = options.playbackRates.length; - index = options.playbackRates.indexOf(player.playbackRate()); - player.playbackRate(options.playbackRates[(size + index - 1) % size]); - } - } - } - }); -}); +var player = videojs('player', options); if (location.pathname.startsWith('/embed/')) { player.overlay({ @@ -254,5 +192,273 @@ if (!video_data.params.listen && video_data.params.annotations) { xhr.send(); } +function increase_volume(delta) { + const curVolume = player.volume(); + let newVolume = curVolume + delta; + if (newVolume > 1) { + newVolume = 1; + } else if (newVolume < 0) { + newVolume = 0; + } + player.volume(newVolume); +} + +function toggle_muted() { + const isMuted = player.muted(); + player.muted(!isMuted); +} + +function skip_seconds(delta) { + const duration = player.duration(); + const curTime = player.currentTime(); + let newTime = curTime + delta; + if (newTime > duration) { + newTime = duration; + } else if (newTime < 0) { + newTime = 0; + } + player.currentTime(newTime); +} + +function set_time_percent(percent) { + const duration = player.duration(); + const newTime = duration * (percent / 100); + player.currentTime(newTime); +} + +function toggle_play() { + if (player.paused()) { + player.play(); + } else { + player.pause(); + } +} + +const toggle_captions = (function() { + let toggledTrack = null; + const onChange = function(e) { + toggledTrack = null; + }; + const bindChange = function(onOrOff) { + player.textTracks()[onOrOff]('change', onChange); + }; + // Wrapper function to ignore our own emitted events and only listen + // to events emitted by Video.js on click on the captions menu items. + const setMode = function(track, mode) { + bindChange('off'); + track.mode = mode; + window.setTimeout(function() { + bindChange('on'); + }, 0); + }; + bindChange('on'); + return function() { + if (toggledTrack !== null) { + if (toggledTrack.mode !== 'showing') { + setMode(toggledTrack, 'showing'); + } else { + setMode(toggledTrack, 'disabled'); + } + toggledTrack = null; + return; + } + + // Used as a fallback if no captions are currently active. + // TODO: Make this more intelligent by e.g. relying on browser language. + let fallbackCaptionsTrack = null; + + const tracks = player.textTracks(); + for (let i = 0; i < tracks.length; i++) { + const track = tracks[i]; + if (track.kind !== 'captions') { + continue; + } + + if (fallbackCaptionsTrack === null) { + fallbackCaptionsTrack = track; + } + if (track.mode === 'showing') { + setMode(track, 'disabled'); + toggledTrack = track; + return; + } + } + + // Fallback if no captions are currently active. + if (fallbackCaptionsTrack !== null) { + setMode(fallbackCaptionsTrack, 'showing'); + toggledTrack = fallbackCaptionsTrack; + } + }; +})(); + +function toggle_fullscreen() { + if (player.isFullscreen()) { + player.exitFullscreen(); + } else { + player.requestFullscreen(); + } +} + +function increase_playback_rate(steps) { + const maxIndex = options.playbackRates.length - 1; + const curIndex = options.playbackRates.indexOf(player.playbackRate()); + let newIndex = curIndex + steps; + if (newIndex > maxIndex) { + newIndex = maxIndex; + } else if (newIndex < 0) { + newIndex = 0; + } + player.playbackRate(options.playbackRates[newIndex]); +} + +window.addEventListener('keydown', e => { + if (e.target.tagName.toLowerCase() === 'input') { + // Ignore input when focus is on certain elements, e.g. form fields. + return; + } + // See https://github.com/ctd1500/videojs-hotkeys/blob/bb4a158b2e214ccab87c2e7b95f42bc45c6bfd87/videojs.hotkeys.js#L310-L313 + const isPlayerFocused = false + || e.target === document.querySelector('.video-js') + || e.target === document.querySelector('.vjs-tech') + || e.target === document.querySelector('.iframeblocker') + || e.target === document.querySelector('.vjs-control-bar') + ; + let action = null; + + const code = e.keyCode; + const key = e.key; + switch (key) { + case ' ': + case 'k': + action = toggle_play; + break; + + case 'ArrowUp': + if (isPlayerFocused) { + action = increase_volume.bind(this, 0.1); + } + break; + case 'ArrowDown': + if (isPlayerFocused) { + action = increase_volume.bind(this, -0.1); + } + break; + + case 'm': + action = toggle_muted; + break; + + case 'ArrowRight': + action = skip_seconds.bind(this, 5); + break; + case 'ArrowLeft': + action = skip_seconds.bind(this, -5); + break; + case 'l': + action = skip_seconds.bind(this, 10); + break; + case 'j': + action = skip_seconds.bind(this, -10); + break; + + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + const percent = (code - 48) * 10; + action = set_time_percent.bind(this, percent); + break; + + case 'c': + action = toggle_captions; + break; + case 'f': + action = toggle_fullscreen; + break; + + case 'N': + action = next_video; + break; + case 'P': + // TODO: Add support to play back previous video. + break; + + case '.': + // TODO: Add support for next-frame-stepping. + break; + case ',': + // TODO: Add support for previous-frame-stepping. + break; + + case '>': + action = increase_playback_rate.bind(this, 1); + break; + case '<': + action = increase_playback_rate.bind(this, -1); + break; + + default: + console.info('Unhandled key down event: %s:', key, e); + break; + } + + if (action) { + e.preventDefault(); + action(); + } +}, false); + +// Add support for controlling the player volume by scrolling over it. Adapted from +// https://github.com/ctd1500/videojs-hotkeys/blob/bb4a158b2e214ccab87c2e7b95f42bc45c6bfd87/videojs.hotkeys.js#L292-L328 +(function() { + const volumeStep = 0.05; + const enableVolumeScroll = true; + const enableHoverScroll = true; + const doc = document; + const pEl = document.getElementById('player'); + + 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) { + var activeEl = doc.activeElement; + if (enableHoverScroll) { + // If we leave this undefined then it can match non-existent elements below + activeEl = 0; + } + + // When controls are disabled, hotkeys will be disabled as well + if (player.controls()) { + if (volumeHover) { + if (enableVolumeScroll) { + event = window.event || event; + var delta = Math.max(-1, Math.min(1, (event.wheelDelta || -event.detail))); + event.preventDefault(); + + if (delta == 1) { + increase_volume(volumeStep); + } else if (delta == -1) { + increase_volume(-volumeStep); + } + } + } + } + }; + + player.on('mousewheel', mouseScroll); + player.on("DOMMouseScroll", mouseScroll); +}()); + // Since videojs-share can sometimes be blocked, we defer it until last player.share(shareOptions); |
