Youtube key shortcuts FIX

Fix player controls Space, Left, Right, Up, Down to behave consistently after page load or clicking individual controls. Not focusing the mute button anymore.

当前为 2018-02-25 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Youtube key shortcuts FIX
  3. // @namespace https://github.com/GregHerendi
  4. // @version 1.0.4
  5. // @description Fix player controls Space, Left, Right, Up, Down to behave consistently after page load or clicking individual controls. Not focusing the mute button anymore.
  6. // @author Greg Herendi
  7. // @license MIT
  8. // @copyright 2018, Greg Herendi (https://github.com/GregHerendi)
  9. // @match *://*.youtube.com/*
  10. // @exclude *://*.youtube.com/tv*
  11. // @exclude *://*.youtube.com/live_chat*
  12. // @grant none
  13. // @require http://code.jquery.com/jquery-latest.js
  14. // ==/UserScript==
  15.  
  16. /* To test classicUI add to url:
  17. &disable_polymer=1
  18. ?disable_polymer=1
  19. */
  20.  
  21. /* Description:
  22. Player is focused right after pageload, no need to click in player frame.
  23. Click outside movie frame to get standard page controls: Up, Down, Left, Right (scroll)
  24. Press Esc to focus player, Shift-Esc to cycle other areas.
  25. Space pauses video on the whole webpage except textboxes
  26.  
  27. Youtube player controls will work consistently when player is focused:
  28. - Left / Right (5sec backwards / forwards), Up / Down (volume up / down), Home (restart video), End (skip to end)
  29. After clicking (focusing) progressbar/volume slidebars this is no longer changed: in progressbar Up / Down (jump 10 sec), in volume Left / Right (volume down / up)
  30. - 0-9 (jump to 0%-90%), C (switch closed captions/subtitles), +/- (change cc font size) -- unaffected
  31.  
  32. Global shortcuts:
  33. - Esc (exit fullscreen) -- now additionally focuses player when not in fullscreen
  34. - Space (play/pause) -- now on whole page, not only when player is focused
  35. Global shortcuts, unaffected:
  36. - F (fullscreen), M (mute)
  37. - K (play/pause)
  38. - J / L (10sec backwards / forwards)
  39. - , (previous frame), . (next frame)
  40. - Shift-, (speed -0.25), Shift-. (speed +0.25)
  41. - Shift-N (next video), Shift-P (previous video)
  42. - / (focus searchfield, only in classic ui)
  43. - ENTER (push the focused button)
  44.  
  45. Fixes the following awkward behaviour of Youtube player controls after clicking them (making the individual control focused):
  46. - after clicking the mute button Space toggles mute instead of pausing
  47. - after clicking the progressbar Up/Down will jump 10 sec in the video instead of changing the volume
  48. - after clicking the volume slider Left/Right will change the volume instead of jumping the video.
  49. - after clicking subtitle button Space will toggle subtitles
  50. - after clicking settings button Space will toggle settings
  51.  
  52. Sources of youtube shortcut behaviour:
  53. https://shortcutworld.com/Youtube-Player/win/Youtube-Player_Shortcuts
  54. https://www.hongkiat.com/blog/useful-youtube-keyboard-shortcuts-to-know/
  55. https://support.google.com/youtube/answer/7631406?hl=en
  56. 2010: flash player? https://www.makeuseof.com/tag/comprehensive-guide-youtube-player-keyboard-shortcuts/
  57. */
  58.  
  59. /* Changelog:
  60. 1.0.4:
  61. Shift-Esc cycles focus through 3 page areas: videos, masthead (search), content below video
  62. Mousewheel over player ajusts volume
  63. Switching to fullscreen focuses the player (enables Up/Down volume control, Left/Right jump)
  64. player, video, comment focus is visualized (box-shadow style) on both classicUI and materialUI
  65. clarify capturing event handlers captureKeydown(), captureFocus(): run at earlier phase than onKeydown()
  66. support embedded player, cache playerElem; channel pages have a second player, that is unsupported yet and has the default behaviour
  67. documented shortcut behaviour and sources
  68. 1.0.3:
  69. added keydown handler: Esc switches focus between player and page, Space pauses anywhere on page, except textboxes
  70. arrow keys are redirected from sliders directly to the player, no special behaviour when progressbar/volume slider is focused
  71. therefore no more need to steal focus from player controls when clicked
  72. tabindex set for player frame and removed for .caption-window
  73. 1.0.2:
  74. support both classic and material design (polymer) html5 player
  75. allow focusing by TAB: listen for mousedown & focus events to identify focusing by mouse
  76. added css: translucent lightblue background for focused element to aid TABing
  77. 1.0.1:
  78. support classic html5 player (#movie_player element is constructed later than userscript loaded)
  79. also broke material design (polymer) html5 player support
  80. @match *://
  81. 1.0: support material design (polymer) html5 player
  82. */
  83.  
  84. // Debug log
  85. //console.log("Youtube keys fix: loaded");
  86.  
  87. (function () {
  88. 'use strict';
  89.  
  90.  
  91. var playerContainer; // = document.getElementById('player-container') || document.getElementById('player') in embeds
  92. var playerElem; // = document.getElementById('movie_player') || playerContainer.querySelector('.html5-video-player') in embeds
  93. //var playerFocused;
  94. var isMaterialUI, isClassicUI;
  95. var lastFocusedPageArea;
  96. var areaOrder= [ null ], areaContainers= [ null ], areaFocusDefault= [ null ], areaFocusedSubelement= [ null ];
  97. //var areaContainers= {}, areaFocusedSubelement= {};
  98.  
  99. function isSubelementOf(elementWithin, ancestor) {
  100. if (!ancestor) return null;
  101. for (; elementWithin; elementWithin= elementWithin.parentElement) {
  102. if (elementWithin.id == ancestor) return true;
  103. }
  104. return false;
  105. }
  106.  
  107. function getAreaOf(elementWithin) {
  108. for (var i= 1; i<areaContainers.length; i++) if (isSubelementOf(elementWithin, areaContainers[i])) return i;
  109. return 0;
  110. //for (var area in areaContainers) if (isSubelementOf(document.activeElement, areaContainers[area])) return area;
  111. }
  112. function getFocusedArea() { return getAreaOf(document.activeElement); }
  113.  
  114. function tryFocus(newFocus) {
  115. newFocus= $(newFocus);
  116. if (!newFocus.length) return null;
  117. if (!newFocus.is(':visible()')) return false;
  118. //var oldFocus= document.activeElement;
  119. newFocus.focus();
  120. // document.activeElement is not updated yet?
  121. //console.log('Youtube keys fix: new activeElement=', document.activeElement);
  122. return true;
  123. /*
  124. var done= (document.activeElement !== document.body && (document.activeElement !== oldFocus || oldFocus === newFocus));
  125. if (done) console.log('Youtube keys fix: focused', document.activeElement);
  126. else console.log('Youtube keys fix: failed to focus', newFocus);
  127. return done;
  128. */
  129. }
  130.  
  131. function focusNextArea() {
  132. // Focus next area's areaFocusedSubelement (activeElement)
  133. var currentArea= getFocusedArea() || 0;
  134. var nextArea= (lastFocusedPageArea && lastFocusedPageArea !== currentArea) ? lastFocusedPageArea : currentArea + 1;
  135. // captureFocus() will store lastFocusedPageArea again if moving to a non-player area
  136. // if moving to the player then lastFocusedPageArea resets, Shift-Esc willmove to movie list (2)
  137. lastFocusedPageArea= null;
  138. // To enter player at end of round: nextArea= 1; To skip player: nextArea= 2;
  139. if (nextArea >= areaContainers.length) nextArea= 2;
  140.  
  141. var done= tryFocus( areaFocusedSubelement[nextArea] );
  142. if (!done) done= tryFocus( $(areaFocusDefault[nextArea]) );
  143. //if (!done) done= tryFocus( areaContainers[nextArea] );
  144. }
  145.  
  146. function focusAreaDefaultElement() {
  147. // Reset focus to area's default
  148. var area= getFocusedArea();
  149. // If not in area then focus player
  150. if (area === 0) area= 1;
  151.  
  152. var done= tryFocus( $(areaFocusDefault[area]) );
  153. if (!done) done= tryFocus( document.getElementById(areaContainers[area]) );
  154. if (!done && area !== 1) done= tryFocus( $(areaFocusDefault[1]) );
  155. }
  156.  
  157. function focusPlayer() {
  158. // Reset focus to player if focus is inside player area
  159. // Focus player's areaFocusedSubelement if outside
  160. var done= (getFocusedArea() !== 1) && tryFocus( areaFocusedSubelement[1] );
  161. if (!done) done= tryFocus( $(areaFocusDefault[1]) );
  162. }
  163.  
  164. function handleEsc(event) {
  165. event.preventDefault();
  166. event.stopPropagation();
  167. // Shift-Esc resets page focus to watch next video
  168. if (event.shiftKey) focusNextArea();
  169. //else if (??) focusAreaDefaultElement();
  170. else focusPlayer();
  171.  
  172. // Show that focus indicator blue frame and background
  173. $(playerElem).addClass('ytp-probably-keyboard-focus');
  174. // Show focus border - it shows by default after pressing Tab
  175. //$(document.documentElement).removeClass('no-focus-outline');
  176. }
  177.  
  178. function onKeydown(event) {
  179. // Debug log of key event
  180. //if (event.key != 'Shift') console.log("Youtube keys fix: " + event.type + " " + event.which + " ->", event.target, event);
  181.  
  182. // Esc switches focus between player(player-api/player-container(movie_player)) and webpage outside the player(masthead(buttons)/main(related/info/meta/comments))
  183. // event.target is focused (received the keypress)
  184. // onKeydown not executed when fullscreen, and this should not handle Esc
  185. if (event.which == 27) return handleEsc(event);
  186. }
  187.  
  188.  
  189. function redirectEvent(event, cloneEvent) {
  190. if (!playerElem) initPlayer();
  191. if (!playerElem || !$(playerElem).is(':visible()')) return;
  192. //var cloneEvent= $.extend(new Event(event.type), event);
  193. cloneEvent= cloneEvent || new Event(event.type);
  194. cloneEvent.redirectedEvent= event;
  195. // shallow copy every property
  196. for (var k in event) if (!(k in cloneEvent)) cloneEvent[k]= event[k];
  197.  
  198. event.preventDefault();
  199. event.stopPropagation();
  200. event.stopImmediatePropagation();
  201. //console.log('Youtube keys fix: dispatch cloneEvent=', cloneEvent);
  202. playerElem.dispatchEvent(cloneEvent);
  203. }
  204.  
  205. // Solution from Youtube Plus: https://github.com/ParticleCore/Particle/blob/master/src/Userscript/YouTubePlus.user.js#L885
  206. //var arrowKeys= { home/end/left/up/right/down };
  207. var keyHandlingElements= { INPUT:1, TEXTAREA:1, IFRAME:1, OBJECT:1, EMBED:1 };
  208.  
  209. function captureKeydown(event) {
  210. // Debug log of key event
  211. //if (event.key != 'Shift') console.log("Youtube keys fix: capture " + event.type + " " + event.which + " ->", event); //, event);
  212.  
  213. // Esc switches focus between player(player-api/player-container(movie_player)) and webpage outside the player(masthead(buttons)/main(related/info/meta/comments))
  214. // event.target is focused (received the keypress)
  215. // captureKeydown not executed when fullscreen, and this should not handle Esc
  216. var textbox= keyHandlingElements[event.target.tagName] || event.target.isContentEditable;// || event.target.getAttribute('role') == 'textbox';
  217. // capture only in textboxes to override their behaviour, general handling in onKeydown()
  218. if (event.which == 27 && textbox) return handleEsc(event);
  219.  
  220. // TAB: do the default
  221. //if (event.which == 9) return;
  222.  
  223. // Ignore events for the playerElem to avoid recursion
  224. //if (playerElem == document.activeElement) return;
  225. if (playerElem === event.target) return;
  226.  
  227. // Redirect Space (32) to pause video, if not in a textbox
  228. var redirectSpace= 32 == event.which && !textbox;
  229. // Sliders' key handling behaviour is inconsistent with the default player behaviour. To disable them
  230. // arrowkey events are redirected: Home/End/Left/Up/Right/Down (35-40) to video position/volume
  231. var redirectArrows= 35 <= event.which && event.which <= 40 && event.target.getAttribute('role') == 'slider' && isSubelementOf(event.target, 'player');
  232. if (redirectSpace || redirectArrows) return redirectEvent(event);
  233. }
  234.  
  235.  
  236. function captureMouse(event) {
  237. // Called when mouse button is pressed/released over an element.
  238. // Debug log of mouse button event
  239. console.log("Youtube keys fix: capture " + event.type + " ->", event.target);
  240. }
  241.  
  242.  
  243. function redirectFocus(event, newFocus) {
  244. if (!newFocus) return;
  245. event.preventDefault();
  246. event.stopPropagation();
  247. event.stopImmediatePropagation();
  248. //console.log('Youtube keys fix: redirect focus=', newFocus);
  249. newFocus.focus();
  250. }
  251.  
  252. function onMouse(event) {
  253. // Called when mouse button is pressed/released over an element.
  254. // Debug log of mouse button event
  255. //console.log("Youtube keys fix: " + event.type + " ->", event.target);
  256.  
  257. // click outside of areas focuses player
  258. if (0 === getAreaOf(event.target)) return redirectFocus(event, playerElem);
  259. }
  260.  
  261. function onWheel(event) {
  262. //console.log("Youtube keys fix: " + event.type + " " + event.deltaY + " phase " + event.eventPhase + " ->", event.currentTarget, event);
  263. if (!playerElem.contains(event.target)) return;
  264. var deltaY= null !== event.deltaY ? event.deltaY : event.wheelDeltaY;
  265. /*
  266. if (!playerElem || !playerElem.setVolume || !playerElem.getVolume) return;
  267. //var videoElem= playerElem.querySelector('video');
  268. //if (event.target !== videoElem) return;
  269.  
  270. var sign= Math.sign(deltaY);
  271. var newVol= playerElem.getVolume() * 100 - (sign * 5);
  272. console.log("Youtube keys fix: " + event.type + " set volume " + newVol, event);
  273. playerElem.setVolume(newVol);
  274. */
  275.  
  276. var up= deltaY <= 0; // null == 0 -> up
  277. var cloneEvent= new Event('keydown');
  278. cloneEvent.which= cloneEvent.keyCode= up ? 38 : 40;
  279. cloneEvent.key= up ? 'ArrowUp': 'ArrowDown';
  280. redirectEvent(event, cloneEvent);
  281. }
  282.  
  283. function onFullscreen(event) {
  284. if (document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement) {
  285. if (getFocusedArea() !== 1) {
  286. onFullscreen.prevFocus= document.activeElement;
  287. focusPlayer();
  288. }
  289. } else if (onFullscreen.prevFocus) {
  290. onFullscreen.prevFocus.focus();
  291. onFullscreen.prevFocus= null;
  292. }
  293. }
  294.  
  295. function captureFocus(event) {
  296. // Called when an element gets focus (by clicking or TAB)
  297. // Debug log of focused element
  298. //console.log("Youtube keys fix: capture " + event.type + " ->", event.target);
  299.  
  300. // Window will focus the activeElement, do nothing at the moment
  301. if (event.target === window) return;
  302.  
  303. // Focus player if focusing body (default focus, eg. pressing Esc)
  304. if (event.target === document.body) return redirectFocus(event, areaFocusedSubelement[1] || playerElem);
  305.  
  306. // Cycle back focus when stepping out of player (Tab) -- not only for Tab, causes unexpected behaviour
  307. //var newPlayerFocused= isSubelementOf(event.target, playerElem);
  308. //if (playerFocused && !newPlayerFocused && areaFocusedSubelement[1] != playerElem) return redirectFocus(event, playerElem);
  309. // areaFocusedSubelement[1] == playerElem: Shift-Tab would get stuck on the player, todo: find last element to focus
  310. // -> $(playerElem).select('[tabindex][tabindex!=-1]').last()
  311.  
  312. // Save focused element inside player or on page
  313. var area= getAreaOf(event.target);
  314. //if (0 === area) return redirectFocus(event, playerElem);
  315. if (0 !== area) {
  316. areaFocusedSubelement[area]= event.target;
  317. //if (areaContainers[area]) document.getElementById(areaContainers[area]).activeElement= event.target;
  318. // store if not focusing player area
  319. if (area !== 1) lastFocusedPageArea= area;
  320. }
  321. }
  322.  
  323.  
  324.  
  325. function chainInitFunc(f1, f2) {
  326. return (function() {
  327. $(document).ready(f1);
  328. //if (f1) f1.apply(this, arguments);
  329. if (f2) f2.apply(this, arguments);
  330. });
  331. }
  332.  
  333. // Run init on onYouTubePlayerReady ('#movie_player' created).
  334. //console.log("onYouTubePlayerReady=", window.onYouTubePlayerReady);
  335. window.onYouTubePlayerReady= chainInitFunc(initPlayer, window.onYouTubePlayerReady);
  336. initEvents();
  337. initStyle();
  338. initDom();
  339.  
  340. //document.addEventListener("DOMContentLoaded", function() {
  341. $(document).ready(function () {
  342. //console.log("Youtube keys fix: $(document).ready()");
  343. // Init if '#movie_player' is created.
  344. //initDom();
  345. initPlayer();
  346. });
  347.  
  348. function initEvents() {
  349. // Handlers are capture type to see all events before they are consumed
  350. //document.addEventListener('mousedown', captureMouse, true);
  351. //document.addEventListener('mouseup', captureMouse, true);
  352. // captureFocus captures focus changes before the event is handled
  353. document.addEventListener('focus', captureFocus, true); // does not capture body.focus() in Opera, material design
  354. //window.addEventListener('focusin', captureFocus);
  355. // captureKeydown is run before original handlers to have a chance to modify the events
  356. document.addEventListener('mousedown', onMouse);
  357. // mousewheel on player adjusts volume
  358. document.addEventListener('wheel', onWheel, true);
  359. document.addEventListener('keydown', captureKeydown, true);
  360. // onKeydown handles keypress in the bubbling phase to handle Esc.
  361. document.addEventListener('keydown', onKeydown);
  362. if (document.onwebkitfullscreenchange !== undefined) document.addEventListener('webkitfullscreenchange', onFullscreen);
  363. if (document.onmozfullscreenchange !== undefined) document.addEventListener('mozfullscreenchange', onFullscreen);
  364. if (document.MSFullscreenChange !== undefined) document.addEventListener('MSFullscreenChange', onFullscreen);
  365. }
  366.  
  367. function initStyle() {
  368. // Style for materialUI player, video list item, comment highlight:
  369. // #masthead-container is present on all materialUI pages: index, watch, etc.
  370. if (document.getElementById('masthead')) $(document.head).append('<style name="yt-fix-materialUI type="text/css">\n\
  371. html:not(.no-focus-outline) #player-container:focus-within { box-shadow: 0 0 20px 0px rgba(0,0,0,0.8); }\n\
  372. .ytp-probably-keyboard-focus :focus { background-color: rgba(120, 180, 255, 0.6); }\n\
  373. html:not(.no-focus-outline) ytd-video-primary-info-renderer #container:focus-within, \n\
  374. html:not(.no-focus-outline) ytd-video-secondary-info-renderer #container:focus-within { box-shadow: 0 0 10px 0px rgba(0,0,0,0.4); }\n\
  375. html:not(.no-focus-outline) ytd-comment-renderer:focus-within { box-shadow: 0 0 10px 0px rgba(0,0,0,0.3); }\n\
  376. html:not(.no-focus-outline) ytd-compact-video-renderer #dismissable:focus-within { box-shadow: 0 0 15px 1px rgba(0,0,100,0.4); }\n\
  377. a.yt-simple-endpoint.ytd-compact-video-renderer { margin-top: 3px; }\n\
  378. </style>');
  379.  
  380. // Style for classicUI player, video list item, comment-simplebox highlight and layout rearranging for the highlight:
  381. // #yt-masthead-container is present on all classicUI pages: index, watch, etc.
  382. if (document.getElementById('yt-masthead-container')) $(document.head).append('<style name="yt-fix-classicUI" type="text/css">\n\
  383. #player-api:focus-within { box-shadow: 0 0 20px 0px rgba(0,0,0,0.8); }\n\
  384. .ytp-probably-keyboard-focus :focus { background-color: rgba(120, 180, 255, 0.6); }\n\
  385. html:not(.no-focus-outline) #masthead-search-terms.masthead-search-terms-border:focus-within { border: 1px solid #4d90fe; box-shadow: inset 0px 0px 10px 2px #4d90fe; }\n\
  386. html:not(.no-focus-outline) #watch-header:focus-within, \n\
  387. html:not(.no-focus-outline) #action-panel-details:focus-within { box-shadow: 0 0 10px 0px rgba(0,0,0,0.4); }\n\
  388. #watch-discussion { padding: 10px; }\n\
  389. .comments-header-renderer { padding: 5px; }\n\
  390. .comment-simplebox-renderer:focus-within .comment-simplebox-renderer-collapsed-content, \
  391. .comment-simplebox-renderer:focus-within .comment-simplebox-frame { border-width: 3px; }\n\
  392. .comment-simplebox-renderer:focus-within .comment-simplebox-arrow .arrow-inner { left: 7px; top: 3px; }\n\
  393. .comment-section-sort-menu { margin-bottom: 10px; }\n\
  394. .yt-alert-naked.yt-alert.zero-step-tooltip { margin-top: 20px; }\n\
  395. html:not(.no-focus-outline) .comment-renderer:focus-within { box-shadow: 0 0 10px 0px rgba(0,0,0,0.3); }\n\
  396. .comment-thread-renderer { margin: 0 0 25px 0; }\n\
  397. .comment-renderer { padding: 5px; }\n\
  398. .comment-renderer-content { position: relative; }\n\
  399. .comment-replies-renderer .comment-renderer/*, .comment-replies-renderer .feedback-banner*/ { margin: 2px 0; }\n\
  400. .comment-replies-renderer-view, .comment-replies-renderer-hide { margin: 1px 0 7px 5px; }\n\
  401. .comment-replies-renderer-paginator { margin-top: 1px; margin-left: 5px; }\n\
  402. html:not(.no-focus-outline) .video-list-item:focus-within { box-shadow: 0 0 15px 1px rgba(0,0,100,0.4); }\n\
  403. html:not(.no-focus-outline) .video-list-item:focus-within .related-item-action-menu .yt-uix-button { opacity: 1; }\n\
  404. html:not(.no-focus-outline) .video-list-item:focus-within .video-actions { right: 2px; }\n\
  405. html:not(.no-focus-outline) .video-list-item:focus-within .video-time, \n\
  406. html:not(.no-focus-outline) .related-list-item:focus-within .video-time-overlay { right: -60px; }\n\
  407. #watch7-sidebar-contents { padding-right: 10px; }\n\
  408. #watch7-sidebar-contents .checkbox-on-off { margin-right: 5px; }\n\
  409. #watch7-sidebar .watch-sidebar-head { margin-bottom: 5px; margin-left: 0; }\n\
  410. #watch7-sidebar .watch-sidebar-section { padding-left: 5px; margin-bottom: 0; }\n\
  411. #watch7-sidebar .watch-sidebar-separation-line { margin: 10px 5px; }\n\
  412. .video-list-item .thumb-wrapper { margin: 0; }\n\
  413. .video-list-item { margin-left: 5px; }\n\
  414. .video-list-item .content-wrapper a { padding-top: 3px; min-height: 91px; }\n\
  415. .related-list-item .content-wrapper { margin-left: 176px; margin-right: 5px; }\n\
  416. .related-list-item .related-item-action-menu { top: 3px; right: 0; }\n\
  417. .related-item-dismissable .related-item-action-menu .yt-uix-button { margin: 0; height: 20px; width: 20px; }\n\
  418. </style>');
  419. }
  420.  
  421. function initDom() {
  422. // @match *://*.youtube.com/embed/*
  423. isMaterialUI= (null != document.getElementById('masthead'));
  424. isClassicUI= (null != document.getElementById('yt-masthead-container'));
  425. // with MaterialUI there is a leftover, emptied #player-api element
  426. playerContainer= isMaterialUI ? document.getElementById('player-container') : isClassicUI ? document.getElementById('player-api') : document.getElementById('player');
  427. // isEmbeddedUI= !isMaterialUI && !isClassicUI;
  428.  
  429. // Areas' root elements
  430. areaOrder= [ null, 'player', 'masthead', 'videos', 'content' ];
  431. areaContainers= isMaterialUI ? [ null, 'player', 'masthead-container', 'related', 'main' ]
  432. : [ null, 'player', 'yt-masthead-container', 'watch7-sidebar', 'watch7-content' ];
  433. /*
  434. areaOrder= [ null, 'player', 'videos', 'masthead', 'title', 'description', 'comments' ];
  435. areaContainers[1]= areaContainers.player= playerContainer;
  436. areaContainers[2]= areaContainers.masthead= document.getElementById('masthead-container') || document.getElementById('yt-masthead-container');
  437. areaContainers[3]= areaContainers.videos= document.getElementById('related') || document.getElementById('watch7-sidebar');
  438. areaContainers[4]= areaContainers.content= document.getElementById('main') || document.getElementById('watch7-content');
  439. areaContainers.length= 5;
  440. areaContainers[4]= areaContainers.title= document.getElementById('info') || document.getElementById('watch-header');
  441. areaContainers[5]= areaContainers.description= document.getElementById('meta') || document.getElementById('action-panel-details');
  442. areaContainers[6]= areaContainers.comments= document.getElementById('comments') || document.getElementById('watch-discussion');
  443. areaContainers.length= 7;
  444. */
  445.  
  446. // Areas' default element to focus
  447. areaFocusDefault[0]= null;
  448. areaFocusDefault[1]= isMaterialUI || isClassicUI ? '#movie_player' : '#player .html5-video-player';
  449. areaFocusDefault[2]= isMaterialUI ? '#masthead input#search' : '#masthead-search-term';
  450. areaFocusDefault[3]= isMaterialUI ? '#items a.ytd-compact-video-renderer:first()' : '#watch7-sidebar-modules a.content-link:first()';
  451. areaFocusDefault[4]= isMaterialUI ? '#info #menu #top-level-buttons button:last()' : '#watch8-action-buttons button:first()';
  452. /*
  453. areaFocusDefault[5]= isMaterialUI ? '#meta paper-button:visible():last()' : '#action-panel-details button:visible():last()';
  454. areaFocusDefault[6]= isMaterialUI ? '#comments #header #sort-menu paper-button:first()' : '#watch-discussion .comment-section-sort-menu button:first()';
  455. */
  456. areaFocusDefault.length= 5;
  457. }
  458.  
  459. function initPlayer() {
  460. if (playerElem) return;
  461.  
  462. // The movie player frame '#movie_player', might not be generated yet.
  463. playerElem= document.getElementById('movie_player') || $('#player .html5-video-player')[0];
  464. if (!playerElem) {
  465. console.log("Youtube keys fix failed to find '#movie_player' element: not created yet");
  466. return false;
  467. }
  468.  
  469. //console.log("Youtube keys fix: initPlayer()");
  470. // Movie player frame (element) is focused when loading the page to get movie player keyboard controls.
  471. playerElem.focus();
  472.  
  473. $('#player .caption-window').attr('tabindex', '-1');
  474. //var caption= playerElem.querySelector && playerElem.querySelector('.caption-window'); if (caption) caption.setAttribute('tabindex', -1);
  475. }
  476.  
  477. })();
  478.  
  479.