raingart / HTML5 Video Speed Control with keyboard hotkey

// ==UserScript==
// @name         HTML5 Video Speed Control with keyboard hotkey
// @version      0.6
// @description  hotkey "Z", "X", "C"
// @author       raingart
// @license      Apache-2.0
// @namespace    html5-rate
// @include      http*://*
// @run-at       document-end
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @require      https://cdn.jsdelivr.net/gh/kufii/My-UserScripts@c7f613292672252995cb02a0cab3b6acb18ccac5/libs/gm_config.js
// ==/UserScript==
/*jshint esversion: 6 */

// skip is not iframe
//if (window.self === window.top) return;

(function() {

const keysList = ['none','KeyA','KeyB','KeyC','KeyD','KeyE','KeyF','KeyG','KeyH','KeyI','KeyJ','KeyK','KeyL','KeyM','KeyN','KeyO','KeyP','KeyQ','KeyR','KeyS','KeyT','KeyU','KeyV','KeyW','KeyX','KeyY','KeyZ','[',']','+','-',',','.','/','<',';','\\','ArrowUp','ArrowDown','ArrowLeft','ArrowRight','ShiftLeft','ShiftRight','ControlLeft','ControlRight','AltLeft','AltRight',0,1,2,3,4,5,6,7,8,9];

const config = GM_config([
   {
      key: 'rate-up',
      label: 'Speed up',
      default: 'KeyC',
      type: 'dropdown',
      values: keysList,
   },
   {
      key: 'rate-down',
      label: 'Speed down',
      default: 'KeyX',
      type: 'dropdown',
      values: keysList,
   },
   {
      key: 'rate-default',
      label: 'Speed reset',
      default: 'KeyZ',
      type: 'dropdown',
      values: keysList,
   },
   {
      key: 'default-speed',
      label: 'Default speed',
      default: 1.3,
      min: .1,
      max: 5,
      step: .05,
      type: 'number',
   },
]);
const conf = config.load();
config.onsave = cfg => (conf = cfg);
GM_registerMenuCommand('Options hotkeys', config.setup);

let
  video,
  step = .1;

document.addEventListener('play', captureActiveVideoElement, true);
document.addEventListener('playing', registerShortcutKeys, { capture: true, once: true });
document.addEventListener('playing', setDefaultSpeed, { capture: true, once: true });

function captureActiveVideoElement(evt) {
  video = evt.target;
}

function registerShortcutKeys(evt) {
  document.addEventListener('keydown', evt => {
    if (['TEXTAREA', 'SELECT'].includes(evt.target.tagName)
       || (evt.target.tagName == 'INPUT' && ['email', 'text', 'password', 'search', 'url'].includes(evt.target.getAttribute('type')))
       || evt.target.isContentEditable
       ) return;

    switch (conf['rate-up'].length === 1 ? evt.key : evt.code) {
       case conf['rate-default']:
          video.playbackRate = 1;
          break;
       case conf['rate-down']:
          video.playbackRate -= step;
          break;
       case conf['rate-up']:
          video.playbackRate += step;
          break;
    }
    video.playbackRate = +(video.playbackRate).toFixed(3); // rounding
  });

  video.addEventListener('ratechange', function () {
    showNotification(this.playbackRate + 'x');
  });
}

function setDefaultSpeed(evt) {
  if (video.playbackRate !== +conf['default-speed']) video.playbackRate = +conf['default-speed'];
}

function showNotification(text) {
  if (typeof this.fate === 'number') clearTimeout(fate);

  this.notification = (this.notification || (function () {
      const el = document.createElement('div');
      Object.assign(el.style, {
          position: 'fixed',
          top: '15px',
          left: '10px',
          'z-index': 99999,
          'background-color': 'rgba(0,0,0,.5)',
          padding: '8px 10px',
          'border-radius': '3px',
          'font-family': 'arial',
          'font-size': '1.8em',
          'font-weight': 'bold',
          color: '#fff',
      });
      return document.body.appendChild(el);
    })());
    this.notification.textContent = text;
    this.notification.style.opacity = 1;
    // this.notification.style.visibility = 'visible';

  this.fate = setTimeout(() => {
     this.notification.style.transition = 'opacity 200ms ease-in';
     this.notification.style.opacity = 0;
     // hud.style.visibility = 'hidden';
  }, 800); //total 1s = 800ms + 200ms(hud.style.transition)
}

})();