🏠 Home 

Youtube floating player

Pins the video player in its original position while you scroll the page.

// ==UserScript==
// @name        Youtube floating player
// @description Pins the video player in its original position while you scroll the page.
// @version     3.2
// @author      REVerdi
// @namespace   https://openuserjs.org/users/REVerdi
// @copyright   2014+, REVerdi (https://openuserjs.org/users/REVerdi)
// @license     (CC) Attribution Non-Commercial Share Alike; http://creativecommons.org/licenses/by-nc-sa/3.0/
// Por causa do SPF (Structured Page Fragments), não posso usar  // @include   http*://www.youtube.com/watch?*
// porque se o 1° link no YouTube não for do tipo acima, esse script nunca será executado.
// @include     http*://www.youtube.com/*
// @grant       none
// ==/UserScript==
/*
TESTADO APENAS NO FIREFOX
ONLY TESTED ON FIREFOX
*/
/*
O YouTube "quase" exatamente como eu queria!
Eu não sou programador, então não joguem tomates podres em mim :)
YouTube "almost" exactly as I wanted!
I'm not a programmer, so don't throw rotten tomatoes at me :)
*/
// Based on the ideia of drhouse (http://userscripts.org/scripts/show/186872)
// and contains source code written by tforbus:
// http://www.tristinforbus.com/
// https://github.com/tforbus/youtube-fixed-video-bookmarklet
// http://www.whattheforbus.com/youtube-bookmarklet [removed]
// https://chrome.google.com/webstore/detail/video-pinner/egfhbaheiflmihggjcfmnmchkijkcdpl [removed]
(function(){
"use strict";
var _window;
if (typeof unsafeWindow !== undefined){
_window = unsafeWindow;
}
else {
_window = window;
}
/*
* constants
*/
const        BODY_ID = 'body';
const      PLAYER_ID = 'player';                                                //why not 'movie_player'?
const     THEATER_ID = 'theater-background';
const    PLAYLIST_ID = 'watch-appbar-playlist';
const     CONTENT_ID = 'watch7-content';
const     SIDEBAR_ID = 'watch7-sidebar';
const PLACEHOLDER_ID = 'placeholder-player';                                    //to detect if the user is or is not on a video page
const   BUTTON_CLASS = 'ytp-size-button ytp-button';                            //player view toggle button class
/*
* flow control variables
*/
var       eventListenersAdded = 0;
var bodyMutationObserverAdded = 0;
/*
* user is playing a...
*/
const SINGLE_VIDEO = 1;
const     PLAYLIST = 2;
/*
* player view mode
*/
var playerViewMode;
const NOT_DETECTED = 0;
const DEFAULT_VIEW = 1;
const THEATER_MODE = 2;
var playerViewToggleButton;                                                     //tem que ser pública para que se possa rodar o removeEventListener
//http://www.w3schools.com/colors/colors_names.asp
//if (document.getElementById(        PLAYER_ID)) document.getElementById(        PLAYER_ID).style.border = "thick solid cyan"   ;
//if (document.getElementById(       THEATER_ID)) document.getElementById(       THEATER_ID).style.border = "thick solid red"    ;
//if (document.getElementById(      PLAYLIST_ID)) document.getElementById(      PLAYLIST_ID).style.border = "thick solid pink"   ;
//if (document.getElementById(       SIDEBAR_ID)) document.getElementById(       SIDEBAR_ID).style.border = "thick solid yellow" ;
//if (document.getElementById(   PLACEHOLDER_ID)) document.getElementById(   PLACEHOLDER_ID).style.border = "thick solid green"  ;
//if (document.getElementById(   'movie_player')) document.getElementById(   'movie_player').style.border = "thick solid blue"   ;
//if (document.getElementById('player-playlist')) document.getElementById('player-playlist').style.border = "thick solid fuchsia";
function userIsOnAVideoPage() {                                                 //user is where?
var playerPlaceHolder = document.getElementById(PLACEHOLDER_ID);
if( playerPlaceHolder ) return 1;                                           //user IS     on a video page
else                    return 0;                                           //user is NOT on a video page
}
function userIsPlayingA() {
if( /list/.test(document.location) === false ) return SINGLE_VIDEO;
else                                           return PLAYLIST;
}
function getPlayerViewToggleButton() {
var player  = document.getElementById(PLAYER_ID);
var buttons = player.getElementsByTagName('BUTTON');
for( var b = 0; b < buttons.length; b++ ) {
if( buttons[b].className == BUTTON_CLASS) {
var buttonTitle = buttons[b].getAttribute('title');
if( buttonTitle != 'null' ) return buttons[b];
}
}
}
function resizePlayer() {
var  playerRect;
var theaterRect;
var sidebarRect;
var playlist;
var  player = document.getElementById( PLAYER_ID);
var theater = document.getElementById(THEATER_ID);
var sidebar = document.getElementById(SIDEBAR_ID);
var content = document.getElementById(CONTENT_ID);
var contentRect = content.getBoundingClientRect();
var playerPlaceHolder = document.getElementById(PLACEHOLDER_ID);
playerPlaceHolder.firstElementChild.style.backgroundColor = 'transparent';
player.style.top      = '60px';                                            //determinado por tentativa e erro :S
player.style.position = 'fixed';
theater.style.position = 'fixed';
sidebar.style.position = 'inherit';                                         //static|absolute|fixed|relative|initial|inherit
switch( playerViewMode ) {
case DEFAULT_VIEW:
//          var content = document.getElementById(CONTENT_ID);
//          var contentRect = content.getBoundingClientRect();                  //bottom, height, left, right, top, width, x, y
player.style.left = contentRect.left + 'px';
switch( userIsPlayingA() ) {
case SINGLE_VIDEO:
player.style.zIndex = 998;
sidebar.style.zIndex = 999;
break;
case PLAYLIST:
player.style.zIndex = 999;
sidebar.style.zIndex = 998;
playlist = document.getElementById(PLAYLIST_ID);            //para desfazer o que foi feito para playlist em modo teatro
playlist.style.top      = '';
playlist.style.position = '';
playlist.style.width    = '';
playlist.style.left     = '';
playerRect =  player.getBoundingClientRect();                    //to limit the
sidebarRect = sidebar.getBoundingClientRect();                    //width of
player.style.width = sidebarRect.right - playerRect.left + 'px'; //the playlist
break;
}
break;
case THEATER_MODE:
player.style.zIndex = 999;
sidebar.style.zIndex = 998;
playerRect =  player.getBoundingClientRect();
theaterRect = theater.getBoundingClientRect();
player.style.left = (theaterRect.width / 2) - (playerRect.width / 2)  + 'px';
switch( userIsPlayingA() ) {
case SINGLE_VIDEO:
//nothing to do
break;
case PLAYLIST:
sidebarRect = sidebar.getBoundingClientRect();
playlist = document.getElementById(PLAYLIST_ID);
playlist.style.top      = '170px';                          //determinado por tentativa e erro :S
playlist.style.position = 'fixed';
playlist.style.width = sidebarRect.right - contentRect.right + 'px';
playlist.style.left  = contentRect.right + 'px';
//or
//playlist.style.width = sidebarRect.width + 'px';
//playlist.style.left  = sidebarRect.left  + 'px';
break;
}
break;
}
}
function pageResize() {
resizePlayer();
}
function playerViewToggleButtonClick() {
switch( playerViewMode ) {
case DEFAULT_VIEW:
playerViewMode = THEATER_MODE;
break;
case THEATER_MODE:
playerViewMode = DEFAULT_VIEW;
break;
}
resizePlayer();
}
function getPlayerViewMode() {
var playerViewToggleButtonTitle = playerViewToggleButton.getAttribute('title');
if( playerViewToggleButtonTitle != 'null' ) {
playerViewToggleButtonTitle = playerViewToggleButtonTitle.toLowerCase();
switch( playerViewToggleButtonTitle ) {                                 //detecta o modo de visualização pelo título do botão (eu sei, depende do idioma :S)
case 'theater mode':                                                //'Theater mode'
case 'modo teatro':                                                 //'Modo Teatro'
playerViewMode = DEFAULT_VIEW;
break;
case 'default view':                                                //'Default view'
case 'visualização padrão':                                         //'Visualização padrão'
playerViewMode = THEATER_MODE;
break;
}
}
//else playerViewMode = NOT_DETECTED;                                         //isso ocorre, mas daí é mantido o último valor válido detectado
}
/*
function initElements() {                                                       //merged into resizePlayer()
var  player = document.getElementById( PLAYER_ID);
var theater = document.getElementById(THEATER_ID);
var sidebar = document.getElementById(SIDEBAR_ID);
var playerPlaceHolder = document.getElementById(PLACEHOLDER_ID);
playerPlaceHolder.firstElementChild.style.backgroundColor = 'transparent';
player.style.top      = '60px';                                            //determinado por tentativa e erro :S
player.style.position = 'fixed';
theater.style.position = 'fixed';
sidebar.style.position = 'inherit';                                         //static|absolute|fixed|relative|initial|inherit
}
*/
//https://developer.mozilla.org/pt-BR/docs/Web/API/MutationObserver
//http://www.w3schools.com/jsref/met_element_addeventlistener.asp
var bodyMutationObserver = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if( userIsOnAVideoPage() ) {
if( eventListenersAdded === 0 ) {                                   //só adiciona uma vez
playerViewToggleButton = getPlayerViewToggleButton();
playerViewToggleButton.addEventListener('click', playerViewToggleButtonClick, false);
_window.addEventListener('resize', pageResize, false);
eventListenersAdded = 1;
getPlayerViewMode();
//initElements();
//resizePlayer();                                                 //aqui não funciona 100%, portanto...
}
resizePlayer();                                                     //...tem que ficar aqui e ser chamada várias vezes :S
}
else {                                                                  //user is NOT on a video page
if( eventListenersAdded ) {                                         //só remove uma vez
var player = document.getElementById(PLAYER_ID);
player.style.top      = '';
player.style.position = '';
player.style.zIndex   = '';
player.style.left     = '';
player.style.width    = '';
playerViewToggleButton.removeEventListener('click', playerViewToggleButtonClick, false);
_window.removeEventListener('resize', pageResize, false);
eventListenersAdded = 0;
}
}
});
});
/*
function remBodyMutationObserver();
if( bodyMutationObserverAdded == 1 ) {                                      //( bodyMutationObserverAdded == 1 ) ou apenas ( bodyMutationObserverAdded )
bodyMutationObserver.disconnect();
bodyMutationObserverAdded = 0;
}
}
*/
function addBodyMutationObserver() {
if( bodyMutationObserverAdded === 0 ) {                                     //( bodyMutationObserverAdded === 0 ) ou apenas ( !bodyMutationObserverAdded )
var config = { attributes: true, characterData: true, childList: true };
var target = document.getElementById(BODY_ID);                          //tem que ser 'body', porque 'page' só funciona quando a 1ª página NÃO for uma de vídeo
if( target !== null ) {                                                 //( target !== null ) ou apenas ( target )
bodyMutationObserver.observe(target, config);
bodyMutationObserverAdded = 1;
}
}
}
addBodyMutationObserver();                                                      //initScript == entryPoint
})();