Resize YT To Window Size

Moves the YouTube video to the top of the website and fill the window with the video player.

当前为 2017-04-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Resize YT To Window Size
  3. // @description Moves the YouTube video to the top of the website and fill the window with the video player.
  4. // @author Chris H (Zren / Shade)
  5. // @icon https://youtube.com/favicon.ico
  6. // @homepageURL https://github.com/Zren/ResizeYoutubePlayerToWindowSize/
  7. // @namespace http://xshade.ca
  8. // @version 96
  9. // @include http*://*.youtube.com/*
  10. // @include http*://youtube.com/*
  11. // @include http*://*.youtu.be/*
  12. // @include http*://youtu.be/*
  13. // @grant none
  14. // ==/UserScript==
  15.  
  16. // Github: https://github.com/Zren/ResizeYoutubePlayerToWindowSize
  17. // GreasyFork: https://greasyfork.org/scripts/811-resize-yt-to-window-size
  18. // OpenUserJS.org: https://openuserjs.org/scripts/zren/Resize_YT_To_Window_Size
  19. // Userscripts.org: http://userscripts-mirror.org/scripts/show/153699
  20.  
  21. (function (window) {
  22. "use strict";
  23. //--- Imported Globals
  24. // yt
  25. // ytcenter
  26. // html5Patched (Youtube+)
  27. // ytplayer
  28. var uw = window;
  29.  
  30. //--- Already Loaded?
  31. // GreaseMonkey loads this script twice for some reason.
  32. if (uw.ytwp) return;
  33. //--- Is iframe?
  34. function inIframe () {
  35. try {
  36. return window.self !== window.top;
  37. } catch (e) {
  38. return true;
  39. }
  40. }
  41. if (inIframe()) return;
  42.  
  43. //--- Utils
  44. function isStringType(obj) { return typeof obj === 'string'; }
  45. function isArrayType(obj) { return obj instanceof Array; }
  46. function isObjectType(obj) { return typeof obj === 'object'; }
  47. function isUndefined(obj) { return typeof obj === 'undefined'; }
  48. function buildVenderPropertyDict(propertyNames, value) {
  49. var d = {};
  50. for (var i in propertyNames)
  51. d[propertyNames[i]] = value;
  52. return d;
  53. }
  54. function addClass(el, value) {
  55. var classes = value.split(' ');
  56. for (var i = 0; i < classes.length; i++) {
  57. el.classList.add(classes[i]);
  58. }
  59. }
  60. function removeClass(el, value) {
  61. var classes = value.split(' ');
  62. for (var i = 0; i < classes.length; i++) {
  63. el.classList.remove(classes[i]);
  64. }
  65. }
  66. function observe(selector, config, callback) {
  67. var observer = new MutationObserver(function(mutations) {
  68. mutations.forEach(function(mutation){
  69. callback(mutation);
  70. });
  71. });
  72. var target = document.querySelector(selector);
  73. if (!target) {
  74. return null;
  75. }
  76. observer.observe(target, config);
  77. return observer;
  78. }
  79.  
  80. //--- Stylesheet
  81. var JSStyleSheet = function(id) {
  82. this.id = id;
  83. this.stylesheet = '';
  84. };
  85.  
  86. JSStyleSheet.prototype.buildRule = function(selector, styles) {
  87. var s = "";
  88. for (var key in styles) {
  89. s += "\t" + key + ": " + styles[key] + ";\n";
  90. }
  91. return selector + " {\n" + s + "}\n";
  92. };
  93.  
  94. JSStyleSheet.prototype.appendRule = function(selector, k, v) {
  95. if (isArrayType(selector))
  96. selector = selector.join(',\n');
  97. var newStyle;
  98. if (!isUndefined(k) && !isUndefined(v) && isStringType(k)) { // v can be any type (as we stringify it).
  99. var d = {};
  100. d[k] = v;
  101. newStyle = this.buildRule(selector, d);
  102. } else if (!isUndefined(k) && isUndefined(v) && isObjectType(k)) {
  103. newStyle = this.buildRule(selector, k);
  104. } else {
  105. // Invalid Arguments
  106. console.log('Illegal arguments', arguments);
  107. return;
  108. }
  109.  
  110. this.stylesheet += newStyle;
  111. };
  112.  
  113. JSStyleSheet.injectIntoHeader = function(injectedStyleId, stylesheet) {
  114. var styleElement = document.getElementById(injectedStyleId);
  115. if (!styleElement) {
  116. styleElement = document.createElement('style');
  117. styleElement.type = 'text/css';
  118. styleElement.id = injectedStyleId;
  119. document.getElementsByTagName('head')[0].appendChild(styleElement);
  120. }
  121. styleElement.appendChild(document.createTextNode(stylesheet));
  122. };
  123.  
  124. JSStyleSheet.prototype.injectIntoHeader = function(injectedStyleId, stylesheet) {
  125. JSStyleSheet.injectIntoHeader(this.id, this.stylesheet);
  126. };
  127.  
  128. //--- History
  129. var HistoryEvent = function() {}
  130. HistoryEvent.listeners = []
  131.  
  132. HistoryEvent.dispatch = function(state, title, url) {
  133. var stack = this.listeners
  134. for (var i = 0, l = stack.length; i < l; i++) {
  135. stack[i].call(this, state, title, url)
  136. }
  137. }
  138. HistoryEvent.onPushState = function(state, title, url) {
  139. HistoryEvent.dispatch(state, title, url)
  140. return HistoryEvent.origPushState.apply(window.history, arguments)
  141. }
  142. HistoryEvent.onReplaceState = function(state, title, url) {
  143. HistoryEvent.dispatch(state, title, url)
  144. return HistoryEvent.origReplaceState.apply(window.history, arguments)
  145. }
  146. HistoryEvent.inject = function() {
  147. if (!HistoryEvent.injected) {
  148. HistoryEvent.origPushState = window.history.pushState
  149. HistoryEvent.origReplaceState = window.history.replaceState
  150.  
  151. window.history.pushState = HistoryEvent.onPushState
  152. window.history.replaceState = HistoryEvent.onReplaceState
  153. HistoryEvent.injected = true
  154. }
  155. }
  156.  
  157. HistoryEvent.timerId = 0
  158. HistoryEvent.onTick = function() {
  159. if (HistoryEvent.lastPath != window.location.pathname) {
  160. HistoryEvent.dispatch({}, document.title, window.location.href)
  161. HistoryEvent.lastPath = window.location.pathname
  162. }
  163. }
  164. HistoryEvent.startTimer = function() {
  165. HistoryEvent.lastPath = window.location.pathname
  166. HistoryEvent.timerId = setInterval(HistoryEvent.onTick, 500)
  167. }
  168. HistoryEvent.stopTimer = function() {
  169. clearInterval(HistoryEvent.timerId)
  170. }
  171.  
  172.  
  173. //--- Constants
  174. var scriptShortName = 'ytwp'; // YT Window Player
  175. var scriptStyleId = scriptShortName + '-style'; // ytwp-style
  176. var scriptBodyClassId = scriptShortName + '-window-player'; // .ytwp-window-player
  177. var viewingVideoClassId = scriptShortName + '-viewing-video'; // .ytwp-viewing-video
  178. var topOfPageClassId = scriptShortName + '-scrolltop'; // .ytwp-scrolltop
  179. var scriptBodyClassSelector = 'body.' + scriptBodyClassId; // body.ytwp-window-player
  180.  
  181. var videoContainerId = 'player';
  182. var videoContainerPlacemarkerId = scriptShortName + '-placemarker'; // ytwp-placemarker
  183.  
  184. var transitionProperties = ["transition", "-ms-transition", "-moz-transition", "-webkit-transition", "-o-transition"];
  185. var transformProperties = ["transform", "-ms-transform", "-moz-transform", "-webkit-transform", "-o-transform"];
  186.  
  187. //--- YTWP
  188. var ytwp = uw.ytwp = {
  189. scriptShortName: scriptShortName, // YT Window Player
  190. log_: function(logger, args) { logger.apply(console, ['[' + this.scriptShortName + '] '].concat(Array.prototype.slice.call(args))); return 1; },
  191. log: function() { return this.log_(console.log, arguments); },
  192. error: function() { return this.log_(console.error, arguments); },
  193.  
  194. initialized: false,
  195. pageReady: false,
  196. watchPage: false,
  197. };
  198.  
  199. ytwp.util = {
  200. isWatchUrl: function (url) {
  201. if (!url)
  202. url = uw.location.href;
  203. return url.match(/https?:\/\/(www\.)?youtube.com\/watch\?/);
  204. }
  205. };
  206.  
  207. ytwp.html5 = {
  208. app: null,
  209. YTRect: null,
  210. YTApplication: null,
  211. playerInstances: null,
  212. moviePlayerElement: null,
  213. };
  214. ytwp.html5.getPlayerRect = function() {
  215. var moviePlayerElement = this.element || ytwp.html5.moviePlayerElement || document.querySelector('movie_player');
  216. return new ytwp.html5.YTRect(moviePlayerElement.clientWidth, moviePlayerElement.clientHeight);
  217. };
  218. ytwp.html5.getApplicationClass = function() {
  219. if (ytwp.html5.YTApplication === null) {
  220. var testEl = document.createElement('div');
  221. var testAppInstance = uw.yt.player.Application.create(testEl, {});
  222. // var testAppInstance = uw.yt.player.Application.create("player-api", uw.ytplayer.config);
  223. ytwp.html5.YTApplication = testAppInstance.constructor;
  224.  
  225. // Cleanup testAppInstance
  226. var playerInstances = ytwp.html5.getPlayerInstances();
  227.  
  228. var testAppInstanceKey = null;
  229. Object.keys(playerInstances).forEach(function(key) {
  230. if (playerInstances[key] === testAppInstance) {
  231. testAppInstanceKey = key;
  232. }
  233. });
  234. testAppInstance.dispose();
  235. delete playerInstances[testAppInstanceKey];
  236. }
  237. return ytwp.html5.YTApplication;
  238. };
  239. ytwp.html5.getPlayerInstances = function() {
  240. if (ytwp.html5.playerInstances === null) {
  241. var YTApplication = ytwp.html5.getApplicationClass();
  242. if (YTApplication === null)
  243. return null;
  244.  
  245. // Use yt.player.Application.create to find the playerInstancesKey.
  246. // function (a,b){try{var c=e9.D(a);if(e9.o[c]){try{e9.o[c].dispose()}catch(e){Fi(e)}e9.o[c]=null}var d=new e9(a,b);Kb(d,function(){e9.o[c]=null});return e9.o[c]=d}catch(e){throw Fi(e),e.stack;}}
  247. var appCreateRegex = /^function \(a,b\)\{try\{var c=([a-zA-Z_$][\w_$]*)\.([a-zA-Z_$][\w_$]*)\(a\);if\(([a-zA-Z_$][\w_$]*)\.([a-zA-Z_$][\w_$]*)\[c\]\)/;
  248. var fnString = yt.player.Application.create.toString();
  249. var m = appCreateRegex.exec(fnString);
  250. if (m) {
  251. var playerInstancesKey = m[4];
  252. ytwp.html5.playerInstances = YTApplication[playerInstancesKey];
  253. } else {
  254. ytwp.error('Error trying to find playerInstancesKey.', fnString);
  255. }
  256. ytwp.html5.playerInstances = YTApplication[playerInstancesKey];
  257. }
  258.  
  259. return ytwp.html5.playerInstances;
  260. };
  261. ytwp.html5.getPlayerInstance = function() {
  262. if (!ytwp.html5.app) {
  263. var playerInstances = ytwp.html5.getPlayerInstances();
  264. ytwp.log('playerInstances', playerInstances);
  265. var appInstance = null;
  266. var appInstanceKey = null;
  267. Object.keys(playerInstances).forEach(function(key) {
  268. appInstanceKey = key;
  269. appInstance = playerInstances[key];
  270. });
  271. ytwp.html5.app = appInstance;
  272. }
  273. return ytwp.html5.app;
  274. };
  275. ytwp.html5.autohideControls = function() {
  276. var moviePlayerElement = document.getElementById('movie_player');
  277. if (!moviePlayerElement) return;
  278. // ytwp.log(moviePlayerElement.classList);
  279. removeClass(moviePlayerElement, 'autohide-controlbar autominimize-controls-aspect autohide-controls-fullscreenonly autohide-controls hide-controls-when-cued autominimize-progress-bar autominimize-progress-bar-fullscreenonly autohide-controlbar-fullscreenonly autohide-controls-aspect autohide-controls-fullscreen autominimize-progress-bar-non-aspect');
  280. addClass(moviePlayerElement, 'autominimize-progress-bar autohide-controls hide-controls-when-cued');
  281. // ytwp.log(moviePlayerElement.classList);
  282. };
  283. ytwp.html5.update = function() {
  284. if (!ytwp.html5.playerInstances)
  285. return;
  286. for (var key in ytwp.html5.playerInstances) {
  287. var playerInstance = ytwp.html5.playerInstances[key];
  288. ytwp.html5.updatePlayerInstance(playerInstance);
  289. }
  290. };
  291. ytwp.html5.replaceClientRect = function(app, moviePlayerKey, clientRectFnKey) {
  292. var moviePlayer = app[moviePlayerKey];
  293. ytwp.html5.moviePlayerElement = moviePlayer.element;
  294. ytwp.html5.YTRect = moviePlayer[clientRectFnKey].call(moviePlayer).constructor;
  295. moviePlayer[clientRectFnKey] = ytwp.html5.getPlayerRect;
  296. };
  297. ytwp.html5.setRectFn = function(app, moviePlayerKey, clientRectFnKey) {
  298. ytwp.html5.moviePlayerElement = document.getElementById('movie_player');
  299. var moviePlayer = app[moviePlayerKey];
  300. ytwp.html5.YTRect = moviePlayer[clientRectFnKey].call(moviePlayer).constructor;
  301. moviePlayer.constructor.prototype[clientRectFnKey] = ytwp.html5.getPlayerRect;
  302. };
  303. ytwp.html5.updatePlayerInstance = function(app) {
  304. if (!app) {
  305. return;
  306. }
  307.  
  308. var moviePlayerElement = document.getElementById('movie_player');
  309. var moviePlayer = null;
  310. var moviePlayerKey = null;
  311.  
  312. // function (a,b){return this.isDisposed()?!1:this.R.P.apply(this.R,arguments)}
  313. var applyFnRegex1 = /^function \(a,b\)\{return this\.isDisposed\(\)\?!1:this\.([a-zA-Z_$][\w_$]*)\.([a-zA-Z_$][\w_$]*)\.apply\(this\.([a-zA-Z_$][\w_$]*),arguments\)\}$/;
  314. // function (a,b){return this.O.Y.apply(this.O,arguments)}
  315. var applyFnRegex2 = /^function \(a,b\)\{return this\.([a-zA-Z_$][\w_$]*)\.([a-zA-Z_$][\w_$]*)\.apply\(this\.([a-zA-Z_$][\w_$]*),arguments\)\}$/;
  316. var applyFnKey = null;
  317. var applyKey1 = null;
  318. var applyKey2 = null;
  319.  
  320. // function (a){var b=this.j.X(),c=n$.L.xb.call(this);a||"detailpage"!=b.ma||b.ib||b.experiments.T||(c.height+=30);return c}
  321. // function (a){var b=this.app.X(),c=n$.M.xb.call(this);a||!JK(b)||b.ab||b.experiments.U||(c.height+=30);return c}
  322. var clientRectFnRegex1 = /^(function \(a\)\{var b=this\.([a-zA-Z_$][\w_$]*)\.([a-zA-Z_$][\w_$]*)\(\)).*(\|\|\(c\.height\+=30\);return c})$/;
  323. // function (){var a=this.A.U();if(window.matchMedia){if((a.wb||a.Fb)&&window.matchMedia("(width: "+window.innerWidth+"px) and (height: "+window.innerHeight+"px)").matches)return new H(window.innerWidth,window.innerHeight);if("detailpage"==a.ja&&"blazer"!=a.j&&!a.Fb){a=a.experiments.A;if(window.matchMedia(S6.C).matches)return new H(426,a?280:240);var b=this.A.ha;if(window.matchMedia(b?S6.o:S6.j).matches)return new H(1280,a?760:720);if(b||window.matchMedia(S6.A).matches)return new H(854,a?520:480);if(window.matchMedia(S6.B).matches)return new H(640,a?400:360)}}return new H(this.element.clientWidth,this.element.clientHeight)}
  324. // Tail: return new H(this.element.clientWidth,this.element.clientHeight)}
  325. // function (){var a=this.app.T,b=Yh()==this.element;if(b&&Lp())return new g.ef(window.outerWidth,window.outerHeight);if(b||a.Xe){var c;window.matchMedia&&(a="(width: "+window.innerWidth+"px) and (height: "+window.innerHeight+"px)",this.F&&this.F.media==a||(this.F=window.matchMedia(a)),c=this.F&&this.F.matches);if(c)return new g.ef(window.innerWidth,window.innerHeight)}else if(this.N){if(a.experiments.ba("flex_theater_mode")&&this.app.fa||a.experiments.ba("player_scaling_360p_to_720p")&&this.da.matches)return new g.ef(this.element.clientWidth,this.element.clientHeight);if(this.N.matches)return new g.ef(426,240);a=this.app.fa;if((a?this.aa:this.$).matches)return new g.ef(1280,720);if(a||this.ca.matches)return new g.ef(854,480);if(this.fa.matches)return new g.ef(640,360)}return new g.ef(this.element.clientWidth,this.element.clientHeight)}
  326. // Tail: return new g.ef(this.element.clientWidth,this.element.clientHeight)}
  327. var clientRectFnRegex2 = /^(function \()(.|\n)*(return new ([a-zA-Z_$][\w_$]*\.)?([a-zA-Z_$][\w_$]*)\(this\.element\.clientWidth,this\.element\.clientHeight\)})$/;
  328. var clientRectFn = null;
  329. var clientRectFnKey = null;
  330.  
  331. var fnAlreadyReplacedCount = 0;
  332.  
  333. for (var key1 in app) {
  334. var val1 = app[key1];//console.log(key1, val1);
  335. if (typeof val1 === 'object' && val1 !== null && val1.element === moviePlayerElement) {
  336. moviePlayer = val1;
  337. moviePlayerKey = key1;
  338.  
  339. for (var key2 in moviePlayer) {
  340. var val2 = moviePlayer[key2];//console.log(key1, key2, val2);
  341. if (typeof val2 === 'function') {
  342. var fnString = val2.toString();
  343. // console.log(fnString);
  344. if (clientRectFn === null && (clientRectFnRegex1.test(fnString) || clientRectFnRegex2.test(fnString))) {
  345. clientRectFn = val2;
  346. clientRectFnKey = key2;
  347. } else if (val2 === ytwp.html5.getPlayerRect) {
  348. fnAlreadyReplacedCount += 1;
  349. clientRectFn = val2;
  350. clientRectFnKey = key2;
  351. } else if (applyFnRegex2.test(fnString)) {
  352. console.log('applyFnRegex2', key1, key2, applyKey1, applyKey2, moviePlayerKey)
  353. applyKey1 = key1;
  354. applyKey2 = key2;
  355. } else {
  356. // console.log(key1, key2, val2, '[Not Used]');
  357. }
  358. }
  359. }
  360. } else if (typeof val1 === 'object' && val1 !== null && typeof val1.logEvent === 'function') {
  361. for (var key2 in val1) {
  362. var val2 = val1[key2];//console.log(key1, key2, val2);
  363. if (typeof val2 === 'function') {
  364. var fnString = val2.toString();
  365. // console.log(fnString);
  366. if (applyKey2 === null && applyFnRegex1.test(fnString)) {
  367. console.log('applyFnRegex1', key1, key2, applyKey1, applyKey2, moviePlayerKey)
  368. applyKey1 = key1;
  369. applyKey2 = key2;
  370. }
  371. }
  372. }
  373. } else if (typeof val1 === 'function') {
  374. var fnString = val1.toString();
  375. if (applyFnRegex1.test(fnString)) {
  376. applyFnKey = key1;
  377. }
  378. }
  379. }
  380.  
  381. if (fnAlreadyReplacedCount > 0) {
  382. // return;
  383. }
  384.  
  385. if (moviePlayer === null || clientRectFn === null) {
  386. console.log('[ytwp] ', '[Error]', 'HTML5 Player has changed or there\'s multiple playerInstances and this one has been destroyed.');
  387. console.log('moviePlayer', moviePlayerKey, moviePlayer);
  388. console.log('clientRectFn', clientRectFnKey, clientRectFn);
  389. console.log('fnAlreadyReplacedCount', fnAlreadyReplacedCount);
  390. if (moviePlayer === null) {
  391. console.log('Debugging: moviePlayer');
  392. var table = [];
  393. Object.keys(app).forEach(function(key1) {
  394. var val1 = app[key1];
  395. table.push({
  396. key: key1,
  397. element: typeof val1 === 'object' && val1 !== null && val1.element === moviePlayerElement,
  398. val: val1,
  399. });
  400. });
  401. console.table(table);
  402. }
  403. if (moviePlayer != null) {
  404. console.log('Debugging: clientRectFn');
  405. var table = [];
  406. for (var key2 in moviePlayer) {
  407. var val2 = moviePlayer[key2];
  408. table.push({
  409. key: key2,
  410. returns: moviePlayer[key2] && moviePlayer[key2].toString().indexOf('return'),
  411. src: moviePlayer[key2] && moviePlayer[key2].toString(),
  412. });
  413. }
  414. console.table(table);
  415. }
  416. return;
  417. }
  418. ytwp.html5.setRectFn(app, moviePlayerKey, clientRectFnKey);
  419.  
  420. if (applyFnKey) { // Probably not needed.
  421. ytwp.log('applyFnKey', applyFnKey);
  422. app[applyFnKey]('resize', ytwp.html5.getPlayerRect());
  423. } else if (applyKey1 && applyKey2) {
  424. ytwp.log('applyKey', applyKey1, applyKey2, app[applyKey1][applyKey2]);
  425. app[applyKey1][applyKey2]('resize', ytwp.html5.getPlayerRect());
  426. } else {
  427. ytwp.log('applyFn not found');
  428. //app.oa.T('resize', ytwp.html5.getPlayerRect()); // tempfix
  429. }
  430. };
  431.  
  432.  
  433. ytwp.init = function() {
  434. ytwp.log('init');
  435. if (!ytwp.initialized) {
  436. ytwp.isWatchPage = ytwp.util.isWatchUrl();
  437. if (ytwp.isWatchPage) {
  438. if (!document.getElementById(scriptStyleId)) {
  439. ytwp.event.initStyle();
  440. }
  441. ytwp.initScroller();
  442. ytwp.initialized = true;
  443. ytwp.pageReady = false;
  444. }
  445. }
  446. ytwp.event.onWatchInit();
  447. if (ytwp.isWatchPage) {
  448. ytwp.html5PlayerFix();
  449. }
  450. }
  451.  
  452. ytwp.initScroller = function() {
  453. // Register listener & Call it now.
  454. uw.addEventListener('scroll', ytwp.onScroll, false);
  455. uw.addEventListener('resize', ytwp.onScroll, false);
  456. ytwp.onScroll();
  457. }
  458.  
  459. ytwp.onScroll = function() {
  460. var viewportHeight = document.documentElement.clientHeight;
  461.  
  462. // topOfPageClassId
  463. if (ytwp.isWatchPage && uw.scrollY == 0) {
  464. document.body.classList.add(topOfPageClassId);
  465. //var player = document.getElementById('movie_player');
  466. //if (player)
  467. // player.focus();
  468. } else {
  469. document.body.classList.remove(topOfPageClassId);
  470. }
  471.  
  472. // viewingVideoClassId
  473. if (ytwp.isWatchPage && uw.scrollY <= viewportHeight) {
  474. document.body.classList.add(viewingVideoClassId);
  475. } else {
  476. document.body.classList.remove(viewingVideoClassId);
  477. }
  478. }
  479.  
  480. ytwp.event = {
  481. initStyle: function() {
  482. ytwp.log('initStyle');
  483. ytwp.style = new JSStyleSheet(scriptStyleId);
  484. ytwp.event.buildStylesheet();
  485. // Duplicate stylesheet targeting data-spf-name if enabled.
  486. if (uw.spf) {
  487. var temp = scriptBodyClassSelector;
  488. scriptBodyClassSelector = 'body[data-spf-name="watch"]';
  489. ytwp.event.buildStylesheet();
  490. ytwp.style.appendRule('body[data-spf-name="watch"]:not(.ytwp-window-player) #masthead-positioner', {
  491. 'position': 'absolute',
  492. 'top': '100% !important'
  493. });
  494. }
  495. ytwp.style.injectIntoHeader();
  496. },
  497. buildStylesheet: function() {
  498. ytwp.log('buildStylesheet');
  499. //--- Browser Scrollbar
  500. ytwp.style.appendRule(scriptBodyClassSelector + '::-webkit-scrollbar', {
  501. 'width': '0',
  502. 'height': '0',
  503. });
  504.  
  505. //--- Video Player
  506. var d;
  507. d = buildVenderPropertyDict(transitionProperties, 'left 0s linear, padding-left 0s linear');
  508. d['padding'] = '0 !important';
  509. d['margin'] = '0 !important';
  510. ytwp.style.appendRule([
  511. scriptBodyClassSelector + ' #player',
  512. scriptBodyClassSelector + '.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible #player',
  513. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #player',
  514. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #player-legacy',
  515. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #watch7-main-container',
  516. ], d);
  517. //
  518. d = buildVenderPropertyDict(transitionProperties, 'width 0s linear, left 0s linear');
  519.  
  520. // Bugfix for Firefox
  521. // Parts of the header (search box) are hidden under the player.
  522. // Firefox doesn't seem to be using the fixed header+guide yet.
  523. d['float'] = 'initial';
  524.  
  525. // Skinny mode
  526. d['left'] = 0;
  527. d['margin-left'] = 0;
  528.  
  529. ytwp.style.appendRule(scriptBodyClassSelector + ' #player-api', d);
  530.  
  531. // Theatre mode
  532. ytwp.style.appendRule(scriptBodyClassSelector + ' .watch-stage-mode #player .player-api', {
  533. 'left': 'initial',
  534. 'margin-left': 'initial',
  535. });
  536. // Hide the cinema/wide mode button since it's useless.
  537. //ytwp.style.appendRule(scriptBodyClassSelector + ' #movie_player .ytp-size-button', 'display', 'none');
  538.  
  539. // !important is mainly for simplicity, but is needed to override the !important styling when the Guide is open due to:
  540. // .sidebar-collapsed #watch7-video, .sidebar-collapsed #watch7-main, .sidebar-collapsed .watch7-playlist { width: 945px!important; }
  541. // Also, Youtube Center resizes #player at element level.
  542. // Don't resize if Youtube+'s html.floater is detected.
  543. ytwp.style.appendRule(
  544. [
  545. scriptBodyClassSelector + ' #player',
  546. scriptBodyClassSelector + ' #player-api',
  547. 'html:not(.floater) ' + scriptBodyClassSelector + ' #movie_player',
  548. scriptBodyClassSelector + ' #player-mole-container',
  549. 'html:not(.floater) ' + scriptBodyClassSelector + ' .html5-video-container',
  550. 'html:not(.floater) ' + scriptBodyClassSelector + ' .html5-main-video',
  551. ],
  552. {
  553. 'width': '100% !important',
  554. 'min-width': '100% !important',
  555. 'max-width': '100% !important',
  556. 'height': '100vh !important',
  557. 'min-height': '100vh !important',
  558. 'max-height': '100vh !important',
  559. }
  560. );
  561.  
  562. ytwp.style.appendRule(
  563. [
  564. scriptBodyClassSelector + ' #player',
  565. scriptBodyClassSelector + ' .html5-main-video',
  566. ],
  567. {
  568. 'top': '0 !important',
  569. 'right': '0 !important',
  570. 'bottom': '0 !important',
  571. 'left': '0 !important',
  572. }
  573. );
  574. // Resize #player-unavailable, #player-api
  575. // Using min/max width/height will keep
  576. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-width', 'width', '100% !important');
  577. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height', 'height', '100% !important');
  578.  
  579. // Fix video overlays
  580. ytwp.style.appendRule([
  581. scriptBodyClassSelector + ' .html5-video-player .ad-container-single-media-element-annotations', // Ad
  582. scriptBodyClassSelector + ' .html5-video-player .ytp-upnext', // Autoplay Next Video
  583. ], 'top', '0');
  584.  
  585. //--- Move Video Player
  586. ytwp.style.appendRule(scriptBodyClassSelector + ' #player', {
  587. 'position': 'absolute',
  588. // Already top:0; left: 0;
  589. });
  590. ytwp.style.appendRule(scriptBodyClassSelector, { // body
  591. 'margin-top': '100vh',
  592. });
  593.  
  594.  
  595. //--- Sidebar
  596. // Remove the transition delay as you can see it moving on page load.
  597. d = buildVenderPropertyDict(transitionProperties, 'margin-top 0s linear, padding-top 0s linear');
  598. d['margin-top'] = '0 !important';
  599. d['top'] = '0 !important';
  600. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch7-sidebar', d);
  601.  
  602. ytwp.style.appendRule(scriptBodyClassSelector + '.cardified-page #watch7-sidebar-contents', 'padding-top', '0');
  603.  
  604. //--- Absolutely position the fixed header.
  605. // Masthead
  606. d = buildVenderPropertyDict(transitionProperties, 'top 0s linear !important');
  607. ytwp.style.appendRule(scriptBodyClassSelector + '.hide-header-transition #masthead-positioner', d);
  608. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #masthead-positioner', {
  609. 'position': 'absolute',
  610. 'top': '100% !important'
  611. });
  612. // Lower masthead below Youtube+'s html.floater
  613. ytwp.style.appendRule('html.floater ' + scriptBodyClassSelector + '.' + viewingVideoClassId + ' #masthead-positioner', {
  614. 'z-index': '5',
  615. });
  616.  
  617. // Guide
  618. // When watching the video, we need to line it up with the masthead.
  619. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #appbar-guide-menu', {
  620. 'display': 'initial',
  621. 'position': 'absolute',
  622. 'top': '100% !important' // Masthead height
  623. });
  624. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #page.watch #guide', {
  625. 'display': 'initial',
  626. 'margin': '0',
  627. 'position': 'initial'
  628. });
  629.  
  630.  
  631. //---
  632. // Hide Scrollbars
  633. ytwp.style.appendRule(scriptBodyClassSelector + '.' + topOfPageClassId, 'overflow-x', 'hidden');
  634.  
  635.  
  636. //--- Fix Other Possible Style Issues
  637. ytwp.style.appendRule(scriptBodyClassSelector + ' #placeholder-player', 'display', 'none');
  638. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch-sidebar-spacer', 'display', 'none');
  639. ytwp.style.appendRule(scriptBodyClassSelector + ' .skip-nav', 'display', 'none');
  640.  
  641. //--- Whitespace Leftover From Moving The Video
  642. ytwp.style.appendRule(scriptBodyClassSelector + ' #page.watch', 'padding-top', '0');
  643. ytwp.style.appendRule(scriptBodyClassSelector + ' .player-branded-banner', 'height', '0');
  644.  
  645. //--- Youtube+ Compatiblity
  646. ytwp.style.appendRule(scriptBodyClassSelector + ' #body-container', 'position', 'static');
  647. ytwp.style.appendRule('.part_static_size:not(.content-snap-width-skinny-mode) ' + scriptBodyClassSelector + ' .watch-non-stage-mode #player-playlist', 'width', '1066px');
  648.  
  649. //--- Playlist Bar
  650. ytwp.style.appendRule([
  651. scriptBodyClassSelector + ' #placeholder-playlist',
  652. scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist',
  653. ], {
  654. 'height': '540px !important',
  655. 'max-height': '540px !important',
  656. });
  657.  
  658. d = buildVenderPropertyDict(transitionProperties, 'transform 0s linear');
  659. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch-appbar-playlist', d);
  660. d = buildVenderPropertyDict(transformProperties, 'translateY(0px)');
  661. d['margin-left'] = '0';
  662. d['top'] = 'calc(100vh + 60px)';
  663. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist', d);
  664. ytwp.style.appendRule(scriptBodyClassSelector + ' .playlist-videos-list', {
  665. 'max-height': '470px !important',
  666. 'height': 'initial !important',
  667. });
  668. //---
  669. // Material UI
  670. ytwp.style.appendRule(scriptBodyClassSelector + '.ytwp-scrolltop #extra-buttons', 'display', 'none !important');
  671. // ytwp.style.appendRule('body > #player:not(.ytd-watch)', 'display', 'none');
  672. // ytwp.style.appendRule('body.ytwp-viewing-video #content:not(app-header-layout) ytd-page-manager', 'margin-top', '0 !important');
  673. // ytwp.style.appendRule('.ytd-watch-0 #content-separator.ytd-watch', 'margin-top', '0');
  674. ytwp.style.appendRule('ytd-app', 'position', 'static !important');
  675. ytwp.style.appendRule('ytd-watch #top', 'margin-top', '71px !important'); // 56px (topnav height) + 15px (margin)
  676. ytwp.style.appendRule('ytd-watch #container', 'margin-top', '0 !important');
  677. ytwp.style.appendRule('ytd-watch #content-separator', 'margin-top', '0 !important');
  678. ytwp.style.appendRule(scriptBodyClassSelector + '.ytwp-viewing-video ytd-app #masthead-container.ytd-app', {
  679. 'position': 'absolute',
  680. 'top': '100vh',
  681. });
  682. ytwp.style.appendRule(scriptBodyClassSelector + '.ytwp-viewing-video ytd-watch #masthead-positioner', {
  683. 'top': '100vh !important',
  684. });
  685. },
  686. onWatchInit: function() {
  687. ytwp.log('onWatchInit');
  688. if (!ytwp.initialized) return;
  689. if (ytwp.pageReady) return;
  690.  
  691. ytwp.event.addBodyClass();
  692. ytwp.pageReady = true;
  693. },
  694. onDispose: function() {
  695. ytwp.log('onDispose');
  696. ytwp.initialized = false;
  697. ytwp.pageReady = false;
  698. ytwp.isWatchPage = false;
  699. ytwp.html5.app = null;
  700. // ytwp.html5.YTRect = null;
  701. ytwp.html5.YTApplication = null;
  702. ytwp.html5.playerInstances = null;
  703. //ytwp.html5.moviePlayerElement = null;
  704. },
  705. addBodyClass: function() {
  706. // Insert CSS Into the body so people can style around the effects of this script.
  707. document.body.classList.add(scriptBodyClassId);
  708. ytwp.log('Applied ' + scriptBodyClassSelector);
  709. },
  710. };
  711.  
  712. ytwp.html5PlayerFix = function() {
  713. ytwp.log('html5PlayerFix');
  714.  
  715. try {
  716. if (!uw.ytcenter // Youtube Center
  717. && !uw.html5Patched // Youtube+
  718. && (!ytwp.html5.app)
  719. && (uw.ytplayer && uw.ytplayer.config)
  720. && (uw.yt && uw.yt.player && uw.yt.player.Application && uw.yt.player.Application.create)
  721. ) {
  722. ytwp.html5.app = ytwp.html5.getPlayerInstance();
  723. }
  724.  
  725. ytwp.html5.update();
  726. ytwp.html5.autohideControls();
  727. } catch (e) {
  728. ytwp.error(e);
  729. }
  730. }
  731.  
  732. ytwp.fixMasthead = function() {
  733. ytwp.log('fixMasthead');
  734. var el = document.querySelector('#masthead-positioner-height-offset');
  735. if (el) {
  736. ytwp.fixMastheadElement(el);
  737. }
  738. }
  739. ytwp.fixMastheadElement = function(el) {
  740. ytwp.log('fixMastheadElement', el);
  741. if (el.style.height) { // != ""
  742. setTimeout(function(){
  743. el.style.height = ""
  744. document.querySelector('#appbar-guide-menu').style.marginTop = "";
  745. }, 0);
  746. }
  747. }
  748.  
  749. ytwp.registerMastheadFix = function() {
  750. ytwp.log('registerMastheadFix');
  751. // Fix the offset when closing the Share widget (element.style.height = ~275px).
  752.  
  753. observe('#masthead-positioner-height-offset', {
  754. attributes: true,
  755. }, function(mutation) {
  756. console.log(mutation.type, mutation)
  757. if (mutation.attributeName === 'style') {
  758. var el = mutation.target;
  759. if (el.style.height) { // != ""
  760. setTimeout(function(){
  761. el.style.height = ""
  762. document.querySelector('#appbar-guide-menu').style.marginTop = "";
  763. }, 0);
  764. }
  765.  
  766. }
  767. });
  768. }
  769.  
  770. //--- Material UI
  771. ytwp.materialPageTransition = function() {
  772. ytwp.init();
  773.  
  774. if (ytwp.util.isWatchUrl()) {
  775. ytwp.event.addBodyClass();
  776. if (!ytwp.html5.app) {
  777. ytwp.log('materialPageTransition !ytwp.html5.app', ytwp.html5.app)
  778. setTimeout(ytwp.materialPageTransition, 100);
  779. }
  780. } else {
  781. ytwp.event.onDispose();
  782. document.body.classList.remove(scriptBodyClassId);
  783. }
  784. ytwp.onScroll();
  785. ytwp.fixMasthead();
  786. };
  787.  
  788. //--- Listeners
  789. ytwp.registerListeners = function() {
  790. ytwp.registerMaterialListeners();
  791. ytwp.registerMastheadFix();
  792. };
  793.  
  794. ytwp.registerMaterialListeners = function() {
  795. // For Material UI
  796. HistoryEvent.listeners.push(ytwp.materialPageTransition);
  797. HistoryEvent.startTimer();
  798. // HistoryEvent.inject();
  799. // HistoryEvent.listeners.push(console.log.bind(console));
  800. };
  801.  
  802. ytwp.main = function() {
  803. ytwp.registerListeners();
  804. ytwp.init();
  805. ytwp.fixMasthead();
  806. };
  807.  
  808. ytwp.main();
  809. })(typeof unsafeWindow !== 'undefined' ? unsafeWindow : window);