YouTube: Floating Chat Window on Fullscreen

To make floating chat window on fullscreen

目前为 2023-08-14 提交的版本。查看 最新版本

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