first time using this site. I have been using this mini player youtube extension for a while. Its not on the chrome store anymore but I have a folder of it from the developer. The developer isn’t working on this anymore so I am asking here. I like this extension but I have been having this issue whenever I open a youtube link externally, it throws off the youtube player when scrolling. It’s not the biggest issue in the world but I am just seeing if there is something that can be done to fix it. I am not a big coder so I am asking here. I am not sure if this can be fixed or its something conflicting with new youtube since this extension is pretty old. I pasted some links with the full code and some pictures.
Thank you
“use strict”;
(function() {
let DOCK_DOCKED = 'docked', DOCK_FLOATING = 'floating';
let PAGE_CHANNEL = 'channel', PAGE_HOME = 'home', PAGE_SEARCH = 'search', PAGE_SUBSCRIPTIONS = 'subscriptions', PAGE_WATCH = 'watch';
let SCREEN_PADDING = 16;
let currentAnimation, currentAnimationCancelled, currentDockMode, currentVideoId, currentPage;
let queryParams, isNewPage;
let screen, screenHeader, screenTitle, screenButtonClose, screenButtonMove, screenButtonResizeTopLeft, screenButtonResizeTopRight, screenButtonResizeBottomLeft, screenButtonResizeBottomRight, isScreenMove, isScreenResize;
let iframe, iframeVideo, moviePlayer, moviePlayerObserver = new MutationObserver(onMoviePlayerObserve), playerAPI, video, videoTitle;
let intervalCurrentTime, intervalIframeInitialize, intervalVideoInitialize;
let localStorage = window.localStorage['rjgtav.youtube-mini-player'] ? JSON.parse(window.localStorage['rjgtav.youtube-mini-player']) : {};
// Thanks to https://www.reddit.com/user/roombascj
// Workaround: Chrome doesn't fire 'resize' event when it adds the scrollbar
// Source: https://pastebin.com/tgDxgPza
let windowResizeListener = document.createElement('iframe');
windowResizeListener.id = 'rj-resize-listener';
windowResizeListener.onload = () => windowResizeListener.contentWindow.addEventListener('resize', onWindowResize);
window.addEventListener('scroll', onWindowScroll);
document.addEventListener('DOMContentLoaded', onPageLoad);
document.addEventListener('spfdone', onPageLoad);
// #YoutubeMaterialDesign
document.addEventListener('yt-navigate-finish', onPageLoad);
document.addEventListener('yt-update-title', onPageTitle);
function initializeIframe () {
if (iframe != null) return;
screen = document.createElement('div');
screen.id = 'rj-miniplayer-screen';
screen.style.position = 'fixed';
document.body.appendChild(screen);
screenHeader = document.createElement('div');
screenHeader.className = 'rj-miniplayer-header';
screen.appendChild(screenHeader);
screenButtonMove = document.createElement('div');
screenButtonMove.className = 'rj-miniplayer-move';
screenButtonMove.title = 'Move';
screenButtonMove.style.backgroundImage = "url('" + chrome.extension.getURL('assets/button/move.png') + "')";
screenButtonMove.addEventListener('mousedown', onScreenMouseDown);
screenHeader.appendChild(screenButtonMove);
screenTitle = document.createElement('div');
screenTitle.className = 'rj-miniplayer-title';
screenTitle.addEventListener('click', onScreenTitleClick);
screenHeader.appendChild(screenTitle);
screenButtonClose = document.createElement('div');
screenButtonClose.className = 'rj-miniplayer-close';
screenButtonClose.title = 'Close';
screenButtonClose.style.backgroundImage = "url('" + chrome.extension.getURL('assets/button/close.png') + "')";
screenButtonClose.addEventListener('click', onScreenButtonCloseClick);
screenHeader.appendChild(screenButtonClose);
screenButtonResizeBottomRight = document.createElement('div');
screenButtonResizeBottomRight.className = 'rj-miniplayer-resize bottom-right';
screenButtonResizeBottomRight.title = 'Resize';
screenButtonResizeBottomRight.addEventListener('mousedown', onScreenMouseDown);
screen.appendChild(screenButtonResizeBottomRight);
screenButtonResizeTopRight = document.createElement('div');
screenButtonResizeTopRight.className = 'rj-miniplayer-resize top-right';
screenButtonResizeTopRight.title = 'Resize';
screenButtonResizeTopRight.addEventListener('mousedown', onScreenMouseDown);
screenHeader.appendChild(screenButtonResizeTopRight);
iframe = document.createElement('iframe');
iframe.setAttribute('allowfullscreen', '1');
iframe.setAttribute('frameborder', '0');
iframe.onload = onIframeLoad;
screen.appendChild(iframe);
}
function updateIframeSource () {
if (iframe == null) return;
// If it's the same video, we keep playing it
if (currentVideoId == queryParams.v) return;
currentVideoId = queryParams.v;
iframeVideo = null;
iframe.contentWindow.location.replace('https://www.youtube.com/embed/' + currentVideoId + '?autoplay=1&modestbranding=1&rel=0&showinfo=0');
// We periodically update the video.currentTime, so the Share button on Youtube works as expected
// Update: this has a high performance impact :/
//clearInterval(intervalCurrentTime);
//intervalCurrentTime = setInterval(() => iframeVideo ? video.currentTime = iframeVideo.currentTime : null, 1000);
}
function initializeVideo() {
if (video != null) {
video.removeEventListener('playing', onVideoPlay);
video.removeEventListener('play', onVideoPlay);
video.removeEventListener('seeking', onVideoSeek);
video.removeEventListener('seeked', onVideoSeek);
}
// Prevent Youtube from auto-playing
// Workaround: we use intervals so this still runs when the tab isn't focused
clearInterval(intervalVideoInitialize);
intervalVideoInitialize = setInterval(() => {
// Hide moviePlayer to increase performance
moviePlayer = document.getElementById('movie_player');
moviePlayerObserver.observe(moviePlayer, { childList: true });
if (moviePlayer == null) return;
playerAPI = moviePlayer.parentNode;
video = playerAPI.getElementsByTagName('video')[0];
if (video == null) return;
video.pause();
video.volume = 0;
video.addEventListener('playing', onVideoPlay);
video.addEventListener('play', onVideoPlay);
video.addEventListener('seeking', onVideoSeek);
video.addEventListener('seeked', onVideoSeek);
clearInterval(intervalVideoInitialize);
}, 25);
}
function _updateScreenState(noAnimation, forceAnimation) {
if (playerAPI == null || screen == null) return;
let dockMode = (currentPage != PAGE_WATCH || window.scrollY > screen.offsetHeight / 2) ? DOCK_FLOATING : DOCK_DOCKED;
// Cancel current animation
if (currentDockMode != dockMode) {
screen.removeEventListener('transitionend', onScreenTransitionEnd);
currentAnimationCancelled = currentAnimation != null;
currentAnimation = null;
}
let positionEnd = _loadScreenState(dockMode), positionStart;
let isAnimating = forceAnimation || currentDockMode != dockMode || currentAnimation != null;
currentDockMode = dockMode;
if (isAnimating) {
// Update animation states
if (currentAnimation == null) {
// Note: updating the height/width causes a stutter/redraw on the iframe. Therefore we do it at the beginning, where it is less noticeable
positionStart = screen.getBoundingClientRect();
screen.style.height = positionEnd.height + 'px';
screen.style.width = positionEnd.width + 'px';
screen.style.transform = 'scale(' + (positionStart.width / positionEnd.width) + ')';
} else {
positionStart = currentAnimation.start;
}
currentAnimation = { end: positionEnd, start: positionStart, onScreenTransitionEnd: currentAnimation ? currentAnimation.onScreenTransitionEnd : null };
if (currentDockMode == DOCK_DOCKED) {
iframe.style.boxShadow = 'initial';
}
// Animate
if (!noAnimation) {
window.requestAnimationFrame(() => window.requestAnimationFrame(() => window.requestAnimationFrame(() => {
// Abort animation if cancelled
if (currentAnimation == null)
return;
screen.style.transition = '.2s ease-in-out';
if (currentAnimationCancelled) {
screen.style.transform = '';
onScreenTransitionEnd();
} else {
screen.style.transform =
'translateX(' + (currentAnimation.end.left - currentAnimation.start.left) + 'px)' +
'translateY(' + (currentAnimation.end.top - currentAnimation.start.top) + 'px)';
}
// Prevent multiple addEventListener during a single transition (e.g. when scrolling, this method is spammed)
if (!currentAnimation || !currentAnimation.onScreenTransitionEnd) {
currentAnimation.onScreenTransitionEnd = onScreenTransitionEnd;
screen.addEventListener('transitionend', onScreenTransitionEnd);
}
})));
} else {
onScreenTransitionEnd();
}
} else {
// When the movie is docked at the top of the watch page
screen.style.transition = '';
screen.style.height = positionEnd.height + 'px';
screen.style.width = positionEnd.width + 'px';
screen.style.top = positionEnd.top + 'px';
screen.style.left = positionEnd.left + 'px';
}
}
function _updateScreenVisibility(visible) {
if (visible) {
if (moviePlayer) moviePlayer.style.display = 'none';
screen.style.display = 'initial';
_updateScreenState(true);
} else {
if (moviePlayer) moviePlayer.style.display = 'initial';
screen.style.display = 'none';
}
}
function _calculateDefaultScreenState(dockMode) {
let state = {};
state.height = playerAPI.offsetHeight / 2;
state.width = playerAPI.offsetWidth / 2;
// #YoutubeMaterialDesign - when switching to the Home page and such
if (currentPage != PAGE_WATCH && (state.width == 0 || state.height == 0)) {
let backupState = _loadScreenState(DOCK_DOCKED, PAGE_WATCH);
state.height = backupState.height / 2;
state.width = backupState.width / 2;
}
if (currentPage == PAGE_CHANNEL || currentPage == PAGE_HOME || currentPage == PAGE_SUBSCRIPTIONS) {
state.top = (window.innerHeight - state.height - SCREEN_PADDING);
state.left = (window.innerWidth - state.width - SCREEN_PADDING - SCREEN_PADDING);
}
if (currentPage == PAGE_SEARCH) {
state.top = (window.innerHeight - state.height) / 2;
state.left = (window.innerWidth - state.width - SCREEN_PADDING - SCREEN_PADDING);
}
if (currentPage == PAGE_WATCH) {
if (dockMode == DOCK_FLOATING) {
let sidebar = document.getElementById('watch7-sidebar') || document.getElementById('related'); // #YoutubeMaterialDesign
state.top = (window.innerHeight - state.height) / 2;
state.left = (sidebar.getBoundingClientRect().left - (state.width - sidebar.offsetWidth) / 2);
} else {
state = playerAPI.getBoundingClientRect();
}
}
return _saveScreenState(dockMode, state.height, state.width, state.left, state.top);
}
function _loadScreenState(dockMode, page) {
let state = localStorage.screenState = localStorage.screenState || {};
state = state[page || currentPage] = state[page || currentPage] || {};
state = state[dockMode] = state[dockMode] || {};
// Load default state, if none was previously saved
if ((currentPage == PAGE_WATCH && dockMode == DOCK_DOCKED) || state.height == null || state.width == null || state.left == null || state.top == null)
state = _calculateDefaultScreenState(dockMode);
// In case the screen is outside the screen, move it back inside
if (dockMode == DOCK_FLOATING) {
if (state.left + state.width + SCREEN_PADDING > window.innerWidth)
state.left = window.innerWidth - state.width - SCREEN_PADDING;
if (state.left < SCREEN_PADDING)
state.left = SCREEN_PADDING;
if (state.top + state.height + SCREEN_PADDING > window.innerHeight)
state.top = window.innerHeight - state.height - SCREEN_PADDING;
if (state.top < 100)
state.top = 100;
}
return state;
}
function _saveScreenState(dockMode, height, width, left, top) {
let state = localStorage.screenState = localStorage.screenState || {};
state = state[currentPage] = state[currentPage] || {};
state = state[dockMode] = state[dockMode] || {};
// Save to localStorage
state.height = height;
state.width = width;
state.left = left;
state.top = top;
window.localStorage['rjgtav.youtube-mini-player'] = JSON.stringify(localStorage);
return state;
}
function _getBoundingDocumentRect(e) {
var box = e.getBoundingClientRect();
var body = document.body;
var docEl = document.documentElement;
var scrollTop = window.pageYOffset || docEl.scrollTop || body.scrollTop;
var scrollLeft = window.pageXOffset || docEl.scrollLeft || body.scrollLeft;
var clientTop = docEl.clientTop || body.clientTop || 0;
var clientLeft = docEl.clientLeft || body.clientLeft || 0;
var height = e.offsetHeight;
var width = e.offsetWidth;
var top = box.top + scrollTop - clientTop;
var left = box.left + scrollLeft - clientLeft;
return { height, width, top, left };
}
//---------------------------------------------------------------------
// Event Handlers
//---------------------------------------------------------------------
// Thanks to http://stackoverflow.com/questions/34077641/how-to-detect-page-navigation-on-youtube-and-modify-html-before-page-is-rendered
function onPageLoad() {
let isFirstTime = iframe == null;
// Parse query params
queryParams = {};
let params = window.location.search.substring(1).split('&');
params.forEach((param) => {
let pair = param.split('=');
queryParams[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);
})
// Parse current page
currentPage = null;
currentPage = currentPage == null && queryParams.v != null ? PAGE_WATCH : currentPage;
currentPage = currentPage == null && queryParams.search_query != null ? PAGE_SEARCH : currentPage;
currentPage = currentPage == null && window.location.href.indexOf('/channel/') != -1 ? PAGE_CHANNEL : currentPage;
currentPage = currentPage == null && window.location.href.indexOf('/feeed/subscriptions') != -1 ? PAGE_SUBSCRIPTIONS : currentPage;
currentPage = currentPage == null ? PAGE_HOME : currentPage;
if (currentPage == PAGE_WATCH) {
initializeIframe();
initializeVideo();
onPageTitle();
updateIframeSource();
// Make sure the mini player is visible (in case it was hidden by the user)
_updateScreenVisibility(true);
}
document.body.appendChild(windowResizeListener); // Note: we need to append this before the window.onload
window.requestAnimationFrame(() => {
_updateScreenState(isFirstTime, true);
});
}
function onPageTitle(event) {
let index = document.title.indexOf('- YouTube');
if (index > -1)
screenTitle.innerText = document.title.substring(0, index);
}
function onIframeLoad(event) {
// Workaround: we use intervals so this still runs when the tab isn't focused
clearInterval(intervalIframeInitialize);
intervalIframeInitialize = setInterval(() => {
iframeVideo = iframe.contentWindow.document.getElementsByTagName('video')[0];
if (iframeVideo) {
iframeVideo.currentTime = video.currentTime;
iframeVideo.onended = onIframeVideoEnded;
clearInterval(intervalIframeInitialize);
}
}, 25);
}
function onIframeVideoEnded(event) {
if (currentPage != PAGE_WATCH) return;
// In order to trigger Youtube's functionality (e.g. AutoPlay, Playlist), we need to make the original video trigger the 'ended' event
// Therefore, we seek to the end and play it. See #onVideoSeek() for details
video.currentTime = video.duration - 0.25;
_updateScreenVisibility(false);
}
function onScreenButtonCloseClick(event) {
iframeVideo.pause();
_updateScreenVisibility(false);
}
function onScreenMouseDown(event) {
event.preventDefault();
isScreenMove = event.target == screenButtonMove;
isScreenResize = event.target == screenButtonResizeBottomRight || event.target == screenButtonResizeTopRight;
iframe.style.pointerEvents = 'none';
screen.classList.add('dragging');
screenButtonMove.removeEventListener('mousedown', onScreenMouseDown);
screenButtonResizeBottomRight.removeEventListener('mousedown', onScreenMouseDown);
screenButtonResizeTopRight.removeEventListener('mousedown', onScreenMouseDown);
document.body.addEventListener('mouseup', onScreenMouseUp);
document.body.addEventListener('mousemove', onScreenMouseMove);
}
function onScreenMouseUp(event) {
event.preventDefault();
if (isScreenMove) {
// Save
_saveScreenState(currentDockMode, screen.offsetHeight, screen.offsetWidth, screen.offsetLeft, screen.offsetTop);
}
if (isScreenResize) {
var currentSize = screen.getBoundingClientRect();
//screen.style.left =(screen.offsetLeft - (currentSize.width - screen.offsetWidth) / 2) + 'px';
//screen.style.top = (screen.offsetTop - (currentSize.height - screen.offsetHeight) / 2) + 'px';
screen.style.height = currentSize.height + 'px';
screen.style.width = currentSize.width + 'px';
screen.style.transform = '';
// Save
_saveScreenState(currentDockMode, currentSize.height, currentSize.width, screen.offsetLeft, screen.offsetTop);
}
isScreenMove = isScreenResize = false;
iframe.style.pointerEvents = 'initial';
screen.classList.remove('dragging');
document.body.removeEventListener('mouseup', onScreenMouseUp);
document.body.removeEventListener('mousemove', onScreenMouseMove);
screenButtonMove.addEventListener('mousedown', onScreenMouseDown);
screenButtonResizeBottomRight.addEventListener('mousedown', onScreenMouseDown);
screenButtonResizeTopRight.addEventListener('mousedown', onScreenMouseDown);
}
function onScreenMouseMove(event) {
event.preventDefault();
if (isScreenMove) {
screen.style.left = Math.min(window.innerWidth - screen.offsetWidth - SCREEN_PADDING, Math.max(SCREEN_PADDING, parseInt(screen.style.left) + event.movementX)) + 'px';
screen.style.top = Math.min(window.innerHeight - screen.offsetHeight - SCREEN_PADDING, Math.max(148, parseInt(screen.style.top) + event.movementY)) + 'px';
}
if (isScreenResize) {
var currentSize = screen.getBoundingClientRect();
var width = screen.offsetWidth;
var newWidth = currentSize.width + event.movementX;
screen.style.transform = 'scaleX(' + newWidth / width + ') scaleY(' + newWidth / width + ')';
}
}
function onScreenTitleClick(event) {
var videoUrl = window.location.href.split('&t=')[0];
window.location.href = videoUrl + (iframeVideo ? ('&t=' + parseInt(iframeVideo.currentTime)) : '');
}
function onScreenTransitionEnd(event) {
if (event && event.propertyName != 'transform') return;
screen.removeEventListener('transitionend', onScreenTransitionEnd);
if (currentDockMode == DOCK_FLOATING) {
iframe.style.boxShadow = '0 14px 28px rgba(0,0,0,0.25), 0 10px 10px rgba(0,0,0,0.22)';
}
screen.className = currentDockMode;
screen.style.left = currentAnimation.end.left + 'px';
screen.style.top = currentAnimation.end.top + 'px';
screen.style.transform = '';
screen.style.transition = '';
currentAnimation = null;
currentAnimationCancelled = false;
}
function onMoviePlayerObserve(mutators) {
if (mutators[0].addedNodes.length > 0)
initializeVideo();
}
function onVideoPlay(event) {
if (video == null) return;
if (!(video.duration > 0 && Math.abs(video.currentTime - video.duration) <= 1)) // See #onIframeVideoEnded() for details
video.pause();
}
function onVideoSeek(event) {
if (video == null) return;
if (video.duration > 0 && Math.abs(video.currentTime - video.duration) <= 1) { // See #onIframeVideoEnded() for details
video.play();
} else {
video.pause();
if (iframeVideo != null)
iframeVideo.currentTime = video.currentTime;
// Make sure the mini player is visible (in case it was hidden by the user)
_updateScreenVisibility(true);
}
}
function onWindowResize() {
window.requestAnimationFrame(() => {
_updateScreenState();
});
}
function onWindowScroll(event) {
_updateScreenState();
}
})();