YouTube Auto Buffer & Auto HD

Buffers the video without autoplaying and puts it in HD if the option is on. For Firefox, Opera, & Chrome

目前为 2014-04-24 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube Auto Buffer & Auto HD
  3. // @namespace http://userscripts.org/users/23652
  4. // @description Buffers the video without autoplaying and puts it in HD if the option is on. For Firefox, Opera, & Chrome
  5. // @icon https://raw.github.com/joesimmons/YouTube---Auto-Buffer---Auto-HD/master/media/logo-64x64.png
  6. // @include http://*.youtube.com/*
  7. // @include http://youtube.com/*
  8. // @include https://*.youtube.com/*
  9. // @include https://youtube.com/*
  10. // @copyright JoeSimmons
  11. // @author JoeSimmons
  12. // @version 1.2.87
  13. // @license GPL version 3 or any later version; http://www.gnu.org/copyleft/gpl.html
  14. // @require http://userscripts.org/scripts/source/49700.user.js?name=GM_config
  15. // @require https://raw.github.com/joesimmons/jsl/master/versions/jsl-1.3.1.js
  16. // @require http://userscripts.org/scripts/source/186053.user.js?name=YouTube_Button_Container
  17. // @require http://userscripts.org/scripts/source/419926.user.js?name=YouTube_Disable_Red_Bar
  18. //@require http://usocheckup.dune.net/49366.js
  19. // @grant GM_info
  20. // @grant GM_getValue
  21. // @grant GM_log
  22. // @grant GM_openInTab
  23. // @grant GM_registerMenuCommand
  24. // @grant GM_setValue
  25. // @grant GM_xmlhttpRequest
  26. // ==/UserScript==
  27.  
  28. /* CHANGELOG
  29.  
  30. 1.2.87 (1/22/2014)
  31. - fixed odd internal error that made the player not show
  32. - fixed problem with "Red Bar" not being disabled properly
  33.  
  34. 1.2.86 (12/21/2013)
  35. - fixed Large player button (as far as I can tell)
  36. - added Automatic quality option (the YouTube default)
  37. - switched the DASH option default to enabled
  38. this is really what the script was made for... auto-buffering
  39.  
  40. 1.2.85 (12/11/2013)
  41. - added a script icon
  42. - added a "Player Color Scheme" option
  43. - fixed bug with SPF not being de-activated properly in non-Firefox browsers
  44. - fixed volume bug
  45. - fixed adding time in url bug. it will now skip to the right portion of the video
  46. - changed internal name of the "Activation Mode" option. shouldn't affect the user
  47. - the script doesn't add any javascript to the page anymore
  48. it uses onYouTubePlayerReady to detect when the player is ready;
  49. it's much more performant than an interval
  50.  
  51. 1.2.84 (10/31/2013)
  52. - added primitive type checking when copying ytplayer.config.args into the flashvars.
  53. this fixes the issue with Flashgot and possibly other add-ons
  54. - fixed non-activation by moving the _spf_state check to the top of init.
  55. this disables SPF on every YouTube page now, and should make the script activate correctly
  56. - changed all RegExp test methods to match. match seems more consistent.
  57. I've had cases where test doesn't work, but match does
  58.  
  59. 1.2.83 (10/28/2013)
  60. - added auto HD, volume, and more activation modes for html5 (thanks to youtube updating its API)
  61. - changed the default quality to 1080p
  62. - changed the wording of some options
  63. - changed the "Disable Dash Playback" option to false for default
  64. - disabled SPF (aka Red Bar feature) completely until I get playlists working better
  65. - changed the setPref prototype function to a regular function
  66.  
  67. 1.2.82 (9/5/2013)
  68. - added support for older Firefox versions (tested on 3.6)
  69. - added a new option to disable 'dash' playback (videos loading in blocks/pieces)
  70. - re-added ad removal feature (experimental for now)
  71.  
  72. 1.2.81
  73. - fixed HTML5 support. YT changed tag names so the script got confused
  74. - made a few minor performance tweaks
  75. - fixed 'play symbol in title' bug in autobuffer mode (it would show playing, even though it's paused/buffering)
  76.  
  77. 1.2.80
  78. - switched to JSL.setInterval for consistency and drift accommodation
  79. - visual tweaks to:
  80. msg().
  81. the rest of the page now dims while the msg box is visible
  82. changed the spacing of most of the elements
  83. changed the font sizes and the font (Arial)
  84. added a close button instead of requiring a double click
  85. made it auto-open the options screen when the msg is closed
  86. GM_config.
  87. made the background color more mellow and moved the section title near the middle
  88.  
  89. 1.2.79
  90. - adjusted to the new play symbol in the youtube title feature
  91. - Changed margins on the settings button when in footer
  92. - Switched JSL/pushState checking order.
  93. Previously in 1.2.78, if JSL didn't exist or wasn't @required, the script
  94. would still loop every 500ms to re-set the pushState method, even though the
  95. script wasn't going to be running.
  96. I switched that so that JSL has to exist before the script does anything.
  97.  
  98. 1.2.78
  99. - Fixed bug where options button wasn't getting added to the footer with the new Red Bar YT feature
  100.  
  101. 1.2.77
  102. - Adapted to the new YouTube feature that uses HTML5's history.pushState to load videos
  103. - Small fixes here and there
  104. - Excluded (with RegExp) pages without videos on them
  105. - Fixed GM_config.log()
  106. - Declared all variables at the beginning of functions
  107. - Made finding the video player a little more reliable
  108. - Make 'autoplay on playlists' work with HTML5 videos
  109.  
  110. 1.2.76
  111. - Added new quality option ('1080p+' - for anything higher than 1080p)
  112.  
  113. 1.2.75
  114. - Added a new option (to move option button to page footer)
  115. - Added a new option (to autoplay on playlists regardless of auto[play/buffer] setting)
  116. - Added a first time user message box
  117. - Fixed bug with GM_config's [set/get]Value functions. Chrome/Opera were not using localStorage before this update
  118.  
  119. 1.2.74
  120. - Adapted to YouTube's new layout
  121.  
  122. 1.2.73
  123. - Added compatibility for user pages
  124.  
  125. 1.2.72
  126. - Made it fully working again in Opera & Chrome
  127. - Switched from setInterval to setTimeout due to instability
  128. - Added an anonymous function wrapper
  129.  
  130. 1.2.71
  131. - Added compatibility for HTML5
  132.  
  133. */
  134.  
  135.  
  136.  
  137.  
  138. // run the script in an IIFE, to hide its variables from the global scope
  139. (function (undefined) {
  140.  
  141. 'use strict';
  142.  
  143. var aBlank = ['', '', ''],
  144. URL = location.href,
  145. navID = 'watch7-user-header',
  146. rYoutubeUrl = /^https?:\/\/([^\.]+\.)?youtube\.com\//,
  147. // rYoutubeBlacklistedUrl = /^https?:\/\/([^\.]+\.)?youtube\.com\/(feed\/(?!subscriptions)|account|inbox|my_|tags|view_all|analytics)/i,
  148. rList = /[?&]list=/i,
  149. rPlaySymbol = /^\u25B6\s*/,
  150. script_name = 'YouTube - Auto-Buffer & Auto-HD',
  151. tTime = (URL.match(/[&#?]t=([sm0-9]+)/) || aBlank)[1],
  152. ads = [
  153. 'supported_without_ads',
  154. 'ad3_module',
  155. 'adsense_video_doc_id',
  156. 'allowed_ads',
  157. 'baseUrl',
  158. 'cafe_experiment_id',
  159. 'afv_inslate_ad_tag',
  160. 'advideo',
  161. 'ad_device',
  162. 'ad_channel_code_instream',
  163. 'ad_channel_code_overlay',
  164. 'ad_eurl',
  165. 'ad_flags',
  166. 'ad_host',
  167. 'ad_host_tier',
  168. 'ad_logging_flag',
  169. 'ad_preroll',
  170. 'ad_slots',
  171. 'ad_tag',
  172. 'ad_video_pub_id',
  173. 'aftv',
  174. 'afv',
  175. 'afv_ad_tag',
  176. 'afv_instream_max',
  177. 'afv_ad_tag_restricted_to_instream',
  178. 'afv_video_min_cpm',
  179. 'prefetch_ad_live_stream'
  180. ],
  181. hasMainBeenRun, nav, uw, wait_intv;
  182.  
  183. function toNum(a) {
  184. return parseInt(a, 10);
  185. }
  186.  
  187. // msg by JoeSimmons
  188. function msg(infoObject) {
  189.  
  190. var box_id_name = 'script_msg',
  191. box = document.getElementById(box_id_name),
  192. rLinebreaks = /[\r\n]/g,
  193. title = typeof infoObject.title === 'string' && infoObject.title.length > 3 ? infoObject.title : 'Message Box by JoeSimmons.';
  194.  
  195. // add BR tags to line breaks
  196. infoObject.text = infoObject.text.replace(rLinebreaks, '<br />\n');
  197.  
  198. function msg_close(event) {
  199. event.preventDefault();
  200.  
  201. document.getElementById(box_id_name).style.display = 'none';
  202.  
  203. if (typeof infoObject.onclose === 'function') {
  204. infoObject.onclose();
  205. }
  206. }
  207.  
  208. if (box == null) {
  209. JSL.addStyle('' +
  210. '@keyframes blink { ' +
  211. '50% { color: #B95C00; } ' +
  212. '}\n\n' +
  213. '#' + box_id_name + ' .msg-header { ' +
  214. 'animation: blink 1s linear infinite normal; ' +
  215. '}' +
  216. '');
  217. document.body.appendChild(
  218. JSL.create('div', {id : box_id_name, style : 'position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 999999; background-color: rgba(0, 0, 0, 0.6);'}, [
  219. // main box
  220. JSL.create('div', {id : box_id_name + '_box', style : 'position: absolute; top: 25%; left: 25%; width: 50%; height: 50%; padding-top: 50px; background-color: #E9E9E9; border: 3px double #006195;'}, [
  221. // header
  222. JSL.create('div', {style : 'margin: 0 auto; padding-bottom: 40px; color: #F07800; font-size: 21pt; font-family: Arial, Verdana, "Myriad Pro"; font-weight: normal; text-shadow: 2px 2px 4px #C7C7C7; text-align: center;', 'class' : 'msg-header', textContent : title}),
  223.  
  224. // text (message)
  225. JSL.create('div', {innerHTML : infoObject.text, style : 'text-align: center; margin: 0 auto; padding-top: 39px; border-top: 1px solid #B0B0B0; color: #000000; font-size: 11pt; font-family: Arial, Verdana, "Myriad Pro"; font-weight: normal; text-shadow: 0 0 8px #AEAEAE;'}),
  226.  
  227. // close button
  228. JSL.create('div', {style : 'position: absolute; bottom: 20px; left: 0; width: 100%; text-align: center;'}, [
  229. JSL.create('input', {id : box_id_name + '_close', type : 'button', value : 'Close Message', onclick : msg_close, style : 'margin: 0 auto; padding: 2px 20px; font-size: 11pt; font-family: Arial, Verdana, "Myriad Pro"; font-weight: normal;'})
  230. ])
  231. ])
  232. ])
  233. );
  234. } else {
  235. box.innerHTML += infoObject.text;
  236. }
  237. }
  238.  
  239. // will return true if the value is a primitive value
  240. function isPrimitiveType(value) {
  241. switch (typeof value) {
  242. case 'string': case 'number': case 'boolean': case 'undefined': {
  243. return true;
  244. }
  245. case 'object': {
  246. return !value;
  247. }
  248. }
  249.  
  250. return false;
  251. }
  252.  
  253. function setPref(str, values) {
  254. var i, value, rQuery;
  255.  
  256. for (i = 0; value = values[i]; i += 1) {
  257. // (several lines for readability)
  258. rQuery = new RegExp('[?&]?' + value[0] + '=[^&]*');
  259. str = str.replace(rQuery, '') + '&' + value[0] + '=' + value[1];
  260. str = str.replace(/^&+|&+$/g, '');
  261. }
  262.  
  263. return str;
  264. }
  265.  
  266. // unwraps the element so we can use its methods freely
  267. function unwrap(elem) {
  268. if (elem) {
  269. if ( typeof XPCNativeWrapper === 'function' && typeof XPCNativeWrapper.unwrap === 'function' ) {
  270. return XPCNativeWrapper.unwrap(elem);
  271. } else if (elem.wrappedJSObject) {
  272. return elem.wrappedJSObject;
  273. }
  274. }
  275.  
  276. return elem;
  277. }
  278.  
  279. function fixPlaySymbol() {
  280. document.title = document.title.replace(rPlaySymbol, '');
  281. }
  282.  
  283. // grabs the un-wrapped player
  284. function getPlayer() {
  285. var doc = uw.document;
  286. return doc.getElementById('c4-player') || doc.getElementById('movie_player');
  287. }
  288.  
  289. // adds the Options button below the video
  290. function addButton() {
  291. var footer = GM_config.get('footer') === true,
  292. footerHolder = document.getElementById('footer-main');
  293.  
  294. addButtonToContainer('Auto-Buffer Options', function () { GM_config.open(); }, 'autobuffer-options');
  295.  
  296. if (footer && footerHolder) {
  297. footerHolder.appendChild( document.getElementById('autobuffer-options') );
  298. }
  299. }
  300.  
  301. // this function sets up the script
  302. function init() {
  303. hasMainBeenRun = false;
  304.  
  305. // get the raw window object of the YouTube page
  306. uw = typeof unsafeWindow !== 'undefined' ? unsafeWindow : unwrap(window);
  307.  
  308. uw.onYouTubePlayerReady = function onYouTubePlayerReady(player) {
  309. if (typeof player === 'object' && hasMainBeenRun === false) {
  310. window.postMessage('YTAB__ready', '*');
  311. }
  312. };
  313.  
  314. JSL.waitFor({
  315. selector : '#c4-player, #movie_player',
  316. verifier : function (elem) {
  317. elem = unwrap( elem[0] );
  318. return typeof elem.stopVideo === 'function';
  319. },
  320. done : function () {
  321. if (hasMainBeenRun === false) {
  322. main();
  323. }
  324. }
  325. });
  326. }
  327.  
  328. // this is the main function. it does all the autobuffering, quality/volume changing, annotation hiding, etc
  329. function main() {
  330. var player = getPlayer(),
  331. parent = player.parentNode,
  332. alreadyBuffered = false,
  333. time = 0,
  334. args, arg, buffer_intv, fv, isHTML5, playerClone,
  335. playIfPlaylist, val, userOpts;
  336.  
  337. // don't let main() run again unless a new video is loaded
  338. hasMainBeenRun = true;
  339.  
  340. // remove the player out of the document temporarily while other things are being done,
  341. // to reduce the time the player may be playing the video
  342. parent.removeChild(player);
  343.  
  344. // set up the user options object
  345. userOpts = {
  346. activationMode : GM_config.get('activationMode'),
  347. disableDash : GM_config.get('disableDash') === true,
  348. hideAnnotations : GM_config.get('hideAnnotations') === true,
  349. hideAds : GM_config.get('hideAds') === true,
  350. quality : GM_config.get('autoHD'),
  351. theme : GM_config.get('theme'),
  352. volume : GM_config.get('volume')
  353. };
  354.  
  355. // set up other variables
  356. playerClone = player.cloneNode(true);
  357. fv = player.getAttribute('flashvars');
  358. isHTML5 = !!document.querySelector('video.html5-main-video');
  359. playIfPlaylist = !!URL.match(rList) && GM_config.get('autoplayplaylists') === true;
  360.  
  361. if (uw.ytplayer && uw.ytplayer.config && uw.ytplayer.config.args) {
  362. args = uw.ytplayer.config.args;
  363. }
  364.  
  365. // set the volume to the user's preference
  366. if (userOpts.volume != 1000) {
  367. player.setVolume(userOpts.volume);
  368. }
  369.  
  370. if (isHTML5) {
  371. if (player.getPlaybackQuality() !== userOpts.quality) {
  372. player.setPlaybackQuality(userOpts.quality);
  373. }
  374.  
  375. if (!playIfPlaylist) {
  376. if (userOpts.activationMode === 'buffer') {
  377. player.pauseVideo();
  378. } else if (userOpts.activationMode === 'none') {
  379. player.stopVideo();
  380. }
  381. }
  382. } else {
  383. // copy 'ytplayer.config.args' into the flash vars
  384. if (args) {
  385. for (arg in args) {
  386. val = args[arg];
  387. if ( args.hasOwnProperty(arg) && isPrimitiveType(val) ) {
  388. fv = setPref(fv, [ [ arg, encodeURIComponent(val) ] ]);
  389. }
  390. }
  391. }
  392.  
  393. // ad removal
  394. if (userOpts.hideAds) {
  395. fv = fv.replace(new RegExp('(&amp;|[&?])?(' + ads.join('|') + ')=[^&]*', 'g'), '');
  396. /*
  397. fv = setPref(fv,
  398. ads.map(function (ad) {
  399. return [ad, ''];
  400. })
  401. );
  402. */
  403. }
  404.  
  405. // disable DASH playback
  406. if (userOpts.disableDash) {
  407. fv = setPref(fv, [
  408. ['dashmpd', ''],
  409. ['dash', '0']
  410. ]);
  411. }
  412.  
  413. // edit the flashvars
  414. fv = setPref(fv, [
  415. ['vq', userOpts.quality], // set the quality
  416. ['autoplay', (userOpts.activationMode !== 'none' || playIfPlaylist) ? '1' : '0' ], // enable/disable autoplay
  417. ['iv_load_policy', userOpts.hideAnnotations ? '3' : '1' ], // enable/disable annotations
  418. ['theme', userOpts.theme], // use light/dark theme
  419.  
  420. // some "just-in-case" settings
  421. ['enablejsapi', '1'], // enable JS API
  422. ['jsapicallback', 'onYouTubePlayerReady'], // enable JS ready callback
  423. ['fs', '1'], // enable fullscreen button, just in-case
  424. ['modestbranding', '1'], // hide YouTube logo in player
  425. ['disablekb', '0'] // enable keyboard controls in player
  426. ]);
  427.  
  428. // handle video starting time
  429. if ( tTime.match(/\d+m/) ) {
  430. time += toNum( tTime.match(/(\d+)m/)[1] ) * 60;
  431. }
  432. if ( tTime.match(/\d+s/) ) {
  433. time += toNum( tTime.match(/(\d+)s/)[1] );
  434. }
  435. if ( tTime.match(/^\d+$/) ) {
  436. time += toNum(tTime);
  437. }
  438. if (time <= 3) {
  439. // if no time is in the url, check the player's time
  440. try {
  441. // sometimes causes a weird error.
  442. // it will say getCurrentTime isn't a function,
  443. // even though the typeof is "function",
  444. // and alerting its value says [native code]
  445. time = player.getCurrentTime();
  446. } catch (e) {}
  447. if (time <= 3) {
  448. time = 0;
  449. }
  450. }
  451. fv = setPref( fv, [ ['start', time] ] );
  452.  
  453. // set the new player's flashvars
  454. playerClone.setAttribute('flashvars', fv);
  455.  
  456. // replace the original player with the modified clone
  457. parent.appendChild(playerClone);
  458.  
  459. if (userOpts.activationMode === 'buffer' && playIfPlaylist === false) {
  460. // handle auto-buffering
  461. buffer_intv = JSL.setInterval(function () {
  462. var player = getPlayer();
  463.  
  464. if (player && typeof player.getPlayerState === 'function') {
  465. JSL.clearInterval(buffer_intv);
  466.  
  467. // pause the video so it can buffer
  468. player.pauseVideo();
  469.  
  470. // seek back to beginning if time elapsed is not much
  471. if (player.getCurrentTime() <= 3) {
  472. player.seekTo(0);
  473. }
  474.  
  475. // adjust to the 'play symbol in title' feature
  476. window.setTimeout(fixPlaySymbol, 1000);
  477. }
  478. }, 100);
  479. } else if (userOpts.activationMode === 'none') {
  480. // adjust to the 'play symbol in title' feature
  481. window.setTimeout(fixPlaySymbol, 1500);
  482. }
  483. }
  484.  
  485. // show the first time user message, then set it to never show again
  486. if (GM_config.getValue('yt-autobuffer-autohd-first', 'yes') === 'yes') {
  487. msg({
  488. text : 'Welcome to "' + script_name + '".\n\n\n\n' +
  489. 'There is an options button below the video.\n\n\n\n' +
  490. 'The options screen will automatically open when you close this message.',
  491. title : '"' + script_name + '" Message',
  492. onclose : function () { GM_config.open(); }
  493. });
  494. GM_config.setValue('yt-autobuffer-autohd-first', 'no');
  495. }
  496. }
  497.  
  498. // make sure the page is not in a frame
  499. // & is on a YouTube page (the @include works most of the time, but this is 100%)
  500. // & isn't on a blacklisted YouTube page
  501. if ( window !== window.top || !URL.match(rYoutubeUrl) /*|| URL.match(rYoutubeBlacklistedUrl)*/ ) { return; }
  502.  
  503. // quit if JSL/GM_config is non-existant
  504. if (typeof JSL === 'undefined' || typeof GM_config === 'undefined' || typeof addButtonToContainer === 'undefined') {
  505. return alert('' +
  506. 'A @require is missing.\n\n' +
  507. 'Either you\'re not using the correct plug-in, or @require isn\'t working.\n\n' +
  508. 'Please review the script\'s main page to see which browser & add-on to use.' +
  509. '');
  510. }
  511.  
  512. // add a user-script command
  513. if (typeof GM_registerMenuCommand === 'function') {
  514. GM_registerMenuCommand('"' + script_name + '" Options', GM_config.open);
  515. }
  516.  
  517. // init GM_config
  518. GM_config.init('"' + script_name + '" Options', {
  519. activationMode : {
  520. section : ['Main Options'],
  521. label : 'Activation Mode',
  522. type : 'select',
  523. options : {
  524. 'buffer' : 'Auto Buffer (aka Auto Pause)',
  525. 'play' : 'Auto Play',
  526. 'none' : 'Stop Loading Immediately'
  527. },
  528. 'default' : 'buffer'
  529. },
  530. autoHD : {
  531. label : 'Auto HD',
  532. type : 'select',
  533. options : {
  534. 'default' : 'Automatic (default)',
  535. 'tiny' : '144p',
  536. 'small' : '240p',
  537. 'medium' : '360p',
  538. 'large' : '480p',
  539. 'hd720' : '720p (HD)',
  540. 'hd1080' : '1080p (HD)',
  541. 'hd1440' : '1440p (HD)',
  542. 'highres' : 'Original (highest)'
  543. },
  544. 'default' : 'hd1080'
  545. },
  546. disableDash : {
  547. label : 'Disable DASH Playback',
  548. type : 'checkbox',
  549. 'default' : true,
  550. title : '"DASH" loads the video in blocks/pieces; disrupts autobuffering -- Note: Qualities are limited when disabled'
  551. },
  552. hideAds : {
  553. label : 'Disable Ads',
  554. type : 'checkbox',
  555. 'default' : true,
  556. title : 'Should disable advertisements. AdBlock is better, though. Get that instead'
  557. },
  558. hideAnnotations : {
  559. label : 'Disable Annotations',
  560. type : 'checkbox',
  561. 'default' : false
  562. },
  563. theme : {
  564. section : ['Other Options'],
  565. label : 'Player Color Scheme',
  566. type : 'select',
  567. options : {
  568. 'dark' : 'Dark Theme',
  569. 'light' : 'Light Theme'
  570. },
  571. 'default' : 'dark'
  572. },
  573. volume : {
  574. label : 'Set volume to: ',
  575. type : 'select',
  576. options : {
  577. '1000' : 'Don\'t Change',
  578. '0' : 'Off',
  579. '5' : '5%',
  580. '10' : '10%',
  581. '20' : '20%',
  582. '25' : '25% (quarter)',
  583. '30' : '30%',
  584. '40' : '40%',
  585. '50' : '50% (half)',
  586. '60' : '60%',
  587. '70' : '70%',
  588. '75' : '75% (three quarters',
  589. '80' : '80%',
  590. '90' : '90%',
  591. '100' : '100% (full)',
  592. },
  593. title : 'What to set the volume to',
  594. 'default' : '1000'
  595. },
  596. autoplayplaylists : {
  597. label : 'Autoplay on Playlists (override)',
  598. type : 'checkbox',
  599. 'default' : false,
  600. title : 'This will enable autoplay on playlists, regardless of the "Activation Mode" option'
  601. },
  602. footer : {
  603. label : 'Options Button In Footer',
  604. type : 'checkbox',
  605. 'default' : false,
  606. title : 'This will make the options button show at the bottom of the page in the footer'
  607. }
  608. }, '' +
  609. 'body { ' +
  610. 'background-color: #DDDDDD !important; ' +
  611. 'color: #434343 !important; ' +
  612. 'font-family: Arial, Verdana, sans-serif !important; ' +
  613. '}' +
  614. '#config_header { ' +
  615. 'font-size: 16pt !important; ' +
  616. '}' +
  617. '.config_var { ' +
  618. 'margin-left: 20% !important; ' +
  619. 'margin-top: 20px !important; ' +
  620. '}' +
  621. '#header { ' +
  622. 'margin-bottom: 40px !important; ' +
  623. 'margin-top: 20px !important; ' +
  624. '}' +
  625. '.indent40 { ' +
  626. 'margin-left: 20% !important; ' +
  627. '}' +
  628. '.config_var * { ' +
  629. 'font-size: 10pt !important; ' +
  630. '}' +
  631. '.section_header_holder { ' +
  632. 'border-bottom: 1px solid #BBBBBB !important; ' +
  633. 'margin-top: 14px !important; ' +
  634. '}' +
  635. '.section_header { ' +
  636. 'background-color: #BEDBFF !important; ' +
  637. 'color: #434343 !important; ' +
  638. 'margin-left: 20% !important; ' +
  639. 'margin-top: 8px !important; ' +
  640. 'padding: 2px 200px !important; ' +
  641. 'text-decoration: none !important; ' +
  642. '}' +
  643. '.section_kids { ' +
  644. 'margin-bottom: 14px !important; ' +
  645. '}' +
  646. '.saveclose_buttons { ' +
  647. 'font-size: 14pt !important; ' +
  648. '}' +
  649. '#buttons_holder { ' +
  650. 'padding-right: 50px; ' +
  651. '}' +
  652. '', {
  653. close : function () {
  654. JSL('#c4-player, #movie_player').css('visibility', 'visible');
  655. JSL('#lights_out').hide();
  656. },
  657. open : function () {
  658. JSL('#c4-player, #movie_player').css('visibility', 'hidden');
  659. JSL('#lights_out').show('block');
  660. JSL('#GM_config').css('height', '80%').css('width', '80%');
  661. }
  662. });
  663.  
  664. // this is for the "lights out" feature of GM_config
  665. JSL.runAt('interactive', function () {
  666. JSL(document.body).append('div', {
  667. id : 'lights_out',
  668. style : 'display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 999998; background: rgba(0, 0, 0, 0.72);'
  669. });
  670.  
  671. // call the function that sets up everything
  672. init();
  673. });
  674.  
  675. // add a message listener for when the unsafeWindow function fires a message
  676. window.addEventListener('message', function (msg) {
  677. if (msg.data === 'YTAB__ready') {
  678. main();
  679. }
  680. }, false);
  681.  
  682. // wait for an element that can hold the options button to load,
  683. // then run our add button function
  684. JSL.waitFor({
  685. selector : '#watch7-headline, #gh-overviewtab div.c4-spotlight-module-component, #footer-main',
  686. done : addButton
  687. });
  688.  
  689. }());