diff --git a/goodtube.js b/goodtube.js index 70386b5..6bfa71c 100644 --- a/goodtube.js +++ b/goodtube.js @@ -870,7 +870,16 @@ ------------------------------------------------------------------------------------------ */ // Add keyboard shortcuts function goodTube_shortcuts_init() { + let keyDownFired = false; document.addEventListener('keydown', function (event) { + // Only do this once + if (keyDownFired) { + return; + } + + // Indicate the keydown has fired + keyDownFired = true; + // Don't do anything if we're holding control OR alt OR the command key on mac OR the "hide and mute ads" fallback is active if (event.ctrlKey || event.altKey || event.metaKey || goodTube_fallback) { return; @@ -924,7 +933,7 @@ event.stopImmediatePropagation(); // Pass the keyboard shortcut to the iframe - goodTube_player.contentWindow.postMessage('goodTube_shortcut_' + keyPressed, '*'); + goodTube_player.contentWindow.postMessage('goodTube_shortcut_keydown_' + keyPressed, '*'); } // If we're not holding down the shift key @@ -978,7 +987,7 @@ event.stopImmediatePropagation(); // Pass the keyboard shortcut to the iframe - goodTube_player.contentWindow.postMessage('goodTube_shortcut_' + keyPressed, '*'); + goodTube_player.contentWindow.postMessage('goodTube_shortcut_keydown_' + keyPressed, '*'); // Force mouse move to make sure fullscreen hides var event = new Event('mousemove'); @@ -1007,6 +1016,120 @@ } } }, true); + + + document.addEventListener('keyup', function (event) { + // Indicate the keydown has not fired + keyDownFired = false; + + // Don't do anything if we're holding control OR alt OR the command key on mac OR the "hide and mute ads" fallback is active + if (event.ctrlKey || event.altKey || event.metaKey || goodTube_fallback) { + return; + } + + // Make sure we're watching a video + if (window.location.href.indexOf('/watch?') === -1) { + return; + } + + // Get the key pressed in lower case + let keyPressed = event.key.toLowerCase(); + + // If we're not focused on a HTML form element + let focusedElement = event.srcElement; + let focusedElement_tag = false; + let focusedElement_id = false; + if (focusedElement) { + if (typeof focusedElement.nodeName !== 'undefined') { + focusedElement_tag = focusedElement.nodeName.toLowerCase(); + } + + if (typeof focusedElement.getAttribute !== 'undefined') { + focusedElement_id = focusedElement.getAttribute('id'); + } + } + + if ( + !focusedElement || + ( + focusedElement_tag.indexOf('input') === -1 && + focusedElement_tag.indexOf('label') === -1 && + focusedElement_tag.indexOf('select') === -1 && + focusedElement_tag.indexOf('textarea') === -1 && + focusedElement_tag.indexOf('fieldset') === -1 && + focusedElement_tag.indexOf('legend') === -1 && + focusedElement_tag.indexOf('datalist') === -1 && + focusedElement_tag.indexOf('output') === -1 && + focusedElement_tag.indexOf('option') === -1 && + focusedElement_tag.indexOf('optgroup') === -1 && + focusedElement_id !== 'contenteditable-root' + ) + ) { + if ( + // Speed up playback + keyPressed === '>' || + // Slow down playback + keyPressed === '<' + ) { + event.preventDefault(); + event.stopImmediatePropagation(); + + // Pass the keyboard shortcut to the iframe + goodTube_player.contentWindow.postMessage('goodTube_shortcut_keyup_' + keyPressed, '*'); + } + + // If we're not holding down the shift key + if (!event.shiftKey) { + if ( + // Prev frame (24fps calculation) + keyPressed === ',' || + // Next frame (24fps calculation) + keyPressed === '.' || + // Prev 5 seconds + keyPressed === 'arrowleft' || + // Next 5 seconds + keyPressed === 'arrowright' || + // Toggle play/pause + keyPressed === ' ' || keyPressed === 'k' || + // Toggle mute + keyPressed === 'm' || + // Toggle fullscreen + keyPressed === 'f' || + // Toggle captions + keyPressed === 'c' || + // Prev 10 seconds + keyPressed === 'j' || + // Next 10 seconds + keyPressed === 'l' || + // Start of video + keyPressed === 'home' || + // End of video + keyPressed === 'end' || + // Skip to percentage + keyPressed === '0' || + keyPressed === '1' || + keyPressed === '2' || + keyPressed === '3' || + keyPressed === '4' || + keyPressed === '5' || + keyPressed === '6' || + keyPressed === '7' || + keyPressed === '8' || + keyPressed === '9' + ) { + event.preventDefault(); + event.stopImmediatePropagation(); + + // Pass the keyboard shortcut to the iframe + goodTube_player.contentWindow.postMessage('goodTube_shortcut_keyup_' + keyPressed, '*'); + + // Force mouse move to make sure fullscreen hides + var event = new Event('mousemove'); + document.dispatchEvent(event); + } + } + } + }, true); } // Trigger a keyboard shortcut (currently unused) @@ -1252,7 +1375,7 @@ ------------------------------------------------------------------------------------------ */ // Init function goodTube_init() { - // Listen for messages from the iframe + // Listen for messages from the iframes window.addEventListener('message', goodTube_receiveMessage); // Mute and pause all Youtube videos @@ -1305,6 +1428,15 @@ return; } + // Make sure the DOM is ready, if not retry (this ensures that the message will fire eventually) + if ((document.readyState !== 'interactive' && document.readyState !== 'complete') || !document.body || !document.head) { + // Clear timeout first to solve memory leak issues + clearTimeout(goodTube_receiveMessage_timeout); + + // Create a new timeout + goodTube_receiveMessage_timeout = setTimeout(() => { goodTube_receiveMessage(event); }, 1); + } + // Proxy iframe has loaded else if (event.data === 'goodTube_proxyIframe_loaded') { goodTube_proxyIframeLoaded = true; @@ -1369,8 +1501,8 @@ goodTube_autoplay = 'true'; } - // Sync main player (only if the "hide and mute ads" fallback is inactive) - else if (event.data.indexOf('goodTube_syncMainPlayer_') !== -1 && !goodTube_fallback) { + // Sync main player (only if we're viewing a video page AND the "hide and mute ads" fallback is inactive) + else if (event.data.indexOf('goodTube_syncMainPlayer_') !== -1 && window.location.href.indexOf('/watch?') !== -1 && !goodTube_fallback) { // Target the youtube video element let youtubeVideoElement = document.querySelector('#movie_player video'); @@ -1443,7 +1575,7 @@ } } - // Enable "hide and mute ads" fallback + // Disable "hide and mute ads" fallback else if (event.data === 'goodTube_fallback_disable') { goodTube_fallback = false; @@ -2309,7 +2441,7 @@ focusedElement_id !== 'contenteditable-root' ) ) { - if (keyPressed === ' ' || keyPressed === 'm' || keyPressed === 'i') { + if (keyPressed === ' ' || keyPressed === 'k' || keyPressed === 'm' || keyPressed === 'i') { event.preventDefault(); event.stopImmediatePropagation(); } @@ -2571,9 +2703,6 @@ // Init when DOM is ready let goodTube_iframe_init_domReady_timeout = setTimeout(() => {}, 0); function goodTube_iframe_init_domReady() { - // Add the main styles - goodTube_iframe_style(); - // Get the iframe API goodTube_iframe_api = document.getElementById('movie_player'); @@ -2594,6 +2723,9 @@ return; } + // Add the main styles + goodTube_iframe_style(); + // Add custom buttons goodTube_iframe_addCustomButtons(); @@ -2603,6 +2735,9 @@ // Add keyboard shortcuts goodTube_iframe_addKeyboardShortcuts(); + // Support double speed shortcuts + goodTube_iframe_supportDoubleSpeed_init(); + // Support picture in picture goodTube_iframe_pip(); @@ -2659,6 +2794,9 @@ goodTube_fallback = true; window.top.postMessage('goodTube_fallback_enable', '*'); + // Support double speed shortcuts + goodTube_iframe_supportDoubleSpeed_init(); + // Remove the fullscreen timeout (this stops it looping) clearTimeout(goodTube_iframe_fullscreen_timeout); } @@ -2671,6 +2809,9 @@ goodTube_fallback = false; window.top.postMessage('goodTube_fallback_disable', '*'); + // Support double speed shortcuts + goodTube_iframe_supportDoubleSpeed_init(); + // Remove the fullscreen timeout (this stops it looping) clearTimeout(goodTube_iframe_fullscreen_timeout); } @@ -2761,6 +2902,11 @@ top: 0 !important; } + /* Disable click events on the top area */ + .ytp-chrome-top { + pointer-events: none !important; + } + /* Always show the next button */ .ytp-next-button { opacity: 1 !important; @@ -3007,20 +3153,32 @@ // Add keyboard shortcuts function goodTube_iframe_addKeyboardShortcuts() { + let keyDownFired = false; document.addEventListener('keydown', function (event) { + // Only do this once + if (keyDownFired) { + return; + } + + // Indicate the keydown has fired + keyDownFired = true; + // Don't do anything if we're holding control OR alt OR the command key on mac OR the "hide and mute ads" fallback is active if (event.ctrlKey || event.altKey || event.metaKey || goodTube_fallback) { return; } + // Get the key pressed (in lowercase) + let keyPressed = event.key.toLowerCase(); + // Theater mode (t) - if (event.key === 't') { + if (keyPressed === 't') { // Tell the top window to toggle theater mode window.top.postMessage('goodTube_theater', '*'); } // Picture in picture (i) - if (event.key === 'i') { + if (keyPressed === 'i') { let pipButton = document.querySelector('.ytp-pip-button'); if (pipButton) { pipButton.click(); @@ -3028,17 +3186,496 @@ } // Prev video (shift+p) - else if (event.key.toLowerCase() === 'p' && event.shiftKey) { + else if (keyPressed === 'p' && event.shiftKey) { // Tell the top window to go to the previous video window.top.postMessage('goodTube_prevVideo', '*'); } // Next video (shift+n) - else if (event.key.toLowerCase() === 'n' && event.shiftKey) { + else if (keyPressed === 'n' && event.shiftKey) { // Tell the top window to go to the next video window.top.postMessage('goodTube_nextVideo', '*'); } }); + + document.addEventListener('keyup', function (event) { + // Indicate the keydown has not fired + keyDownFired = false; + }); + } + + // Support double speed shortcuts + let goodTube_iframe_supportDoubleSpeed_holdTimeout = setTimeout(() => {}, 0); + let goodTube_iframe_supportDoubleSpeed_doublePlaybackRate = false; + let goodTube_iframe_supportDoubleSpeed_currentPlaybackRate = -1; + let goodTube_iframe_supportDoubleSpeed_keyDownFired = false; + let goodTube_iframe_supportDoubleSpeed_mouseDownFired = false; + let goodTube_iframe_supportDoubleSpeed_allowNextClick = false; + let goodTube_iframe_supportDoubleSpeed_videoElement = document.querySelector('video'); + let goodTube_iframe_supportDoubleSpeed_doubleSpeedElement = document.querySelector('.goodTube_doubleSpeed'); + + function goodTube_iframe_supportDoubleSpeed_keydown(event) { + // If the "hide mute ads" fallback is active, don't do anything + if (goodTube_fallback) { + return; + } + + // Make sure we're not holding down the mouse + if (goodTube_iframe_supportDoubleSpeed_mouseDownFired) { + event.preventDefault(); + event.stopImmediatePropagation(); + return; + } + + // Don't do anything if we're holding control OR alt OR the command key on mac OR the "hide and mute ads" fallback is active + if (event.ctrlKey || event.altKey || event.metaKey || goodTube_fallback) { + return; + } + + // Get the key pressed (in lowercase) + let keyPressed = event.key.toLowerCase(); + + // 2x playback rate + if (keyPressed === ' ' || keyPressed === 'k') { + // Prevent default actions + event.preventDefault(); + event.stopImmediatePropagation(); + + // Make sure 2x playback isn't already active + if (goodTube_iframe_supportDoubleSpeed_doublePlaybackRate) { + return; + } + + // Only do this once + if (goodTube_iframe_supportDoubleSpeed_keyDownFired) { + return; + } + + // Indicate that the keydown has fired + goodTube_iframe_supportDoubleSpeed_keyDownFired = true; + + // Save the current playback rate + goodTube_iframe_supportDoubleSpeed_currentPlaybackRate = goodTube_iframe_api.getPlaybackRate(); + + // Clear the hold timeout + clearTimeout(goodTube_iframe_supportDoubleSpeed_holdTimeout); + + // Create a timeout to move into 2x playback rate after 1s + goodTube_iframe_supportDoubleSpeed_holdTimeout = setTimeout(() => { + // Set to 2x playback rate + goodTube_iframe_api.setPlaybackRate(2); + + // Play the video + goodTube_iframe_api.playVideo(); + + // Show the UI element + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.display = 'block'; + + // Indicate that 2x playback rate happened + goodTube_iframe_supportDoubleSpeed_doublePlaybackRate = true; + }, 1000); + } + } + + function goodTube_iframe_supportDoubleSpeed_keypress(event) { + // If the "hide mute ads" fallback is active, don't do anything + if (goodTube_fallback) { + return; + } + + // Make sure we're not holding down the mouse + if (goodTube_iframe_supportDoubleSpeed_mouseDownFired) { + event.preventDefault(); + event.stopImmediatePropagation(); + return; + } + + // Don't do anything if we're holding control OR alt OR the command key on mac OR the "hide and mute ads" fallback is active + if (event.ctrlKey || event.altKey || event.metaKey || goodTube_fallback) { + return; + } + + // Get the key pressed (in lowercase) + let keyPressed = event.key.toLowerCase(); + + // 2x playback rate + if (keyPressed === ' ' || keyPressed === 'k') { + // Prevent default actions + event.preventDefault(); + event.stopImmediatePropagation(); + } + } + + function goodTube_iframe_supportDoubleSpeed_keyup(event) { + // If the "hide mute ads" fallback is active, don't do anything + if (goodTube_fallback) { + return; + } + + // Make sure we're not holding down the mouse + if (goodTube_iframe_supportDoubleSpeed_mouseDownFired) { + event.preventDefault(); + event.stopImmediatePropagation(); + return; + } + + // Don't do anything if we're holding control OR alt OR the command key on mac OR the "hide and mute ads" fallback is active + if (event.ctrlKey || event.altKey || event.metaKey || goodTube_fallback) { + return; + } + + // Get the key pressed (in lowercase) + let keyPressed = event.key.toLowerCase(); + + // 2x playback rate + if (keyPressed === ' ' || keyPressed === 'k') { + // Clear the hold timeout + clearTimeout(goodTube_iframe_supportDoubleSpeed_holdTimeout); + + // If double playback rate did not happen + if (!goodTube_iframe_supportDoubleSpeed_doublePlaybackRate) { + // Click the video element (we must do it this way, it's the only reliable method) + goodTube_iframe_supportDoubleSpeed_allowNextClick = true; + goodTube_iframe_supportDoubleSpeed_videoElement.dispatchEvent(new PointerEvent('mousedown', { bubbles: true, cancelable: true })); + goodTube_iframe_supportDoubleSpeed_videoElement.dispatchEvent(new PointerEvent('click', { bubbles: true, cancelable: true })); + goodTube_iframe_supportDoubleSpeed_videoElement.dispatchEvent(new PointerEvent('mouseup', { bubbles: true, cancelable: true })); + goodTube_iframe_supportDoubleSpeed_allowNextClick = false; + } + // Otherwise, double playback rate did happen + else { + // Restore the playback rate + goodTube_iframe_api.setPlaybackRate(goodTube_iframe_supportDoubleSpeed_currentPlaybackRate); + + // Hide the UI element + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.display = 'none'; + + // Indicate that the double playback rate has not happened + goodTube_iframe_supportDoubleSpeed_doublePlaybackRate = false; + } + + // Indicate that the keydown has not fired + goodTube_iframe_supportDoubleSpeed_keyDownFired = false; + + // Prevent default actions + event.preventDefault(); + event.stopImmediatePropagation(); + } + } + + function goodTube_iframe_supportDoubleSpeed_mouseDownTouchStart(event) { + // If we're allowing the next click, don't do anything + if (goodTube_iframe_supportDoubleSpeed_allowNextClick) { + return; + } + + // If the "hide mute ads" fallback is active, don't do anything + if (goodTube_fallback) { + return; + } + + // Make sure we're not holding down spacebar + if (goodTube_iframe_supportDoubleSpeed_keyDownFired) { + event.preventDefault(); + event.stopImmediatePropagation(); + return; + } + + // Indicate that the mousedown has fired + goodTube_iframe_supportDoubleSpeed_mouseDownFired = true; + + // Prevent default actions + event.preventDefault(); + event.stopImmediatePropagation(); + + // Save the current playback rate + goodTube_iframe_supportDoubleSpeed_currentPlaybackRate = goodTube_iframe_api.getPlaybackRate(); + + // Clear the hold timeout + clearTimeout(goodTube_iframe_supportDoubleSpeed_holdTimeout); + + // Create a timeout to move into 2x playback rate after 1s + goodTube_iframe_supportDoubleSpeed_holdTimeout = setTimeout(() => { + // Set to 2x playback rate + goodTube_iframe_api.setPlaybackRate(2); + + // Play the video + goodTube_iframe_api.playVideo(); + + // Show the UI element + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.display = 'block'; + + // Indicate that 2x playback rate happened + goodTube_iframe_supportDoubleSpeed_doublePlaybackRate = true; + }, 1000); + } + + function goodTube_iframe_supportDoubleSpeed_click(event) { + // If we're allowing the next click, don't do anything + if (goodTube_iframe_supportDoubleSpeed_allowNextClick) { + return; + } + + // If the "hide mute ads" fallback is active, don't do anything + if (goodTube_fallback) { + return; + } + + // Prevent default actions + event.preventDefault(); + event.stopImmediatePropagation(); + } + + function goodTube_iframe_supportDoubleSpeed_mouseUpTouchEnd(event) { + // If we're allowing the next click, don't do anything + if (goodTube_iframe_supportDoubleSpeed_allowNextClick) { + return; + } + + // If the "hide mute ads" fallback is active, don't do anything + if (goodTube_fallback) { + return; + } + + // Make sure we're not holding down spacebar + if (goodTube_iframe_supportDoubleSpeed_keyDownFired) { + event.preventDefault(); + event.stopImmediatePropagation(); + return; + } + + // Clear the hold timeout + clearTimeout(goodTube_iframe_supportDoubleSpeed_holdTimeout); + + // If double playback rate did not happen + if (!goodTube_iframe_supportDoubleSpeed_doublePlaybackRate) { + // Click the video element (we must do it this way, it's the only reliable method) + goodTube_iframe_supportDoubleSpeed_allowNextClick = true; + goodTube_iframe_supportDoubleSpeed_videoElement.dispatchEvent(new PointerEvent('mousedown', { bubbles: true, cancelable: true })); + goodTube_iframe_supportDoubleSpeed_videoElement.dispatchEvent(new PointerEvent('click', { bubbles: true, cancelable: true })); + goodTube_iframe_supportDoubleSpeed_videoElement.dispatchEvent(new PointerEvent('mouseup', { bubbles: true, cancelable: true })); + goodTube_iframe_supportDoubleSpeed_allowNextClick = false; + } + // Otherwise, double playback rate did happen + else { + // Restore the playback rate + goodTube_iframe_api.setPlaybackRate(goodTube_iframe_supportDoubleSpeed_currentPlaybackRate); + + // Hide the UI element + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.display = 'none'; + + // Indicate that the double playback rate has not happened + goodTube_iframe_supportDoubleSpeed_doublePlaybackRate = false; + } + + // Indicate that the mousedown has not fired + goodTube_iframe_supportDoubleSpeed_mouseDownFired = false; + + // Prevent default actions + event.preventDefault(); + event.stopImmediatePropagation(); + } + + function goodTube_iframe_supportDoubleSpeed_mouseOutTouchCancel(event) { + // If the "hide mute ads" fallback is active, don't do anything + if (goodTube_fallback) { + return; + } + + // Make sure we're not holding down spacebar + if (goodTube_iframe_supportDoubleSpeed_keyDownFired) { + event.preventDefault(); + event.stopImmediatePropagation(); + return; + } + + // Prevent default actions + event.preventDefault(); + event.stopImmediatePropagation(); + + // Clear the hold timeout + clearTimeout(goodTube_iframe_supportDoubleSpeed_holdTimeout); + + // If double playback rate happened + if (goodTube_iframe_supportDoubleSpeed_doublePlaybackRate) { + // Restore the playback rate + goodTube_iframe_api.setPlaybackRate(goodTube_iframe_supportDoubleSpeed_currentPlaybackRate); + + // Hide the UI element + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.display = 'none'; + + // Indicate that the double playback rate has not happened + goodTube_iframe_supportDoubleSpeed_doublePlaybackRate = false; + } + + // Indicate that the mousedown has not fired + goodTube_iframe_supportDoubleSpeed_mouseDownFired = false; + } + + let goodTube_iframe_supportDoubleSpeed_init_timeout = setTimeout(() => {}, 0); + function goodTube_iframe_supportDoubleSpeed_init() { + /* Setup vars + -------------------------------------------------- */ + clearTimeout(goodTube_iframe_supportDoubleSpeed_holdTimeout); + goodTube_iframe_supportDoubleSpeed_holdTimeout = setTimeout(() => {}, 0); + goodTube_iframe_supportDoubleSpeed_doublePlaybackRate = false; + goodTube_iframe_supportDoubleSpeed_currentPlaybackRate = 1; + goodTube_iframe_supportDoubleSpeed_keyDownFired = false; + goodTube_iframe_supportDoubleSpeed_mouseDownFired = false; + goodTube_iframe_supportDoubleSpeed_videoElement = document.querySelector('video'); + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement = document.querySelector('.goodTube_doubleSpeed'); + + + /* If the "hide and mute ads" fallback is active, disable this + -------------------------------------------------- */ + if (goodTube_fallback) { + document.removeEventListener('keydown', goodTube_iframe_supportDoubleSpeed_keydown, true); + document.removeEventListener('keypress', goodTube_iframe_supportDoubleSpeed_keypress, true); + document.removeEventListener('keyup', goodTube_iframe_supportDoubleSpeed_keyup, true); + + if (goodTube_iframe_supportDoubleSpeed_videoElement) { + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('mousedown', goodTube_iframe_supportDoubleSpeed_mouseDownTouchStart, true); + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('touchstart', goodTube_iframe_supportDoubleSpeed_mouseDownTouchStart, true); + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('click', goodTube_iframe_supportDoubleSpeed_click, true); + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('mouseup', goodTube_iframe_supportDoubleSpeed_mouseUpTouchEnd, true); + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('touchend', goodTube_iframe_supportDoubleSpeed_mouseUpTouchEnd, true); + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('mouseout', goodTube_iframe_supportDoubleSpeed_mouseOutTouchCancel, true); + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('touchcancel', goodTube_iframe_supportDoubleSpeed_mouseOutTouchCancel, true); + } + + if (goodTube_iframe_supportDoubleSpeed_doubleSpeedElement) { + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.display = 'none'; + } + + return; + } + + + /* Make sure the video element exists + -------------------------------------------------- */ + if (!goodTube_iframe_supportDoubleSpeed_videoElement) { + // Clear timeout first to solve memory leak issues + clearTimeout(goodTube_iframe_supportDoubleSpeed_init_timeout); + + // Create a new timeout + goodTube_iframe_supportDoubleSpeed_init_timeout = setTimeout(goodTube_iframe_supportDoubleSpeed, 100); + + return; + } + + + /* Make sure the API is all good + -------------------------------------------------- */ + // Get the iframe API + goodTube_iframe_api = document.getElementById('movie_player'); + + if (!goodTube_iframe_api || typeof goodTube_iframe_api.getPlaybackRate !== 'function' || typeof goodTube_iframe_api.setPlaybackRate !== 'function' || typeof goodTube_iframe_api.playVideo !== 'function') { + // Clear timeout first to solve memory leak issues + clearTimeout(goodTube_iframe_supportDoubleSpeed_init_timeout); + + // Create a new timeout + goodTube_iframe_supportDoubleSpeed_init_timeout = setTimeout(goodTube_iframe_supportDoubleSpeed, 100); + + return; + } + + + /* Restore the playback speed to start with + -------------------------------------------------- */ + goodTube_iframe_api.setPlaybackRate(goodTube_iframe_supportDoubleSpeed_currentPlaybackRate); + + + /* Add the UI (2x speed) - first load only + -------------------------------------------------- */ + // Only add the UI it doesn't exist already + if (!goodTube_iframe_supportDoubleSpeed_doubleSpeedElement) { + // Create the element + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement = document.createElement('div'); + + // Add the classes + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.classList.add('goodTube_doubleSpeed'); + + // Style the element (this is required for fullscreen mode) + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.position = 'relative'; + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.zIndex = '999'; + + // Hide the element + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.display = 'none'; + + // Populate the element + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.innerHTML = ` +
+
+
2x
+
+ + + +
+
+
+ `; + + // Add the element to the page + let targetElement = document.querySelector('.html5-video-player'); + if (targetElement) { + targetElement.appendChild(goodTube_iframe_supportDoubleSpeed_doubleSpeedElement); + } + } + // Otherwise, hide the UI to start + else { + goodTube_iframe_supportDoubleSpeed_doubleSpeedElement.style.display = 'none'; + } + + + /* Key down + -------------------------------------------------- */ + document.removeEventListener('keydown', goodTube_iframe_supportDoubleSpeed_keydown, true); + document.addEventListener('keydown', goodTube_iframe_supportDoubleSpeed_keydown, true); + + + /* Key press + -------------------------------------------------- */ + document.removeEventListener('keypress', goodTube_iframe_supportDoubleSpeed_keypress, true); + document.addEventListener('keypress', goodTube_iframe_supportDoubleSpeed_keypress, true); + + + /* Key up + -------------------------------------------------- */ + document.removeEventListener('keyup', goodTube_iframe_supportDoubleSpeed_keyup, true); + document.addEventListener('keyup', goodTube_iframe_supportDoubleSpeed_keyup, true); + + + /* Mouse down / touch start + -------------------------------------------------- */ + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('mousedown', goodTube_iframe_supportDoubleSpeed_mouseDownTouchStart, true); + goodTube_iframe_supportDoubleSpeed_videoElement.addEventListener('mousedown', goodTube_iframe_supportDoubleSpeed_mouseDownTouchStart, true); + + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('touchstart', goodTube_iframe_supportDoubleSpeed_mouseDownTouchStart, true); + goodTube_iframe_supportDoubleSpeed_videoElement.addEventListener('touchstart', goodTube_iframe_supportDoubleSpeed_mouseDownTouchStart, true); + + + /* Click + -------------------------------------------------- */ + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('click', goodTube_iframe_supportDoubleSpeed_click, true); + goodTube_iframe_supportDoubleSpeed_videoElement.addEventListener('click', goodTube_iframe_supportDoubleSpeed_click, true); + + + /* Mouse up / touch end + -------------------------------------------------- */ + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('mouseup', goodTube_iframe_supportDoubleSpeed_mouseUpTouchEnd, true); + goodTube_iframe_supportDoubleSpeed_videoElement.addEventListener('mouseup', goodTube_iframe_supportDoubleSpeed_mouseUpTouchEnd, true); + + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('touchend', goodTube_iframe_supportDoubleSpeed_mouseUpTouchEnd, true); + goodTube_iframe_supportDoubleSpeed_videoElement.addEventListener('touchend', goodTube_iframe_supportDoubleSpeed_mouseUpTouchEnd, true); + + + /* Mouse out / touch cancel + -------------------------------------------------- */ + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('mouseout', goodTube_iframe_supportDoubleSpeed_mouseOutTouchCancel, true); + goodTube_iframe_supportDoubleSpeed_videoElement.addEventListener('mouseout', goodTube_iframe_supportDoubleSpeed_mouseOutTouchCancel, true); + + goodTube_iframe_supportDoubleSpeed_videoElement.removeEventListener('touchcancel', goodTube_iframe_supportDoubleSpeed_mouseOutTouchCancel, true); + goodTube_iframe_supportDoubleSpeed_videoElement.addEventListener('touchcancel', goodTube_iframe_supportDoubleSpeed_mouseOutTouchCancel, true); } // Load a video @@ -3055,16 +3692,29 @@ 'startSeconds': startSeconds } ); + + // Support double speed shortcuts + goodTube_iframe_supportDoubleSpeed_init(); } } // Receive a message from the parent window + let goodTube_iframe_receiveMessage_timeout = setTimeout(() => {}, 0); function goodTube_iframe_receiveMessage(event) { // Make sure some data exists if (typeof event.data !== 'string') { return; } + // Make sure the DOM is ready, if not retry (this ensures that the message will fire eventually) + if ((document.readyState !== 'interactive' && document.readyState !== 'complete') || !document.body || !document.head) { + // Clear timeout first to solve memory leak issues + clearTimeout(goodTube_iframe_receiveMessage_timeout); + + // Create a new timeout + goodTube_iframe_receiveMessage_timeout = setTimeout(() => { goodTube_iframe_receiveMessage(event); }, 1); + } + // Load video if (event.data.indexOf('goodTube_load_') !== -1) { let bits = event.data.replace('goodTube_load_', '').split('|||'); @@ -3120,20 +3770,20 @@ // Show or hide the end screen thumbnails else if (event.data === 'goodTube_endScreen_show') { - if (document.body.classList.contains('goodTube_hideEndScreen')) { + if (document.body && document.body.classList.contains('goodTube_hideEndScreen')) { document.body.classList.remove('goodTube_hideEndScreen'); } } else if (event.data === 'goodTube_endScreen_hide') { - if (!document.body.classList.contains('goodTube_hideEndScreen')) { + if (document.body && !document.body.classList.contains('goodTube_hideEndScreen')) { document.body.classList.add('goodTube_hideEndScreen'); } } - // Keyboard shortcut - else if (event.data.indexOf('goodTube_shortcut_') !== -1) { + // Keyboard shortcut (keydown) + else if (event.data.indexOf('goodTube_shortcut_keydown_') !== -1) { // Get the key pressed - let keyPressed = event.data.replace('goodTube_shortcut_', ''); + let keyPressed = event.data.replace('goodTube_shortcut_keydown_', ''); // Target the player let player = document.querySelector('video'); @@ -3227,12 +3877,15 @@ // Toggle play/pause if (keyPressed === ' ' || keyPressed === 'k') { - if (player.paused || player.ended) { - player.play(); - } - else { - player.pause(); - } + // Simulate a spacebar keydown event on the