您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Display chat window on stream screen and apply custom stylesheet
当前为
// ==UserScript== // @name Youtube live, Simple Chat Stylizer // @name:ja Youtube live, シンプルチャットスタイル // @description Display chat window on stream screen and apply custom stylesheet // @description:ja チャットウィンドウを配信画面上に配置し、カスタムスタイルを適応する // @namespace http://tampermonkey.net/ // @version 1.5.6 // @author You // @match https://www.youtube.com/* // @grant none // @run-at document-end // @nowrap // ==/UserScript== (function() { 'use strict'; const configAreaID = "simple-chat-stylizer-config"; const configKey = "simple-chat-stylizer"; const SINGLE_WINDOW_PARAMS = "toolber=no,menubar=no,scrollbar=no,titlebar=no,location=no,directories=no,status=no,resizable=yes"; const CHAT_CONNECTION_TIMEOUT = 60 * 1000; // 1 min const TAG_HIGHLIGHTED = "highlighted_", TAG_HIDDEN = "hidden_"; const EMOTE_MODE = { disable: 0, fever: 1, hideThreeStamp: 2, } // config object var c = { backgroundColor: "#fff0", chatFontSize: "13", isEnableChatOutline: true, chatOutlineColor: "#036", isHideAuthorName: false, isAuthorNameRightSide: true, authorNameMaxWidth: "100", isHideThumbnail: false, isHideBadge: false, simpleMemberChat: true, isHideHeader: true, isHideFooter: false, isHideCommonEmotes: true, isMemberOnly: false, isCountHeavyUser: false, isReloadWhenChatClogged: false, isHideEmotes: false, isFeverEmotes: false, disableChatFlickers: false, superChatViewType: "history-header-message", isFixModerator: true, moderatorChatTimeout: "20", windowWidth: "430", windowHeight: "720", chatWindowPosition: "right-top", windowOpacity: "0.9", enableFilter: true, chatFilter: "", fullscreenMode: false, }; var isEnableFiltering = false; window.addEventListener("load", onLoaded); return; function onLoaded() { if (location.host == "www.youtube.com") { // チャットウィンドウ if (location.pathname.indexOf("/live_chat") == 0) { if (window == window.parent || window.parent.location.host != "www.youtube.com") return; loadConfig(); addLiveChatStyle(); setTimeout(updateChatWindowHeight, 100); setTimeout(setupChatObservation, 100); window.addEventListener("focus", focusInputField); window.addEventListener("mouseover", fixPlayerConflict); } // iframe else if (location.pathname.indexOf("/embed") == 0) { return; } else { // チャットウィンドウからも呼び出すのでグローバルにも定義する window.updateChatWindowHeight = updateChatWindowHeight; window.toggleConfig = toggleConfig; loadConfig(); addConfigArea(); addFloatWindowStyle(); setTimeout(updateDynamicLayout, 20); setTimeout(updateDynamicLayout, 3000); window.addEventListener("focus", focusInputField); window.addEventListener("yt-set-theater-mode-enabled", () => setTimeout(updateDynamicLayout, 20)); window.addEventListener("fullscreenchange", () => setTimeout(updateDynamicLayout, 20)); window.addEventListener("play", () => setTimeout(updateDynamicLayout, 20)); window.addEventListener("resize", () => setTimeout(updateDynamicLayout, 20)); window.addEventListener("yt-update-title", () => setTimeout(updateDynamicLayout, 20)); } } } /** ***************** * Config * ******************/ function addConfigArea() { // console.log("addConfigArea()"); if (document.querySelector(`#${configAreaID}`)) return; var localize = { ja: { groupLayout: "レイアウト・表示非表示", groupDecoration: "テキスト装飾", groupAdditional: "追加機能", groupEmotes: "絵文字", groupFiltering: "チャットNG", configHeader: "Youbute live, シンプルチャットスタイル 設定", fullscreenMode: "全画面表示(ページ再読み込みで適応)", backgroundColor: "背景色", fontSize: "文字サイズ", chatOutline: "文字枠を有効化する", chatOutlineColor: "文字枠の色", hideAuthorName: "チャット投稿者名を非表示にする", authorNameIsRight: "投稿者名を右側に表示する", autnorNameMaxWidth: "投稿者名の最大横幅(長すぎる場合は省略します)", hideThumbnail: "ユーザーアイコンを非表示にする", hideBadge: "メンバーバッヂを非表示にする", simpleMemberChat: "メンバーチャットを簡易表示にする", hideHeader: "ヘッダーを非表示にする", hideFooter: "フッターを非表示にする (チャットができなくなります)", hideCommonEmotes: "チャンネル専用絵文字以外を非表示にする", isHideEmotes: "絵文字のみのチャットを非表示にする", isFeverEmotes: "絵文字のみのチャットを背景に流す", memberOnly: "メンバーチャットのみ表示する", isCountHeavyUser: "チャット頻度を可視化する", reloadWhenChatClogged : "チャットが詰まったら更新する", disableChatFlickers: "チャットのちらつきを低減する", superChatViewType: "スーパーチャットの表示方法", superChat: { all: "全て表示", historyMessage: "履歴 + チャットメッセージ", history: "履歴のみ", headerMessage: "チャットのみ", message: "チャットメッセージのみ", none: "全て表示しない", }, highlightModerator: "モデレーターを強調表示する(オーナーは常時)", highlightTimeout: "強調表示する時間 (秒)", windowWidth: "チャットウィンドウの横幅", windowHeight: "チャットウィンドウの高さ", chatWindowPosition: "チャットウィンドウの位置", position: { rightTop: "右上", rightBottom: "右下", leftTop: "左上", leftBottom: "左下", }, windowOpacity: "チャットウィンドウの透明度 (0~1で指定/0は透明)", chatFilter: "チャットフィルタ (正規表現が使えます/一行に一項目)", save: "保存", close: "閉じる", }, en: { groupLayout: "Layout / Visibility", groupDecoration: "Text Decoration", groupEmotes: "Emotes", groupAdditional: "Additional", groupFiltering: "Chat Filtering", fullscreenMode: "Screen fix to window (Apply to reload page)", configHeader: "Simple chat stylizer Config", backgroundColor: "Background color", fontSize: "Font size", chatOutline: "Enabled chat outline", chatOutlineColor: "Chat outline color", hideAuthorName: "Hide author name", authorNameIsRight: "Author name is display to right side", autnorNameMaxWidth: "Max width of author name(If too wide, omit the part)", hideThumbnail: "Hide user icon", hideBadge: "Hide member badge icon", simpleMemberChat: "Display simplify member chat", hideHeader: "Hide header panel", hideFooter: "Hide footer panel (you can't chat)", hideCommonEmotes: "Hide emotes except for the channel", isHideEmotes: "Hidden if chat is emotes only", isFeverEmotes: "Animate on background if chat is emotes only", memberOnly: "Display member chat only", isCountHeavyUser: "Display chat frequency", reloadWhenChatClogged : "Reload Chat window when connection clagged", disableChatFlickers: "Decrement chat flickers", superChatViewType: "View type of super chat", superChat: { all: "All", historyMessage: "History + Message only", history: "History only", headerMessage: "Chat only", message: "Message only", none: "Not display all", }, highlightModerator: "Highlight moderator chat (Owner chat is always)", highlightTimeout: "Time period that highlight (sec)", windowWidth: "Width of Chat window", windowHeight: "Height of Chat window", chatWindowPosition: "Position of Chat window", position: { rightTop: "Right Top", rightBottom: "Right Bottom", leftTop: "Left Top", leftBottom: "Left Bottom", }, windowOpacity: "Opacity of Chat window (Set value of 0 to 1 / 0 is transport)", chatFilter: "Chat filter (Can use regix / Input a filter each one line)", save: "Save", close: "Close", }, } var t = localize[window.navigator.language] || localize.en; const config = document.querySelector("#" + configAreaID) || document.createElement("form"); config.id = configAreaID; config.innerHTML = ` <div class="group"> <header>${t.groupLayout}</header> <input type="checkbox" name="isHideAuthorName" id="scs-isHideAuthorName"> <label for="scs-isHideAuthorName">${t.hideAuthorName}</label><br /> <input type="checkbox" name="isAuthorNameRightSide" id="scs-isAuthorNameRightSide"> <label for="scs-isAuthorNameRightSide">${t.authorNameIsRight}</label><br /> <input type="text" name="authorNameMaxWidth" id="scs-authorNameMaxWidth"> <label for="scs-authorNameMaxWidht">${t.autnorNameMaxWidth}</label><br /> <input type="checkbox" name="isHideThumbnail" id="scs-isHideThumbnail"> <label for="scs-isHideThumbnail">${t.hideThumbnail}</label><br /> <input type="checkbox" name="isHideBadge" id="scs-isHideBadge"> <label for="scs-isHideBadge">${t.hideBadge}</label><br /> <input type="checkbox" name="simpleMemberChat" id="scs-simpleMemberChat"> <label for="scs-isHideBadge">${t.simpleMemberChat}</label><br /> <input type="checkbox" name="isHideHeader" id="scs-isHideHeader"> <label for="scs-isHideHeader">${t.hideHeader}</label><br /> <input type="checkbox" name="isHideFooter" id="scs-isHideFooter"> <label for="scs-isHideFooter">${t.hideFooter}</label><br /> <label class="dummy"></label> <label for="scs-superChatViewType" for="scs-superChatViewType" class="for-select">${t.superChatViewType}</label> <select type="dropdown" name="superChatViewType" id="scs-superChatViewType" value="history-header-message"> <option value="history-header-message">${t.superChat.all}</option> <option value="history-message">${t.superChat.historyMessage}</option> <option value="history">${t.superChat.history}</option> <option value="header-message">${t.superChat.headerMessage}</option> <option value="message">${t.superChat.message}</option> <option value="none">${t.superChat.none}</option> </select><br /> <input type="text" name="windowWidth" id="scs-windowWidth"> <label for="scs-widowWidth">${t.windowWidth}</label><br /> <input type="text" name="windowHeight" id="scs-windowHeight"> <label for="scs-windowHeight">${t.windowHeight}</label><br /> <label class="dummy"></label> <label for="scs-chatWindowPosition" for="scs-chatWindowPosition" class="for-select">${t.chatWindowPosition}</label> <select type="dropdown" name="chatWindowPosition" id="scs-chatWindowPosition" value="right-top"> <option value="right-top">${t.position.rightTop}</option> <option value="right-bottom">${t.position.rightBottom}</option> <option value="left-top">${t.position.leftTop}</option> <option value="left-bottom">${t.position.leftBottom}</option> </select><br /> <input type="checkbox" name="fullscreenMode" id="scs-fullscreenMode"> <label for="scs-fullscreenMode">${t.fullscreenMode}</label><br /> </div> <div class="group"> <header>${t.groupDecoration}</header> <input type="text" name="backgroundColor" id="scs-backgroundColor"> <label for="scs-backgroundColor">${t.backgroundColor}</label><br /> <input type="text" name="chatFontSize" id="scs-chatFontSize"> <label for="scs-chatFontSize">${t.fontSize}</label><br /> <input type="checkbox" name="isEnableChatOutline" id="scs-isEnableChatOutline"> <label for="scs-isEnableChatOutline">${t.chatOutline}</label><br /> <input type="text" name="chatOutlineColor" id="scs-chatOutlineColor"> <label for="scs-chatOutlineColor">${t.chatOutlineColor}</label><br /> <input type="text" name="windowOpacity" id="scs-windowOpacity"> <label for="scs-windowOpacity">${t.windowOpacity}</label><br /> </div> <div class="group"> <header>${t.groupEmotes}</header> <input type="checkbox" name="isHideEmotes" id="scs-isHideEmotes"> <label for="scs-isHideEmotes">${t.isHideEmotes}</label><br /> <input type="checkbox" name="isFeverEmotes" id="scs-isFeverEmotes"> <label for="scs-isFeverEmotes">${t.isFeverEmotes}</label><br /> <input type="checkbox" name="isHideCommonEmotes" id="scs-isHideCommonEmotes"> <label for="scs-isHideCommonEmotes">${t.hideCommonEmotes}</label><br /> </div> <div class="group"> <header>${t.groupAdditional}</header> <input type="checkbox" name="isFixModerator" id="scs-isFixModerator"> <label for="scs-isFixModerator">${t.highlightModerator}</label><br /> <input type="text" name="moderatorChatTimeout" id="scs-moderatorChatTimeout"> <label for="scs-moderatorChatTimeout">${t.highlightTimeout}</label><br /> <input type="checkbox" name="isCountHeavyUser" id="scs-isCountHeavyUser"> <label for="scs-isCountHeavyUser">${t.isCountHeavyUser}</label><br /> <input type="checkbox" name="isReloadWhenChatClogged" id="scs-isReloadWhenChatClogged"> <label for="scs-isReloadWhenChatClogged">${t.reloadWhenChatClogged}</label><br /> </div> <div class="group"> <header>${t.groupFiltering}</header> <input type="checkbox" name="isMemberOnly" id="scs-isMemberOnly"> <label for="scs-isMemberOnly">${t.memberOnly}</label><br /> <input type="checkbox" name="disableChatFlickers" id="scs-disableChatFlickers"> <label for="scs-disableChatFlickers">${t.disableChatFlickers}</label><br /> <input type="checkbox" name="enableFilter" id="scs-enableFilter"> <label for="scs-enableFilter">${t.chatFilter}</label><br /> <textarea name="chatFilter" value="" id="scs-chatFilter"></textarea> </div> <button id="${configAreaID}-save">${t.save}</button> <button id="${configAreaID}-close">${t.close}</button> `; // フォームの値を設定から復元する var $params = config.querySelectorAll(`#${configAreaID} input, #${configAreaID} select, #${configAreaID} textarea`); for (var i = 0; i < $params.length; i++) { var $p = $params[i]; var value = c[$p.name]; if (typeof value != "undefined") { if ($p.type == "checkbox") { $p.checked = !!value; } else { $p.value = value; } } } document.body.appendChild(config); config.querySelector("textarea").addEventListener("input", textareaOnInput); config.querySelector("textarea").addEventListener("focus", textareaOnInput); config.querySelector(`#${configAreaID}-save`).addEventListener("click", saveButtonOnClick); config.querySelector(`#${configAreaID}-close`).addEventListener("click", closeButtonOnClick); localize = t = null; } function toggleConfig() { document.querySelector(`#${configAreaID}`).classList.toggle("open"); } // 設定を読み込む function loadConfig() { Object.assign(c, JSON.parse(localStorage[configKey] || "{}")); // console.log("loaded config", c); } // フォームの設定を保存する function updateConfigFromForm() { var $params = document.querySelectorAll(`#${configAreaID} input, #${configAreaID} select, #${configAreaID} textarea`); for (var i = 0; i < $params.length; i++) { var $p = $params[i]; var value = c[$p.name]; if ($p.type == "checkbox") { c[$p.name] = $p.checked; } else { c[$p.name] = $p.value; } } localStorage[configKey] = JSON.stringify(c); // console.log("updated config", c); } // チャットアニメーションを有効にするかどうか function isNoTranslate() { return isEnableFiltering || c.isFeverEmotes; } function saveButtonOnClick(ev) { ev.preventDefault(); ev.stopPropagation(); ev.stopImmediatePropagation(); updateConfigFromForm(); addFloatWindowStyle(); document.querySelector("#chatframe").contentWindow.location.reload(); return false; } function closeButtonOnClick(ev) { ev.preventDefault(); ev.stopPropagation(); ev.stopImmediatePropagation(); document.querySelector(`#${configAreaID}`).classList.remove("open"); } function textareaOnInput() { this.style.height = "1px"; this.style.height = (this.scrollHeight) + "px"; } /** ***************** * Chat Filtering * ******************/ // コメント監視を開始 function setupChatObservation() { var lastPostedTime = new Date().getTime(); const filterRegex = new RegExp(c.chatFilter.split(/[\r\n]+/) .map(x => (x || "").trim("\n").trim("\r").trim()) .filter(x => x != "") .map(x => "(" + x + ")").join("|"), "i"), emotesOnlyRegex = /^[\s\n\r]*$/, users = {}; var emoteCount = 0; const emoteCountLimit = 200; const undefined = void 0; const scroller = document.querySelector("#item-scroller.yt-live-chat-item-list-renderer"); const observer = new MutationObserver((mutations) => { var chatWindowHeight; if (c.isFeverEmotes) { chatWindowHeight = scroller.offsetHeight; } mutations.forEach((mutation) => { if (mutation && !mutation.addedNodes) return; mutation.addedNodes.forEach((chat) => { // console.log(chat); if (chat.nodeName != "YT-LIVE-CHAT-TEXT-MESSAGE-RENDERER") return; const msg = chat.querySelector("#message"); if (msg == undefined) return; // 強調表示中のチャットが再利用された if (chat.hasAttribute(TAG_HIGHLIGHTED)) { unhighlightChat(chat); } // コメント回数を集計する if (c.isCountHeavyUser) { var authorName = chat.querySelector("#author-name").__shady_native_textContent; var data = users[authorName]; if (!data) { data = users[authorName] = { count: 0 }; } if (c.isCountHeavyUser) { data.count++; chat.style.setProperty("--heavy-user", data.count + "%"); } } /* if (Math.random() < 0.05) { highlightChat(chat); return; }*/ // モデレータチャット const author = chat.getAttribute("author-type"); if (author == "owner" || (c.isFixModerator && author == "moderator")) { highlightChat(chat); // フィルタリング無効 return; } // チャットフィルタ if (msg.innerText.match(emotesOnlyRegex)) { if (c.isHideEmotes) { chat.setAttribute(TAG_HIDDEN, ""); } else if (c.isFeverEmotes && author != "owner" && author != "moderator") { chat.setAttribute(TAG_HIDDEN, ""); if (emoteCount < emoteCountLimit) { var emotes = msg.querySelectorAll(".emoji"); emotes.forEach(e => { var startY = Math.random() * chatWindowHeight * 0.25; e.style.setProperty("--fever-height", `-${chatWindowHeight - startY}px`); e.style.setProperty("--fever-width", `${40 + Math.random() * 30}px`); e.setAttribute("fever_", ""); e.setAttribute(`fever${Math.floor(Math.random() * 5) + 1}_`, ""); e.style.left = (5 + Math.random() * 90) + "%"; e.style.bottom = startY + "px"; scroller.appendChild(e); }); emoteCount += emotes.length; setTimeout(() => { emotes.forEach(e => e.remove()); emoteCount -= emotes.length; }, 4000); } } } else if (isEnableFiltering && msg.innerText.match(filterRegex)) { chat.setAttribute(TAG_HIDDEN, ""); // console.log("filtered", chat.__shady_native_textContent); } else { // チャット要素は使いまわされてる // @see https://greasyfork.org/ja/scripts/409664-chat-filter-for-youtube-live/code chat.removeAttribute(TAG_HIDDEN); } }); lastPostedTime = new Date().getTime(); }); }); // 一定時間チャットがなければ、更新する setInterval(() => { if (!c.isReloadWhenChatClogged) return; var player = document.querySelector("#ytd-player"); if ((player && player.player_ && player.player_.getPlayerState()) == 2) return; if (new Date().getTime() - lastPostedTime < CHAT_CONNECTION_TIMEOUT) return; location.reload(); }, 30 * 1000); observer.observe(document.querySelector("#items.yt-live-chat-item-list-renderer"), { childList: true }); } /** ***************** * Chat * ******************/ function addLiveChatStyle() { // console.log("addLiveChatStyle() : ", location.href); const localize = { ja: { toggleAutoFocus: "チャット入力欄に自動フォーカス", toggleLeft: "左側表示を切り替え", toggleBottom: "下側表示を切り替え", toggleFiltering: "チャットフィルタを有効", toggleHeader: "ヘッダーの表示を切り替え", toggleChat: "チャットの表示を切り替え", toggleFooter: "フッターを表示を切り替え", popup: "ポップアップ", reload: "更新", config: "設定", close: "閉じる", }, en: { toggleAutoFocus: "Toggle auto focus to input field", toggleLeft: "Toggle left position", toggleBottom: "Toggle bottom position", toggleFiltering: "Toggle chat filtering", toggleHeader: "Toggle header", toggleChat: "Toggle chat", toggleFooter: "Toggle footer", popup: "Popup Single bordered window", reload: "Reload", config: "Configration", close: "Close", } }; // toggle buttons const t = localize[window.navigator.language] || localize.en, buttons = document.querySelector("#chat-buttons-container") || document.createElement("div"); buttons.id = "chat-buttons-container"; buttons.innerHTML = ` <div id="chat-left-button" class="chat-toggle-button" title="${t.toggleLeft}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <path d="M154.52,265.848l90.964,69.014c2.329,1.766,4.674,2.702,6.78,2.702c2.148,0,4.022-0.974,5.276-2.741 c1.199-1.688,1.807-3.99,1.807-6.844v-26.424c0-6.952,5.656-12.608,12.607-12.608h75.036c8.705,0,15.788-7.085,15.788-15.788 v-34.313c0-8.703-7.083-15.788-15.788-15.788h-75.036c-6.951,0-12.607-5.656-12.607-12.608v-26.425 c0-7.065-3.659-9.584-7.082-9.584c-2.106,0-4.451,0.936-6.78,2.702l-90.964,69.014c-3.416,2.59-5.297,6.087-5.297,9.849 C149.223,259.762,151.103,263.259,154.52,265.848z""></path> <path d="M256,0C114.842,0,0.002,114.84,0.002,256S114.842,512,256,512c141.158,0,255.998-114.84,255.998-256 S397.158,0,256,0z M256,66.785c104.334,0,189.216,84.879,189.216,189.215S360.334,445.215,256,445.215S66.783,360.336,66.783,256 S151.667,66.785,256,66.785z""></path> </g> </svg> </div> <div id="chat-bottom-button" class="chat-toggle-button" title="${t.toggleBottom}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <path d="M256,0C114.842,0,0.002,114.84,0.002,256S114.842,512,256,512c141.158,0,255.998-114.84,255.998-256 S397.158,0,256,0z M256,66.785c104.334,0,189.216,84.879,189.216,189.215S360.334,445.215,256,445.215S66.783,360.336,66.783,256 S151.667,66.785,256,66.785z"></path> <path d="M246.151,357.482c2.591,3.416,6.087,5.299,9.849,5.299c3.762,0,7.257-1.883,9.848-5.295l69.014-90.97 c2.665-3.513,3.393-6.945,2.046-9.655c-1.347-2.713-4.518-4.208-8.93-4.208h-26.424c-6.953,0-12.609-5.652-12.609-12.604v-75.035 c0-8.707-7.082-15.792-15.788-15.792h-34.312c-8.706,0-15.788,7.084-15.788,15.792v75.035c0,6.952-5.656,12.604-12.609,12.604 h-26.422c-4.412,0-7.586,1.495-8.93,4.208c-1.347,2.71-0.621,6.142,2.046,9.658L246.151,357.482z"></path> </g> </svg> </div> <div id="chat-filter-button" class="chat-toggle-button" title="${t.toggleFiltering}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <polygon points="4.263,0 4.263,85.338 202.063,238.938 202.063,512 309.937,443.726 309.937,238.938 507.737,85.338 507.737,0 "></polygon> </g> </svg> </div> <div id="chat-header-button" class="chat-toggle-button" title="${t.toggleHeader}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <circle cx="256" cy="55.091" r="55.091"></circle> <circle cx="256" cy="256" r="55.091"></circle> <circle cx="256" cy="456.909" r="55.091"></circle> </g> </svg> </div> <div id="chat-chat-button" class="chat-toggle-button" title="${t.toggleChat}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <path d="M352.705,62.572h-193.41C71.321,62.572,0,133.893,0,221.855c0,87.986,71.321,159.307,159.295,159.307h152.17 c22.76,0,29.872,10.569,29.872,19.22c0,12.791-6.649,24.796-22.748,36.268c-9.969,7.101-2.128,12.779,5.69,12.779 c101.678,0,187.72-110.942,187.72-227.574C512,133.893,440.691,62.572,352.705,62.572z M135.054,252.109 c-16.722,0-30.254-13.543-30.254-30.254s13.531-30.242,30.254-30.242c16.7,0,30.232,13.531,30.232,30.242 S151.755,252.109,135.054,252.109z M256,252.109c-16.699,0-30.254-13.543-30.254-30.254s13.555-30.242,30.254-30.242 c16.7,0,30.254,13.531,30.254,30.242S272.7,252.109,256,252.109z M376.946,252.109c-16.699,0-30.23-13.543-30.23-30.254 s13.531-30.242,30.23-30.242c16.723,0,30.254,13.531,30.254,30.242S393.668,252.109,376.946,252.109z"></path> </g> </svg> </div> <div id="chat-footer-button" class="chat-toggle-button" title="${t.toggleFooter}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <path d="M498.781,74.344c-3.531-9.313-8.906-18.234-16.125-26.297c-14.797-16.5-31.359-28.594-48.813-36.484 C416.391,3.656,398.109,0,380.031,0c-27.688,0-54.797,8.5-78.719,23.156s-44.75,35.484-59.938,60.609l-0.047,0.078L13.219,472.609 l-0.031,0.016c-3.219,5.594-5.141,11.313-5.172,17.344c0,2.719,0.422,5.5,1.391,8.172c1.438,4.016,4.219,7.766,7.906,10.234 c3.656,2.5,8,3.641,12.109,3.625c2.938,0,5.766-0.563,8.453-1.469c4.031-1.438,7.719-3.688,11.109-6.656s6.484-6.625,9.281-10.969 c0.344-0.516,0.844-1.313,1.563-2.453c5.297-8.422,21.766-34.922,36.953-59.359c14.094-22.656,27.031-43.5,28.828-46.406 c0.156-0.156,0.516-0.516,0.813-0.672l0.406-0.172c0.125-0.031,0.25-0.063,0.516-0.063c0.25,0,0.688,0.031,1.406,0.25 s1.75,0.625,3.125,1.438l0.156,0.125l0.188,0.078c9.813,5.484,19,9.438,27.672,12.016c8.656,2.594,16.797,3.828,24.375,3.828 c9.594,0.016,18.297-2,25.703-5.484c5.547-2.625,10.375-6.031,14.391-9.844c6.047-5.781,10.359-12.453,13.203-19.141 c2.828-6.719,4.281-13.422,4.313-19.906c-0.031-4.453-0.656-8.828-2.703-13.281c-1.031-2.203-2.5-4.453-4.594-6.484 c-1.844-1.781-4.266-3.281-6.938-4.188c0.219-0.641,0.703-1.219,0.938-1.391l0.094-0.063c0.047,0,0.141,0.016,0.328,0.063 c5.109,1.453,14.141,3.922,25.281,6.031c11.156,2.109,24.422,3.859,38.281,3.875c10-0.016,20.328-0.922,30.422-3.484 c10.063-2.531,19.953-6.781,28.641-13.531c7.906-6.156,14.219-13.281,18.641-21.125c4.422-7.828,6.906-16.469,6.891-25.219 c0.016-5.719-1.047-11.438-3.141-16.844c-3.156-8.156-8.609-15.563-15.875-21.734c-7.266-6.203-16.328-11.266-27.078-15.109 c-2.688-0.938-4.734-2.531-5.906-3.969c-0.594-0.719-0.953-1.406-1.141-1.859c0-0.016,0-0.016,0-0.031 c0.438-0.219,1.172-0.484,2.281-0.734c1.328-0.297,3.156-0.516,5.516-0.5c3-0.016,6.844,0.344,11.516,1.266v-0.016 c11.609,2.625,22.953,3.844,33.859,3.844c36.313,0,67.875-13.5,90.406-33.844c11.25-10.172,20.297-22.063,26.594-34.953 c6.281-12.875,9.797-26.781,9.797-40.859C503.984,93.375,502.313,83.656,498.781,74.344z"></path> </g> </svg> </div> <div id="chat-popup-button" class="chat-toggle-button" on_ title="${t.popup}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="512px" height="512px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <path d="M280.781,144.391l42.047,59.125c-57.813,65.688-217.281,145.766-217.281,145.766 c161.422,12.406,285.594-40.672,285.594-40.672l42.047,68.313L512,144.391H280.781z"></path> <polygon points="296.453,393.547 296.453,418.984 68.297,418.984 68.297,93.031 364.75,93.031 364.75,24.734 0,24.734 0,487.266 364.75,487.266 364.75,418.563 349.375,393.547 "></polygon> </g> </svg> </div> <div id="chat-reload-button" class="chat-toggle-button" on_ title="${t.reload}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <path d="M446.025,92.206c-40.762-42.394-97.487-69.642-160.383-72.182c-15.791-0.638-29.114,11.648-29.752,27.433 c-0.638,15.791,11.648,29.114,27.426,29.76c47.715,1.943,90.45,22.481,121.479,54.681c30.987,32.235,49.956,75.765,49.971,124.011 c-0.015,49.481-19.977,94.011-52.383,126.474c-32.462,32.413-76.999,52.368-126.472,52.382 c-49.474-0.015-94.025-19.97-126.474-52.382c-32.405-32.463-52.368-76.992-52.382-126.474c0-3.483,0.106-6.938,0.302-10.364 l34.091,16.827c3.702,1.824,8.002,1.852,11.35,0.086c3.362-1.788,5.349-5.137,5.264-8.896l-3.362-149.834 c-0.114-4.285-2.88-8.357-7.094-10.464c-4.242-2.071-9.166-1.809-12.613,0.738L4.008,182.45c-3.05,2.221-4.498,5.831-3.86,9.577 c0.61,3.759,3.249,7.143,6.966,8.974l35.722,17.629c-1.937,12.166-3.018,24.602-3.018,37.279 c-0.014,65.102,26.475,124.31,69.153,166.944C151.607,465.525,210.8,492.013,275.91,492 c65.095,0.014,124.302-26.475,166.937-69.146c42.678-42.635,69.167-101.842,69.154-166.944 C512.014,192.446,486.844,134.565,446.025,92.206z"></path> </g> </svg> </div> <div id="chat-config-button" class="chat-toggle-button" on_ title="${t.config}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <path d="M502.325,307.303l-39.006-30.805c-6.215-4.908-9.665-12.429-9.668-20.348c0-0.084,0-0.168,0-0.252 c-0.014-7.936,3.44-15.478,9.667-20.396l39.007-30.806c8.933-7.055,12.093-19.185,7.737-29.701l-17.134-41.366 c-4.356-10.516-15.167-16.86-26.472-15.532l-49.366,5.8c-7.881,0.926-15.656-1.966-21.258-7.586 c-0.059-0.06-0.118-0.119-0.177-0.178c-5.597-5.602-8.476-13.36-7.552-21.225l5.799-49.363 c1.328-11.305-5.015-22.116-15.531-26.472L337.004,1.939c-10.516-4.356-22.646-1.196-29.701,7.736l-30.805,39.005 c-4.908,6.215-12.43,9.665-20.349,9.668c-0.084,0-0.168,0-0.252,0c-7.935,0.014-15.477-3.44-20.395-9.667L204.697,9.675 c-7.055-8.933-19.185-12.092-29.702-7.736L133.63,19.072c-10.516,4.356-16.86,15.167-15.532,26.473l5.799,49.366 c0.926,7.881-1.964,15.656-7.585,21.257c-0.059,0.059-0.118,0.118-0.178,0.178c-5.602,5.598-13.36,8.477-21.226,7.552 l-49.363-5.799c-11.305-1.328-22.116,5.015-26.472,15.531L1.939,174.996c-4.356,10.516-1.196,22.646,7.736,29.701l39.006,30.805 c6.215,4.908,9.665,12.429,9.668,20.348c0,0.084,0,0.167,0,0.251c0.014,7.935-3.44,15.477-9.667,20.395L9.675,307.303 c-8.933,7.055-12.092,19.185-7.736,29.701l17.134,41.365c4.356,10.516,15.168,16.86,26.472,15.532l49.366-5.799 c7.882-0.926,15.656,1.965,21.258,7.586c0.059,0.059,0.118,0.119,0.178,0.178c5.597,5.603,8.476,13.36,7.552,21.226l-5.799,49.364 c-1.328,11.305,5.015,22.116,15.532,26.472l41.366,17.134c10.516,4.356,22.646,1.196,29.701-7.736l30.804-39.005 c4.908-6.215,12.43-9.665,20.348-9.669c0.084,0,0.168,0,0.251,0c7.936-0.014,15.478,3.44,20.396,9.667l30.806,39.007 c7.055,8.933,19.185,12.093,29.701,7.736l41.366-17.134c10.516-4.356,16.86-15.168,15.532-26.472l-5.8-49.366 c-0.926-7.881,1.965-15.656,7.586-21.257c0.059-0.059,0.119-0.119,0.178-0.178c5.602-5.597,13.36-8.476,21.225-7.552l49.364,5.799 c11.305,1.328,22.117-5.015,26.472-15.531l17.134-41.365C514.418,326.488,511.258,314.358,502.325,307.303z M281.292,329.698 c-39.68,16.436-85.172-2.407-101.607-42.087c-16.436-39.68,2.407-85.171,42.087-101.608c39.68-16.436,85.172,2.407,101.608,42.088 C339.815,267.771,320.972,313.262,281.292,329.698z"></path> </g> </svg> </div> <div id="chat-toggle-button" class="chat-toggle-button" on_ title="${t.close}"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 512 512" style="width: 16px; height: 16px; opacity: 1;" xml:space="preserve"> <g> <polygon points="512,52.535 459.467,0.002 256.002,203.462 52.538,0.002 0,52.535 203.47,256.005 0,459.465 52.533,511.998 256.002,308.527 459.467,511.998 512,459.475 308.536,256.005 "></polygon> </g> </svg> </div> `; document.querySelector("yt-live-chat-app > #contents").appendChild(buttons); // document.querySelector("#chat-focus-button").onclick = focusButtonOnClick; document.querySelector("#chat-left-button").onclick = leftButtonOnClick; document.querySelector("#chat-filter-button").onclick = filterButtonOnClick; document.querySelector("#chat-bottom-button").onclick = bottomButtonOnClick; document.querySelector("#chat-header-button").onclick = headerButtonOnClick; document.querySelector("#chat-chat-button").onclick = chatButtonOnClick; document.querySelector("#chat-footer-button").onclick = footerButtonOnClick; document.querySelector("#chat-popup-button").onclick = popupButtonOnClick; document.querySelector("#chat-reload-button").onclick = reloadButtonOnClick; document.querySelector("#chat-config-button").onclick = () => window.parent.toggleConfig(); document.querySelector("#chat-toggle-button").onclick = toggleButtonOnClick; var stylesheet = ""; // チャット stylesheet += ` yt-live-chat-renderer.yt-live-chat-app { --scrollbar-width: 0 !important; } #message.yt-live-chat-text-message-renderer, #message.yt-live-chat-paid-message-renderer { font-weight: bold; font-size: ${appendUnit(c.chatFontSize)}; position: relative; } yt-live-chat-header-renderer, yt-live-chat-renderer, yt-live-chat-message-input-renderer, yt-live-chat-ticker-renderer { background: ${c.backgroundColor}; } yt-live-chat-server-error-message { background: #fff; } #item-scroller.yt-live-chat-item-list-renderer { scrollbar-width: none; } #item-scroller.yt-live-chat-item-list-renderer::-webkit-scrollbar { display: none !important; } #item-scroller.yt-live-chat-item-list-renderer #items.yt-live-chat-item-list-renderer[no_translate_], #item-scroller.animated.yt-live-chat-item-list-renderer #items.yt-live-chat-item-list-renderer[no_translate_] { transform: translateY(0) !important; } yt-live-chat-text-message-renderer { transition: height .2s linear; } yt-live-chat-text-message-renderer[${TAG_HIDDEN}] { height: 0; opacity: 0; padding: 0; } yt-live-chat-text-message-renderer[${TAG_HIGHLIGHTED}] { position: absolute; background: #fff; right: 0; left: 0; height: auto; z-index: 100; } `; // チャットちらつき低減 if (c.disableChatFlickers) { stylesheet += `#items.yt-live-chat-item-list-renderer yt-live-chat-text-message-renderer:nth-last-child(-n + 3) { height: 0; opacity: 0; padding: 0; margin: 0; }`; } // 文字枠をつける if (c.isEnableChatOutline) { stylesheet += ` #message.yt-live-chat-text-message-renderer { color: #ffffff; letter-spacing : 3px; text-shadow : 2px 2px 1px ${c.chatOutlineColor}, -2px 2px 1px ${c.chatOutlineColor}, 2px -2px 1px ${c.chatOutlineColor}, -2px -2px 1px ${c.chatOutlineColor}, 2px 0px 1px ${c.chatOutlineColor}, 0px 2px 1px ${c.chatOutlineColor}, -2px 0px 1px ${c.chatOutlineColor}, 0px -2px 1px ${c.chatOutlineColor}; } #message.yt-live-chat-paid-message-renderer { color: #ffffff; letter-spacing : 1px; line-height: 1.1; text-shadow : 1px 1px 1px ${c.chatOutlineColor}, -1px 1px 1px ${c.chatOutlineColor}, 1px -1px 1px ${c.chatOutlineColor}, -1px -1px 1px ${c.chatOutlineColor}, 1px 0px 1px ${c.chatOutlineColor}, 0px 1px 1px ${c.chatOutlineColor}, -1px 0px 1px ${c.chatOutlineColor}, 0px -1px 1px ${c.chatOutlineColor}; }`; } // ユーザー名非表示 if (c.isHideAuthorName) { stylesheet += "#items.yt-live-chat-item-list-renderer #author-name.yt-live-chat-author-chip { display: none; }"; } // ユーザー名右側 else if (c.isAuthorNameRightSide) { stylesheet += `#content.yt-live-chat-text-message-renderer #author-name.yt-live-chat-author-chip { position: absolute; right: 10px; top: 0px; opacity: 0.7; transform: scale(0.8); }`; } // ユーザー名通常表示 else { stylesheet += `#items.yt-live-chat-item-list-renderer #author-name.yt-live-chat-author-chip { max-width: ${c.authorNameMaxWidth}px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }`; } // ユーザーサムネ非表示 if (c.isHideThumbnail) { stylesheet += `#items.yt-live-chat-item-list-renderer yt-live-chat-text-message-renderer:not([${TAG_HIGHLIGHTED}]) #author-photo { display: none !important; }`; } // メンバーバッジ非表示 if (c.isHideBadge) { stylesheet += `#items.yt-live-chat-item-list-renderer yt-live-chat-text-message-renderer:not([${TAG_HIGHLIGHTED}]) #chat-badges { display: none !important; }`; } // メンバーチャットのヘッダー非表示 if (c.simpleMemberChat) { stylesheet += `#items.yt-live-chat-item-list-renderer yt-live-chat-membership-item-renderer #card #header { display: none; }`; } // フッター非表示 stylesheet += "#panel-pages[hide_] { display: none !important; }"; if (c.isHideFooter) { document.querySelector("#panel-pages").setAttribute("hide_", ""); } else { document.querySelector("#chat-footer-button").setAttribute("on_", ""); } // ヘッダー非表示 stylesheet += "yt-live-chat-header-renderer[hide_] { display: none !important; }"; if (c.isHideHeader) { document.querySelector("yt-live-chat-header-renderer").setAttribute("hide_", ""); } else { document.querySelector("#chat-header-button").setAttribute("on_", ""); } // チャット非表示 stylesheet += `#contents[hidechat_] #ticker, #contents[hidechat_] #separator, #contents[hidechat_] #chat { display: none !important; height: 0; min-height: 0; }`; if (c.isHideChat) { document.querySelector("#contents").setAttribute("hidechat_", ""); } else { document.querySelector("#chat-chat-button").setAttribute("on_", ""); } // メンバーチャットのみ if (c.isMemberOnly) { stylesheet += `#items.yt-live-chat-item-list-renderer yt-live-chat-text-message-renderer[author-type='']:not([${TAG_HIGHLIGHTED}]) { display: none; }`; } // チャンネル用絵文字以外非表示 if (c.isHideCommonEmotes) { stylesheet += "yt-emoji-picker-renderer #category-buttons { display: none !important; }"; var emoteCategories = ["UCkszU2WH9gy1mb0dV-11UJg/CIW60IPp_dYCFcuqTgodEu4IlQ", "😀", "🐵", "🍇", "🌍", "🎃", "👓", "🏧"]; for (var i = 0; i < emoteCategories.length; i++) { stylesheet += "[aria-activedescendant='" + emoteCategories[i] + "'] { display: none; }"; } } const chat = window.parent.document.querySelector("#chat"); // チャットウィンドウ左側 if (c.chatWindowPosition.indexOf("left") >= 0) { document.querySelector("#chat-left-button").setAttribute("on_", ""); chat && chat.setAttribute("leftside_", ""); } else { document.querySelector("#chat-left-button").removeAttribute("on_"); chat && chat.removeAttribute("leftside_", ""); } // チャットウィンドウ下側 if (c.chatWindowPosition.indexOf("bottom") >= 0) { document.querySelector("#chat-bottom-button").setAttribute("on_", ""); chat && chat.setAttribute("bottomside_", ""); } else { document.querySelector("#chat-bottom-button").removeAttribute("on_"); chat && chat.removeAttribute("bottomside_"); } // チャットフィルタ if (c.enableFilter) { document.querySelector("#chat-filter-button").setAttribute("on_", ""); isEnableFiltering = true; } if (isNoTranslate()) { document.querySelector("#items.yt-live-chat-item-list-renderer").setAttribute("no_translate_", ""); } else { document.querySelector("#items.yt-live-chat-item-list-renderer").removeAttribute("no_translate_"); } if (c.isCountHeavyUser) { stylesheet += ` #content.yt-live-chat-text-message-renderer { position: relative; width: 100%; } #content.yt-live-chat-text-message-renderer::after { display: block; position: absolute; left: 0; bottom: 0; height: 4px; opacity: 0.3; background: #dc143c; content: ""; width: var(--heavy-user, 0%); max-width: 100%; z-index: -1; }`; } // ログインユーザー名 stylesheet += "#input-container yt-live-chat-author-chip { display: none; }"; // スパチャ時ポップアップ stylesheet += "yt-live-chat-product-picker-renderer { background: var(--yt-emoji-picker-category-background-color) }"; // チャット入力欄 stylesheet += `#input.yt-live-chat-message-input-renderer { background-color: var(--yt-emoji-picker-category-background-color); color: var(--yt-emoji-picker-category-color); font-weight: bold; margin-top: -4px; margin-bottom: 4px; } #buttons.yt-live-chat-message-input-renderer { position: absolute; right: 20px; top: 0; } #message-buttons.yt-live-chat-message-input-renderer { display: none; } #input-panel.yt-live-chat-renderer::after { display: none; } #avatar.yt-live-chat-message-input-renderer { margin-top: -4px; }`; // コントロールボタン stylesheet += `body { pointer-events: all; } yt-live-chat-app #chat-buttons-container { position: absolute; right: 0; bottom: 0; opacity: 0; transition: opacity .2s linear; } yt-live-chat-app:hover #chat-buttons-container { opacity: 1; } yt-live-chat-app .chat-toggle-button { display: inline-block; color: #fff; background: #8f8f8f; padding: 2px; cursor: pointer; margin-left: 3px; z-index: 2021; } yt-live-chat-app .chat-toggle-button svg { vertical-align: middle; fill: #fff; } yt-live-chat-app .chat-toggle-button[on_] { background: #1E90FF; } ytp-ad-skip-button-container { z-index: 9999999; }`; // スーパーチャット表示 // 履歴非表示 if (c.superChatViewType.indexOf("history") < 0) { stylesheet += `#ticker.yt-live-chat-renderer { display: none; }`; } // チャットヘッダー非表示 if (c.superChatViewType.indexOf("header") < 0) { stylesheet += ` #content.yt-live-chat-paid-message-renderer { border-radius: 2px; } #header.yt-live-chat-paid-message-renderer, #price-column.yt-live-chat-paid-sticker-renderer, #header-subtext.yt-live-chat-membership-item-renderer { display: none; } #author-photo.yt-live-chat-membership-item-renderer, #author-photo.yt-live-chat-membership-item-renderer img, #author-photo.yt-live-chat-paid-sticker-renderer, #author-photo.yt-live-chat-paid-sticker-renderer img, #sticker.yt-live-chat-paid-sticker-renderer img { width: 32px; height: 32px; } `; } // チャットメッセージ非表示 if (c.superChatViewType.indexOf("message") < 0) { stylesheet += `yt-live-chat-paid-message-renderer, yt-live-chat-membership-item-renderer, yt-live-chat-paid-sticker-renderer { display: none; }`; } // ガイドライン非表示 stylesheet += "yt-live-chat-viewer-engagement-message-renderer { display: none; }"; // 絵文字フィーバー stylesheet += ` .emoji.yt-live-chat-text-message-renderer[fever_] { position: absolute; display: inline-block; bottom: 0; width: 32px; height: 32px; z-index: -1; opacity: 0; animation: emotesFeverHorizontal 1s alternate infinite ease-in-out, emotesFeverVertical 4s alternate 1 ease-in; } .emoji.yt-live-chat-text-message-renderer[fever1_] { animation-delay: 0s, 0s; } .emoji.yt-live-chat-text-message-renderer[fever2_] { animation-delay: -0.4s, 0s; } .emoji.yt-live-chat-text-message-renderer[fever3_] { animation-delay: -0.8s, 0s; } .emoji.yt-live-chat-text-message-renderer[fever4_] { animation-delay: -1.2s, 0s; } .emoji.yt-live-chat-text-message-renderer[fever5_] { animation-delay: -1.6s, 0s; } @keyframes emotesFeverHorizontal { 100% { transform: translateX(var(--fever-width)) rotateZ(4deg); } } @keyframes emotesFeverVertical { 0% { opacity: 0; } 10% { opacity: 0.9; } 80% { opacity: 0.6; } 100% { opacity: 0; transform: translateY(var(--fever-height)); } } `; // スタイルを適応する var style = document.createElement("style"); style.type ="text/css"; style.innerHTML = stylesheet; document.querySelector("head").appendChild(style); } function focusButtonOnClick() { this.toggleAttribute("on_"); } function filterButtonOnClick() { isEnableFiltering = this.toggleAttribute("on_"); if (isNoTranslate()) { document.querySelector("#items.yt-live-chat-item-list-renderer").setAttribute("no_translate_", ""); } else { document.querySelector("#items.yt-live-chat-item-list-renderer").removeAttribute("no_translate_"); } } function leftButtonOnClick() { if (window.parent.document.querySelector("#chat").toggleAttribute("leftside_")) { this.setAttribute("on_", ""); } else { this.removeAttribute("on_"); } } function bottomButtonOnClick() { if (window.parent.document.querySelector("#chat").toggleAttribute("bottomside_")) { this.setAttribute("on_", ""); } else { this.removeAttribute("on_"); } } function headerButtonOnClick() { if (document.querySelector("yt-live-chat-header-renderer").toggleAttribute("hide_")) { this.removeAttribute("on_"); } else { this.setAttribute("on_", ""); } return false; } function chatButtonOnClick() { if (document.querySelector("yt-live-chat-app > #contents").toggleAttribute("hidechat_")) { this.removeAttribute("on_"); } else { this.setAttribute("on_", ""); } return false; } function footerButtonOnClick() { if (document.querySelector("#panel-pages").toggleAttribute("hide_")) { this.removeAttribute("on_"); } else { this.setAttribute("on_", ""); } return false; } function popupButtonOnClick() { popupSingleBorderWindow(window.parent.location.href); if (window.parent != window) { parent.close(); } return false; } function reloadButtonOnClick() { location.reload(); return false; } function toggleButtonOnClick() { window.top.document.querySelector("#show-hide-button #button").click(); return false; } // チャットウィンドウのみから使用する // 設定ポップアップとチャットウィンドウとの干渉を解決する var fixPlayerConflictTimer; function fixPlayerConflict() { var chat = window.parent.document.querySelector("#chatframe"); if (!chat) return; var button = window.parent.document.querySelector(".ytp-settings-button"); if (button && button.getAttribute("aria-expanded") == "true") { chat.style.pointerEvents = "none"; chat.style.opacity = 0; // setInterval を多重起動しない clearTimeout(fixPlayerConflictTimer); fixPlayerConflictTimer = setInterval(fixPlayerConflict, 500); } else { chat.style.pointerEvents = "all"; chat.style.opacity = c.windowOpacity; clearTimeout(fixPlayerConflictTimer); } } // 動的にレイアウトを調整する // ウィンドウのリサイズなどに合わせて呼び出す function updateDynamicLayout() { if (c.fullscreenMode && document.querySelector("ytd-watch-flexy:not([hidden])") && document.querySelector("ytd-watch-flexy[theater]")) { setFullscreenStyle(); } else { removeFullscreenStyle(); } updateChatWindowHeight(); updateStickyChat(); } // チャットウィンドウの高さを調整する function updateChatWindowHeight() { // チャットウィンドウ内からでも動くようにする if (window != window.parent) { window.parent.updateChatWindowHeight && window.parent.updateChatWindowHeight(); return; } var height = 0; const player = document.querySelector("#player-theater-container"), controls = document.querySelector(".ytp-chrome-bottom"); if (player) { height += player.clientHeight; } if (controls) { height -= controls.clientHeight; } var chatframe = document.querySelector("#chatframe"); if (chatframe) { if (height == 0) { delete chatframe.style.maxHeight; } else { chatframe.style.maxHeight = `${height}px`; } } var chat = document.querySelector("#chat"); if (chat) { if (height == 0) { delete chat.style.maxHeight; } else { chat.style.maxHeight = `${height}px`; } } } // 強調表示をやめる function unhighlightChat(chat) { // console.log("unhighlightChat()"); chat.removeAttribute(TAG_HIDDEN); chat.removeAttribute(TAG_HIGHLIGHTED); delete chat.style.bottom; updateStickyChat(); } // チャットを強調表示する function highlightChat(chat) { var time = new Date().getTime(); chat.removeAttribute(TAG_HIDDEN); chat.setAttribute(TAG_HIGHLIGHTED, time); updateStickyChat(); setTimeout(function () { // 終了前に要素が再利用されたら time が変わっている if (chat.getAttribute(TAG_HIGHLIGHTED) == time) { unhighlightChat(chat); // 要素の高さの更新が遅れることがある?再試行 setTimeout(updateStickyChat, 100); } }, c.moderatorChatTimeout * 1000); } // 強調表示したチャットの位置を更新する function updateStickyChat() { var chats = document.querySelectorAll(`yt-live-chat-text-message-renderer[${TAG_HIGHLIGHTED}]`); if (!chats) return; var sum = 0; for (var i = chats.length - 1; i >= 0; i--) { chats[i].style.bottom = sum + "px"; sum += chats[i].offsetHeight; } } /** ***************** * Float Window * ******************/ function addFloatWindowStyle() { // console.log("addFloatWindowStyle()", location.href); // #chat default selector // ytd-watch-flexy[flexy_][js-panel-height_] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy var stylesheet = ""; stylesheet += ` .ytp-offline-slate-background { background-size: contain; } ytd-watch-flexy[flexy][js-panel-height_][fullscreen] #player-theater-container.ytd-watch-flexy, ytd-watch-flexy[flexy][js-panel-height_][theater] #player-theater-container.ytd-watch-flexy { min-height: unset; } ytd-watch-flexy[flexy][js-panel-height_][fullscreen] #chat, ytd-watch-flexy[flexy][js-panel-height_][theater] #chat, ytd-watch-flexy[flexy_][js-panel-height_][fullscreen] #chat, ytd-watch-flexy[flexy_][js-panel-height_][theater] #chat { transition: opacity .2s linear; min-height: unset; } ytd-watch-flexy[flexy][js-panel-height_][fullscreen] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy, ytd-watch-flexy[flexy][js-panel-height_][theater] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy { position: absolute; border: 0; right: 8px; width: ${appendUnit(c.windowWidth)}; height: 100%; min-height: 0px; top: calc(var(--ytd-watch-flexy-masthead-height)); z-index: 2019; /* #masthead is 2020 */ box-sizing: border-box; margin: 0; pointer-events: none; } ytd-app[masthead-hidden_] ytd-watch-flexy[flexy][js-panel-height_][fullscreen] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy, ytd-app[masthead-hidden_] ytd-watch-flexy[flexy][js-panel-height_][theater] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy { top: 0; } ytd-watch-flexy[flexy][js-panel-height_][fullscreen] #chat.ytd-watch-flexy[leftside_]:not([collapsed]).ytd-watch-flexy, ytd-watch-flexy[flexy][js-panel-height_][theater] #chat.ytd-watch-flexy[leftside_]:not([collapsed]).ytd-watch-flexy { left: 8px; rignt: unset; } ytd-watch-flexy[flexy][js-panel-height_][fullscreen] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy #chatframe, ytd-watch-flexy[flexy][js-panel-height_][theater] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy #chatframe { position: absolute; top: 0; height: ${appendUnit(c.windowHeight)}; padding: 8px; box-sizing: border-box; pointer-events: all; opacity: ${c.windowOpacity}; } ytd-watch-flexy[flexy][js-panel-height_][fullscreen] #chat.ytd-watch-flexy[bottomside_]:not([collapsed]).ytd-watch-flexy #chatframe, ytd-watch-flexy[flexy][js-panel-height_][theater] #chat.ytd-watch-flexy[bottomside_]:not([collapsed]).ytd-watch-flexy #chatframe { top: unset; bottom: 0; } ytd-watch-flexy[flexy][js-panel-height_][fullscreen] #chat.ytd-watch-flexy[collapsed].ytd-watch-flexy #chatframe, ytd-watch-flexy[flexy][js-panel-height_][theater] #chat.ytd-watch-flexy[collapsed].ytd-watch-flexy #chatframe { height: 0; } ytd-app[masthead-hidden_] #masthead-container.ytd-app { z-index: -1; } `; // show hide button stylesheet += ` #chat:not([collapsed]) #show-hide-button { display: none; } #show-hide-button.ytd-live-chat-frame > ytd-toggle-button-renderer.ytd-live-chat-frame { background: ${c.backgroundColor}; transition: background .2s ease; } #show-hide-button.ytd-live-chat-frame > ytd-toggle-button-renderer.ytd-live-chat-frame:hover { background: #fffc; } `; // config stylesheet += ` #${configAreaID} { display: none; position: fixed; right: 8px; top: 8px; font-size: 13px; line-height: 1.75rem; border: solid 1px #808080; padding: 16px; background: #fff; z-index: 10000; transition: height .25s ease-in-out; max-height: 90%; overflow-y: auto; } #${configAreaID}.open { display: block; } #${configAreaID}::after { position: absolute; font-size: 10px; color: #acacac; top: 3px; right: 8px; content: "Youtube live, Simple Chat Stylizer"; } #${configAreaID} header { font-size: 16px; margin: 8px 0 4px 4px; } #${configAreaID} input { display: inline-block; width: 60px; min-height: 20px; text-align: center; vertical-align: middle; box-sizing: border-box; margin: 1px 8px 1px 0; } #${configAreaID} .dummy { display: inline-block; width: 60px; min-height: 20px; box-sizing: border-box; margin: 1px 8px 1px 0; } #${configAreaID} label { display: inline-block; min-height: 20px; vertical-align: middle; box-sizing: border-box; } #${configAreaID} label.for-select { margin-right: 8px; } #${configAreaID} textarea { min-height: 2rem; max-width: 1000px; min-width: 100px; width: 100%; display: block; } #${configAreaID} button { display: inline-block; width: 200px; height: 32px; margin: 3px 0; } #${configAreaID}-toggle { padding: 0 4px; } #${configAreaID}-toggle::after { display: block; content: "SCS"; cursor: pointer; padding: 0 4px; line-height: var(--yt-button-icon-size, 40px); } `; // single border window stylesheet += `ytd-app[masthead-hidden_] { --ytd-masthead-height: 0px !important; }`; const initTimer = setInterval(() => { const styleID = "yt-live-chat-float-on-screen-stylesheet"; const style = document.createElement("style"); style.type = "text/css"; style.id = styleID; style.innerHTML = stylesheet; const head = document.querySelector("head"); if (head) { head.appendChild(style); clearInterval(initTimer); } }, 500); } // チャット入力欄にフォーカスする function focusInputField() { var frame = document.querySelector("#chatframe"), input; if (frame && frame.contentDocument.querySelector("#chat-focus-button[on_]")) { input = frame.querySelector("yt-live-chat-text-input-field-renderer #input[contenteditable]"); } else if (document.querySelector("#chat-focus-button[on_]")) { input = document.querySelector("yt-live-chat-text-input-field-renderer #input[contenteditable]"); } if (input) { setTimeout(() => input.focus(), 50); } } function appendUnit(value) { if (value.match(/(%|cm|mm|Q|in|pt|px|em|ex|rem|ch|lh|vm|vh|vmin|vmax)/)) { return value; } else { return value + "px"; } } /** ***************** * Single Bordered Window * ******************/ var fullscreenSytleTimer = 0; function setFullscreenStyle() { // console.log("update single border style"); fullscreenSytleTimer = clearTimeout(setFullscreenStyle); fullscreenSytleTimer = setTimeout(function () { var app = document.querySelector("ytd-app"); app.setAttribute("masthead-hidden_", ""); var player = document.querySelector("#movie_player"); // player.classList.add("ytd-fullscreen"); // player.classList.add("ytd-big-mode"); var flexy = document.querySelector("ytd-watch-flexy"); flexy.setAttribute("fullscreen", ""); // flexy.setAttribute("theater", ""); // flexy.setAttribute("theater-requested_", ""); }, 300); } function removeFullscreenStyle() { var app = document.querySelector("ytd-app"); app.removeAttribute("masthead-hidden_", ""); var player = document.querySelector("#movie_player"); // player.classList.add("ytd-fullscreen"); // player.classList.add("ytd-big-mode"); var flexy = document.querySelector("ytd-watch-flexy"); flexy.removeAttribute("fullscreen", ""); // flexy.setAttribute("theater", ""); // flexy.setAttribute("theater-requested_", ""); } function popupSingleBorderWindow(url) { url = url || location.href; window.open(url, url, SINGLE_WINDOW_PARAMS); } })();