Resize YT To Window Size

Moves the YouTube video to the top of the website and resizes it to the window size.

当前为 2015-07-26 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Resize YT To Window Size
  3. // @description Moves the YouTube video to the top of the website and resizes it to the window size.
  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 69
  9. // @include http*://*.youtube.com/*
  10. // @include http*://youtube.com/*
  11. // @include http*://*.youtu.be/*
  12. // @include http*://youtu.be/*
  13. // ==/UserScript==
  14.  
  15. // Github: https://github.com/Zren/ResizeYoutubePlayerToWindowSize
  16. // GreasyFork: https://greasyfork.org/scripts/811-resize-yt-to-window-size
  17. // OpenUserJS.org: https://openuserjs.org/scripts/zren/Resize_YT_To_Window_Size
  18. // Userscripts.org: http://userscripts-mirror.org/scripts/show/153699
  19.  
  20. (function (window) {
  21. "use strict";
  22. //--- Imported Globals
  23. // yt
  24. // ytcenter
  25. // html5Patched (Youtube+)
  26. // ytplayer
  27. var uw = window.top;
  28.  
  29. //--- Already Loaded?
  30. // GreaseMonkey loads this script twice for some reason.
  31. if (uw.ytwp) return;
  32.  
  33. //--- Utils
  34. function isStringType(obj) { return typeof obj === 'string'; }
  35. function isArrayType(obj) { return obj instanceof Array; }
  36. function isObjectType(obj) { return typeof obj === 'object'; }
  37. function isUndefined(obj) { return typeof obj === 'undefined'; }
  38. function buildVenderPropertyDict(propertyNames, value) {
  39. var d = {};
  40. for (var i in propertyNames)
  41. d[propertyNames[i]] = value;
  42. return d;
  43. }
  44.  
  45. //--- jQuery
  46. // Based on jQuery
  47. // https://github.com/jquery/jquery/blob/master/src/manipulation.js
  48. var core_rnotwhite = /\S+/g;
  49. var rclass = /[\t\r\n\f]/g;
  50. var rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;
  51.  
  52. var jQuery = {
  53. trim: function( text ) {
  54. return (text || "").replace( rtrim, "" );
  55. },
  56. addClass: function( elem, value ) {
  57. var classes, cur, clazz, j,
  58. proceed = typeof value === "string" && value;
  59.  
  60. if ( proceed ) {
  61. // The disjunction here is for better compressibility (see removeClass)
  62. classes = ( value || "" ).match( core_rnotwhite ) || [];
  63.  
  64. cur = elem.nodeType === 1 && ( elem.className ?
  65. ( " " + elem.className + " " ).replace( rclass, " " ) :
  66. " "
  67. );
  68.  
  69. if ( cur ) {
  70. j = 0;
  71. while ( (clazz = classes[j++]) ) {
  72. if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
  73. cur += clazz + " ";
  74. }
  75. }
  76. elem.className = jQuery.trim( cur );
  77. }
  78. }
  79. },
  80. removeClass: function( elem, value ) {
  81. var classes, cur, clazz, j,
  82. proceed = arguments.length === 0 || typeof value === "string" && value;
  83.  
  84. if ( proceed ) {
  85. classes = ( value || "" ).match( core_rnotwhite ) || [];
  86.  
  87. // This expression is here for better compressibility (see addClass)
  88. cur = elem.nodeType === 1 && ( elem.className ?
  89. ( " " + elem.className + " " ).replace( rclass, " " ) :
  90. ""
  91. );
  92.  
  93. if ( cur ) {
  94. j = 0;
  95. while ( (clazz = classes[j++]) ) {
  96. // Remove *all* instances
  97. while ( cur.indexOf( " " + clazz + " " ) >= 0 ) {
  98. cur = cur.replace( " " + clazz + " ", " " );
  99. }
  100. }
  101. elem.className = value ? jQuery.trim( cur ) : "";
  102. }
  103. }
  104. }
  105. };
  106.  
  107.  
  108. //--- Stylesheet
  109. var JSStyleSheet = function(id) {
  110. this.id = id;
  111. this.stylesheet = '';
  112. };
  113.  
  114. JSStyleSheet.prototype.buildRule = function(selector, styles) {
  115. var s = "";
  116. for (var key in styles) {
  117. s += "\t" + key + ": " + styles[key] + ";\n";
  118. }
  119. return selector + " {\n" + s + "}\n";
  120. };
  121.  
  122. JSStyleSheet.prototype.appendRule = function(selector, k, v) {
  123. if (isArrayType(selector))
  124. selector = selector.join(',\n');
  125. var newStyle;
  126. if (!isUndefined(k) && !isUndefined(v) && isStringType(k)) { // v can be any type (as we stringify it).
  127. var d = {};
  128. d[k] = v;
  129. newStyle = this.buildRule(selector, d);
  130. } else if (!isUndefined(k) && isUndefined(v) && isObjectType(k)) {
  131. newStyle = this.buildRule(selector, k);
  132. } else {
  133. // Invalid Arguments
  134. console.log('Illegal arguments', arguments);
  135. return;
  136. }
  137.  
  138. this.stylesheet += newStyle;
  139. };
  140.  
  141. JSStyleSheet.injectIntoHeader = function(injectedStyleId, stylesheet) {
  142. var styleElement = document.getElementById(injectedStyleId);
  143. if (!styleElement) {
  144. styleElement = document.createElement('style');
  145. styleElement.type = 'text/css';
  146. styleElement.id = injectedStyleId;
  147. document.getElementsByTagName('head')[0].appendChild(styleElement);
  148. }
  149. styleElement.appendChild(document.createTextNode(stylesheet));
  150. };
  151.  
  152. JSStyleSheet.prototype.injectIntoHeader = function(injectedStyleId, stylesheet) {
  153. JSStyleSheet.injectIntoHeader(this.id, this.stylesheet);
  154. };
  155.  
  156. //--- Constants
  157. var scriptShortName = 'ytwp'; // YT Window Player
  158. var scriptStyleId = scriptShortName + '-style'; // ytwp-style
  159. var scriptBodyClassId = scriptShortName + '-window-player'; // .ytwp-window-player
  160. var viewingVideoClassId = scriptShortName + '-viewing-video'; // .ytwp-viewing-video
  161. var topOfPageClassId = scriptShortName + '-scrolltop'; // .ytwp-scrolltop
  162. var scriptBodyClassSelector = 'body.' + scriptBodyClassId; // body.ytwp-window-player
  163.  
  164. var videoContainerId = 'player';
  165. var videoContainerPlacemarkerId = scriptShortName + '-placemarker'; // ytwp-placemarker
  166.  
  167. var transitionProperties = ["transition", "-ms-transition", "-moz-transition", "-webkit-transition", "-o-transition"];
  168. var transformProperties = ["transform", "-ms-transform", "-moz-transform", "-webkit-transform", "-o-transform"];
  169.  
  170. //--- YTWP
  171. var ytwp = uw.ytwp = {
  172. scriptShortName: scriptShortName, // YT Window Player
  173. log_: function(logger, args) { logger.apply(console, ['[' + this.scriptShortName + '] '].concat(Array.prototype.slice.call(args))); return 1; },
  174. log: function() { return this.log_(console.log, arguments); },
  175. error: function() { return this.log_(console.error, arguments); },
  176.  
  177. initialized: false,
  178. pageReady: false,
  179. watchPage: false,
  180. };
  181.  
  182. ytwp.util = {
  183. isWatchUrl: function (url) {
  184. if (!url)
  185. url = uw.location.href;
  186. return url.match(/https?:\/\/(www\.)?youtube.com\/watch\?/);
  187. }
  188. };
  189.  
  190. ytwp.html5 = {
  191. app: null,
  192. YTRect: null,
  193. YTApplication: null,
  194. playerInstances: null,
  195. moviePlayerElement: null,
  196. };
  197. ytwp.html5.getPlayerRect = function() {
  198. return new ytwp.html5.YTRect(ytwp.html5.moviePlayerElement.clientWidth, ytwp.html5.moviePlayerElement.clientHeight);
  199. };
  200. ytwp.html5.getApplicationClass = function() {
  201. if (ytwp.html5.YTApplication === null) {
  202. var testEl = document.createElement('div');
  203. var testAppInstance = uw.yt.player.Application.create(testEl, {});
  204. ytwp.html5.YTApplication = testAppInstance.constructor;
  205.  
  206. // Cleanup testAppInstance
  207. var playerInstances = ytwp.html5.getPlayerInstances();
  208.  
  209. var testAppInstanceKey = null;
  210. Object.keys(playerInstances).forEach(function(key) {
  211. if (playerInstances[key] === testAppInstance) {
  212. testAppInstanceKey = key;
  213. }
  214. });
  215. testAppInstance.dispose();
  216. delete playerInstances[testAppInstanceKey];
  217. }
  218. return ytwp.html5.YTApplication;
  219. };
  220. ytwp.html5.getPlayerInstances = function() {
  221. if (ytwp.html5.playerInstances === null) {
  222. var YTApplication = ytwp.html5.getApplicationClass();
  223. if (YTApplication === null)
  224. return null;
  225.  
  226. // Use yt.player.Application.create to find the playerInstancesKey.
  227. // function (a,b){try{var c=t$.B(a);if(t$.j[c]){try{t$.j[c].dispose()}catch(d){cg(d)}t$.j[c]=null}var e=new t$(a,b);Ji(e,function(){t$.j[c]=null});return t$.j[c]=e}catch(g){throw cg(g),g;}}
  228. 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\]\)/;
  229. var fnString = yt.player.Application.create.toString();
  230. var m = appCreateRegex.exec(fnString);
  231. if (m) {
  232. var playerInstancesKey = m[4];
  233. ytwp.html5.playerInstances = YTApplication[playerInstancesKey];
  234. } else {
  235. ytwp.error('Error trying to find playerInstancesKey.', fnString);
  236. }
  237. ytwp.html5.playerInstances = YTApplication.j;
  238. }
  239.  
  240. return ytwp.html5.playerInstances;
  241. };
  242. ytwp.html5.getPlayerInstance = function() {
  243. if (!ytwp.html5.app) {
  244. var playerInstances = ytwp.html5.getPlayerInstances();
  245. ytwp.log('playerInstances', playerInstances);
  246. var appInstance = null;
  247. var appInstanceKey = null;
  248. Object.keys(playerInstances).forEach(function(key) {
  249. appInstanceKey = key;
  250. appInstance = playerInstances[key];
  251. });
  252. ytwp.html5.app = appInstance;
  253. }
  254. return ytwp.html5.app;
  255. };
  256. ytwp.html5.autohideControls = function() {
  257. var moviePlayerElement = document.getElementById('movie_player');
  258. if (!moviePlayerElement) return;
  259. // ytwp.log(moviePlayerElement.classList);
  260. jQuery.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');
  261. jQuery.addClass(moviePlayerElement, 'autominimize-progress-bar autohide-controls hide-controls-when-cued');
  262. // ytwp.log(moviePlayerElement.classList);
  263. };
  264. ytwp.html5.update = function() {
  265. if (!ytwp.html5.playerInstances)
  266. return;
  267. for (var key in ytwp.html5.playerInstances) {
  268. var playerInstance = ytwp.html5.playerInstances[key];
  269. ytwp.html5.updatePlayerInstance(playerInstance);
  270. }
  271. };
  272. ytwp.html5.replaceClientRect = function(app, moviePlayerKey, clientRectFnKey) {
  273. var moviePlayer = app[moviePlayerKey];
  274. ytwp.html5.moviePlayerElement = moviePlayer.element;
  275. ytwp.html5.YTRect = moviePlayer[clientRectFnKey].call(moviePlayer).constructor;
  276. moviePlayer[clientRectFnKey] = ytwp.html5.getPlayerRect;
  277. };
  278. ytwp.html5.setRectFn = function(app, moviePlayerKey, clientRectFnKey) {
  279. ytwp.html5.moviePlayerElement = document.getElementById('movie_player');
  280. var moviePlayer = app[moviePlayerKey];
  281. ytwp.html5.YTRect = moviePlayer[clientRectFnKey].call(moviePlayer).constructor;
  282. moviePlayer.constructor.prototype[clientRectFnKey] = ytwp.html5.getPlayerRect;
  283. };
  284. ytwp.html5.updatePlayerInstance = function(app) {
  285. if (!app) {
  286. return;
  287. }
  288.  
  289. var moviePlayerElement = document.getElementById('movie_player');
  290. var moviePlayer = null;
  291. var moviePlayerKey = null;
  292.  
  293. // 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}
  294. // 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}
  295. var clientRectFnRegex1 = /^(function \(a\)\{var b=this\.([a-zA-Z_$][\w_$]*)\.([a-zA-Z_$][\w_$]*)\(\)).*(\|\|\(c\.height\+=30\);return c})$/;
  296. // 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)}
  297. var clientRectFnRegex2 = /^(function \()(.|\n)*(return new ([a-zA-Z_$][\w_$]*)\(this\.element\.clientWidth,this\.element\.clientHeight\)})$/;
  298. var clientRectFn = null;
  299. var clientRectFnKey = null;
  300.  
  301. var fnAlreadyReplacedCount = 0;
  302.  
  303. Object.keys(app).forEach(function(key1) {
  304. var val1 = app[key1];//console.log(key1, val1);
  305. if (typeof val1 === 'object' && val1 !== null && val1.element === moviePlayerElement) {
  306. moviePlayer = val1;
  307. moviePlayerKey = key1;
  308.  
  309. for (var key2 in moviePlayer) {
  310. var val2 = moviePlayer[key2];//console.log(key1, key2, val2);
  311. if (typeof val2 === 'function') {
  312. var fnString = val2.toString();
  313. // console.log(fnString);
  314. if (clientRectFn === null && (clientRectFnRegex1.test(fnString) || clientRectFnRegex2.test(fnString))) {
  315. clientRectFn = val2;
  316. clientRectFnKey = key2;
  317. } else if (val2 === ytwp.html5.getPlayerRect) {
  318. fnAlreadyReplacedCount += 1;
  319. } else {
  320. // console.log(key1, key2, val2, '[Not Used]');
  321. }
  322. }
  323. }
  324. }
  325. });
  326.  
  327. if (fnAlreadyReplacedCount > 0) {
  328. return;
  329. }
  330.  
  331. if (moviePlayer === null || clientRectFn === null) {
  332. console.log('[ytwp] ', '[Error]', 'HTML5 Player has changed or there\'s multiple playerInstances and this one has been destroyed.');
  333. console.log('moviePlayer', moviePlayerKey, moviePlayer);
  334. console.log('clientRectFn', clientRectFnKey, clientRectFn);
  335. console.log('fnAlreadyReplacedCount', fnAlreadyReplacedCount);
  336. if (moviePlayer === null) {
  337. console.log('Debugging: moviePlayer');
  338. var table = [];
  339. Object.keys(app).forEach(function(key1) {
  340. var val1 = app[key1];
  341. table.push({
  342. key: key1,
  343. element: typeof val1 === 'object' && val1 !== null && val1.element === moviePlayerElement,
  344. val: val1,
  345. });
  346. });
  347. console.table(table);
  348. }
  349. if (moviePlayer != null) {
  350. console.log('Debugging: clientRectFn');
  351. var table = [];
  352. for (var key2 in moviePlayer) {
  353. var val2 = moviePlayer[key2];
  354. table.push({
  355. key: key2,
  356. returns: moviePlayer[key2] && moviePlayer[key2].toString().indexOf('return'),
  357. src: moviePlayer[key2] && moviePlayer[key2].toString(),
  358. });
  359. }
  360. console.table(table);
  361. }
  362. return;
  363. }
  364. ytwp.html5.setRectFn(app, moviePlayerKey, clientRectFnKey);
  365. };
  366.  
  367.  
  368.  
  369. ytwp.event = {
  370. init: function() {
  371. ytwp.log('init');
  372. if (!ytwp.initialized) {
  373. ytwp.isWatchPage = ytwp.util.isWatchUrl();
  374. if (ytwp.isWatchPage) {
  375. ytwp.event.initStyle();
  376. ytwp.event.initScroller();
  377. ytwp.initialized = true;
  378. ytwp.pageReady = false;
  379. }
  380. }
  381. ytwp.event.onWatchInit();
  382. ytwp.event.html5PlayerFix();
  383. },
  384. initScroller: function() {
  385. // Register listener & Call it now.
  386. unsafeWindow.addEventListener('scroll', ytwp.event.onScroll, false);
  387. unsafeWindow.addEventListener('resize', ytwp.event.onScroll, false);
  388. ytwp.event.onScroll();
  389. },
  390. onScroll: function() {
  391. var viewportHeight = document.documentElement.clientHeight;
  392.  
  393. // topOfPageClassId
  394. if (unsafeWindow.scrollY == 0) {
  395. jQuery.addClass(document.body, topOfPageClassId);
  396. } else {
  397. jQuery.removeClass(document.body, topOfPageClassId);
  398. }
  399.  
  400. // viewingVideoClassId
  401. if (unsafeWindow.scrollY <= viewportHeight) {
  402. jQuery.addClass(document.body, viewingVideoClassId);
  403. } else {
  404. jQuery.removeClass(document.body, viewingVideoClassId);
  405. }
  406. },
  407. initStyle: function() {
  408. ytwp.log('initStyle');
  409. ytwp.style = new JSStyleSheet(scriptStyleId);
  410. ytwp.event.buildStylesheet();
  411. ytwp.style.injectIntoHeader();
  412. },
  413. buildStylesheet: function() {
  414. ytwp.log('buildStylesheet');
  415. //--- Video Player
  416.  
  417. //
  418. var d;
  419. d = buildVenderPropertyDict(transitionProperties, 'left 0s linear, padding-left 0s linear');
  420. d['padding'] = '0 !important';
  421. d['margin'] = '0 !important';
  422. ytwp.style.appendRule([
  423. scriptBodyClassSelector + ' #player',
  424. scriptBodyClassSelector + '.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible #player',
  425. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #player',
  426. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #player-legacy',
  427. scriptBodyClassSelector + '.ltr.ytcenter-site-center.ytcenter-non-resize.ytcenter-guide-visible.guide-collapsed #watch7-main-container',
  428. ], d);
  429. //
  430. d = buildVenderPropertyDict(transitionProperties, 'width 0s linear, left 0s linear');
  431.  
  432. // Bugfix for Firefox
  433. // Parts of the header (search box) are hidden under the player.
  434. // Firefox doesn't seem to be using the fixed header+guide yet.
  435. d['float'] = 'initial';
  436.  
  437. // Skinny mode
  438. d['left'] = 0;
  439. d['margin-left'] = 0;
  440.  
  441. ytwp.style.appendRule(scriptBodyClassSelector + ' #player-api', d);
  442.  
  443. // Theatre mode
  444. ytwp.style.appendRule(scriptBodyClassSelector + ' .watch-stage-mode #player .player-api', {
  445. 'left': 'initial',
  446. 'margin-left': 'initial',
  447. });
  448.  
  449. // !important is mainly for simplicity, but is needed to override the !important styling when the Guide is open due to:
  450. // .sidebar-collapsed #watch7-video, .sidebar-collapsed #watch7-main, .sidebar-collapsed .watch7-playlist { width: 945px!important; }
  451. // Also, Youtube Center resizes #player at element level.
  452. ytwp.style.appendRule(
  453. [
  454. scriptBodyClassSelector + ' #player',
  455. scriptBodyClassSelector + ' #movie_player',
  456. scriptBodyClassSelector + ' #player-mole-container',
  457. scriptBodyClassSelector + ' .html5-video-container',
  458. scriptBodyClassSelector + ' .html5-main-video',
  459. ],
  460. {
  461. 'width': '100% !important',
  462. 'min-width': '100% !important',
  463. 'max-width': '100% !important',
  464. 'height': '100% !important',
  465. 'min-height': '100% !important',
  466. 'max-height': '100% !important',
  467. }
  468. );
  469.  
  470. ytwp.style.appendRule(
  471. [
  472. scriptBodyClassSelector + ' #player',
  473. scriptBodyClassSelector + ' .html5-main-video',
  474. ],
  475. {
  476. 'top': '0 !important',
  477. 'right': '0 !important',
  478. 'bottom': '0 !important',
  479. 'left': '0 !important',
  480. }
  481. );
  482. // Resize #player-unavailable, #player-api
  483. // Using min/max width/height will keep
  484. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-width', 'width', '100% !important');
  485. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height', 'height', '100% !important');
  486.  
  487. //--- Move Video Player
  488. ytwp.style.appendRule(scriptBodyClassSelector + ' #player', {
  489. 'position': 'absolute',
  490. // Already top:0; left: 0;
  491. });
  492. ytwp.style.appendRule(scriptBodyClassSelector, { // body
  493. 'margin-top': '100vh',
  494. });
  495.  
  496.  
  497. //--- Sidebar
  498. // Remove the transition delay as you can see it moving on page load.
  499. d = buildVenderPropertyDict(transitionProperties, 'margin-top 0s linear, padding-top 0s linear');
  500. d['margin-top'] = '0 !important';
  501. d['top'] = '0 !important';
  502. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch7-sidebar', d);
  503.  
  504. ytwp.style.appendRule(scriptBodyClassSelector + '.cardified-page #watch7-sidebar-contents', 'padding-top', '0');
  505.  
  506. //--- Absolutely position the fixed header.
  507. // Masthead
  508. d = buildVenderPropertyDict(transitionProperties, 'top 0s linear !important');
  509. ytwp.style.appendRule(scriptBodyClassSelector + '.hide-header-transition #masthead-positioner', d);
  510. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #masthead-positioner', {
  511. 'position': 'absolute',
  512. 'top': '100% !important'
  513. });
  514.  
  515. // Guide
  516. // When watching the video, we need to line it up with the masthead.
  517. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #appbar-guide-menu', {
  518. 'display': 'initial',
  519. 'position': 'absolute',
  520. 'top': '100% !important' // Masthead height
  521. });
  522. ytwp.style.appendRule(scriptBodyClassSelector + '.' + viewingVideoClassId + ' #page.watch #guide', {
  523. 'display': 'initial',
  524. 'margin': '0',
  525. 'position': 'initial'
  526. });
  527.  
  528. //---
  529. // Hide Scrollbars
  530. ytwp.style.appendRule(scriptBodyClassSelector + '.' + topOfPageClassId, 'overflow-x', 'hidden');
  531.  
  532.  
  533. //--- Fix Other Possible Style Issues
  534. ytwp.style.appendRule(scriptBodyClassSelector + ' #placeholder-player', 'display', 'none');
  535. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch-sidebar-spacer', 'display', 'none');
  536. ytwp.style.appendRule(scriptBodyClassSelector + ' .skip-nav', 'display', 'none');
  537.  
  538. //--- Whitespace Leftover From Moving The Video
  539. ytwp.style.appendRule(scriptBodyClassSelector + ' #page.watch', 'padding-top', '0');
  540. ytwp.style.appendRule(scriptBodyClassSelector + ' .player-branded-banner', 'height', '0');
  541.  
  542. //--- Youtube+ Compatiblity
  543. ytwp.style.appendRule(scriptBodyClassSelector + ' #body-container', 'position', 'static');
  544.  
  545. //--- Playlist Bar
  546. ytwp.style.appendRule([
  547. scriptBodyClassSelector + ' #placeholder-playlist',
  548. scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist',
  549. ], 'height', '490px !important');
  550. d = buildVenderPropertyDict(transitionProperties, 'transform 0s linear');
  551. ytwp.style.appendRule(scriptBodyClassSelector + ' #watch-appbar-playlist', d);
  552. d = buildVenderPropertyDict(transformProperties, 'translateY(0px)');
  553. d['margin-left'] = '0';
  554. d['top'] = 'calc(100vh + 60px)';
  555. ytwp.style.appendRule(scriptBodyClassSelector + ' #player .player-height#watch-appbar-playlist', d);
  556. ytwp.style.appendRule(scriptBodyClassSelector + ' .playlist-videos-list', 'height', '430px');
  557. },
  558. onWatchInit: function() {
  559. ytwp.log('onWatchInit');
  560. if (!ytwp.initialized) return;
  561. if (ytwp.pageReady) return;
  562.  
  563. ytwp.event.addBodyClass();
  564. ytwp.pageReady = true;
  565. },
  566. onDispose: function() {
  567. ytwp.log('onDispose');
  568. ytwp.initialized = false;
  569. ytwp.pageReady = false;
  570. ytwp.isWatchPage = false;
  571. ytwp.html5.app = null;
  572. },
  573. addBodyClass: function() {
  574. // Insert CSS Into the body so people can style around the effects of this script.
  575. jQuery.addClass(document.body, scriptBodyClassId);
  576. ytwp.log('Applied ' + scriptBodyClassSelector);
  577. },
  578. html5PlayerFix: function() {
  579. ytwp.log('html5PlayerFix');
  580.  
  581. try {
  582. if (!uw.ytcenter // Youtube Center
  583. && !uw.html5Patched // Youtube+
  584. && (!ytwp.html5.app)
  585. && (uw.ytplayer && uw.ytplayer.config)
  586. && (uw.yt && uw.yt.player && uw.yt.player.Application && uw.yt.player.Application.create)
  587. ) {
  588. ytwp.html5.app = ytwp.html5.getPlayerInstance();
  589. }
  590.  
  591. ytwp.html5.update();
  592. ytwp.html5.autohideControls();
  593. } catch (e) {
  594. ytwp.error(e);
  595. }
  596. },
  597.  
  598. };
  599.  
  600.  
  601. ytwp.pubsubListeners = {
  602. 'init': function() { // Not always called
  603. ytwp.event.init();
  604. },
  605. 'init-watch': function() { // Not always called
  606. ytwp.event.init();
  607. },
  608. 'player-added': function() { // Not always called
  609. // Usually called after init-watch, however this is called before init when going from channel -> watch page.
  610. // The init event is when the body element resets all it's classes.
  611. ytwp.event.init();
  612. },
  613. // 'player-resize': function() {},
  614. // 'player-playback-start': function() {},
  615. 'appbar-guide-delay-load': function() {
  616. // Listen to a later event that is always called in case the others are missed.
  617. ytwp.event.init();
  618.  
  619. // Channel -> /watch
  620. if (ytwp.util.isWatchUrl())
  621. ytwp.event.addBodyClass();
  622. },
  623. // 'dispose-watch': function() {},
  624. 'dispose': function() {
  625. ytwp.event.onDispose();
  626. }
  627. };
  628.  
  629. ytwp.registerYoutubeListeners = function() {
  630. ytwp.registerYoutubePubSubListeners();
  631. };
  632.  
  633. ytwp.registerYoutubePubSubListeners = function() {
  634. // Subscribe
  635. for (var eventName in ytwp.pubsubListeners) {
  636. var eventListener = ytwp.pubsubListeners[eventName];
  637. uw.yt.pubsub.instance_.subscribe(eventName, eventListener);
  638. }
  639. };
  640.  
  641. ytwp.main = function() {
  642. try {
  643. ytwp.registerYoutubeListeners();
  644. } catch(e) {
  645. ytwp.error("Could not hook yt.pubsub", e);
  646. setTimeout(ytwp.main, 1000);
  647. }
  648. ytwp.event.init();
  649. };
  650.  
  651. ytwp.main();
  652. })(typeof unsafeWindow !== 'undefined' ? unsafeWindow : window);