Youtube floating player

Youtube floating player.

目前为 2016-09-03 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube floating player
  3. // @description Youtube floating player.
  4. // @version 3.0
  5. // @author REVerdi
  6. // @namespace https://openuserjs.org/users/REVerdi
  7. // @copyright 2014+, REVerdi (https://openuserjs.org/users/REVerdi)
  8. // @license (CC) Attribution Non-Commercial Share Alike; http://creativecommons.org/licenses/by-nc-sa/3.0/
  9. // Por causa do SPF (Structured Page Fragments), não posso usar // @include http*://www.youtube.com/watch?*
  10. // porque se o 1° link no YouTube não for do tipo acima, esse script nunca será executado.
  11. // @include http*://www.youtube.com/*
  12. // @grant none
  13. // ==/UserScript==
  14.  
  15.  
  16. /*
  17. TESTADO APENAS NO FIREFOX
  18.  
  19. ONLY TESTED ON FIREFOX
  20. */
  21.  
  22.  
  23. /*
  24. O YouTube "quase" exatamente como eu queria!
  25. Eu não sou programador, então não joguem tomates podres em mim :)
  26.  
  27. YouTube "almost" exactly as I wanted!
  28. I'm not a programmer, so don't throw rotten tomatoes at me :)
  29. */
  30.  
  31.  
  32. // Based on the ideia of drhouse (http://userscripts.org/scripts/show/186872)
  33. // and contains source code written by tforbus:
  34. // https://chrome.google.com/webstore/detail/video-pinner/egfhbaheiflmihggjcfmnmchkijkcdpl
  35. // https://github.com/tforbus/youtube-fixed-video-bookmarklet
  36. // http://www.whattheforbus.com/youtube-bookmarklet
  37. // http://www.tristinforbus.com/
  38.  
  39.  
  40. (function(){
  41.  
  42.  
  43. "use strict";
  44.  
  45.  
  46. var _window;
  47. if (typeof unsafeWindow !== undefined){
  48. _window = unsafeWindow;
  49. }
  50. else {
  51. _window = window;
  52. }
  53.  
  54.  
  55. /*
  56. * constants
  57. */
  58. const BODY_ID = 'body';
  59.  
  60. const PLAYER_ID = 'player'; //why not 'movie_player'?
  61. const THEATER_ID = 'theater-background';
  62. const PLAYLIST_ID = 'watch-appbar-playlist';
  63. const CONTENT_ID = 'watch7-content';
  64. const SIDEBAR_ID = 'watch7-sidebar';
  65. const PLACEHOLDER_ID = 'placeholder-player'; //to detect if the user is or is not on a video page
  66.  
  67. const BUTTON_CLASS = 'ytp-size-button ytp-button'; //player view toggle button class
  68.  
  69. /*
  70. * flow control variables
  71. */
  72. var eventListenersAdded = 0;
  73. var bodyMutationObserverAdded = 0;
  74.  
  75. /*
  76. * user is playing a...
  77. */
  78. const SINGLE_VIDEO = 1;
  79. const PLAYLIST = 2;
  80.  
  81. /*
  82. * player view mode
  83. */
  84. var playerViewMode;
  85. const NOT_DETECTED = 0;
  86. const DEFAULT_VIEW = 1;
  87. const THEATER_MODE = 2;
  88.  
  89.  
  90. var playerViewToggleButton; //tem que ser pública para que se possa rodar o removeEventListener
  91.  
  92.  
  93. //http://www.w3schools.com/colors/colors_names.asp
  94. //if (document.getElementById( PLAYER_ID)) document.getElementById( PLAYER_ID).style.border = "thick solid cyan" ;
  95. //if (document.getElementById( THEATER_ID)) document.getElementById( THEATER_ID).style.border = "thick solid red" ;
  96. //if (document.getElementById( PLAYLIST_ID)) document.getElementById( PLAYLIST_ID).style.border = "thick solid pink" ;
  97. //if (document.getElementById( SIDEBAR_ID)) document.getElementById( SIDEBAR_ID).style.border = "thick solid yellow" ;
  98. //if (document.getElementById( PLACEHOLDER_ID)) document.getElementById( PLACEHOLDER_ID).style.border = "thick solid green" ;
  99. //if (document.getElementById( 'movie_player')) document.getElementById( 'movie_player').style.border = "thick solid blue" ;
  100. //if (document.getElementById('player-playlist')) document.getElementById('player-playlist').style.border = "thick solid fuchsia";
  101.  
  102.  
  103. function userIsOnAVideoPage() { //user is where?
  104. var playerPlaceHolder = document.getElementById(PLACEHOLDER_ID);
  105. if( playerPlaceHolder ) return 1; //user IS on a video page
  106. else return 0; //user is NOT on a video page
  107. }
  108.  
  109.  
  110. function userIsPlayingA() {
  111. if( /list/.test(document.location) === false ) return SINGLE_VIDEO;
  112. else return PLAYLIST;
  113. }
  114.  
  115.  
  116. function getPlayerViewToggleButton() {
  117. var player = document.getElementById(PLAYER_ID);
  118. var buttons = player.getElementsByTagName('BUTTON');
  119. for( var b = 0; b < buttons.length; b++ ) {
  120. if( buttons[b].className == BUTTON_CLASS) {
  121. var buttonTitle = buttons[b].getAttribute('title');
  122. if( buttonTitle != 'null' ) return buttons[b];
  123. }
  124. }
  125. }
  126.  
  127.  
  128.  
  129.  
  130. function resizePlayer() {
  131. var playerRect;
  132. var sidebarRect;
  133. var playlist;
  134. var player = document.getElementById( PLAYER_ID);
  135. var sidebar = document.getElementById(SIDEBAR_ID);
  136. var content = document.getElementById(CONTENT_ID);
  137. var contentRect = content.getBoundingClientRect();
  138. switch( playerViewMode ) {
  139. case DEFAULT_VIEW:
  140. // var content = document.getElementById(CONTENT_ID);
  141. // var contentRect = content.getBoundingClientRect(); //bottom, height, left, right, top, width, x, y
  142. player.style.left = contentRect.left + 'px';
  143. switch( userIsPlayingA() ) {
  144. case SINGLE_VIDEO:
  145. player.style.zIndex = 998;
  146. sidebar.style.zIndex = 999;
  147. break;
  148. case PLAYLIST:
  149. player.style.zIndex = 999;
  150. sidebar.style.zIndex = 998;
  151. playlist = document.getElementById(PLAYLIST_ID); //para desfazer o que foi feito para playlist em modo teatro
  152. playlist.style.top = '';
  153. playlist.style.position = '';
  154. playlist.style.width = '';
  155. playlist.style.left = '';
  156. playerRect = player.getBoundingClientRect(); //to limit the
  157. sidebarRect = sidebar.getBoundingClientRect(); //width of
  158. player.style.width = sidebarRect.right - playerRect.left + 'px'; //the playlist
  159. break;
  160. }
  161. break;
  162. case THEATER_MODE:
  163. player.style.zIndex = 999;
  164. sidebar.style.zIndex = 998;
  165. var theater = document.getElementById(THEATER_ID);
  166. var theaterRect = theater.getBoundingClientRect();
  167. playerRect = player.getBoundingClientRect();
  168. player.style.left = (theaterRect.width / 2) - (playerRect.width / 2) + 'px';
  169. switch( userIsPlayingA() ) {
  170. case SINGLE_VIDEO:
  171. //nothing to do
  172. break;
  173. case PLAYLIST:
  174. sidebarRect = sidebar.getBoundingClientRect();
  175. playlist = document.getElementById(PLAYLIST_ID);
  176. playlist.style.top = '170px'; //determinado por tentativa e erro :S
  177. playlist.style.position = 'fixed';
  178. playlist.style.width = sidebarRect.right - contentRect.right + 'px';
  179. playlist.style.left = contentRect.right + 'px';
  180. //or
  181. //playlist.style.width = sidebarRect.width + 'px';
  182. //playlist.style.left = sidebarRect.left + 'px';
  183. break;
  184. }
  185. break;
  186. }
  187. }
  188.  
  189.  
  190.  
  191.  
  192. function pageResize() {
  193. resizePlayer();
  194. }
  195.  
  196.  
  197.  
  198.  
  199. function playerViewToggleButtonClick() {
  200. switch( playerViewMode ) {
  201. case DEFAULT_VIEW:
  202. playerViewMode = THEATER_MODE;
  203. break;
  204. case THEATER_MODE:
  205. playerViewMode = DEFAULT_VIEW;
  206. break;
  207. }
  208. resizePlayer();
  209. }
  210.  
  211.  
  212.  
  213.  
  214. function getPlayerViewMode() {
  215. var playerViewToggleButtonTitle = playerViewToggleButton.getAttribute('title');
  216. if( playerViewToggleButtonTitle != 'null' ) {
  217. switch( playerViewToggleButtonTitle ) { //detecta o modo de visualização pelo título do botão (eu sei, depende do idioma :S)
  218. case 'Theater Mode':
  219. case 'Modo Teatro':
  220. playerViewMode = DEFAULT_VIEW;
  221. break;
  222. case 'Default View':
  223. case 'Visualização padrão':
  224. playerViewMode = THEATER_MODE;
  225. break;
  226. }
  227. }
  228. //else playerViewMode = NOT_DETECTED; //isso ocorre, mas daí é mantido o último valor válido detectado
  229. }
  230.  
  231.  
  232.  
  233.  
  234. function initElements() {
  235. var player = document.getElementById( PLAYER_ID);
  236. var theater = document.getElementById(THEATER_ID);
  237. var sidebar = document.getElementById(SIDEBAR_ID);
  238. var playerPlaceHolder = document.getElementById(PLACEHOLDER_ID);
  239. playerPlaceHolder.firstElementChild.style.backgroundColor = 'transparent';
  240. player.style.top = '60px'; //determinado por tentativa e erro :S
  241. player.style.position = 'fixed';
  242. theater.style.position = 'fixed';
  243. sidebar.style.position = 'inherit'; //static|absolute|fixed|relative|initial|inherit
  244. }
  245.  
  246.  
  247.  
  248.  
  249. //https://developer.mozilla.org/pt-BR/docs/Web/API/MutationObserver
  250. //http://www.w3schools.com/jsref/met_element_addeventlistener.asp
  251. var bodyMutationObserver = new MutationObserver(function(mutations) {
  252. mutations.forEach(function(mutation) {
  253. if( userIsOnAVideoPage() ) {
  254. if( eventListenersAdded === 0 ) { //só adiciona uma vez
  255. playerViewToggleButton = getPlayerViewToggleButton();
  256. playerViewToggleButton.addEventListener('click', playerViewToggleButtonClick, false);
  257. _window.addEventListener('resize', pageResize, false);
  258. eventListenersAdded = 1;
  259. getPlayerViewMode();
  260. initElements();
  261. //resizePlayer(); //aqui não funciona 100%, portanto...
  262. }
  263. resizePlayer(); //...tem que ficar aqui e ser chamada várias vezes :S
  264. }
  265. else { //user is NOT on a video page
  266. if( eventListenersAdded ) { //só remove uma vez
  267. var player = document.getElementById(PLAYER_ID);
  268. player.style.top = '';
  269. player.style.position = '';
  270. player.style.zIndex = '';
  271. player.style.left = '';
  272. player.style.width = '';
  273. playerViewToggleButton.removeEventListener('click', playerViewToggleButtonClick, false);
  274. _window.removeEventListener('resize', pageResize, false);
  275. eventListenersAdded = 0;
  276. }
  277. }
  278. });
  279. });
  280. /*
  281. function remBodyMutationObserver();
  282. if( bodyMutationObserverAdded == 1 ) { //( bodyMutationObserverAdded == 1 ) ou apenas ( bodyMutationObserverAdded )
  283. bodyMutationObserver.disconnect();
  284. bodyMutationObserverAdded = 0;
  285. }
  286. }
  287. */
  288. function addBodyMutationObserver() {
  289. if( bodyMutationObserverAdded === 0 ) { //( bodyMutationObserverAdded === 0 ) ou apenas ( !bodyMutationObserverAdded )
  290. var config = { attributes: true, characterData: true, childList: true };
  291. 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
  292. if( target !== null ) { //( target !== null ) ou apenas ( target )
  293. bodyMutationObserver.observe(target, config);
  294. bodyMutationObserverAdded = 1;
  295. }
  296. }
  297. }
  298.  
  299. addBodyMutationObserver(); //initScript == entryPoint
  300.  
  301.  
  302. })();