- // ==UserScript==
- // @name YouTube: Floating Chat Window on Fullscreen
- // @namespace UserScript
- // @match https://www.youtube.com/*
- // @exclude /^https?://\S+\.(txt|png|jpg|jpeg|gif|xml|svg|manifest|log|ini)[^\/]*$/
- // @version 0.1.7
- // @license MIT License
- // @author CY Fung
- // @description To make floating chat window on fullscreen
- // @require https://greasyfork.org/scripts/465819-api-for-customelements-in-youtube/code/API%20for%20CustomElements%20in%20YouTube.js?version=1215161
- // @run-at document-start
- // @grant none
- // @unwrap
- // @allFrames true
- // @inject-into page
- // ==/UserScript==
-
-
- ((__CONTEXT__) => {
-
- const createStyleText = () => `
-
-
- [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) {
- position:fixed !important;
- top: var(--f3-top, 5px) !important;
- left: var(--f3-left, calc(60vw + 100px)) !important;
- height: var(--f3-h, 60vh) !important;
- width: var(--f3-w, 320px) !important;
- display:flex !important;
- flex-direction: column !important;
- padding: 4px;
- cursor: all-scroll;
- z-index:9999;
- box-sizing: border-box !important;
- margin:0 !important;
- opacity: var(--floating-window-opacity, 1.0) !important;
- }
-
- .no-floating[floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) {
-
- top: -300vh !important;
- left: -300vh !important;
- }
-
-
- [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) #show-hide-button[class]{
- flex-grow: 0;
- flex-shrink:0;
- position:static;
- cursor: all-scroll;
- }
- [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) #show-hide-button[class] *[class]{
- cursor: inherit;
- }
- [floating-chat-window]:fullscreen ytd-live-chat-frame#chat:not([collapsed]) iframe[class]{
- flex-grow: 100;
- flex-shrink:0;
- height: 0;
- position:static;
- }
-
-
- html{
- --f7-handle-color: #0cb8da;
- }
- html[dark]{
- --f7-handle-color: #0c74e4;
- }
- :fullscreen .resize-handle{
-
- position: absolute;
- top: 0;
- left: 0;
- bottom: 0;
- background: transparent;
- right: 0;
- border: 4px solid var(--f7-handle-color);
- z-index: 999;
- border-radius: inherit;
- box-sizing: border-box;
- pointer-events:none;
- }
-
- [moving] {
- cursor: all-scroll;
- --pointer-events:initial;
- }
-
- [moving] body {
- --pointer-events:none;
- }
-
- [moving] ytd-live-chat-frame#chat{
-
- --pointer-events:initial;
- }
-
-
- [moving] ytd-live-chat-frame#chat iframe {
-
- --pointer-events:none;
- }
-
-
- [moving="move"] ytd-live-chat-frame#chat {
- background-color: var(--yt-spec-general-background-a);
-
- }
-
-
- [moving="move"] ytd-live-chat-frame#chat iframe {
-
- visibility: collapse;
- }
-
- [moving] * {
- pointer-events:var(--pointer-events) !important;
-
- }
-
- :fullscreen tyt-iframe-popup-btn{
- display: none !important;
- }
-
- [moving] tyt-iframe-popup-btn{
- display: none !important;
- }
-
-
-
- `;
-
- const addCSS = () => {
- let text = createStyleText();
- let style = document.createElement('style');
- style.id = 'rvZ0t';
- style.textContent = text;
- document.head.appendChild(style);
- }
-
- const { Promise, requestAnimationFrame } = __CONTEXT__;
-
- let chatWindowWR = null;
- let showHideButtonWR = null;
-
-
- /* globals WeakRef:false */
-
- /** @type {(o: Object | null) => WeakRef | null} */
- const mWeakRef = typeof WeakRef === 'function' ? (o => o ? new WeakRef(o) : null) : (o => o || null); // typeof InvalidVar == 'undefined'
-
- /** @type {(wr: Object | null) => Object | null} */
- const kRef = (wr => (wr && wr.deref) ? wr.deref() : wr);
-
-
-
- let startX;
- let startY;
- let startWidth;
- let startHeight;
-
-
- let edge = 0;
-
-
- let initialLeft;
- let initialTop;
-
- let stopResize;
- let stopMove;
-
-
- const getXY = (e) => {
- let rect = e.target.getBoundingClientRect();
- let x = e.clientX - rect.left; //x position within the element.
- let y = e.clientY - rect.top; //y position within the element.
- return { x, y }
- }
-
- let beforeEvent = null;
-
- function resizeWindow(e) {
-
-
- const chatWindow = kRef(chatWindowWR);
- if (chatWindow) {
-
- const mEdge = edge;
-
- if (mEdge == 4 || mEdge == 1) {
-
- } else if (mEdge == 8) {
- } else {
- return;
- }
-
-
- Promise.resolve(chatWindow).then(chatWindow => {
- let rect;
-
- if (mEdge == 4 || mEdge == 1) {
-
- let newWidth = startWidth + (startX - e.pageX);
-
- let newLeft = initialLeft + startWidth - newWidth;
- chatWindow.style.setProperty('--f3-w', newWidth + "px");
- chatWindow.style.setProperty('--f3-left', newLeft + "px");
-
-
-
- let newHeight = startHeight + (startY - e.pageY);
-
- let newTop = initialTop + startHeight - newHeight;
- chatWindow.style.setProperty('--f3-h', newHeight + "px");
- chatWindow.style.setProperty('--f3-top', newTop + "px");
-
- rect = {
- x: newLeft,
- y: newTop,
- w: newWidth,
-
- h: newHeight,
-
-
- };
-
-
-
- } else if (mEdge == 8) {
-
- let newWidth = startWidth + e.pageX - startX;
- let newHeight = startHeight + e.pageY - startY;
-
- chatWindow.style.setProperty('--f3-w', newWidth + "px");
- chatWindow.style.setProperty('--f3-h', newHeight + "px");
-
-
- rect = {
- x: initialLeft,
- y: initialTop,
- w: newWidth,
-
- h: newHeight,
-
-
- };
-
- }
-
-
-
- updateOpacity(chatWindow, rect, screen);
-
- })
-
-
- e.stopPropagation();
- e.preventDefault();
-
-
- }
-
- }
-
- function moveWindow(e) {
-
-
- const chatWindow = kRef(chatWindowWR);
- if (chatWindow) {
-
- Promise.resolve(chatWindow).then(chatWindow => {
-
-
- let newX = initialLeft + e.pageX - startX;
- let newY = initialTop + e.pageY - startY;
-
- chatWindow.style.setProperty('--f3-left', newX + "px");
- chatWindow.style.setProperty('--f3-top', newY + "px");
-
-
-
- updateOpacity(chatWindow, {
- x: newX,
- y: newY,
- w: startWidth,
-
- h: startHeight,
-
-
- }, screen);
-
- });
-
-
-
- e.stopPropagation();
- e.preventDefault();
-
- }
- }
-
-
-
-
- function initializeResize(e) {
-
- if (!document.fullscreenElement) return;
-
- if (e.target.id !== 'chat') return;
-
-
- const { x, y } = getXY(e);
- edge = 0;
- if (x < 16 && y < 16) { edge = -1 }
- else if (x < 16) edge = 4;
- else if (y < 16) edge = 1;
- else edge = 8;
-
- if (edge <= 0) return;
-
- startX = e.pageX;
- startY = e.pageY;
-
- const chatWindow = kRef(chatWindowWR);
- if (chatWindow) {
-
- Promise.resolve(chatWindow).then(chatWindow => {
-
- let rect = chatWindow.getBoundingClientRect();
- initialLeft = rect.x;
- initialTop = rect.y;
-
-
-
- startWidth = rect.width;
- startHeight = rect.height;
-
-
- chatWindow.style.setProperty('--f3-left', initialLeft + "px");
- chatWindow.style.setProperty('--f3-top', initialTop + "px");
- chatWindow.style.setProperty('--f3-w', startWidth + "px");
- chatWindow.style.setProperty('--f3-h', startHeight + "px");
-
- });
-
- }
-
-
-
-
- document.documentElement.setAttribute('moving', 'resize');
-
- document.documentElement.removeEventListener("mousemove", resizeWindow, false);
- document.documentElement.removeEventListener("mousemove", moveWindow, false);
- document.documentElement.removeEventListener("mouseup", stopResize, false);
- document.documentElement.removeEventListener("mouseup", stopMove, false);
-
- document.documentElement.addEventListener("mousemove", resizeWindow);
- document.documentElement.addEventListener("mouseup", stopResize);
-
- }
-
-
- let updateOpacityRid = 0;
-
- function updateOpacity(chatWindow, rect, screen) {
-
- let tid = ++updateOpacityRid;
-
- requestAnimationFrame(() => {
-
-
- if (tid !== updateOpacityRid) return;
-
- let { x, y, w, h } = rect;
- let [left, top, right, bottom] = [x, y, x + w, y + h];
-
-
- let opacityW = (Math.min(right, screen.width) - Math.max(0, left)) / w;
- let opacityH = (Math.min(bottom, screen.height) - Math.max(0, top)) / h;
-
- let opacity = Math.min(opacityW, opacityH);
-
- chatWindow.style.setProperty('--floating-window-opacity', Math.round(opacity * 100 * 5, 0) / 5 / 100);
-
-
- })
-
-
-
-
-
- }
-
- function initializeMove(e) {
-
- if (!document.fullscreenElement) return;
-
-
-
- const chatWindow = kRef(chatWindowWR);
-
-
-
- startX = e.pageX;
- startY = e.pageY;
-
-
- if (chatWindow) {
-
- Promise.resolve(chatWindow).then(chatWindow => {
-
-
- let rect = chatWindow.getBoundingClientRect();
- initialLeft = rect.x;
- initialTop = rect.y;
-
-
-
- startWidth = rect.width;
- startHeight = rect.height;
-
-
- chatWindow.style.setProperty('--f3-left', initialLeft + "px");
- chatWindow.style.setProperty('--f3-top', initialTop + "px");
- chatWindow.style.setProperty('--f3-w', startWidth + "px");
- chatWindow.style.setProperty('--f3-h', startHeight + "px");
-
- })
-
-
- }
-
-
-
- document.documentElement.setAttribute('moving', 'move');
-
- document.documentElement.removeEventListener("mousemove", resizeWindow, false);
- document.documentElement.removeEventListener("mousemove", moveWindow, false);
- document.documentElement.removeEventListener("mouseup", stopResize, false);
- document.documentElement.removeEventListener("mouseup", stopMove, false);
-
- document.documentElement.addEventListener("mousemove", moveWindow, false);
- document.documentElement.addEventListener("mouseup", stopMove, false);
-
- e.stopPropagation();
- e.preventDefault();
-
- beforeEvent = e;
-
- }
-
-
- function checkClick(beforeEvent, currentEvent) {
-
- if (currentEvent.timeStamp - beforeEvent.timeStamp < 300 && currentEvent.timeStamp - beforeEvent.timeStamp > 30) {
-
- document.documentElement.classList.add('no-floating');
-
- }
-
- }
-
-
- stopResize = (e) => {
-
- document.documentElement.removeAttribute('moving');
- document.documentElement.removeEventListener("mousemove", resizeWindow);
- }
-
- stopMove = (e) => {
- document.documentElement.removeAttribute('moving');
- document.documentElement.removeEventListener("mousemove", moveWindow);
-
- checkClick(beforeEvent, e)
- beforeEvent = null;
- }
-
-
- function reset() {
-
- document.documentElement.removeAttribute('moving');
- document.documentElement.removeEventListener("mousemove", resizeWindow, false);
- document.documentElement.removeEventListener("mousemove", moveWindow, false);
- document.documentElement.removeEventListener("mouseup", stopResize, false);
- document.documentElement.removeEventListener("mouseup", stopMove, false);
-
-
- startX = 0;
- startY = 0;
- startWidth = 0;
- startHeight = 0;
-
-
- edge = 0;
-
-
- initialLeft = 0;
- initialTop = 0;
-
- beforeEvent = null;
-
-
- }
-
- function setChat(chat) {
-
- let resizeHandle = HTMLElement.prototype.querySelector.call(chat, '.resize-handle')
- if (resizeHandle) return;
-
-
- 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;
- if (!script) addCSS();
-
- if (!document.documentElement.hasAttribute('floating-chat-window')) document.documentElement.setAttribute('floating-chat-window', '');
-
-
- resizeHandle = document.createElement("div");
- resizeHandle.className = "resize-handle";
- chat.appendChild(resizeHandle);
- resizeHandle = null;
-
- let chatWindow;
- let showHideButton;
-
- chatWindow = kRef(chatWindowWR);
- showHideButton = kRef(showHideButtonWR);
-
-
-
- if (chatWindow) chatWindow.removeEventListener("mousedown", initializeResize, false);
- if (showHideButton) showHideButton.removeEventListener("mousedown", initializeMove, false);
-
-
- chatWindow = chat;
- showHideButton = HTMLElement.prototype.querySelector.call(chat, '#show-hide-button');
- chatWindowWR = mWeakRef(chat)
- showHideButtonWR = mWeakRef(showHideButton);
-
-
-
- chatWindow.addEventListener("mousedown", initializeResize, false);
- showHideButton.addEventListener("mousedown", initializeMove, false);
-
- reset();
-
- }
-
- function noChat(chat) {
-
- let chatWindow;
- let showHideButton;
-
- chatWindow = kRef(chatWindowWR);
- showHideButton = kRef(showHideButtonWR);
-
-
-
- if (chatWindow) chatWindow.removeEventListener("mousedown", initializeResize, false);
- if (showHideButton) showHideButton.removeEventListener("mousedown", initializeMove, false);
-
-
- let resizeHandle = HTMLElement.prototype.querySelector.call(chat, '.resize-handle')
- if (resizeHandle) {
- resizeHandle.remove();
- }
-
- chat.removeEventListener("mousedown", initializeResize, false);
-
-
- showHideButton = HTMLElement.prototype.querySelector.call(chat, '#show-hide-button');
-
- if (showHideButton) showHideButton.removeEventListener("mousedown", initializeMove, false);
-
-
- reset();
- }
-
- document.addEventListener('fullscreenchange', () => {
- document.documentElement.classList.remove('no-floating')
- })
-
- customYtElements.whenRegistered('ytd-live-chat-frame', (proto) => {
-
-
- proto.attached = ((attached) => (function () { Promise.resolve(this).then(setChat); return attached.apply(this, arguments) }))(proto.attached);
-
- proto.detached = ((detached) => (function () { Promise.resolve(this).then(noChat); return detached.apply(this, arguments) }))(proto.detached);
-
- let chat = document.querySelector('ytd-live-chat-frame');
- if (chat) Promise.resolve(chat).then(setChat);
-
- })
-
-
- })({ Promise, requestAnimationFrame });