// ==UserScript== // @name Transparent Twitch Chat // @description Why decide between missing a PogChamp or sacrificing precious screen space, when you can have the best of both worlds! // @version {VERSION} // @namespace https://chylex.com // @homepageURL https://github.com/chylex/Transparent-Twitch-Chat // @supportURL https://github.com/chylex/Transparent-Twitch-Chat/issues // @updateURL https://github.com/chylex/Transparent-Twitch-Chat/raw/master/dist/TransparentTwitchChat.user.js // @include https://www.twitch.tv/* // @run-at document-end // @grant GM_getValue // @grant GM_setValue // @noframes // ==/UserScript== const settings = { globalSwitch: true, chatWidth: 350, chatFilters: "", playerPosition: "center center", grayTheme: false, hideHeader: true, hideChatInput: false, hidePinnedCheer: false, hideConversations: false, transparentChat: true, smoothTextShadow: false, chatLeftSide: false, backgroundOpacity: 30, hideBadgeTurbo: true, hideBadgePrime: true, hideBadgeSubscriber: true, hideBadgeVIP: false, hideBadgeSubGift: false, hideBadgeBitCheer: false, hideBadgeLeader: false, badgeOpacity: 85 }; if (typeof GM_getValue !== "undefined"){ for(let key in settings){ settings[key] = GM_getValue(key, settings[key]); } } const isFirefox = "mozPaintCount" in window; function tryRemoveElement(ele){ if (ele && ele.parentNode){ ele.parentNode.removeChild(ele); } } function onSettingsUpdated(){ generateCustomCSS(); refreshChatFilters(); if (typeof GM_setValue !== "undefined"){ for(let key in settings){ GM_setValue(key, settings[key]); } } } // Styles function generateCustomCSS(){ if (!settings.globalSwitch){ tryRemoveElement(document.getElementById("chylex-ttc-style-custom")); return; } const wa = ":not(.ttcwa)"; // selector priority workaround const rcol = ".right-column--theatre"; const rcolBlur = ".right-column--theatre:not(:hover)"; const isTheatre = ".ttc-theatre"; const fullWidth = ".ttc-rcol-collapsed"; const fullScreen = ".ttc-player-fullscreen"; let style = document.getElementById("chylex-ttc-style-custom"); if (!style){ style = document.createElement("style"); style.id = "chylex-ttc-style-custom"; } style.innerHTML = `@#rem{{ @#css{{ // fix scrollbars ${rcolBlur} .chat-list__lines .simplebar-track.vertical { visibility: hidden !important; } @#css}} ${isFirefox ? `@#css{{ ${rcol} .video-chat__message-list-wrapper:not(.video-chat__message-list-wrapper--unsynced) { overflow-y: hidden !important; } @#css}}` : ``} @#css{{ // general player styles ${isTheatre} .video-player video { object-position: ${settings.playerPosition} !important; } // general chat styles ${rcolBlur} .channel-root__right-column${wa} { background: rgba(14, 12, 19, ${settings.backgroundOpacity * 0.01}) !important; } ${rcol}${wa}, ${rcol} .channel-root__right-column${wa} { width: ${settings.chatWidth - 10}px !important; } ${rcol} .video-chat { flex-basis: auto !important; } ${rcol} .video-chat__header { display: none !important; } ${rcol} .room-picker { width: ${settings.chatWidth - 10}px; } ${rcol} .hidden { display: none; } ${rcol} .video-chat__sync-button { width: ${settings.chatWidth - 50}px; z-index: 10; background-color: #b8b5c0 !important; } @#css}} #root[data-a-page-loaded-name="VideoWatchPage"] ${rcolBlur}:not(.right-column--collapsed) .right-column__toggle-visibility { display: none !important; } ${settings.hideHeader ? `@#css{{ ${rcolBlur} .stream-chat-header { display: none !important; } ${rcolBlur}:not(.right-column--collapsed) .right-column__toggle-visibility { display: none !important; } @#css}}` : ``} ${settings.hideChatInput ? `@#css{{ ${rcolBlur} .chat-input { display: none !important; } @#css}}` : ``} ${settings.hidePinnedCheer ? `@#css{{ .channel-leaderboard { display: none; } @#css}}` : ``} ${settings.transparentChat ? `@#css{{ // expand player width body:not(${fullScreen}) .persistent-player--theatre { width: 100% !important; } body:not(${fullWidth}):not(${fullScreen}) .persistent-player--theatre .top-bar, body:not(${fullWidth}):not(${fullScreen}) .persistent-player--theatre .player-controls { padding-right: ${settings.chatWidth - 10}px; } body:not(${fullWidth}):not(${fullScreen}) .persistent-player--theatre .player-overlay-background > div { right: ${settings.chatWidth - 10}px !important; } // chat container transparency ${rcol} .chat-room { background: transparent !important; } ${rcolBlur} .chylex-ttc-chat-container { color: #ece8f3 !important; } ${rcolBlur} .rooms-header, ${rcolBlur} .leaderboard-header-tabbed-layout { background: transparent !important; } ${rcolBlur} .chat-input { opacity: 0.6; } // chat messages ${rcolBlur} .chylex-ttc-chat-container { ${settings.smoothTextShadow ? ` text-shadow: 0 0 2px @#hex(000d), -1px 0 1px @#hex(0006), 0 -1px 1px @#hex(0006), 1px 0 1px @#hex(0006), 0 1px 1px @#hex(0006); ` : ` text-shadow: -1px 0 0 @#hex(000a), 0 -1px 0 @#hex(000a), 1px 0 0 @#hex(000a), 0 1px 0 @#hex(000a); `} } ${rcolBlur} .chat-author__display-name, ${rcolBlur} .vod-message__timestamp { ${settings.smoothTextShadow ? ` text-shadow: -1px 0 1px @#hex(0006), 0 -1px 1px @#hex(0006), 1px 0 1px @#hex(0006), 0 1px 1px @#hex(0006); ` : ` text-shadow: -1px 0 0 @#hex(0008), 0 -1px 0 @#hex(0008), 1px 0 0 @#hex(0008), 0 1px 0 @#hex(0008); `} } ${rcolBlur} .chat-line__message--mention-recipient { text-shadow: none; } ${rcolBlur} .chat-line__message a { color: #cdb9f5 !important; } // conversation menu .whispers--theatre-mode .whispers-threads-box__container:not(.whispers-threads-box__container--open):not(:hover) { opacity: ${Math.max(0.1, settings.backgroundOpacity * 0.01)}; } @#css}}` : `@#css{{ // adapt player size with disabled transparency body:not(${fullWidth}):not(${fullScreen}) .persistent-player--theatre { width: calc(100% - ${settings.chatWidth - 10}px) !important; } body:not(${fullScreen}) .persistent-player--theatre .player-streamstatus { margin-right: 20px !important; } @#css}}`} // conversation menu and bottom margin .whispers--theatre-mode.whispers--right-column-expanded-beside { right: ${settings.chatWidth - 10}px !important; } ${settings.hideConversations ? `@#css{{ .whispers--theatre-mode${wa} { display: none !important; } .video-player__container--theatre-whispers, .highwind-video-player__container--theatre-whispers { bottom: 1px !important; // allows hiding player controls in fullscreen by moving cursor all the way down } @#css}}` : ``} // chat on left side ${settings.chatLeftSide && settings.transparentChat ? `@#css{{ ${isTheatre} .side-nav { display: none !important; } ${rcol}${wa}, ${rcol} .chat-list__lines .simplebar-track.vertical { left: 0 !important; right: auto !important; } ${rcol} .channel-root__right-column${wa} { border-left: none !important; border-right: var(--border-width-default) solid var(--color-border-base) !important; } body:not(${fullWidth}):not(${fullScreen}) .persistent-player--theatre .top-bar { padding-left: ${settings.chatWidth}px; padding-right: 0; } body:not(${fullWidth}):not(${fullScreen}) .persistent-player--theatre .player-controls { padding-left: ${settings.chatWidth - 10}px; padding-right: 0; } body:not(${fullWidth}):not(${fullScreen}) .persistent-player--theatre .player-overlay-background > div { left: ${settings.chatWidth - 10}px !important; right: 0 !important; } .whispers--theatre-mode.whispers--right-column-expanded-beside { right: 0px !important; } ${rcol} .right-column__toggle-visibility { transform: rotate(180deg) !important; } ${rcol}.right-column--collapsed .right-column__toggle-visibility { left: 0.5rem; } @#css}}` : ``} // gray theme ${settings.grayTheme ? `@#css{{ ${rcol} [data-a-target="chat-input"] { background-color: #0e0e0e !important; border-color: #2b2b2b !important; } ${rcol} [data-a-target="chat-input"]:focus { border-color: #787878 !important; box-shadow: 0 0 6px -2px #787878 !important; } ${rcol} [data-a-target="chat-send-button"] { background-color: #2b2b2b !important; border: 1px solid #000000 !important; } ${rcol} [data-a-target="chat-send-button"]:active, ${rcol} [data-a-target="chat-send-button"]:focus { box-shadow: 0 0 6px 0 #787878 !important; } @#css}}` : ``} @#css{{ // badge tweaks ${rcolBlur} a[data-a-target="chat-badge"] { opacity: ${settings.badgeOpacity / 100}; ${settings.badgeOpacity === 0 ? `display: none !important;` : ``} } @#css}} ${settings.hideBadgeTurbo ? `@#css{{ ${rcol} .chat-badge[alt="Turbo"] { display: none; } @#css}}` : ``} ${settings.hideBadgePrime ? `@#css{{ ${rcol} .chat-badge[alt$="Prime"] { display: none; } @#css}}` : ``} ${settings.hideBadgeSubscriber ? `@#css{{ ${rcol} .chat-badge[alt~="Subscriber"] { display: none; } @#css}}` : ``} ${settings.hideBadgeVIP ? `@#css{{ ${rcol} .chat-badge[alt="VIP"] { display: none; } @#css}}` : ``} ${settings.hideBadgeSubGift ? `@#css{{ ${rcol} .chat-badge[alt*="Gift Subs"] { display: none; } @#css}}` : ``} ${settings.hideBadgeBitCheer ? `@#css{{ ${rcol} .chat-badge[alt~="cheer"] { display: none; } @#css}}` : ``} ${settings.hideBadgeLeader ? `@#css{{ ${rcol} .chat-badge[alt*="Bits Leader"], ${rcol} .chat-badge[alt*="Gifter Leader"] { display: none; } @#css}}` : ``} @#css{{ // dynamic styles for settings, replaces default style #chylex-ttc-settings-btn { margin-left: ${settings.chatWidth - 60}px; } @#css}} @#rem}}`; document.head.appendChild(style); } function generateSettingsCSS(){ if (document.getElementById("chylex-ttc-style-settings")){ return; } const style = document.createElement("style"); style.id = "chylex-ttc-style-settings"; style.innerHTML = `@#rem{{@#css{{ // settings button #chylex-ttc-settings-btn { display: none; width: 2.5em; height: 2.5em; position: absolute; bottom: 105px; margin-left: 290px; z-index: 9; cursor: pointer; fill: @#hex(fffa); } .video-chat #chylex-ttc-settings-btn { bottom: 18px; } #chylex-ttc-settings-btn svg { width: 100%; height: 100%; } #chylex-ttc-settings-btn:hover { fill: #fff; } .right-column--theatre:hover #chylex-ttc-settings-btn { display: block; } // settings modal #chylex-ttc-settings-modal { position: absolute; left: 50%; top: 50%; width: 870px; height: 294px; margin-left: -435px; margin-top: -147px; z-index: 10000; background-color: @#hex(111c); } #chylex-ttc-settings-modal #ttc-opt-global-wrapper { position: absolute; margin: 5px 0 0 -69px; display: inline-block; } #chylex-ttc-settings-modal h2 { color: @#hex(fffe); font-size: 24px; text-align: center; margin: 0; padding: 14px 0 13px; background-color: @#hex(0009); } #chylex-ttc-settings-modal .ttc-flex-container { display: flex; flex-direction: row; justify-content: space-between; padding: 8px 4px; } #chylex-ttc-settings-modal p { color: @#hex(fffd); font-size: 14px; margin-top: 8px; padding: 0 9px; } #chylex-ttc-settings-modal p:first-of-type { margin: 0 0 4px; } #chylex-ttc-settings-modal .player-menu__section { padding: 0 12px 2px; } #chylex-ttc-settings-modal .player-menu__header { margin-bottom: 0; color: @#hex(fffa); } #chylex-ttc-settings-modal .player-menu__item { display: flex; align-items: center; margin: 2px 0 9px; padding-left: 1px; } #chylex-ttc-settings-modal .player-menu__item.ttc-setting-small-margin { margin-bottom: 7px; } #chylex-ttc-settings-modal .switch { font-size: 10px; height: 17px; line-height: 17px; } #chylex-ttc-settings-modal .switch span { width: 27px; } #chylex-ttc-settings-modal .switch.active span { left: 25px; } #chylex-ttc-settings-modal .switch::before, #chylex-ttc-settings-modal .switch::after { width: 26px; box-sizing: border-box; } #chylex-ttc-settings-modal .switch::before { text-align: left; padding-left: 5px; } #chylex-ttc-settings-modal .switch::after { text-align: right; padding-right: 3px; } #chylex-ttc-settings-modal input[type="text"] { width: 100%; padding: 1px 4px; } #chylex-ttc-settings-modal input[type="range"] { width: 100%; } #chylex-ttc-settings-modal select { width: 100%; padding: 1px 0; } #chylex-ttc-settings-modal output { color: @#hex(fffc); display: inline-block; flex: 1 1 42px; padding-left: 6px; text-align: left; } #chylex-ttc-settings-modal .tw-toggle__button { width: 4rem; } #chylex-ttc-settings-modal .tw-toggle__button, #chylex-ttc-settings-modal .tw-toggle__button::after { border-radius: 0; } @#css}}@#rem}}`; document.head.appendChild(style); } // Filters function getNodeText(node){ if (node.nodeType === Node.TEXT_NODE){ return node.nodeValue; } if (node.nodeType === Node.ELEMENT_NODE){ if (node.tagName === "IMG"){ return node.getAttribute("alt") || ""; } else{ let text = ""; for(let child of node.childNodes){ text += getNodeText(child); } return text; } } return ""; } var filtersRegex = null; var filtersObserver = new MutationObserver(function(mutations){ for(let mutation of mutations){ for(let added of mutation.addedNodes){ let text; const classes = added.classList; if (classes.contains("chat-line__message")){ const nodes = Array.from(added.childNodes); const colon = nodes.findIndex(node => node.tagName === "SPAN" && node.innerText === ": "); text = nodes.slice(colon+1).map(getNodeText).join(""); } else{ text = getNodeText(added.querySelector(".qa-mod-message") || added); } if (filtersRegex.test(text)){ classes.add("hidden"); } } } }); function refreshChatFilters(){ const chat = document.querySelector(".chat-list__lines .simplebar-content > div, .video-chat__message-list-wrapper ul"); if (!chat){ return false; } const filters = (settings.chatFilters || "").split(",").map(entry => entry.trim()).filter(entry => !!entry); if (filters.length === 0){ filtersRegex = null; } else{ const options = filters.map(entry => entry.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, "(?:\\S*)").replace(/\s+/g, "\\s+")).join("|"); filtersRegex = new RegExp("(?:^|[^a-z0-9])(?:"+options+")(?:$|[^a-z0-9])", "i"); } if (filtersRegex && settings.globalSwitch){ filtersObserver.observe(chat, { childList: true }); } else{ filtersObserver.disconnect(); } return true; } // Helpers var classObserverCallback = function(mutations){ for(let mutation of mutations){ const classes = mutation.target.classList; if (classes.contains("right-column")){ document.body.classList.toggle("ttc-theatre", classes.contains("right-column--theatre")); document.body.classList.toggle("ttc-rcol-collapsed", classes.contains("right-column--collapsed")); } } }; var classObserver = new MutationObserver(classObserverCallback); function setupClassHelpers(){ const col = document.querySelector(".right-column"); if (!col){ return false; } classObserver.observe(col, { attributes: true, attributeFilter: [ "class" ] }); classObserverCallback([ { target: col } ]); return true; } function setupFullscreenHelper(){ for(let event of [ "fullscreenchange", "webkitfullscreenchange", "mozfullscreenchange", "msfullscreenchange" ]){ if ("on" + event in document){ document.addEventListener(event, function(){ document.body.classList.toggle("ttc-player-fullscreen", document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement); }); break; } } } // Settings function debounce(func, wait){ let timeout = -1; return function(){ window.clearTimeout(timeout); timeout = window.setTimeout(func, wait); }; } function createSettingsModal(){ tryRemoveElement(document.getElementById("chylex-ttc-settings-modal")); const generateOptionBase = function(title, item, extra){ extra = extra || {}; return ` <div class="player-menu__section" data-enabled="true"${extra.floatLeft ? ` style="float:left"` : ""}> <div class="player-menu__header"> <span class="js-menu-header">${title}</span> </div> <div class="player-menu__item ${extra.itemClasses ? " " + extra.itemClasses : ""}"> ${item} </div> </div>`; }; const prepareOptionEvent = function(option, setupCallback){ window.setTimeout(function(){ const ele = document.getElementById("ttc-opt-" + option); setupCallback(ele); }, 1); }; const updateOption = function(option, value){ settings[option] = value; onSettingsUpdated(); }; // Concrete option types const generateToggle = function(title, option, floatLeft){ prepareOptionEvent(option, function(ele){ ele.addEventListener("click", function(){ updateOption(option, ele.checked); }); }); return generateOptionBase(title, ` <div class="tw-toggle"> <input class="tw-toggle__input" id="ttc-opt-${option}" value="${settings[option] ? "on" : "off"}" type="checkbox"${settings[option] ? " checked" : ""}> <label for="ttc-opt-${option}" class="tw-toggle__button"></label> </div>`, floatLeft ? { floatLeft: true } : {}); }; const generateTxtbox = function(title, option, cfg){ prepareOptionEvent(option, function(ele){ ele.addEventListener("input", debounce(function(){ updateOption(option, ele.value); }, cfg.wait)); }); return generateOptionBase(title, `<input id="ttc-opt-${option}" type="text" value="${settings[option]}" placeholder="${cfg.placeholder}">`); }; const generateSelect = function(title, option, cfg){ prepareOptionEvent(option, function(ele){ ele.addEventListener("input", function(){ updateOption(option, ele.value); }); }); const initialOption = settings[option]; const optionElements = Object.keys(cfg).map(function(key){ return `<option value="${key}"${key == initialOption ? " selected" : ""}>${cfg[key]}</option>`; }); return generateOptionBase(title, `<select id="ttc-opt-${option}">${optionElements}</select>`); }; const generateSlider = function(title, option, cfg){ prepareOptionEvent(option, function(ele){ const regenerate = debounce(onSettingsUpdated, cfg.wait); ele.addEventListener("input", function(){ settings[option] = parseInt(ele.value, 10); document.getElementById("ttc-optval-" + option).value = ele.value + cfg.text; regenerate(); }); }); return generateOptionBase(title, ` <input id="ttc-opt-${option}" type="range" min="${cfg.min}" max="${cfg.max}" step="${cfg.step}" value="${settings[option]}"> <output id="ttc-optval-${option}" for="ttc-opt-${option}">${settings[option]}${cfg.text}</option> `, { itemClasses: "ttc-setting-small-margin" }); }; // Generate modal const modal = document.createElement("div"); modal.id = "chylex-ttc-settings-modal"; modal.innerHTML = ` <h2> <div id="ttc-opt-global-wrapper" class="tw-toggle"> <input class="tw-toggle__input" id="ttc-opt-global" value="${settings.globalSwitch ? "on" : "off"}" type="checkbox"${settings.globalSwitch ? " checked" : ""}> <label for="ttc-opt-global" class="tw-toggle__button"></label> </div> <span>Transparent Twitch Chat</span> </h2> <div class="ttc-flex-container"> <div style="flex: 0 0 23%"> <p>General</p> ${generateSlider("Chat Width", "chatWidth", { min: 250, max: 600, step: 25, wait: 500, text: "px" })} ${generateTxtbox("Chat Filters", "chatFilters", { wait: 500, placeholder: "Example: kappa, *abc*" })} ${generateSelect("Player Position", "playerPosition", { "top left": "Top Left", "top center": "Top Center", "top right": "Top Right", "center left": "Center Left", "center center": "Center", "center right": "Center Right", "bottom left": "Bottom Left", "bottom center": "Bottom Center", "bottom right": "Bottom Right" })} ${generateToggle("Gray Theme", "grayTheme")} </div> <div style="flex: 0 0 21%"> <p>Transparency</p> ${generateToggle("Transparent Chat", "transparentChat")} ${generateToggle("Smooth Text Shadow", "smoothTextShadow")} ${generateToggle("Chat on Left Side", "chatLeftSide")} ${generateSlider("Background Opacity", "backgroundOpacity", { min: 0, max: 100, step: 5, wait: 100, text: "%" })} </div> <div style="flex: 0 0 16%"> <p>Elements</p> ${generateToggle("Hide Chat Header", "hideHeader")} ${generateToggle("Hide Chat Input", "hideChatInput")} ${generateToggle("Hide Pinned Cheer", "hidePinnedCheer")} ${generateToggle("Hide Whispers", "hideConversations")} </div> <div style="flex: 0 0 19%"> <p>Badges</p> ${generateToggle("Hide Subscriber Badge", "hideBadgeSubscriber")} ${generateToggle("Hide Prime Badge", "hideBadgePrime")} ${generateToggle("Hide Turbo Badge", "hideBadgeTurbo")} ${generateToggle("Hide VIP Badge", "hideBadgeVIP")} </div> <div style="flex: 0 0 21%"> <p style="visibility: hidden">Badges</p> ${generateToggle("Hide Sub Gift Badge", "hideBadgeSubGift")} ${generateToggle("Hide Bit Cheer Badge", "hideBadgeBitCheer")} ${generateToggle("Hide Gift/Bit Leader Badge", "hideBadgeLeader")} ${generateSlider("Badge Opacity", "badgeOpacity", { min: 0, max: 100, step: 5, wait: 100, text: "%" })} </div> </div> `; document.body.appendChild(modal); document.getElementById("ttc-opt-global").addEventListener("click", function(e){ settings.globalSwitch = e.currentTarget.checked; onSettingsUpdated(); }); modal.addEventListener("click", function(e){ e.stopPropagation(); }); } function insertSettingsButton(){ const container = document.querySelector("[data-test-selector='chat-room-component-layout'] > div, .video-chat"); if (!container){ return false; } container.classList.add("chylex-ttc-chat-container"); tryRemoveElement(document.getElementById("chylex-ttc-settings-btn")); tryRemoveElement(document.getElementById("chylex-ttc-settings-modal")); const button = document.createElement("div"); button.id = "chylex-ttc-settings-btn"; button.innerHTML = '<svg viewBox="0 0 15 15"><path d="M8.463,0.062c-0.639,-0.083 -1.287,-0.083 -1.926,0l-0.251,1.333c-0.802,0.159 -1.565,0.475 -2.244,0.929l-1.12,-0.765c-0.511,0.394 -0.969,0.852 -1.363,1.363l0.765,1.12c-0.454,0.679 -0.77,1.442 -0.929,2.244l-1.333,0.251c-0.083,0.639 -0.083,1.287 0,1.926l1.333,0.251c0.159,0.802 0.475,1.565 0.929,2.244l-0.765,1.12c0.394,0.511 0.852,0.969 1.363,1.363l1.12,-0.765c0.679,0.454 1.442,0.77 2.244,0.929l0.251,1.333c0.639,0.083 1.287,0.083 1.926,0l0.251,-1.333c0.802,-0.159 1.565,-0.475 2.244,-0.929l1.12,0.765c0.511,-0.394 0.969,-0.852 1.363,-1.363l-0.765,-1.12c0.454,-0.679 0.77,-1.442 0.929,-2.244l1.333,-0.251c0.083,-0.639 0.083,-1.287 0,-1.926l-1.333,-0.251c-0.159,-0.802 -0.475,-1.565 -0.929,-2.244l0.765,-1.12c-0.394,-0.511 -0.852,-0.969 -1.363,-1.363l-1.12,0.765c-0.679,-0.454 -1.442,-0.77 -2.244,-0.929l-0.251,-1.333Zm-0.963,2.731c2.598,0 4.707,2.109 4.707,4.707c0,2.598 -2.109,4.707 -4.707,4.707c-2.598,0 -4.707,-2.109 -4.707,-4.707c0,-2.598 2.109,-4.707 4.707,-4.707Z"/><rect x="6.75" y="6" width="1.5" height="5.25"/><rect x="4.89" y="4.5" width="5.221" height="1.5"/></svg>'; container.appendChild(button); button.addEventListener("click", function(e){ if (!document.getElementById("chylex-ttc-settings-modal")){ createSettingsModal(); e.stopPropagation(); } }); if (isFirefox && container.classList.contains("video-chat")){ const wrapper = document.querySelector(".video-chat__message-list-wrapper"); const unsynced = "video-chat__message-list-wrapper--unsynced"; wrapper.addEventListener("wheel", function(e){ if (e.deltaY < 0){ wrapper.classList.add(unsynced); } }); wrapper.addEventListener("keydown", function(e){ if (e.keyCode === 38 || e.keyCode === 33){ // up arrow || page up wrapper.classList.add(unsynced); } }); } return true; } // Setup var prevAddress = null; var rehookInterval = null; window.setInterval(function(){ if (location.href != prevAddress){ prevAddress = location.href; var hooks = [ refreshChatFilters, setupClassHelpers, insertSettingsButton ]; window.clearInterval(rehookInterval); rehookInterval = window.setInterval(function(){ for(let index = hooks.length - 1; index >= 0; index--){ if (hooks[index]()){ hooks.splice(index, 1); } } if (hooks.length === 0){ window.clearInterval(rehookInterval); rehookInterval = null; } }, 250); } }, 1000); document.body.addEventListener("click", function(){ tryRemoveElement(document.getElementById("chylex-ttc-settings-modal")); }); setupFullscreenHelper(); generateSettingsCSS(); generateCustomCSS();