/*
 * 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) {
    var videojs = require('video.js');
    module.exports = factory(videojs.default || videojs);
  }
}(this, function (videojs) {
  "use strict";
  if (typeof window !== 'undefined') {
    window['videojs_hotkeys'] = { version: "0.2.28" };
  }

  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) {});
      }
    }

    if (captureDocumentHotkeys) {
      var capDocHK = function (event) { keyDown(event) };
      document.addEventListener('keydown', capDocHK);

      this.dispose = function () {
        document.removeEventListener('keydown', capDocHK);
      }
    } else {
      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);
}));
