beypazarigurusu / Imagus Reddit Video Player

// ==UserScript==
// @name        Imagus Reddit Video Player
// @namespace   https://openuserjs.org/users/beypazarigurusu
// @description Custom HTML5 player with shortcuts for Imagus-Reddit.
// @version     0.0.1
// @author      beypazarigurusu
// @license     MIT
// @include     *://*.reddit.com/*
// @grant       GM_xmlhttpRequest
// @connect     v.redd.it
// @require     https://cdn.jsdelivr.net/combine/npm/@violentmonkey/dom@1,npm/@violentmonkey/ui@0.4
// @require     https://cdnjs.cloudflare.com/ajax/libs/arrive/2.4.1/arrive.min.js
// @downloadURL https://openuserjs.org/install/beypazarigurusu/Imagus_Reddit_Video_Player.min.user.js
// @updateURL   https://openuserjs.org/meta/beypazarigurusu/Imagus_Reddit_Video_Player.meta.js
// ==/UserScript==

(function () {
  'use strict';

  var css_248z = ".imagus-video-wrapper{height:-moz-min-content!important;height:min-content!important;position:fixed!important;left:0!important;right:0!important;top:0!important;bottom:0!important;margin:auto!important;box-shadow:none!important;background-color:#000!important}.imagus-video-wrapper.stickied{box-shadow:0 0 0 100000px rgba(0,0,0,.7)!important}.imagus-video-wrapper videowrapper{height:0!important;padding-top:56.25%!important}.imagus-video-wrapper videowrapper video.custom-video-player{position:absolute!important}@media (min-width:177.778vh){.imagus-video-wrapper videowrapper{height:100%!important;padding-top:0!important}.imagus-video-wrapper videowrapper video.custom-video-player{position:relative!important}}html>div[style*=\"2147483647\"]>img[style*=\"display: block\"]~videowrapper{display:none!important}html>div[style*=\"2147483647\"]{background:none!important;box-shadow:none!important;border:0!important}html>div[style*=\"2147483647\"] videowrapper+div{-webkit-text-fill-color:#e6e6e6!important;box-shadow:none!important;width:100%!important;max-width:100%!important;box-sizing:border-box!important;overflow:hidden!important;text-overflow:ellipsis!important}html>div:not(.stickied) video.custom-video-player+controls,video[controls]:not(.custom-video-player){opacity:0!important;pointer-events:none!important}videowrapper{--wrapper-position:relative;position:var(--wrapper-position)!important;height:100%!important;display:block!important;font-size:0!important;top:0!important;bottom:0!important;left:0!important;right:0!important;background-color:#000!important;overflow:hidden!important}video.custom-video-player+controls timetooltip,video.custom-video-player+controls volumetooltip{position:absolute!important;display:none!important;top:-25px!important;height:22px!important;line-height:22px!important;text-align:center!important;border-radius:4px!important;font-size:12px!important;background:rgba(0,0,0,.7)!important;box-shadow:0 0 4px hsla(0,0%,100%,.5)!important;color:#fff!important;pointer-events:none!important}video.custom-video-player+controls timetooltip{margin-left:-25px!important;width:50px!important}video.custom-video-player+controls volumetooltip{margin-left:-20px!important;width:40px!important}video.custom-video-player.compact+controls timeline timetooltip{top:-25px!important}video.custom-video-player.compact+controls btn,video.custom-video-player.compact+controls rate,video.custom-video-player.compact+controls volume{height:24px!important;line-height:22px!important}video.custom-video-player.compact+controls volume input{padding-bottom:2px!important}video.custom-video-player.compact+controls btn:before{margin-top:-2px!important}video.custom-video-player.compact+controls volume>volumebar{top:6px!important}video.custom-video-player+controls timelinewrapper{line-height:20px!important}video.custom-video-player+controls timeline:hover timetooltip:not(.hidden),video.custom-video-player+controls volume:hover volumetooltip:not(.hidden){display:inline!important}video.custom-video-player{cursor:none!important;max-height:100%!important;height:100%!important;width:100%!important;margin:0!important;padding:0!important;top:0!important;bottom:0!important;left:0!important;right:0!important;background-color:#000!important;border-radius:0!important}video.custom-video-player:not(.contains-source):not([src*=\"/\"]){cursor:auto!important}video.custom-video-player:not(.contains-source):not([src*=\"/\"])+controls{display:none!important}video.custom-video-player+controls>*{background:none!important;outline:none!important;line-height:32px!important;font-family:monospace!important}video.custom-video-player.compact+controls>*{line-height:24px!important}video.custom-video-player+controls{--controls-z-index:1;white-space:nowrap!important;transition:opacity .5s ease 0s!important;background-color:rgba(0,0,0,.85)!important;height:32px!important;width:100%!important;cursor:default!important;font-size:18px!important;-moz-user-select:none!important;user-select:none!important;z-index:var(--controls-z-index)!important;flex:none!important;position:absolute!important;display:flex!important;flex-wrap:wrap!important;opacity:0!important;margin:0!important;bottom:0!important;left:0!important;right:0!important}video.custom-video-player.custom-video-player-hidden,video.custom-video-player.custom-video-player-hidden+controls{opacity:0!important;pointer-events:none!important}video.custom-video-player+controls:hover,video.custom-video-player.active+controls{opacity:1!important}video.custom-video-player+controls timeline{flex-grow:1!important;position:relative!important;align-items:center!important;flex-direction:column!important;height:100%!important}video.custom-video-player+controls timelinewrapper{flex:1 0 480px!important;position:relative!important;align-items:center!important}video.custom-video-player.compact+controls timelinewrapper{order:-1;flex-basis:100%!important;height:20px!important}video.custom-video-player.compact+controls timeline timebar{top:5px!important}video.custom-video-player.compact+controls currenttime,video.custom-video-player.compact+controls totaltime{line-height:20px!important}video.custom-video-player.compact+controls{height:44px!important}video.custom-video-player.compact-2+controls btn.begin,video.custom-video-player.compact-2+controls btn.skip-short,video.custom-video-player.compact-3+controls btn.rate-decrease,video.custom-video-player.compact-3+controls btn.rate-increase,video.custom-video-player.compact-3+controls rate,video.custom-video-player.compact-4+controls btn.skip-long{display:none!important}video.custom-video-player+controls>*{display:inline-flex!important}video.custom-video-player.compact-2+controls btn.rate-increase,video.custom-video-player.compact-4+controls btn.toggle-play{margin-right:auto!important}video.custom-video-player+controls timeline>timebar>timebuffer,video.custom-video-player+controls timeline>timebar>timeprogress,video.custom-video-player+controls volume>volumebar>volumetrail{position:absolute!important;flex:none!important;pointer-events:none!important;height:100%!important;border-radius:20px!important}video.custom-video-player+controls timeline>timebar,video.custom-video-player+controls volume>volumebar{position:absolute!important;height:10px!important;border-radius:20px!important;overflow:hidden!important;background-color:rgba(41,41,41,.85)!important;top:11px!important;left:0!important;right:0!important;pointer-events:none!important;z-index:-1!important;box-shadow:inset 0 0 0 1px #666,inset 0 0 5px hsla(0,0%,40%,.85)!important}video.custom-video-player+controls btn.disabled,video.custom-video-player+controls volume.disabled{filter:brightness(.4);pointer-events:none!important}video.custom-video-player.active.disabled,video.custom-video-player.disabled{cursor:default!important}video.custom-video-player.disabled+controls{opacity:1!important;filter:brightness(.3)sepia(1)hue-rotate(320deg)saturate(5)}video.custom-video-player.disabled+controls>*{pointer-events:none!important}video.custom-video-player+controls volume{max-width:70px!important;flex:1 0 48px!important;position:relative!important;margin:0 12px!important}video.custom-video-player+controls timeline>timebar>timebuffer{background-color:hsla(0,0%,100%,.2)!important;border-top-right-radius:20px!important;border-bottom-right-radius:20px!important;left:0!important}video.custom-video-player+controls timeline>timebar>timeprogress,video.custom-video-player+controls volume>volumebar>volumetrail{background-color:hsla(0,0%,100%,.4)!important;left:0!important}video.custom-video-player+controls volume.disabled volumetrail{width:0!important}video.custom-video-player+controls timeline>input{height:100%!important;width:100%!important}video.custom-video-player.active{cursor:pointer!important}video.custom-video-player+controls btn{border:none!important;cursor:pointer!important;background-color:initial!important;font-family:Segoe UI Symbol!important;font-size:0!important;margin:0!important;align-items:center!important;justify-content:center!important;height:32px!important;padding:0!important;flex:1 1 32px!important;max-width:46px!important;box-sizing:initial!important;position:relative!important;opacity:.86!important;text-shadow:none!important;transition:opacity .3s,text-shadow .3s!important;-webkit-text-fill-color:#fff!important}video.custom-video-player+controls btn.toggle-play{flex:1 1 46px!important}video.custom-video-player+controls btn:hover{opacity:1!important;text-shadow:0 0 8px #fff!important}video.custom-video-player.playback-rate-decreased+controls btn.rate-decrease,video.custom-video-player.playback-rate-increased+controls btn.rate-increase{-webkit-text-fill-color:#0ff!important}video.custom-video-player+controls rate{height:32px!important;width:42px!important;margin:0!important;display:unset!important;text-align:center!important;font-size:14px!important;flex-shrink:0!important;font-weight:700!important;letter-spacing:.5px!important;-webkit-text-fill-color:#fff!important;-moz-user-select:none!important;user-select:none!important;pointer-events:none!important;opacity:.86!important}video.custom-video-player+controls rate[data-current-rate]{pointer-events:all!important;cursor:pointer!important}video.custom-video-player+controls rate[data-current-rate]:hover{transition:opacity .3s,text-shadow .3s!important;opacity:1!important;text-shadow:0 0 8px #fff!important}video.custom-video-player+controls input[type=range]{-webkit-appearance:none!important;background-color:initial!important;outline:none!important;border:0!important;border-radius:6px!important;margin:0!important;top:0!important;bottom:0!important;left:0!important;right:0!important;padding:0!important;width:100%!important;position:relative!important}video.custom-video-player+controls input[type=range]::-webkit-slider-thumb{-webkit-appearance:none!important;background-color:#dbdbdb!important;border:0!important;height:14px!important;width:14px!important;border-radius:50%!important;pointer-events:none!important}video.custom-video-player.muted+controls volume input[type=range]::-webkit-slider-thumb{background-color:grey!important}video.custom-video-player+controls input[type=range]::-moz-range-thumb{-moz-appearance:none!important;background-color:#dbdbdb!important;border:0!important;height:14px!important;width:14px!important;border-radius:50%!important;pointer-events:none!important}video.custom-video-player.muted+controls volume input[type=range]::-moz-range-thumb{background-color:grey!important}video.custom-video-player+controls currenttime,video.custom-video-player+controls totaltime{font-family:monospace,arial!important;font-weight:700!important;font-size:14px!important;letter-spacing:.5px!important;height:100%!important;line-height:32px!important;min-width:58px!important;display:unset!important;-webkit-text-fill-color:#dbdbdb!important}video.custom-video-player+controls btn.rate-decrease,video.custom-video-player+controls btn.rate-increase{padding:0!important;flex:1 0 14px!important;max-width:24px!important}video.custom-video-player+controls btn.rate-decrease{margin-left:auto!important}video.custom-video-player+controls currenttime{padding:0 12px 0 0!important;margin-right:2px!important;text-align:right!important}video.custom-video-player+controls totaltime{padding:0 0 0 12px!important;margin-left:2px!important;text-align:left!important}.direct-video-top-level{margin:0!important;padding:0!important;display:flex!important;align-items:center!important;justify-content:center!important}.direct-video-top-level video{height:calc(56.25vw - 16.875px)!important;min-height:calc(56.25vw - 16.875px)!important;max-height:calc(56.25vw - 16.875px)!important;width:calc(100vw - 30px)!important;min-width:calc(100vw - 30px)!important;max-width:calc(100vw - 30px)!important;margin:auto!important}.direct-video-top-level>video.custom-video-player+controls{position:absolute!important;left:0!important;right:0!important;margin:0 auto!important;width:calc(100vw - 30px)!important;bottom:calc(50vh - 28.125vw + 8.4375px)!important}@media (min-width:177.778vh){.direct-video-top-level video{position:unset!important;height:calc(100vh - 30px)!important;min-height:calc(100vh - 30px)!important;max-height:calc(100vh - 30px)!important;margin:0 auto!important;padding:0!important;background-color:#000!important}.direct-video-top-level>video.custom-video-player+controls,.direct-video-top-level video{width:calc(177.77778vh - 53.33333px)!important;min-width:calc(177.77778vh - 53.33333px)!important;max-width:calc(177.77778vh - 53.33333px)!important}.direct-video-top-level>video.custom-video-player+controls{bottom:15px!important}}.native-fullscreen>:not(video):not(controls),video::-webkit-media-controls{display:none!important}.direct-video-top-level .native-fullscreen video.custom-video-player,.native-fullscreen video.custom-video-player{height:100vh!important;width:100vw!important;max-height:100vh!important;max-width:100vw!important;min-height:100vh!important;min-width:100vw!important;margin:0!important}.native-fullscreen video.custom-video-player+controls{position:fixed!important;bottom:0!important;left:0!important;right:0!important;margin:0!important;width:100vw!important;max-width:100vw!important}video.custom-video-player+controls btn:before{font-family:customVideoPlayerIconFont!important;font-weight:400!important;-webkit-font-smoothing:subpixel-antialiased!important;-moz-osx-font-smoothing:subpixel-antialiased!important;font-size:20px!important}video.custom-video-player+controls btn.skip-short.right:before{content:\"\\e90c\"!important}video.custom-video-player+controls btn.skip-short.left:before{content:\"\\e90b\"!important}video.custom-video-player+controls btn.skip-long.right:before{content:\"\\e901\"!important}video.custom-video-player+controls btn.skip-long.left:before{content:\"\\e902\"!important}video.custom-video-player+controls btn.begin:before{content:\"\\e908\"!important}video.custom-video-player+controls btn.toggle-play:before{content:\"\\e906\"!important;font-size:26px!important}video.custom-video-player.playing+controls btn.toggle-play:before{content:\"\\e905\"!important;font-size:24px!important}video.custom-video-player+controls btn.rate-decrease:before{content:\"\\ea0b\"!important;font-size:10px!important}video.custom-video-player+controls btn.rate-increase:before{content:\"\\ea0a\"!important;font-size:10px!important}video.custom-video-player+controls btn.mute:before{content:\"\\e90a\"!important;font-size:22px!important}video.custom-video-player.muted+controls btn.mute:before{content:\"\\e909\"!important;font-size:22px!important}video.custom-video-player+controls btn.mute.disabled:before{content:\"\\e909\"!important}video.custom-video-player+controls btn.expand:before{content:\"\\e904\"!important;font-size:24px!important}.native-fullscreen video.custom-video-player+controls btn.expand:before{content:\"\\e903\"!important;font-size:24px!important}@font-face{font-family:customVideoPlayerIconFont;src:url(data:application/x-font-woff;charset=utf-8;base64,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) format(\"woff\"),url(customVideoPlayerIconFont.ttf) format(\"truetype\"),url(customVideoPlayerIconFont.svg#customVideoPlayerIconFont) format(\"svg\");font-weight:400;font-style:normal}";

  /* global GM_xmlhttpRequest */
  document.head.append(VM.createElement("style", {
    id: "custom-video-player-style",
    className: "stylus"
  }, css_248z));
  let imagusAudio;
  let audioSync;
  let audioError;
  const $ = document.querySelector.bind(document); // const $$ = document.querySelectorAll.bind(document);

  const SETTINGS = {
    // delay to hide contols and cursor if inactive (set to 3000 milliseconds)
    hideControls: 300,
    // delay for fullscreen double-click (set to 300 milliseconds)
    clickDelay: 300,
    // right-click delay to match imagus user setting (set to 0 milliseconds)
    imagusStickyDelay: 0,
    // right/left arrows keys or inner skip buttons (set to 10 seconds)
    skipNormal: 10,
    // Shift + Arrow keys or outer skip buttons (set to 30 seconds)
    skipShift: 30,
    // Ctrl + Arrow keys skip (set to 1 minute)
    skipCtrl: 1
  };
  const SHORTCUT_FUNCTIONS = {
    toggleCaptions: v => {
      const validTracks = [];

      for (let i = 0; i < v.textTracks.length; i += 1) {
        const tt = v.textTracks[i];

        if (tt.mode === 'showing') {
          tt.mode = 'disabled';

          if (v.textTracks.addEventListener) {
            // If text track event listeners are supported
            // (they are on the most recent Chrome), add
            // a marker to remember the old track. Use a
            // listener to delete it if a different track
            // is selected.
            v.cbhtml5vsLastCaptionTrack = tt.label;

            const cleanup = e => {
              for (let j = 0; j < v.textTracks.length; j += 1) {
                const ott = v.textTracks[j];

                if (ott.mode === 'showing') {
                  delete v.cbhtml5vsLastCaptionTrack;
                  v.textTracks.removeEventListener('change', cleanup);
                  return;
                }
              }
            };

            v.textTracks.addEventListener('change', cleanup);
          }

          return;
        }

        if (tt.mode !== 'hidden') {
          validTracks.push(tt);
        }
      } // If we got here, none of the tracks were selected.

      if (validTracks.length === 0) {
        return true; // Do not prevent default if no UI activated
      } // Find the best one and select it.

      validTracks.sort((a, b) => {
        if (v.cbhtml5vsLastCaptionTrack) {
          const lastLabel = v.cbhtml5vsLastCaptionTrack;

          if (a.label === lastLabel && b.label !== lastLabel) {
            return -1;
          }

          if (b.label === lastLabel && a.label !== lastLabel) {
            return 1;
          }
        }

        const aLang = a.language.toLowerCase();
        const bLang = b.language.toLowerCase();
        const navLang = navigator.language.toLowerCase();

        if (aLang === navLang && bLang !== navLang) {
          return -1;
        }

        if (bLang === navLang && aLang !== navLang) {
          return 1;
        }

        const aPre = aLang.split('-')[0];
        const bPre = bLang.split('-')[0];
        const navPre = navLang.split('-')[0];

        if (aPre === navPre && bPre !== navPre) {
          return -1;
        }

        if (bPre === navPre && aPre !== navPre) {
          return 1;
        }

        return 0;
      })[0].mode = 'showing';
    },
    togglePlay: v => {
      if (v.paused) {
        v.play();
      }
      else {
        v.pause();
      }
    },
    toStart: v => {
      v.currentTime = 0;
    },
    toEnd: v => {
      v.currentTime = v.duration;
    },
    skipLeft: (v, key, shift, ctrl) => {
      if (shift) {
        v.currentTime -= SETTINGS.skipShift;
      }
      else if (ctrl) {
        v.currentTime -= SETTINGS.skipCtrl;
      }
      else {
        v.currentTime -= SETTINGS.skipNormal;
      }
    },
    skipRight: (v, key, shift, ctrl) => {
      if (shift) {
        v.currentTime += SETTINGS.skipShift;
      }
      else if (ctrl) {
        v.currentTime += SETTINGS.skipCtrl;
      }
      else {
        v.currentTime += SETTINGS.skipNormal;
      }
    },
    increaseVol: v => {
      if (audioError) return;

      if (v.nextSibling.querySelector('volume.disabled')) {
        v.volume = 0;
        return;
      }

      const increase = (v.volume + 0.1).toFixed(1);

      if (v.muted) {
        v.muted = !v.muted;
        v.volume = 0.1;
      }
      else {
        if (v.volume <= 0.9) {
          v.volume = increase;
        }
        else {
          v.volume = 1;
        }
      }
    },
    decreaseVol: v => {
      if (audioError) return;

      if (v.nextSibling.querySelector('volume.disabled')) {
        v.volume = 0;
        return;
      }

      const decrease = (v.volume - 0.1).toFixed(1);

      if (v.volume >= 0.1) {
        v.volume = decrease;
      }
      else {
        v.volume = 0;
      }
    },
    toggleMute: v => {
      v.muted = !v.muted;
      if (audioSync) imagusAudio.muted = v.muted;
    },
    toggleFS: v => {
      if (document.fullscreenElement) {
        document.exitFullscreen();
        v.parentElement.classList.remove('native-fullscreen');
      }
      else {
        v.parentElement.classList.add('native-fullscreen');
        v.parentElement.requestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
      }
    },
    reloadVideo: v => {
      const currTime = v.currentTime;
      v.load();
      v.currentTime = currTime;
    },
    slowOrPrevFrame: (v, key, shift) => {
      if (shift) {
        // Less-Than
        v.currentTime -= 1 / 60;
      }
      else {
        // Comma
        if (v.playbackRate >= 0.1) {
          const decrease = (v.playbackRate - 0.1).toFixed(2);
          const rate = v.nextSibling.querySelector('rate');
          v.playbackRate = decrease;
          rate.textContent = `${v.playbackRate}x`;

          if (v.playbackRate !== 1) {
            rate.setAttribute('data-current-rate', `${v.playbackRate}x`);
          }

          if (v.playbackRate === 0.9) {
            v.classList.add('playback-rate-decreased');
          }
          else if (v.playbackRate === 1.1) {
            v.classList.add('playback-rate-increased');
          }
          else if (v.playbackRate === 1) {
            v.classList.remove('playback-rate-decreased');
            v.classList.remove('playback-rate-increased');
            rate.removeAttribute('data-current-rate');
          }
        }
        else {
          v.playbackRate = 0;
        }

        if (audioSync) imagusAudio.playbackRate = v.playbackRate;
      }
    },
    fastOrNextFrame: (v, key, shift) => {
      if (shift) {
        // Greater-Than
        v.currentTime += 1 / 60;
      }
      else {
        // Period
        if (v.playbackRate <= 15.9) {
          const increase = (v.playbackRate += 0.1).toFixed(2);
          const rate = v.nextSibling.querySelector('rate');
          v.playbackRate = increase;
          rate.textContent = `${v.playbackRate}x`;

          if (v.playbackRate !== 1) {
            rate.setAttribute('data-current-rate', `${v.playbackRate}x`);
          }

          if (v.playbackRate === 0.9) {
            v.classList.add('playback-rate-decreased');
          }
          else if (v.playbackRate === 1.1) {
            v.classList.add('playback-rate-increased');
          }
          else if (v.playbackRate === 1) {
            v.classList.remove('playback-rate-decreased');
            v.classList.remove('playback-rate-increased');
            rate.removeAttribute('data-current-rate');
          }
        }
        else {
          v.playbackRate = 16;
        }

        if (audioSync) imagusAudio.playbackRate = v.playbackRate;
      }
    },
    normalSpeed: v => {
      // ?
      v.playbackRate = v.defaultPlaybackRate;
      if (audioSync) imagusAudio.playbackRate = v.playbackRate;
      v.classList.remove('playback-rate-decreased');
      v.classList.remove('playback-rate-increased');
      v.nextSibling.querySelector('rate').textContent = '1x';
      v.nextSibling.querySelector('rate').removeAttribute('data-current-rate');
    },
    toPercentage: (v, key) => {
      v.currentTime = v.duration * (key - 48) / 10.0;
    }
  };
  const KEY_FUNCTIONS = {
    32: SHORTCUT_FUNCTIONS.togglePlay,
    // Space
    75: SHORTCUT_FUNCTIONS.togglePlay,
    // K
    35: SHORTCUT_FUNCTIONS.toEnd,
    // End
    48: SHORTCUT_FUNCTIONS.toStart,
    // 0
    36: SHORTCUT_FUNCTIONS.toStart,
    // Home
    37: SHORTCUT_FUNCTIONS.skipLeft,
    // Left arrow
    74: SHORTCUT_FUNCTIONS.skipLeft,
    // J
    39: SHORTCUT_FUNCTIONS.skipRight,
    // Right arrow
    76: SHORTCUT_FUNCTIONS.skipRight,
    // L
    38: SHORTCUT_FUNCTIONS.increaseVol,
    // Up arrow
    40: SHORTCUT_FUNCTIONS.decreaseVol,
    // Down arrow
    77: SHORTCUT_FUNCTIONS.toggleMute,
    // M
    70: SHORTCUT_FUNCTIONS.toggleFS,
    // F
    67: SHORTCUT_FUNCTIONS.toggleCaptions,
    // C
    82: SHORTCUT_FUNCTIONS.reloadVideo,
    // R
    188: SHORTCUT_FUNCTIONS.slowOrPrevFrame,
    // Comma or Less-Than
    190: SHORTCUT_FUNCTIONS.fastOrNextFrame,
    // Period or Greater-Than
    191: SHORTCUT_FUNCTIONS.normalSpeed,
    // Forward slash or ?
    49: SHORTCUT_FUNCTIONS.toPercentage,
    // 1
    50: SHORTCUT_FUNCTIONS.toPercentage,
    // 2
    51: SHORTCUT_FUNCTIONS.toPercentage,
    // 3
    52: SHORTCUT_FUNCTIONS.toPercentage,
    // 4
    53: SHORTCUT_FUNCTIONS.toPercentage,
    // 5
    54: SHORTCUT_FUNCTIONS.toPercentage,
    // 6
    55: SHORTCUT_FUNCTIONS.toPercentage,
    // 7
    56: SHORTCUT_FUNCTIONS.toPercentage,
    // 8
    57: SHORTCUT_FUNCTIONS.toPercentage // 9

  };

  function customPlayer(v) {
    let videoWrapper;
    let savedTimeKey;
    let mouseDown;
    let isPlaying;
    let isSeeking;
    let earlyXposPercent;
    let preventMouseMove;
    let controlsTimeout;
    let imagusMouseTimeout;
    let imagusVid;
    let muteTillSync;
    let loaded;
    let error;
    let elToFocus;
    let clickCount = 0;
    let repeat = 0;
    const directVideo = /video/.test(document.contentType) && document.body.firstElementChild === v;
    const controls = document.createElement('controls');
    const imagus = v.classList.contains('imagus');

    if (imagus && !imagusVid) {
      imagusVid = v;
      imagusAudio = document.createElement('video');
      imagusAudio.preload = 'auto';
      imagusAudio.autoplay = 'true';
      imagusAudio.className = 'imagus imagus-audio';
      imagusAudio.style = 'display: none!important;';
      imagusVid.parentElement.insertBefore(imagusAudio, imagusVid);
    }

    if (directVideo) {
      elToFocus = document.body;

      if (self === top) {
        document.body.classList.add('direct-video-top-level');
      }
      else {
        document.body.classList.add('direct-video-embed');
      }
    }
    else {
      elToFocus = v;
      videoWrapper = document.createElement('videowrapper');
      v.parentNode.insertBefore(videoWrapper, v);
      videoWrapper.appendChild(v);

      if (!imagus) {
        const compStyles = getComputedStyle(v);
        const position = compStyles.getPropertyValue('position');
        const zIndex = compStyles.getPropertyValue('z-index');

        if (position === 'absolute') {
          videoWrapper.style.setProperty('--wrapper-position', `${position}`);
        }

        if (zIndex !== 'auto') {
          controls.style.setProperty('--controls-z-index', `calc(${zIndex} + 1)`);
        }
      }
    }

    v.parentNode.insertBefore(controls, v.nextSibling);
    const playButton = document.createElement('btn');
    playButton.className = 'toggle-play';
    controls.appendChild(playButton);
    const beginButton = document.createElement('btn');
    beginButton.className = 'begin';
    controls.appendChild(beginButton);
    const skipLongLeft = document.createElement('btn');
    skipLongLeft.className = 'skip-long left';
    controls.appendChild(skipLongLeft);
    const skipShortLeft = document.createElement('btn');
    skipShortLeft.className = 'skip-short left';
    controls.appendChild(skipShortLeft);
    const skipShortRight = document.createElement('btn');
    skipShortRight.className = 'skip-short right';
    controls.appendChild(skipShortRight);
    const skipLongRight = document.createElement('btn');
    skipLongRight.className = 'skip-long right';
    controls.appendChild(skipLongRight);
    const timelineWrapper = document.createElement('timelinewrapper');
    controls.appendChild(timelineWrapper);
    const currentTime = document.createElement('currenttime');
    currentTime.textContent = '0:00';
    timelineWrapper.appendChild(currentTime);
    const timeline = document.createElement('timeline');
    timelineWrapper.appendChild(timeline);
    const timeBar = document.createElement('timebar');
    timeline.appendChild(timeBar);
    const timeBuffer = document.createElement('timebuffer');
    timeBar.appendChild(timeBuffer);
    const timeProgress = document.createElement('timeprogress');
    timeBar.appendChild(timeProgress);
    const timeSlider = document.createElement('input');
    timeSlider.type = 'range';
    timeSlider.value = 0;
    timeSlider.min = 0;
    timeSlider.max = 100;
    timeSlider.step = 0.01;
    timeSlider.textContent = '';
    timeline.appendChild(timeSlider);
    const timeTooltip = document.createElement('timetooltip');
    timeTooltip.className = 'hidden';
    timeTooltip.textContent = '-:-';
    timeline.appendChild(timeTooltip);
    const totalTime = document.createElement('totaltime');
    totalTime.textContent = '-:-';
    timelineWrapper.appendChild(totalTime);
    const rateDecrease = document.createElement('btn');
    rateDecrease.className = 'rate-decrease';
    controls.appendChild(rateDecrease);
    const rate = document.createElement('rate');
    rate.textContent = '1x';
    controls.appendChild(rate);
    const rateIncrease = document.createElement('btn');
    rateIncrease.className = 'rate-increase';
    controls.appendChild(rateIncrease);
    const volume = document.createElement('volume');
    controls.appendChild(volume);
    const volumeBar = document.createElement('volumebar');
    volume.appendChild(volumeBar);
    const volumeTrail = document.createElement('volumetrail');
    volumeBar.appendChild(volumeTrail);
    const volumeSlider = document.createElement('input');
    volumeSlider.type = 'range';
    volumeSlider.min = 0;
    volumeSlider.max = 1;
    volumeSlider.step = 0.01;
    volumeSlider.textContent = '';
    volume.appendChild(volumeSlider);
    const volumeTooltip = document.createElement('volumetooltip');
    volumeTooltip.className = 'hidden';
    volumeTooltip.textContent = '0%';
    volume.appendChild(volumeTooltip);
    const muteButton = document.createElement('btn');
    muteButton.className = 'mute';
    controls.appendChild(muteButton);
    const expandButton = document.createElement('btn');
    expandButton.className = 'expand';
    controls.appendChild(expandButton);
    v.classList.remove('custom-video-player-hidden');
    if (v.querySelector('source')) v.classList.add('contains-source');
    if (videoWrapper) enforcePosition();
    volumeValues();

    v.onloadedmetadata = ev => {
      loaded = true;
      SHORTCUT_FUNCTIONS.normalSpeed(v);
      savedTimeKey = `${location.pathname}${location.search}${v.duration}`;
      const savedTime = localStorage.getItem(savedTimeKey);

      if (timeSlider.value === '0') {
        if (savedTime) v.currentTime = savedTime;
      }
      else if (earlyXposPercent) {
        const time = earlyXposPercent * v.duration / 100;
        v.currentTime = time;
      }

      currentTime.textContent = formatTime(v.currentTime);
      totalTime.textContent = formatTime(v.duration);
      v.classList.remove('disabled');
      sliderValues(ev);
    };

    v.onloadeddata = () => {
      const imagusVreddit = /v(cf)?\.redd\.it/.test(v.src);
      const vHasAudio = hasAudio();

      if (!vHasAudio && !imagusVreddit) {
        v.classList.add('muted');
        volumeSlider.value = 0;
        muteButton.classList.add('disabled');
        volume.classList.add('disabled');
      }
      else if (vHasAudio && !imagusVreddit) {
        if (v.volume && !v.muted) v.classList.remove('muted');
        volumeValues();

        if (volume.classList.contains('disabled')) {
          muteButton.classList.remove('disabled');
          volume.classList.remove('disabled');
        }
      }

      elToFocus.focus({
        preventScroll: true
      });

      if (v.duration <= SETTINGS.skipNormal) {
        skipShortLeft.classList.add('disabled');
        skipShortRight.classList.add('disabled');
      }
      else {
        skipShortLeft.classList.remove('disabled');
        skipShortRight.classList.remove('disabled');
      }

      if (v.duration <= SETTINGS.skipShift) {
        skipLongLeft.classList.add('disabled');
        skipLongRight.classList.add('disabled');
      }
      else {
        skipLongLeft.classList.remove('disabled');
        skipLongRight.classList.remove('disabled');
      }

      if (v.paused) {
        v.classList.add('paused');
        if (videoWrapper) videoWrapper.classList.add('paused');
      }

      if (imagus) v.currentTime = 0;
    };

    v.oncanplay = () => {
      v.oncanplay = null;

      if (!loaded) {
        v.load(); // console.log('Custom video player reloaded');
      }
    };

    v.onprogress = () => {
      if (v.readyState > 1 && v.duration > 0) {
        const buffer = v.buffered.end(v.buffered.length - 1) / v.duration * 100;
        timeBuffer.style.width = `${buffer}%`;
      }
    };

    v.ontimeupdate = ev => {
      if (v.readyState > 0) {
        if (v.duration > 0 && !mouseDown) {
          sliderValues(ev);
          totalTime.textContent = formatTime(v.duration);
          if (!imagus && savedTimeKey) localStorage.setItem(savedTimeKey, v.currentTime);
        }
      }
    };

    v.onvolumechange = ev => {
      if (audioError) return;
      if (audioSync) imagusAudio.volume = v.volume;

      if (v.muted || !v.volume) {
        v.classList.add('muted');
        volumeSlider.value = 0;
        volumeTrail.style.width = '0';
        localStorage.setItem('videomuted', 'true');
      }
      else {
        v.classList.remove('muted');
        sliderValues(ev);

        if (v.volume > 0.1) {
          localStorage.setItem('videovolume', v.volume);
        }
        else {
          localStorage.setItem('videovolume', 0.1);
        }

        localStorage.setItem('videomuted', 'false');
      }
    };

    v.onplay = () => {
      if (v === imagusVid && audioSync) imagusAudio.play();
      v.classList.remove('paused');
      if (videoWrapper) videoWrapper.classList.remove('paused');
      v.classList.add('playing');
    };

    v.onpause = () => {
      if (v === imagusVid && audioSync) imagusAudio.pause();

      if (!isSeeking) {
        v.classList.remove('playing');
        v.classList.add('paused');
        if (videoWrapper) videoWrapper.classList.add('paused');
      }
    };

    v.onended = () => {
      if (localStorage.getItem(savedTimeKey)) localStorage.removeItem(savedTimeKey);
      savedTimeKey = false;
    };

    v.onemptied = () => {
      if (v === imagusVid) {
        if (v.src !== '') {
          if (/v(cf)?\.redd\.it/.test(v.src)) {
            const prefix = v.src.split('DASH')[0].replace('vcf.', 'v.');
            const audioSrc = `${prefix}DASH_audio.mp4`;
            GM_xmlhttpRequest({
              method: 'GET',
              url: audioSrc,
              onload: xhr => {
                imagusAudio.src = xhr.status >= 200 && xhr.status < 300 ? audioSrc : `${prefix}audio`;
              },
              onerror: () => {
                imagusAudio.src = `${prefix}audio`;
              }
            });

            if (!imagusAudio.muted) {
              muteTillSync = true;
              imagusAudio.muted = true;
            }

            if (imagusVid.hasAttribute('loop')) imagusAudio.setAttribute('loop', 'true');
          }

          v.parentElement.parentElement.classList.add('imagus-video-wrapper');
          window.addEventListener('click', imagusClick, true);
          document.addEventListener('keyup', imagusKeys, true);
          document.addEventListener('mousedown', imagusMouseDown, true);
          document.addEventListener('mouseup', imagusMouseUp, true);
        }
        else {
          audioSync = false;
          audioError = false;
          imagusAudio.pause();
          imagusAudio.removeAttribute('src');
          imagusAudio.load();
          imagusAudio.removeAttribute('loop');
          v.parentElement.parentElement.removeAttribute('class');
          timeTooltip.classList.add('hidden');
          window.removeEventListener('click', imagusClick, true);
          document.removeEventListener('keyup', imagusKeys, true);
          document.removeEventListener('mousedown', imagusMouseDown, true);
          document.removeEventListener('mouseup', imagusMouseUp, true);
        }
      }
    };

    v.onerror = () => {
      error = true;
      elToFocus.blur();
      v.classList.add('disabled');
    };

    v.onmousedown = e => {
      if (error && e.button !== 2) return;
      e.stopPropagation();
      e.stopImmediatePropagation();

      if (e.button === 0) {
        clickCount += 1;
        const checkState = v.paused;

        if (clickCount === 1) {
          setTimeout(() => {
            if (clickCount === 1) {
              // avoid conflicts with existing click listeners
              const recheckState = v.paused;
              if (checkState === recheckState) SHORTCUT_FUNCTIONS.togglePlay(v);
            }
            else {
              SHORTCUT_FUNCTIONS.toggleFS(v);
            }

            clickCount = 0;
          }, SETTINGS.clickDelay);
        }
      }
      else if (e.button === 2) {
        window.addEventListener('contextmenu', preventHijack, true);
      }
    };

    v.onmouseup = e => {
      if (e.button === 2) {
        setTimeout(() => {
          window.removeEventListener('contextmenu', preventHijack, true);
        }, 100);
      }

      if (error) elToFocus.blur();
    };

    v.onmousemove = () => {
      if (controlsTimeout) {
        clearTimeout(controlsTimeout);
      }
      else {
        v.classList.add('active');
      }

      if (videoWrapper) {
        videoWrapper.classList.add('active');
      }

      controlsTimeout = setTimeout(() => {
        controlsTimeout = false;
        v.classList.remove('active');

        if (videoWrapper) {
          videoWrapper.classList.remove('active');
        }
      }, SETTINGS.hideControls);
    };

    new ResizeObserver(() => {
      compactControls();
    }).observe(v);

    controls.onmouseup = () => {
      if (error) return;
      elToFocus.focus({
        preventScroll: true
      });
    };

    timeSlider.onmousemove = ev => sliderValues(ev);

    timeSlider.oninput = ev => sliderValues(ev);

    timeSlider.onmousedown = e => {
      if (e.button > 0) return;
      mouseDown = true;
      isSeeking = true;
      if (timeTooltip.classList.contains('hidden')) sliderValues(e);

      if (v.readyState > 0) {
        if (!v.paused) {
          isPlaying = true;
          v.pause();
        }
        else {
          isPlaying = false;
        }
      }
    };

    timeSlider.onmouseup = e => {
      if (e.button > 0) return;
      mouseDown = false;
      isSeeking = false;

      if (v.readyState > 0) {
        sliderValues(e);

        if (isPlaying) {
          v.play();
          isPlaying = false;
        }
      }
    };

    volumeSlider.onmousemove = ev => sliderValues(ev);

    volumeSlider.oninput = ev => {
      if (v.muted) SHORTCUT_FUNCTIONS.toggleMute(v);
      sliderValues(ev);
    };

    muteButton.onmouseup = e => {
      if (e.button > 0) return;
      const lastVolume = localStorage.getItem('videovolume');
      if (v.muted || v.volume) SHORTCUT_FUNCTIONS.toggleMute(v);
      v.volume = lastVolume;
      if (audioSync) imagusAudio.muted = v.muted;
    };

    playButton.onmouseup = e => {
      if (e.button > 0) return;
      SHORTCUT_FUNCTIONS.togglePlay(v);
    };

    skipShortLeft.onmouseup = e => {
      if (e.button > 0) return;
      SHORTCUT_FUNCTIONS.skipLeft(v);
    };

    skipShortRight.onmouseup = e => {
      if (e.button > 0) return;
      SHORTCUT_FUNCTIONS.skipRight(v);
    };

    skipLongLeft.onmouseup = e => {
      if (e.button > 0) return;
      v.currentTime -= SETTINGS.skipShift;
    };

    skipLongRight.onmouseup = e => {
      if (e.button > 0) return;
      v.currentTime += SETTINGS.skipShift;
    };

    beginButton.onmouseup = e => {
      if (e.button > 0) return;
      v.currentTime = 0;
      timeSlider.value = 0;
      timeProgress.style.width = '0';
      currentTime.textContent = '0:00';
    };

    rateDecrease.onmouseup = e => {
      if (e.button > 0) return;
      SHORTCUT_FUNCTIONS.slowOrPrevFrame(v);
    };

    rateIncrease.onmouseup = e => {
      if (e.button > 0) return;
      SHORTCUT_FUNCTIONS.fastOrNextFrame(v);
    };

    rate.onmouseup = e => {
      if (e.button > 0) return;
      SHORTCUT_FUNCTIONS.normalSpeed(v);
    };

    rate.onmouseenter = () => {
      rate.textContent = '1x?';
    };

    rate.onmouseleave = () => {
      const currentRate = rate.getAttribute('data-current-rate');
      if (currentRate) rate.textContent = currentRate;
    };

    expandButton.onmouseup = e => {
      if (e.button > 0) return;
      SHORTCUT_FUNCTIONS.toggleFS(v);
    }; // exiting fullscreen by escape key or other browser provided method

    document.onfullscreenchange = () => {
      if (!document.fullscreenElement) {
        const nativeFS = $('.native-fullscreen');
        if (nativeFS) nativeFS.classList.remove('native-fullscreen');
      }
    };

    if (imagusVid) {
      imagusAudio.onloadedmetadata = () => {
        audioSync = true;
        if (v.hasAttribute('autoplay')) imagusAudio.play();
      };

      imagusAudio.onloadeddata = () => {
        if (v.volume && !v.muted) v.classList.remove('muted');
        volumeValues();

        if (volume.classList.contains('disabled')) {
          muteButton.classList.remove('disabled');
          volume.classList.remove('disabled');
        }
      };

      imagusAudio.onended = () => {
        imagusAudio.currentTime = 0;
        if (imagusVid.hasAttribute('loop')) imagusAudio.play();
      };

      imagusAudio.onerror = () => {
        audioError = true;
        v.classList.add('muted');
        volumeSlider.value = 0;
        muteButton.classList.add('disabled');
        volume.classList.add('disabled');
      };
    }

    if (directVideo) {
      v.removeAttribute('tabindex');
      document.body.setAttribute('tabindex', '0');
      document.addEventListener('keydown', docHandleKeyDown, true);
      document.addEventListener('keypress', docHandleKeyOther, true);
      document.addEventListener('keyup', docHandleKeyOther, true);
    }
    else {
      v.addEventListener('keydown', handleKeyDown, false);
      v.addEventListener('keypress', handleKeyOther, false);
      v.addEventListener('keyup', handleKeyOther, false);
    }

    function sliderValues(ev) {
      let slider;
      let xPosition;
      const vid = audioSync ? imagusAudio && v : v;
      const eType = ev ? ev.type : event && event.type;
      const eTime = eType === 'timeupdate';
      const eVolume = eType === 'volumechange';
      const eMeta = eType === 'loadedmetadata';
      const eData = eType === 'loadeddata';
      const eInput = eType === 'input';
      const eMouseUp = eType === 'mouseup';
      const eMouseMove = eType === 'mousemove';
      const eMouseDown = eType === 'mousedown';

      if (eMeta || eTime || eVolume || eData || !ev) {
        slider = eMeta || eTime ? timeSlider : volumeSlider;
      }
      else {
        slider = ev.target;
      }

      const tooltip = slider.nextSibling;
      const timeTarget = slider === timeSlider;
      const sliderWidth = slider.clientWidth;
      const halfSlider = sliderWidth / 2;
      const slider14ths = halfSlider / 7;
      const eX = ev && ev.offsetX;
      const start7 = eX <= 7;
      const end7 = eX >= sliderWidth - 7;

      if (eMouseMove || eMouseDown) {
        if (start7 || end7) {
          xPosition = start7 ? 0 : sliderWidth;
        }
        else {
          xPosition = eX < halfSlider ? (eX + (-7 + eX / slider14ths)).toFixed(1) : (eX + (eX - halfSlider) / slider14ths).toFixed(1);
        }
      }

      if (eMeta || eTime || eVolume || eData || !ev) {
        xPosition = eMeta || eTime ? (100 / v.duration * v.currentTime * sliderWidth / 100).toFixed(1) : (v.volume * sliderWidth).toFixed(1);
      }

      if (eTime && ev.target === imagusVid && audioSync) {
        if (imagusVid.currentTime - imagusAudio.currentTime >= 0.1 || imagusVid.currentTime - imagusAudio.currentTime <= -0.1) {
          imagusAudio.currentTime = imagusVid.currentTime + 0.06; // console.debug('time sync corrected');

          if (muteTillSync && imagusAudio.readyState > 2) {
            imagusAudio.muted = false;
            muteTillSync = false; // console.debug('unmuted after time correct');
          }
        }
        else if (muteTillSync && imagusAudio.readyState > 2) {
          imagusAudio.muted = false;
          muteTillSync = false; // console.debug('unmuted');
        }
      }

      if (eInput || eMouseUp) xPosition = +tooltip.getAttribute('data-x-position');
      const xPosPercent = timeTarget ? xPosition / sliderWidth * 100 : Math.round(xPosition / sliderWidth * 100);
      let time = xPosPercent * v.duration / 100;

      if (eInput || eMeta || eTime || eVolume || eData || !ev) {
        const valueTrail = timeTarget ? timeProgress : volumeTrail;
        const offset = halfSlider < xPosition ? -7 + xPosition / slider14ths : (xPosition - halfSlider) / slider14ths;
        slider.value = timeTarget ? xPosPercent : xPosPercent / 100;
        valueTrail.style.width = `calc(${xPosPercent}% - ${offset}px)`;

        if (eInput && !timeTarget) {
          if (start7 || end7) {
            vid.volume = start7 ? 0 : 1;
          }
          else {
            vid.volume = xPosPercent / 100;
          }
        }

        if (eInput && timeTarget && v.readyState > 0) currentTime.textContent = formatTime(time);
        if (eTime) currentTime.textContent = formatTime(v.currentTime);
        if (eInput && timeTarget && v.readyState < 1) earlyXposPercent = xPosPercent;

        if (eMeta && !tooltip.classList.contains('hidden')) {
          xPosition = +tooltip.getAttribute('data-x-position');
          time = xPosition / sliderWidth * v.duration;
          tooltip.textContent = formatTime(time);
        }
      }
      else if (eMouseUp) {
        if (audioSync) {
          if (start7 || end7) {
            imagusAudio.currentTime = start7 ? 0 : v.duration;
          }
          else {
            imagusAudio.currentTime = time;
          }
        }

        if (start7 || end7) {
          v.currentTime = start7 ? 0 : v.duration;
        }
        else {
          v.currentTime = time;
        }

        preventMouseMove = true;
        setTimeout(() => {
          preventMouseMove = false;
        }, 10);
      }
      else if (eMouseMove || eMouseDown) {
        if (!preventMouseMove || eMouseDown) {
          tooltip.dataset.xPosition = xPosition;
          tooltip.style.left = `${eX}px`;
          if (v.readyState > 0 && timeTarget) tooltip.textContent = formatTime(time);
          if (!timeTarget) tooltip.textContent = `${xPosPercent}%`;
        }

        tooltip.classList.remove('hidden');
        preventMouseMove = false;
      }
    }

    function formatTime(t) {
      let seconds = Math.round(t);
      const minutes = Math.floor(seconds / 60);
      if (minutes > 0) seconds -= minutes * 60;
      if (seconds.toString().length === 1) seconds = `0${seconds}`;
      return `${minutes}:${seconds}`;
    }

    function volumeValues() {
      const videovolume = localStorage.getItem('videovolume');
      const videomuted = localStorage.getItem('videomuted');

      if (!videovolume && !videomuted || videovolume && videovolume === '1' && videomuted && videomuted !== 'true') {
        v.volume = 1;
        volumeSlider.value = 1;
        volumeTrail.style.width = '100%';
        localStorage.setItem('videovolume', v.volume);
        localStorage.setItem('videomuted', 'false');
      }
      else if (videomuted && videomuted === 'true') {
        v.classList.add('muted');
        volumeSlider.value = 0;
        volumeTrail.style.width = '0';
        v.muted = true;
      }
      else {
        v.volume = videovolume;
        if (audioSync) imagusAudio.volume = v.volume;
        sliderValues();

        if (!volumeSlider.clientWidth) {
          new MutationObserver((_, observer) => {
            const volumeWidthSet = v.parentElement.querySelector('volume input').clientWidth;

            if (volumeWidthSet) {
              sliderValues();
              observer.disconnect();
            }
          }).observe(v.parentElement, {
            childList: true,
            subtree: true,
            attributes: true
          });
        }
      }
    }

    function hasAudio() {
      return v.mozHasAudio || Boolean(v.webkitAudioDecodedByteCount) || Boolean(v.audioTracks && v.audioTracks.length);
    }

    function compactControls() {
      const width = v.clientWidth;

      if (width && width < 892) {
        v.classList.add('compact');
      }
      else {
        v.classList.remove('compact');
      }

      if (width && width < 412) {
        v.classList.add('compact-2');
      }
      else {
        v.classList.remove('compact-2');
      }

      if (width && width < 316) {
        v.classList.add('compact-3');
      }
      else {
        v.classList.remove('compact-3');
      }

      if (width && width < 246) {
        v.classList.add('compact-4');
      }
      else {
        v.classList.remove('compact-4');
      }
    }

    function imagusMouseDown(e) {
      const vid = $('.imagus-video-wrapper');

      if (vid && e.button === 2) {
        e.stopImmediatePropagation();
        imagusMouseTimeout = setTimeout(() => {
          imagusMouseTimeout = 'sticky';
        }, SETTINGS.imagusStickyDelay);
      }
    }

    function imagusMouseUp(e) {
      const vid = $('.imagus-video-wrapper');

      if (vid && e.button === 2) {
        if (imagusMouseTimeout === 'sticky') {
          vid.classList.add('stickied');
          setTimeout(() => {
            v.removeAttribute('controls');
          });
          if (volume.classList.contains('disabled')) volumeSlider.value = 0;
          document.removeEventListener('mousedown', imagusMouseDown, true);
          document.removeEventListener('mouseup', imagusMouseUp, true);
        }
        else {
          clearInterval(imagusMouseTimeout);
          imagusMouseTimeout = false;
        }
      }
    }

    function imagusClick(e) {
      const imagusStickied = $('.imagus-video-wrapper.stickied');

      if (imagusStickied) {
        if (e.target.closest('.imagus-video-wrapper.stickied')) {
          e.stopImmediatePropagation();
        }
        else {
          imagusStickied.removeAttribute('class');
          e.preventDefault();
        }
      }
    }

    function imagusKeys(e) {
      const vid = $('.imagus-video-wrapper');

      if (vid) {
        if (e.keyCode === 13 || e.keyCode === 90) {
          vid.classList.add('stickied');
          setTimeout(() => {
            v.removeAttribute('controls');
          });
          if (volume.classList.contains('disabled')) volumeSlider.value = 0;
          document.removeEventListener('keyup', imagusKeys, true);
          document.removeEventListener('mousedown', imagusMouseDown, true);
          document.removeEventListener('mouseup', imagusMouseUp, true);
        }
      }
    }

    function handleKeyDown(e) {
      if (e.altKey || e.metaKey) return true; // Do not activate

      const func = KEY_FUNCTIONS[e.keyCode];

      if (func) {
        if (func.length < 3 && e.shiftKey || func.length < 4 && e.ctrlKey) return true; // Do not activate

        func(e.target, e.keyCode, e.shiftKey, e.ctrlKey);
        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        return false;
      }
    }

    function handleKeyOther(e) {
      if (e.altKey || e.metaKey) return true; // Do not prevent default

      const func = KEY_FUNCTIONS[e.keyCode];

      if (func) {
        if (func.length < 3 && e.shiftKey || func.length < 4 && e.ctrlKey) return true; // Do not prevent default

        e.preventDefault();
        e.stopPropagation();
        e.stopImmediatePropagation();
        return false;
      }
    }

    function docHandleKeyDown(e) {
      if (document.body !== document.activeElement || e.altKey || e.metaKey) return true; // Do not activate

      const func = KEY_FUNCTIONS[e.keyCode];

      if (func) {
        if (func.length < 3 && e.shiftKey || func.length < 4 && e.ctrlKey) return true; // Do not activate

        func(v, e.keyCode, e.shiftKey, e.ctrlKey);
        e.preventDefault();
        e.stopPropagation();
        return false;
      }
    }

    function docHandleKeyOther(e) {
      if (document.body !== document.activeElement || e.altKey || e.metaKey) return true; // Do not prevent default

      const func = KEY_FUNCTIONS[e.keyCode];

      if (func) {
        if (func.length < 3 && e.shiftKey || func.length < 4 && e.ctrlKey) return true; // Do not prevent default

        e.preventDefault();
        e.stopPropagation();
        return false;
      }
    } // circumvent any scripts attempting to hijack video context menus

    function preventHijack(e) {
      e.stopPropagation();
      e.stopImmediatePropagation();
      const redirectEvent = e.target.ownerDocument.createEvent('MouseEvents');
      redirectEvent.initMouseEvent(e, e.bubbles, e.cancelable);
      return e;
    }

    function enforcePosition() {
      setTimeout(() => {
        let controlsDisplaced = controls !== v.nextSibling;
        const vidDisplaced = videoWrapper !== v.parentNode;

        if (vidDisplaced || controlsDisplaced) {
          if (vidDisplaced) videoWrapper.appendChild(v);
          controlsDisplaced = v !== controls.previousSibling;
          if (controlsDisplaced) videoWrapper.insertBefore(controls, v.nextSibling);
          const bs = videoWrapper.querySelectorAll('videowrapper > *:not(video):not(controls)');

          for (let i = 0; i < bs.length; i += 1) {
            bs[i].remove();
          }
        }

        repeat += 1;
        if (repeat < 10) enforcePosition.call(this);
      }, 100);
    }
  }

  document.arrive('video[controls], video[style*="visibility: inherit !important"]', {
    fireOnAttributesModification: true,
    existing: true
  }, v => {
    if (!v.parentNode.parentNode) return;
    const vP = v.parentNode;
    const vPP = v.parentNode.parentNode;
    const imagus = !v.hasAttribute('controls') && $('html > div[style*="z-index: 2147483647"]') === v.parentNode;
    const vidOrParentsIdOrClass = `${v.id}${v.classList}${vP.id}${vP.classList}${vPP.id}${vPP.classList}`;
    const exclude = v.classList.contains('custom-video-player') || v.classList.contains('imagus') || /(v(ideo)?|me)(-|_)?js|jw|jplay|plyr|kalt|flowp|wisti/i.test(vidOrParentsIdOrClass);

    if (imagus || v.hasAttribute('controls') && !exclude) {
      if (imagus) v.classList.add('imagus');
      v.classList.add('custom-video-player');
      v.classList.add('custom-video-player-hidden');
      v.setAttribute('tabindex', '0');
      v.setAttribute('preload', 'auto');
      v.removeAttribute('controls');
      customPlayer(v);
    }
  });

}());