Youtube Extended Controls

Adding keyboard shortcuts for cycling playback speed setting and looping a specific time span

// ==UserScript==
// @name         Youtube Extended Controls
// @namespace    https://greasyfork.org/de/scripts/421393-youtube-extended-controls
// @version      0.3.1
// @description  Adding keyboard shortcuts for cycling playback speed setting and looping a specific time span
// @author       Guitar Hero
// @grant        none
// @include https://youtube.com/watch*
// @include https://www.youtube.com/watch*

// ==/UserScript==

(function() {

    control = {
        messageBox: {
            lastMessage: null,
            show: function(message) {
                control.messageBox.lastMessage = message;
                document.querySelector(".ytp-bezel-text").innerText = message;
                document.querySelector(".ytp-bezel-text").parentNode.parentNode.style.display = "";
                document.querySelector(".ytp-bezel-text").parentNode.parentNode.classList.remove("ytp-bezel-text-hide");
                setTimeout(control.messageBox.hide, 1000, message);
            },
            hide: function(messageToHide) {
                console.log("message to hide: " + messageToHide);
                if (messageToHide != control.messageBox.lastMessage) {
                    console.log("but last message is: " + control.messageBox.lastMessage);
                    return;
                }
                console.log("will hide last message: " + control.messageBox.lastMessage);
                document.querySelector(".ytp-bezel-text").innerText = "";
                document.querySelector(".ytp-bezel-text").parentNode.parentNode.style.display = "none";
            }
        },
        setup: {
            init: function() {
                console.log("init");
                control.setup.enableLoopButton();
                control.setup.enableSpeedButton();
//                 registrateOnLocationChange();
                document.addEventListener("keydown", control.setup.keyWatcher, false);
            },
            registrateShortcut: function(key, functionToCall) {
                console.log("registering function for key: " + key);
                control.keyToFunctionMap[key.charCodeAt(0)] = functionToCall;
            },
            keyWatcher: function(e) {
                console.log("key pressed!");

                //check if video or "nothing" is focussed (to exclude text fields)
                if (document.activeElement.id != "" && document.activeElement.id != "movie_player") {
                    console.log("not the right focus");
                    return;
                }

                if (!e) {
                    e = window.event;
                }
                control.callFunctionByKey(e.keyCode);
            },
            registrateOnLocationChange: function() {
                var pushState = history.pushState;
                history.pushState = function () {
                    var changedUrl = arguments[2];
                    pushState.apply(history, arguments);
                    control.setup.init();
                }
            },
            enableLoopButton: function() {
                try {
                    control.setup.registrateShortcut("A", control.loop.handler);
                } catch(ex) {
                    console.log("error enabling loop button: " + ex);
                }
            },
            enableSpeedButton: function() {
                try {
                    control.speed.speedSet = control.video.playbackRate;
                    control.setup.registrateShortcut("S", control.speed.handler);
                } catch(ex) {
                    console.log("error enabling speed button: " + ex);
                }
            }
        },
        keyToFunctionMap: {},
        video: document.querySelector('.html5-main-video'),
        speed: {
            availableSpeeds: [1, 0.75, 0.5, 0.25, 0.1],
            speedSet: -1,
            handler: function() {
                var index = control.speed.availableSpeeds.indexOf(control.speed.speedSet);
                index = ++index % control.speed.availableSpeeds.length;
                control.speed.setSpeed(control.speed.availableSpeeds[index]);
                control.messageBox.show(control.speed.speedSet + "x");
            },
            setSpeed: function(speed) {
                control.speed.speedSet = speed;
                control.video.playbackRate = speed;
            }
        },
        loop: {
            begin: null,
            end: null,
            active: false,
            reset: function() {
                control.loop.begin = null;
                control.loop.end = null;
                control.loop.active = false;
            },
            worker: function() {
                console.log("loop worker");
                if (control.loop.active == false) {
                    console.log("loop worker not active, exiting");
                    return;
                }
                if (control.video.currentTime > control.loop.end) {
                    console.log("loop worker - set to beginning");
                    control.jumpToPosition(control.loop.begin);
                }
                console.log("loop worker - setting timeout");
                setTimeout(control.loop.worker, 100);
            },
            handler: function() {
                //set loop begin
                if (control.loop.begin == null) {
                    console.log("setting loop begin");
                    control.messageBox.show("loop begin");
                    control.loop.begin = control.video.currentTime;
                    return;
                }

                //set loop end
                if (control.loop.end == null) {
                    console.log("setting loop end");
                    control.messageBox.show("loop end");
                    control.loop.end = control.video.currentTime;
                    control.loop.active = true;
                    control.jumpToPosition(control.loop.begin);

                    //activate loopWorker which sets back time
                    setTimeout(control.loop.worker, 100);
                    return;
                }

                //button pressed a third time -> deactivating loop
                console.log("resetting loop");
                control.messageBox.show("loop reset");
                control.loop.reset();
            }
        },
        callFunctionByKey: function(key) {
            console.log("call function by key");
            if (control.keyToFunctionMap[key]==null) {
                return;
            }
            control.keyToFunctionMap[key]();
        },
        jumpToPosition: function(time) {
            console.log("jumping to " + time);
            control.video.currentTime = time;
        }
    };

    control.setup.init();
})();