YouTube: Floating Chat Window on Fullscreen

To make floating chat window on fullscreen

当前为 2023-09-21 提交的版本,查看 最新版本

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