diff --git a/youtube-scripts.js b/youtube-scripts.js index 02fd15b32..53fd42320 100644 --- a/youtube-scripts.js +++ b/youtube-scripts.js @@ -2160,12 +2160,19 @@ ImprovedTube.channelDefaultTab = function() { 7.0 SHORTCUTS -------------------------------------------------------------------------------- TODO: CONNECT & TEST +TODO: Use keyboard library ------------------------------------------------------------------------------*/ ImprovedTube.shortcuts = function() { + const printError = (section, message) => { + console.error(`%c[Improve YouTube!] ${section}%c – ${message}`, "font-weight: bold; color: red;", "font-weight: unset; color: unset;"); + } + var self = this, - keys = {}, + /** @type {Partial>} */ + pressedKey = {}, wheel = 0, + /** Is cursor under the video element */ hover = false, status_timer; @@ -2192,11 +2199,36 @@ ImprovedTube.shortcuts = function() { }, 500); } - function start(type = 'keys') { + /** + * @param {KeyboardEvent | WheelEvent} event + */ + function trigger(event) { if (document.activeElement && ['EMBED', 'INPUT', 'OBJECT', 'TEXTAREA', 'IFRAME'].indexOf(document.activeElement.tagName) !== -1 || event.target.isContentEditable) { - return false; + return; } + const buttonsSelectors = { + like: "#menu #top-level-buttons-computed ytd-toggle-button-renderer:nth-child(1) a", + dislike: "#menu #top-level-buttons-computed ytd-toggle-button-renderer:nth-child(2) a", + subscribe: "#subscribe-button a" + }; + const pressButton = ( + /** @type {keyof typeof buttonsSelectors} */ + button + ) => { + const elems = document.querySelectorAll(buttonsSelectors[button]); + const visibleButton = [].find.call(elems, elem => elem.offsetParent !== null); + // TODO throw natural error with `cause` + if (!visibleButton) { + const player = document.querySelector('#movie_player'); + // if there is no player the most likely we are not in watch page? + if (!player) return; + printError("Shortcuts", `Can't find ${button} button in visible DOM`); + return; + } + visibleButton.click(); + }; + var features = { shortcut_auto: function() { var player = document.querySelector('#movie_player'); @@ -2476,25 +2508,14 @@ ImprovedTube.shortcuts = function() { } }, shortcut_like_shortcut: function() { - var like = (document.querySelectorAll('#menu #top-level-buttons-computed ytd-toggle-button-renderer')[0]); - - if (like) { - like.click(); - } + pressButton("like") }, shortcut_dislike_shortcut: function() { - var like = (document.querySelectorAll('#menu #top-level-buttons-computed ytd-toggle-button-renderer')[1]); - - if (like) { - like.click(); - } + pressButton("dislike") }, shortcut_subscribe: function() { - var button = document.querySelector('#subscribe-button .ytd-subscribe-button-renderer'); + pressButton("subscribe") - if (button) { - button.click(); - } }, shortcut_dark_theme: function() { if (document.documentElement.hasAttribute('dark')) { @@ -2556,27 +2577,27 @@ ImprovedTube.shortcuts = function() { } }; - for (var i in features) { - if (self.isset(self.storage[i])) { - var data = JSON.parse(self.storage[i]) || {}; + for (var featureName in features) { + if (self.isset(self.storage[featureName])) { + var data = JSON.parse(self.storage[featureName]) || {}; if ( - (data.keyCode === keys.keyCode || !self.isset(data.keyCode)) && - (data.shiftKey === keys.shiftKey || !self.isset(data.shiftKey)) && - (data.ctrlKey === keys.ctrlKey || !self.isset(data.ctrlKey)) && - (data.altKey === keys.altKey || !self.isset(data.altKey)) && + (data.keyCode === pressedKey.keyCode || !self.isset(data.keyCode)) && + (data.shiftKey === pressedKey.shiftKey || !self.isset(data.shiftKey)) && + (data.ctrlKey === pressedKey.ctrlKey || !self.isset(data.ctrlKey)) && + (data.altKey === pressedKey.altKey || !self.isset(data.altKey)) && ((data.wheel > 0) === (wheel > 0) || !self.isset(data.wheel)) && - ((hover === true && (data.wheel > 0) === (wheel > 0) && Object.keys(keys).length === 0 && keys.constructor === Object) || (self.isset(data.key) || self.isset(data.altKey) || self.isset(data.ctrlKey))) + ((hover === true && (data.wheel > 0) === (wheel > 0) && Object.keys(pressedKey).length === 0) || (self.isset(data.key) || self.isset(data.altKey) || self.isset(data.ctrlKey))) ) { - if (type === 'wheel' && self.isset(data.wheel) || type === 'keys') { + if (event.type === 'wheel' && self.isset(data.wheel) || event.type === 'keydown') { event.preventDefault(); event.stopPropagation(); } - features[i](); - - if (type === 'wheel' && self.isset(data.wheel) || type === 'keys') { - return false; + features[featureName](); + + if (event.type === 'wheel' && self.isset(data.wheel) || event.type === 'keydown') { + return; } } } @@ -2589,19 +2610,14 @@ ImprovedTube.shortcuts = function() { -------------------------------------------------------------------------*/ window.addEventListener('keydown', function(event) { - keys = { - key: event.key, - keyCode: event.keyCode, - shiftKey: event.shiftKey, - ctrlKey: event.ctrlKey, - altKey: event.altKey - }; + // shortcut will be fired again and again until the key is released. To get rid of that: if (event.repeat) return; + pressedKey = event; - start(); + trigger(event); }, true); window.addEventListener('keyup', function(event) { - keys = {}; + pressedKey = {}; }, true); @@ -2614,8 +2630,9 @@ ImprovedTube.shortcuts = function() { hover = false; - for (var i = 0, l = path.length; i < l; i++) { - if (path[i].classList && path[i].classList.contains('html5-video-player')) { + // TODO: refactor with elem.target.closest(".html5-video-player") + for (let elem of path) { + if (elem.classList && elem.classList.contains('html5-video-player')) { hover = true; } } @@ -2627,7 +2644,7 @@ ImprovedTube.shortcuts = function() { window.addEventListener('wheel', function(event) { wheel = event.deltaY; - start('wheel'); + trigger(event); }, { passive: false, capture: true