YouTube: Floating Chat Window on Fullscreen

To make floating chat window on fullscreen

当前为 2023-07-22 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name YouTube: Floating Chat Window on Fullscreen
  3. // @namespace UserScript
  4. // @match https://www.youtube.com/*
  5. // @exclude /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/
  6. // @version 0.2.4
  7. // @license MIT License
  8. // @author CY Fung
  9. // @description To make floating chat window on fullscreen
  10. // @require https://greasyfork.org/scripts/465819-api-for-customelements-in-youtube/code/API%20for%20CustomElements%20in%20YouTube.js?version=1215161
  11. // @run-at document-start
  12. // @grant none
  13. // @unwrap
  14. // @allFrames true
  15. // @inject-into page
  16. // ==/UserScript==
  17.  
  18.  
  19. ((__CONTEXT__) => {
  20.  
  21. const hkey_script = 'vdnvorrwsksy';
  22.  
  23.  
  24. const { isIframe, isTopFrame } = (() => {
  25.  
  26. let isIframe = false, isTopFrame = false;
  27. try {
  28. isIframe = window.document !== top.document
  29. } catch (e) { }
  30.  
  31. try {
  32. isTopFrame = window.document === top.document
  33. } catch (e) { }
  34.  
  35. return { isIframe, isTopFrame };
  36.  
  37. })();
  38.  
  39. if (isIframe ^ isTopFrame) { } else return;
  40.  
  41. if (isTopFrame) {
  42.  
  43.  
  44.  
  45. const createStyleText = () => `
  46.  
  47.  
  48. [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) {
  49. position:fixed !important;
  50. top: var(--f3-top, 5px) !important;
  51. left: var(--f3-left, calc(60vw + 100px)) !important;
  52. height: var(--f3-h, 60vh) !important;
  53. width: var(--f3-w, 320px) !important;
  54. display:flex !important;
  55. flex-direction: column !important;
  56. padding: 4px;
  57. cursor: all-scroll;
  58. z-index:9999;
  59. box-sizing: border-box !important;
  60. margin:0 !important;
  61. opacity: var(--floating-window-opacity, 1.0) !important;
  62. background: transparent;
  63. background-color: rgba(0, 0, 0, 0.5);
  64. transition: background-color 300ms;
  65. }
  66.  
  67. [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]):hover {
  68. background-color: rgba(0, 0, 0, 0.85);
  69.  
  70. }
  71.  
  72. .no-floating[floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) {
  73.  
  74. top: -300vh !important;
  75. left: -300vh !important;
  76. }
  77.  
  78.  
  79. [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) #show-hide-button[class]{
  80. flex-grow: 0;
  81. flex-shrink:0;
  82. position:static;
  83. cursor: all-scroll;
  84. }
  85. [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) #show-hide-button[class] *[class]{
  86. cursor: inherit;
  87. }
  88. [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) iframe[class]{
  89. flex-grow: 100;
  90. flex-shrink:0;
  91. height: 0;
  92. position:static;
  93. }
  94.  
  95.  
  96. html{
  97. --fc7-handle-color: #0cb8da;
  98. }
  99. html[dark]{
  100. --fc7-handle-color: #0c74e4;
  101. }
  102.  
  103. :fullscreen .resize-handle {
  104.  
  105. position: absolute !important;
  106. top: 0;
  107. left: 0;
  108. bottom: 0;
  109. background: transparent;
  110. right: 0;
  111. z-index: 999 !important;
  112. border-radius: inherit !important;
  113. box-sizing: border-box !important;
  114. pointer-events:none !important;
  115. visibility: collapse;
  116. border: 4px solid transparent;
  117. border-color: transparent;
  118. transition: border-color 300ms;
  119. }
  120.  
  121. [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]):hover .resize-handle {
  122.  
  123. visibility: visible;
  124.  
  125. border-color: var(--fc7-handle-color);
  126. }
  127.  
  128. [moving] {
  129. cursor: all-scroll;
  130. --pointer-events:initial;
  131. }
  132.  
  133. [moving] body {
  134. --pointer-events:none;
  135. }
  136.  
  137. [moving] ytd-live-chat-frame#chat{
  138.  
  139. --pointer-events:initial;
  140. }
  141.  
  142.  
  143. [moving] ytd-live-chat-frame#chat iframe {
  144.  
  145. --pointer-events:none;
  146. }
  147.  
  148.  
  149. [moving="move"] ytd-live-chat-frame#chat {
  150. background-color: var(--yt-spec-general-background-a);
  151.  
  152. }
  153.  
  154.  
  155. [moving="move"] ytd-live-chat-frame#chat iframe {
  156.  
  157. visibility: collapse;
  158. }
  159.  
  160. [moving] * {
  161. pointer-events:var(--pointer-events) !important;
  162.  
  163. }
  164. [moving] *, [moving] [class] {
  165. user-select: none !important;
  166. }
  167.  
  168. :fullscreen tyt-iframe-popup-btn{
  169. display: none !important;
  170. }
  171.  
  172. [moving] tyt-iframe-popup-btn{
  173. display: none !important;
  174. }
  175.  
  176. [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) #show-hide-button.ytd-live-chat-frame>ytd-toggle-button-renderer.ytd-live-chat-frame {
  177.  
  178. background: transparent;
  179.  
  180. }
  181.  
  182.  
  183.  
  184. `;
  185.  
  186. const addCSS = () => {
  187. let text = createStyleText();
  188. let style = document.createElement('style');
  189. style.id = 'rvZ0t';
  190. style.textContent = text;
  191. document.head.appendChild(style);
  192. }
  193.  
  194. const { Promise, requestAnimationFrame } = __CONTEXT__;
  195.  
  196. let chatWindowWR = null;
  197. let showHideButtonWR = null;
  198.  
  199.  
  200. /* globals WeakRef:false */
  201.  
  202. /** @type {(o: Object | null) => WeakRef | null} */
  203. const mWeakRef = typeof WeakRef === 'function' ? (o => o ? new WeakRef(o) : null) : (o => o || null); // typeof InvalidVar == 'undefined'
  204.  
  205. /** @type {(wr: Object | null) => Object | null} */
  206. const kRef = (wr => (wr && wr.deref) ? wr.deref() : wr);
  207.  
  208.  
  209.  
  210. let startX;
  211. let startY;
  212. let startWidth;
  213. let startHeight;
  214.  
  215.  
  216. let edge = 0;
  217.  
  218.  
  219. let initialLeft;
  220. let initialTop;
  221.  
  222. let stopResize;
  223. let stopMove;
  224.  
  225.  
  226. const getXY = (e) => {
  227. let rect = e.target.getBoundingClientRect();
  228. let x = e.clientX - rect.left; //x position within the element.
  229. let y = e.clientY - rect.top; //y position within the element.
  230. return { x, y }
  231. }
  232.  
  233. let beforeEvent = null;
  234.  
  235. function resizeWindow(e) {
  236.  
  237.  
  238. const chatWindow = kRef(chatWindowWR);
  239. if (chatWindow) {
  240.  
  241. const mEdge = edge;
  242.  
  243. if (mEdge == 4 || mEdge == 1) {
  244.  
  245. } else if (mEdge == 8 || mEdge == 16) {
  246. } else {
  247. return;
  248. }
  249.  
  250.  
  251. Promise.resolve(chatWindow).then(chatWindow => {
  252. let rect;
  253.  
  254. if (mEdge == 4 || mEdge == 1 || mEdge == 16) {
  255.  
  256. let newWidth = startWidth + (startX - e.pageX);
  257.  
  258. let newLeft = initialLeft + startWidth - newWidth;
  259. chatWindow.style.setProperty('--f3-w', newWidth + "px");
  260. chatWindow.style.setProperty('--f3-left', newLeft + "px");
  261.  
  262.  
  263.  
  264. let newHeight = startHeight + (startY - e.pageY);
  265.  
  266. let newTop = initialTop + startHeight - newHeight;
  267. chatWindow.style.setProperty('--f3-h', newHeight + "px");
  268. chatWindow.style.setProperty('--f3-top', newTop + "px");
  269.  
  270. rect = {
  271. x: newLeft,
  272. y: newTop,
  273. w: newWidth,
  274.  
  275. h: newHeight,
  276.  
  277.  
  278. };
  279.  
  280.  
  281.  
  282. } else if (mEdge == 8) {
  283.  
  284. let newWidth = startWidth + e.pageX - startX;
  285. let newHeight = startHeight + e.pageY - startY;
  286.  
  287. chatWindow.style.setProperty('--f3-w', newWidth + "px");
  288. chatWindow.style.setProperty('--f3-h', newHeight + "px");
  289.  
  290.  
  291. rect = {
  292. x: initialLeft,
  293. y: initialTop,
  294. w: newWidth,
  295.  
  296. h: newHeight,
  297.  
  298.  
  299. };
  300.  
  301. }
  302.  
  303.  
  304.  
  305. updateOpacity(chatWindow, rect, screen);
  306.  
  307. })
  308.  
  309.  
  310. e.stopPropagation();
  311. e.preventDefault();
  312.  
  313.  
  314. }
  315.  
  316. }
  317.  
  318. function moveWindow(e) {
  319.  
  320.  
  321. const chatWindow = kRef(chatWindowWR);
  322. if (chatWindow) {
  323.  
  324. Promise.resolve(chatWindow).then(chatWindow => {
  325.  
  326.  
  327. let newX = initialLeft + e.pageX - startX;
  328. let newY = initialTop + e.pageY - startY;
  329.  
  330. chatWindow.style.setProperty('--f3-left', newX + "px");
  331. chatWindow.style.setProperty('--f3-top', newY + "px");
  332.  
  333.  
  334.  
  335. updateOpacity(chatWindow, {
  336. x: newX,
  337. y: newY,
  338. w: startWidth,
  339.  
  340. h: startHeight,
  341.  
  342.  
  343. }, screen);
  344.  
  345. });
  346.  
  347.  
  348.  
  349. e.stopPropagation();
  350. e.preventDefault();
  351.  
  352. }
  353. }
  354.  
  355.  
  356.  
  357.  
  358. function initializeResize(e) {
  359.  
  360. if (!document.fullscreenElement) return;
  361.  
  362. if (!document.querySelector('[floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed])')) return;
  363.  
  364. if (e.target.id !== 'chat') return;
  365.  
  366.  
  367.  
  368. const { x, y } = getXY(e);
  369. edge = 0;
  370. if (x < 16 && y < 16) { edge = 16; }
  371. else if (x < 16) edge = 4;
  372. else if (y < 16) edge = 1;
  373. else edge = 8;
  374.  
  375. if (edge <= 0) return;
  376.  
  377. startX = e.pageX;
  378. startY = e.pageY;
  379.  
  380. const chatWindow = kRef(chatWindowWR);
  381. if (chatWindow) {
  382.  
  383. Promise.resolve(chatWindow).then(chatWindow => {
  384.  
  385. let rect = chatWindow.getBoundingClientRect();
  386. initialLeft = rect.x;
  387. initialTop = rect.y;
  388.  
  389.  
  390.  
  391. startWidth = rect.width;
  392. startHeight = rect.height;
  393.  
  394.  
  395. chatWindow.style.setProperty('--f3-left', initialLeft + "px");
  396. chatWindow.style.setProperty('--f3-top', initialTop + "px");
  397. chatWindow.style.setProperty('--f3-w', startWidth + "px");
  398. chatWindow.style.setProperty('--f3-h', startHeight + "px");
  399.  
  400. });
  401.  
  402. }
  403.  
  404.  
  405.  
  406.  
  407. document.documentElement.setAttribute('moving', 'resize');
  408.  
  409. document.documentElement.removeEventListener("mousemove", resizeWindow, false);
  410. document.documentElement.removeEventListener("mousemove", moveWindow, false);
  411. document.documentElement.removeEventListener("mouseup", stopResize, false);
  412. document.documentElement.removeEventListener("mouseup", stopMove, false);
  413.  
  414. document.documentElement.addEventListener("mousemove", resizeWindow);
  415. document.documentElement.addEventListener("mouseup", stopResize);
  416.  
  417. }
  418.  
  419.  
  420. let updateOpacityRid = 0;
  421.  
  422. function updateOpacity(chatWindow, rect, screen) {
  423.  
  424. let tid = ++updateOpacityRid;
  425.  
  426. requestAnimationFrame(() => {
  427.  
  428.  
  429. if (tid !== updateOpacityRid) return;
  430.  
  431. let { x, y, w, h } = rect;
  432. let [left, top, right, bottom] = [x, y, x + w, y + h];
  433.  
  434.  
  435. let opacityW = (Math.min(right, screen.width) - Math.max(0, left)) / w;
  436. let opacityH = (Math.min(bottom, screen.height) - Math.max(0, top)) / h;
  437.  
  438. let opacity = Math.min(opacityW, opacityH);
  439.  
  440. chatWindow.style.setProperty('--floating-window-opacity', Math.round(opacity * 100 * 5, 0) / 5 / 100);
  441.  
  442.  
  443. })
  444.  
  445.  
  446.  
  447.  
  448.  
  449. }
  450.  
  451. function initializeMove(e) {
  452.  
  453. if (!document.fullscreenElement) return;
  454. if (!document.querySelector('[floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed])')) return;
  455.  
  456.  
  457.  
  458. const chatWindow = kRef(chatWindowWR);
  459.  
  460.  
  461.  
  462. startX = e.pageX;
  463. startY = e.pageY;
  464.  
  465.  
  466. if (chatWindow) {
  467.  
  468. Promise.resolve(chatWindow).then(chatWindow => {
  469.  
  470.  
  471. let rect = chatWindow.getBoundingClientRect();
  472. initialLeft = rect.x;
  473. initialTop = rect.y;
  474.  
  475.  
  476.  
  477. startWidth = rect.width;
  478. startHeight = rect.height;
  479.  
  480.  
  481. chatWindow.style.setProperty('--f3-left', initialLeft + "px");
  482. chatWindow.style.setProperty('--f3-top', initialTop + "px");
  483. chatWindow.style.setProperty('--f3-w', startWidth + "px");
  484. chatWindow.style.setProperty('--f3-h', startHeight + "px");
  485.  
  486. })
  487.  
  488.  
  489. }
  490.  
  491.  
  492.  
  493. document.documentElement.setAttribute('moving', 'move');
  494.  
  495. document.documentElement.removeEventListener("mousemove", resizeWindow, false);
  496. document.documentElement.removeEventListener("mousemove", moveWindow, false);
  497. document.documentElement.removeEventListener("mouseup", stopResize, false);
  498. document.documentElement.removeEventListener("mouseup", stopMove, false);
  499.  
  500. document.documentElement.addEventListener("mousemove", moveWindow, false);
  501. document.documentElement.addEventListener("mouseup", stopMove, false);
  502.  
  503. e.stopPropagation();
  504. e.preventDefault();
  505.  
  506. beforeEvent = e;
  507.  
  508. }
  509.  
  510.  
  511. function checkClick(beforeEvent, currentEvent) {
  512.  
  513. const d = currentEvent.timeStamp - beforeEvent.timeStamp;
  514. if (d < 300 && d > 30) {
  515.  
  516. document.documentElement.classList.add('no-floating');
  517.  
  518. }
  519.  
  520. }
  521.  
  522.  
  523. stopResize = (e) => {
  524.  
  525. document.documentElement.removeAttribute('moving');
  526. document.documentElement.removeEventListener("mousemove", resizeWindow);
  527. }
  528.  
  529. stopMove = (e) => {
  530. document.documentElement.removeAttribute('moving');
  531. document.documentElement.removeEventListener("mousemove", moveWindow);
  532.  
  533. beforeEvent && checkClick(beforeEvent, e);
  534. beforeEvent = null;
  535. }
  536.  
  537.  
  538. function reset() {
  539.  
  540. document.documentElement.removeAttribute('moving');
  541. document.documentElement.removeEventListener("mousemove", resizeWindow, false);
  542. document.documentElement.removeEventListener("mousemove", moveWindow, false);
  543. document.documentElement.removeEventListener("mouseup", stopResize, false);
  544. document.documentElement.removeEventListener("mouseup", stopMove, false);
  545.  
  546.  
  547. startX = 0;
  548. startY = 0;
  549. startWidth = 0;
  550. startHeight = 0;
  551.  
  552.  
  553. edge = 0;
  554.  
  555.  
  556. initialLeft = 0;
  557. initialTop = 0;
  558.  
  559. beforeEvent = null;
  560.  
  561.  
  562. }
  563.  
  564. function iframeLoaded() {
  565.  
  566. }
  567.  
  568. function iframeFullscreenChanged() {
  569. const iframeDoc = this;
  570.  
  571.  
  572. if (!document.fullscreenElement) {
  573. iframeDoc.documentElement.classList.remove('youtube-floating-chat-iframe');
  574. } else {
  575. iframeDoc.documentElement.classList.add('youtube-floating-chat-iframe');
  576.  
  577. }
  578.  
  579.  
  580. }
  581.  
  582. let iframeFullscreenChangedBinded = null;
  583.  
  584.  
  585.  
  586. function onMessage(evt) {
  587. if (evt.data === 'vdnvorrwsksy') {
  588.  
  589. const iframeWin = evt.source;
  590. const iframeDoc = iframeWin.document;
  591.  
  592.  
  593.  
  594. function onReady() {
  595.  
  596. iframeDoc.head.appendChild(document.createElement('style')).textContent = `
  597.  
  598. .youtube-floating-chat-iframe #right-arrow-container.yt-live-chat-ticker-renderer,
  599. .youtube-floating-chat-iframe #left-arrow-container.yt-live-chat-ticker-renderer
  600.  
  601. {
  602. background: transparent;
  603. }
  604.  
  605. .youtube-floating-chat-iframe yt-live-chat-renderer.yt-live-chat-app {
  606.  
  607. --yt-live-chat-background-color: transparent;
  608. --yt-live-chat-action-panel-background-color: rgba(0, 0, 0, 0.08);
  609. --yt-live-chat-header-background-color: rgba(0, 0, 0, 0.18);
  610. --yt-spec-static-overlay-background-medium: rgba(0, 0, 0, 0.08);
  611. --yt-live-chat-banner-gradient-scrim: transparent;
  612.  
  613. }
  614.  
  615.  
  616.  
  617.  
  618. .youtube-floating-chat-iframe yt-live-chat-renderer.yt-live-chat-app #visible-banners > yt-live-chat-banner-renderer {
  619. --fc7-banner-opacity: 0.86;
  620. }
  621.  
  622. .youtube-floating-chat-iframe yt-live-chat-renderer.yt-live-chat-app #visible-banners > yt-live-chat-banner-renderer[collapsed] {
  623. --fc7-banner-opacity: 0.66;
  624. }
  625.  
  626.  
  627. .youtube-floating-chat-iframe yt-live-chat-app:hover yt-live-chat-renderer.yt-live-chat-app #visible-banners > yt-live-chat-banner-renderer[class] {
  628. --fc7-banner-opacity: 1;
  629. }
  630.  
  631.  
  632. .youtube-floating-chat-iframe yt-live-chat-renderer.yt-live-chat-app {
  633. --fc7-system-message-opacity: 0.66;
  634.  
  635. }
  636.  
  637. .youtube-floating-chat-iframe yt-live-chat-app:hover yt-live-chat-renderer.yt-live-chat-app {
  638. --fc7-system-message-opacity: 1.0;
  639.  
  640. }
  641.  
  642.  
  643. .youtube-floating-chat-iframe yt-live-chat-renderer.yt-live-chat-app #visible-banners > yt-live-chat-banner-renderer {
  644. opacity: var(--fc7-banner-opacity) !important;
  645. }
  646.  
  647.  
  648. .youtube-floating-chat-iframe yt-live-chat-renderer.yt-live-chat-app yt-live-chat-viewer-engagement-message-renderer {
  649. opacity: var(--fc7-system-message-opacity) !important;
  650. }
  651.  
  652.  
  653. .youtube-floating-chat-iframe yt-live-chat-app:not(:hover) yt-live-chat-renderer.yt-live-chat-app yt-live-chat-message-input-renderer {
  654. display: none;
  655. }
  656.  
  657. .youtube-floating-chat-iframe yt-live-chat-app:hover yt-live-chat-renderer.yt-live-chat-app yt-live-chat-message-input-renderer {
  658.  
  659. position: absolute;
  660. transform: translateY(-100%);
  661. left: 0;
  662. right: 0;
  663. opacity: 1;
  664. background: rgba(0,0,0,0.86);
  665. }
  666.  
  667.  
  668. /* hide message with input panel hidden */
  669. .youtube-floating-chat-iframe yt-live-chat-app:not(:hover) > tp-yt-iron-dropdown.yt-live-chat-app yt-tooltip-renderer[slot="dropdown-content"][position-type="OPEN_POPUP_POSITION_TOP"].yt-live-chat-app {
  670. visibility: collapse;
  671. display: none;
  672. }
  673.  
  674.  
  675.  
  676.  
  677.  
  678.  
  679.  
  680.  
  681.  
  682.  
  683. [dark].youtube-floating-chat-iframe yt-live-chat-app ::-webkit-scrollbar-track,
  684. [dark].youtube-floating-chat-iframe yt-live-chat-kevlar-container ::-webkit-scrollbar-track {
  685. background-color: var(--ytd-searchbox-legacy-button-color);
  686. }
  687.  
  688. .youtube-floating-chat-iframe yt-live-chat-app ::-webkit-scrollbar-track,
  689. .youtube-floating-chat-iframe yt-live-chat-kevlar-container ::-webkit-scrollbar-track {
  690. background-color: #fcfcfc;
  691. }
  692.  
  693.  
  694. [dark].youtube-floating-chat-iframe yt-live-chat-app ::-webkit-scrollbar-thumb,
  695. [dark].youtube-floating-chat-iframe yt-live-chat-kevlar-container ::-webkit-scrollbar-thumb{
  696.  
  697. background-color: var(--ytd-searchbox-legacy-button-color);
  698. border: 2px solid var(--ytd-searchbox-legacy-button-color);
  699.  
  700. }
  701.  
  702.  
  703.  
  704. .youtube-floating-chat-iframe yt-live-chat-renderer[has-action-panel-renderer] #action-panel.yt-live-chat-renderer {
  705. --yt-live-chat-action-panel-gradient-scrim: transparent;
  706. }
  707.  
  708.  
  709. .youtube-floating-chat-iframe yt-live-chat-renderer[has-action-panel-renderer] #action-panel.yt-live-chat-renderer yt-live-chat-action-panel-renderer {
  710. --fc7-system-message-opacity2: 0.66;
  711. }
  712.  
  713. .youtube-floating-chat-iframe yt-live-chat-app:hover yt-live-chat-renderer[has-action-panel-renderer] #action-panel.yt-live-chat-renderer yt-live-chat-action-panel-renderer {
  714. --fc7-system-message-opacity2: 1.00;
  715. }
  716.  
  717.  
  718. .youtube-floating-chat-iframe yt-live-chat-renderer[has-action-panel-renderer] #action-panel.yt-live-chat-renderer yt-live-chat-action-panel-renderer {
  719. opacity: var(--fc7-system-message-opacity2) !important;
  720. }
  721.  
  722.  
  723.  
  724.  
  725.  
  726. `;
  727.  
  728.  
  729. if (iframeFullscreenChangedBinded) document.removeEventListener('fullscreenchange', iframeFullscreenChangedBinded, false);
  730. iframeFullscreenChangedBinded = iframeFullscreenChanged.bind(iframeDoc);
  731. document.addEventListener('fullscreenchange', iframeFullscreenChangedBinded, false);
  732.  
  733. iframeFullscreenChangedBinded();
  734.  
  735.  
  736. }
  737.  
  738.  
  739.  
  740. Promise.resolve().then(() => {
  741.  
  742. if (iframeDoc.readyState !== 'loading') {
  743. onReady();
  744. } else {
  745. iframeWin.addEventListener("DOMContentLoaded", onReady, false);
  746. }
  747.  
  748. });
  749.  
  750.  
  751. }
  752.  
  753. }
  754.  
  755.  
  756. function setChat(chat) {
  757.  
  758. let resizeHandle = HTMLElement.prototype.querySelector.call(chat, '.resize-handle')
  759. if (resizeHandle) return;
  760.  
  761.  
  762.  
  763. let cw = (() => {
  764. try {
  765. const { head, body } = chat.$.chatframe.contentWindow.document;
  766. return { head, body }
  767.  
  768. } catch (e) { return null; }
  769. })();
  770.  
  771. if (!cw) return;
  772.  
  773. window.removeEventListener('message', onMessage, false);
  774. window.addEventListener('message', onMessage, false);
  775.  
  776.  
  777.  
  778. let script = document.getElementById('rvZ0t') || (document.evaluate("//div[contains(text(), 'userscript-control[enable-customized-floating-window]')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null) || 0).singleNodeValue;
  779. if (!script) addCSS();
  780.  
  781. if (!document.documentElement.hasAttribute('floating-chat-window')) document.documentElement.setAttribute('floating-chat-window', '');
  782.  
  783.  
  784. chat.setAttribute('allowtransparency', 'true');
  785.  
  786.  
  787.  
  788. resizeHandle = document.createElement("div");
  789. resizeHandle.className = "resize-handle";
  790. chat.appendChild(resizeHandle);
  791. resizeHandle = null;
  792.  
  793. let chatWindow;
  794. let showHideButton;
  795.  
  796. chatWindow = kRef(chatWindowWR);
  797. showHideButton = kRef(showHideButtonWR);
  798.  
  799.  
  800.  
  801. if (chatWindow) chatWindow.removeEventListener("mousedown", initializeResize, false);
  802. if (showHideButton) showHideButton.removeEventListener("mousedown", initializeMove, false);
  803.  
  804.  
  805. chatWindow = chat;
  806. showHideButton = HTMLElement.prototype.querySelector.call(chat, '#show-hide-button');
  807. chatWindowWR = mWeakRef(chat)
  808. showHideButtonWR = mWeakRef(showHideButton);
  809.  
  810.  
  811.  
  812. chatWindow.addEventListener("mousedown", initializeResize, false);
  813. showHideButton.addEventListener("mousedown", initializeMove, false);
  814.  
  815. reset();
  816.  
  817. }
  818.  
  819.  
  820. const fullscreenchangePageFn = () => {
  821. if (!document.fullscreenElement) {
  822. document.documentElement.classList.remove('no-floating')
  823. }
  824. };
  825.  
  826. function noChat(chat) {
  827.  
  828. let chatWindow;
  829. let showHideButton;
  830.  
  831. chatWindow = kRef(chatWindowWR);
  832. showHideButton = kRef(showHideButtonWR);
  833.  
  834.  
  835.  
  836. if (chatWindow) chatWindow.removeEventListener("mousedown", initializeResize, false);
  837. if (showHideButton) showHideButton.removeEventListener("mousedown", initializeMove, false);
  838.  
  839.  
  840. let resizeHandle = HTMLElement.prototype.querySelector.call(chat, '.resize-handle')
  841. if (resizeHandle) {
  842. resizeHandle.remove();
  843. }
  844.  
  845. chat.removeEventListener("mousedown", initializeResize, false);
  846.  
  847.  
  848. showHideButton = HTMLElement.prototype.querySelector.call(chat, '#show-hide-button');
  849.  
  850. if (showHideButton) showHideButton.removeEventListener("mousedown", initializeMove, false);
  851.  
  852.  
  853. reset();
  854. }
  855.  
  856.  
  857. document.removeEventListener('fullscreenchange', fullscreenchangePageFn, false);
  858. document.addEventListener('fullscreenchange', fullscreenchangePageFn, false);
  859. fullscreenchangePageFn();
  860.  
  861. customYtElements.whenRegistered('ytd-live-chat-frame', (proto) => {
  862.  
  863.  
  864. proto.attached = ((attached) => (function () { Promise.resolve(this).then(setChat); return attached.apply(this, arguments) }))(proto.attached);
  865.  
  866. proto.detached = ((detached) => (function () { Promise.resolve(this).then(noChat); return detached.apply(this, arguments) }))(proto.detached);
  867.  
  868. let chat = document.querySelector('ytd-live-chat-frame');
  869. if (chat) Promise.resolve(chat).then(setChat);
  870.  
  871. })
  872.  
  873.  
  874.  
  875.  
  876. } else if (isIframe && top === parent) {
  877.  
  878.  
  879.  
  880. top.postMessage(hkey_script, `${location.protocol}//${location.hostname}`);
  881.  
  882.  
  883.  
  884.  
  885. }
  886.  
  887.  
  888.  
  889.  
  890. })({ Promise, requestAnimationFrame });