🎉 欢迎访问GreasyFork.Org 镜像站!本镜像站由公众号【爱吃馍】搭建,用于分享脚本。联系邮箱📮

Greasy fork 爱吃馍镜像

Youtube live, Simple Chat Stylizer

Display chat window on stream screen and apply custom stylesheet

As of 2020-12-11. See the latest version.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

🚀 安装遇到问题?关注公众号获取帮助

公众号二维码

扫码关注【爱吃馍】

回复【脚本】获取最新教程和防失联地址

// ==UserScript==
// @name         Youtube live, Simple Chat Stylizer
// @name:ja      Youtube live, シンプルチャットスタイル
// @description  Display chat window on stream screen and apply custom stylesheet
// @description:ja チャットウィンドウを配信画面上に配置し、カスタムスタイルを適応する
// @version      1.3.2
// @author       You
// @match        https://www.youtube.com/*
// @grant        none
// @run-at       document-end
// @nowrap
// @namespace http://tampermonkey.net/
// ==/UserScript==

(function() {
    'use strict';
    var configAreaID = "simple-chat-stylizer-config";
    var configKey = "simple-chat-stylizer";
    var SINGLE_WINDOW_PARAMS = "toolber=no,menubar=no,scrollbar=no,titlebar=no,location=no,directories=no,status=no,resizable=yes";

    // config object
    var c = {
        backgroundColor: "#fff",
        chatFontSize: "13",
        isEnableCharFrame: false,
        isHideAuthorName: true,
        isAuthorNameRightSide: true,
        authorNameMaxWidth: "100",
        isHideThumbnail: false,
        isHideBadge: false,
        isHideHeader: true,
        isHideFooter: false,
        isHideCommonEmotes: true,
        isFixModerator: true,
        moderatorChatTimeout: "20",
        windowWidth: "430",
        isWindowWidthRatio: false,
        windowHeight: "720",
        isWindowHeightRatio: false,
        windowIsFromLeft: false,
        windowIsFromBottom: false,
        windowOpacity: "0.9",
        enableFilter: true,
        chatFilter: "",
    };
    var isEnableFiltering = false;

    window.addEventListener("load", onLoaded);
    return;

    function onLoaded() {
        if (location.host == "www.youtube.com") {
            // チャットウィンドウ
            if (location.pathname.indexOf("/live_chat") == 0) {
                loadConfig();
                addLiveChatStyle();
                updateChatWindowHeight();

                setTimeout(setupChatObservation, 1000);
                window.addEventListener("focus", focusInputField);
            } else if (location.pathname.indexOf("/embed") == 0) {
                // blank
            } else {
                // チャットウィンドウからも呼び出すのでグローバルにも定義する
                window.updateChatWindowHeight = updateChatWindowHeight;

                loadConfig();
                if (!document.querySelector("#" + configAreaID)) {
                    addConfigArea();
                }
                addFloatWindowStyle();

                window.addEventListener("resize", () => {
                    if (document.querySelector("[is-watch-page]") && location.href.indexOf("singleborderwindow_") >= 0) {
                        refreshSingleBorderStyle();
                    }
                    updateChatWindowHeight();
                });

                if (document.querySelector("[is-watch-page]") && location.href.indexOf("singleborderwindow_") >= 0) {
                    setTimeout(refreshSingleBorderStyle, 2000);
                }
            }
        }
    }

    /** *****************
     * Config
     * ******************/
    function addConfigArea() {
        // console.log("addConfigArea()");

        const localize = {
            ja: {
                configHeader: "Youbute live, シンプルチャットスタイル 設定",
                backgroundColor: "背景色",
                fontSize: "文字サイズ",
                chatOutline: "文字枠を有効化する",
                hideAuthorName: "チャット投稿者名を非表示にする",
                authorNameIsRight: "投稿者名を右側に表示する",
                autnorNameMaxWidth: "投稿者名の最大横幅(長すぎる場合は省略します)",
                hideThumbnail: "ユーザーアイコンを非表示にする",
                hideBadge: "メンバーバッヂを非表示にする",
                hideHeader: "ヘッダーを非表示にする",
                hideFooter: "フッターを非表示にする (チャットができなくなります)",
                hideCommonEmotes: "チャンネル専用絵文字以外を非表示にする",
                highlightModerator: "モデレーターを強調表示する(オーナーは常時)",
                highlightTimeout: "強調表示する時間 (秒)",
                windowWidth: "チャットウィンドウの横幅 (px/%)",
                windowWidthRatio: "横幅の単位に比率 (%) を使用する",
                windowHeight: "チャットウィンドウの高さ (px/%)",
                windowHeightRatio: "高さの単位に比率 (%) を使用する",
                windowIsFromLeft: "チャットウィンドウを左側に表示する",
                windowIsFromBottom: "チャットウィンドウを下側に表示する",
                windowOpacity: "チャットウィンドウの透明度 (0~1で指定/0は透明)",
                chatFilter: "チャットフィルタ (正規表現が使えます/一行に一項目)",
                save: "保存",
            },
            en: {
                configHeader: "Simple chat stylizer Config",
                backgroundColor: "Background color",
                fontSize: "Font size",
                chatOutline: "Enabled chat outline",
                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",
                hideHeader: "Hide header panel",
                hideFooter: "Hide footer panel (you can't chat)",
                hideCommonEmotes: "Hide emotes except for channel",
                highlightModerator: "Highlight moderator chat (Owner chat is always)",
                highlightTimeout: "Time period that highlight (sec)",
                windowWidth: "Width of Chat window (px/%)",
                windowWidthRatio: "Use ratio for width unit (%)",
                windowHeight: "Height of Chat window (px/%)",
                windowHeightRatio: "Use ratio for height unit (%)",
                windowIsFromLeft: "Window position is form bottom",
                windowIsFromBottom: "Window position is form bottom",
                windowOpacity: "Opacity of Chat window (Assign value of 0 to 1 / 0 is transport)",
                chatFilter: "Chat filter (Can use regix / Input a filter each one line)",
                save: "Save",
            }
        }

        const t = localize[window.navigator.language] || localize.en,
              config = document.querySelector("#" + configAreaID) || document.createElement("div");
        config.innerHTML = `
        <form id="simple-chat-stylizer-config">
            <div style="text-align: center;">${t.configHeader}</div>
            <input type="text" name="backgroundColor" value="#fff8">
            <label>${t.backgroundColor}</label><br />
            <input type="text" name="chatFontSize" value="13">
            <label>${t.fontSize}</label><br />
            <input type="checkbox" name="isEnableCharFrame">
            <label>${t.chatOutline}</label><br />
            <input type="checkbox" name="isHideAuthorName" checked>
            <label>${t.hideAuthorName}</label><br />
            <input type="checkbox" name="isAuthorNameRightSide" checked>
            <label>${t.authorNameIsRight}</label><br />
            <input type="text" name="authorNameMaxWidth" value="100">
            <label>${t.autnorNameMaxWidth}</label><br />
            <input type="checkbox" name="isHideThumbnail">
            <label>${t.hideThumbnail}</label><br />
            <input type="checkbox" name="isHideBadge">
            <label>${t.hideBadge}</label><br />
            <input type="checkbox" name="isHideHeader" checked>
            <label>${t.hideHeader}</label><br />
            <input type="checkbox" name="isHideFooter">
            <label>${t.hideFooter}</label><br />
            <input type="checkbox" name="isHideCommonEmotes" checked>
            <label>${t.hideCommonEmotes}</label><br />
            <input type="checkbox" name="isFixModerator" checked>
            <label>${t.highlightModerator}</label><br />
            <input type="text" name="moderatorChatTimeout" value="20">
            <label>${t.highlightTimeout}</label><br />
            <input type="text" name="windowWidth" value="430">
            <label>${t.windowWidth}</label><br />
            <input type="checkbox" name="isWindowWidthRatio">
            <label>${t.windowWidthRatio}</label><br />
            <input type="text" name="windowHeight" value="720">
            <label>${t.windowHeight}</label><br />
            <input type="checkbox" name="isWindowHeightRatio">
            <label>${t.windowHeightRatio}</label><br />
            <input type="checkbox" name="windowIsFromLeft">
            <label>${t.windowIsFromLeft}</label><br />
            <input type="checkbox" name="windowIsFromBottom">
            <label>${t.windowIsFromBottom}</label><br />
            <input type="text" name="windowOpacity" value="0.9">
            <label>${t.windowOpacity}</label><br />
            <input type="checkbox" name="enableFilter">
            <label>${t.chatFilter}</label><br />
            <textarea name="chatFilter" rows="3" cols="20" value="" ></textarea><br />
            <button style="width: unset;" id="simple-chat-stylizer-config-save">${t.save}</button>
        </form>
        `;

        var waitToLoadWatchPageTimer = setInterval(() => {
            if (!document.querySelector("ytd-watch-flexy")) {
                return;
            }

            clearInterval(waitToLoadWatchPageTimer);

            document.querySelector("ytd-watch-flexy").insertBefore(config, document.querySelector("#columns"));
            document.querySelector("#simple-chat-stylizer-config-save").onclick = saveButtonOnClick;

            // apply config data to form elements
            var $params = document.querySelectorAll(`#${configAreaID} input, #${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;
                    }
                }
            }
        }, 500);
    }

    function loadConfig() {
        Object.assign(c, JSON.parse(localStorage[configKey] || "{}"));

        // console.log("loaded config", c);
    }

    function updateConfigFromForm() {
        var $params = document.querySelectorAll(`#${configAreaID} input, #${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 saveButtonOnClick() {
        updateConfigFromForm();
        addFloatWindowStyle();
        document.querySelector("#chatframe").contentWindow.location.reload();

        return false;
    }

    function singleBorderWindowButtonOnClick() {
        popupSingleBorderWindow();

        return false;
    }

    /** *****************
     * Chat Filtering
     * ******************/
    function setupChatObservation() {
        const filter = new RegExp(c.chatFilter.split(/[\r\n]+/)
                                  .filter((x) => (x || "").trim("\n").trim("\r") != "")
                                  .map((x) => "(" + x + ")").join("|"));
        // console.log("initChatFilter()", filter);

        const observer = new MutationObserver((mutations) => {
            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;

                    // モデレータチャット
                    var type = chat.getAttribute("author-type");
                    if (type == "owner" || (c.isFixModerator && type == "moderator")) {
                        if (chat.hidden_) {
                            chat.setAttribute("show_", "");
                            chat.removeAttribute("hidden_");
                        }
                        highlightChat(chat);
                    }

                    // チャットフィルタ
                    const msg = chat.querySelector("#message");
                    if (isEnableFiltering && msg && msg.innerText.match(filter)) {
                        if (!chat.hidden_) {
                            chat.removeAttribute("show_");
                            chat.setAttribute("hidden_", "");
                        }
                        // console.log("filtered", chat.innerText);
                    } else {
                        // チャット要素は使いまわされてる
                        // @see https://greasyfork.org/ja/scripts/409664-chat-filter-for-youtube-live/code
                        if (chat.hidden_) {
                            chat.setAttribute("show_", "");
                            chat.removeAttribute("hidden_");
                        }
                    }
                });
            });
        });

        observer.observe(document.querySelector("#chat #items"), { childList: true });
    }

    /** *****************
     * Chat
     * ******************/
    function focusInputField() {
        if (document.querySelector("#chat-focus-button[on_]")) {
            document.querySelector("#input").focus();
        }
    }

    function addLiveChatStyle() {
        // console.log("addLiveChatStyle() : ", location.href);

        const localize = {
            ja: {
                toggleAutoFocus: "チャット入力欄に自動フォーカス",
                toggleLeft: "左側表示を切り替え",
                toggleBottom: "下側表示を切り替え",
                toggleFiltering: "チャットフィルタを有効",
                toggleHeader: "ヘッダーの表示を切り替え",
                toggleChat: "チャットの表示を切り替え",
                toggleFooter: "フッターを表示を切り替え",
                popup: "ポップアップ",
                reload: "更新",
                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",
                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-focus-button" class="chat-toggle-button" title="${t.toggleAutoFocus}">
<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="48.002,48 128.002,48 128.002,0 0.002,0 0.002,128 48.002,128 	"></polygon>
	<polygon points="383.998,0 383.998,48 463.998,48 463.998,128 511.998,128 511.998,0 	"></polygon>
	<polygon points="463.998,464 383.998,464 383.998,512 511.998,512 511.998,384 463.998,384 	"></polygon>
	<polygon points="48.002,384 0.002,384 0.002,512 128.002,512 128.002,464 48.002,464 	"></polygon>
	<path d="M204.959,176.07c-0.484-1.461-1.461-2.43-3.156-2.43H171.24c-1.695,0-2.664,0.969-3.149,2.43l-56.758,160.086
		c-0.484,1.454,0,2.422,1.695,2.422h31.774c1.695,0,2.672-0.726,3.156-2.422l9.219-28.867h57.726l9.453,28.867
		c0.493,1.696,1.461,2.422,3.157,2.422h31.531c1.703,0,2.187-0.969,1.703-2.422L204.959,176.07z M167.116,276.969l18.679-57.726
		h0.726l18.68,57.726H167.116z"></path>
	<path d="M381.037,242.039H321.85c-0.969,0-1.454-0.485-1.454-1.453v-34.688c0-0.969,0.485-1.453,1.454-1.453h71.07
		c1.454,0,2.422-0.969,2.422-2.422V176.07c0-1.461-0.969-2.43-2.422-2.43H287.17c-1.461,0-2.43,0.969-2.43,2.43v160.086
		c0,1.454,0.969,2.422,2.43,2.422h30.797c1.461,0,2.43-0.969,2.43-2.422v-62.094c0-0.976,0.485-1.461,1.454-1.461h59.187
		c1.453,0,2.422-0.969,2.422-2.422v-25.711C383.459,243.016,382.49,242.039,381.037,242.039z"></path>
</g>
</svg>
</div>
<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>
	<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-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-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-toggle-button").onclick = toggleButtonOnClick;

        // 強調表示
        var highlight = document.createElement("div");
        highlight.id = "highlight-chat-container";
        document.querySelector("yt-live-chat-item-list-renderer #contents").appendChild(highlight);

        var style = document.createElement("style");
        var stylesheet = "";

        // bold, font-size
        stylesheet += `#message.yt-live-chat-text-message-renderer { font-weight: bold; font-size: ${c.chatFontSize}px; }
                       yt-live-chat-header-renderer, yt-live-chat-renderer,
                       yt-live-chat-message-input-renderer, yt-live-chat-ticker-renderer { background: ${c.backgroundColor}; }
                       #item-scroller { scrollbar-width: none; }
                       yt-live-chat-text-message-renderer {
                           transition: height .5s linear,
                                       padding-top .5s linear,
                                       padding-bottom .5s linear; }
                       yt-live-chat-text-message-renderer[hidden_] {
                           opacity: 0; height: 0; padding-top: 0; padding-bottom: 0; }
                       yt-live-chat-text-message-renderer[show_] { transition: none; }
`;

        // 文字枠をつける
        if (c.isEnableCharFrame) {
            stylesheet += `
               #message.yt-live-chat-text-message-renderer {
                   color: #ffffff; letter-spacing : 4px;
                   text-shadow : 2px 2px 1px #003366, -2px 2px 1px #003366, 2px -2px 1px #003366, -2px -2px 1px #003366,
                                 2px 0px 1px #003366, 0px  2px 1px #003366, -2px  0px 1px #003366, 0px -2px 1px #003366;
               }`;
        }
        // ユーザー名非表示
        if (c.isHideAuthorName) {
            stylesheet += "#content #author-name.yt-live-chat-author-chip { display: none; }";
        }
        // ユーザー名右側
        else if (c.isAuthorNameRightSide) {
            stylesheet += `#content #author-name.yt-live-chat-author-chip {
                               position: absolute;
                               right: 10px; top: 0px;
                               opacity: 0.7; transform: scale(0.8); }`;
        }
        // ユーザー名通常表示
        else {
            stylesheet += `#content #author-name.yt-live-chat-author-chip {
                               max-width: ${c.authorNameMaxWidth}px;
                               white-space: nowrap;
                               overflow: hidden;
                               text-overflow: ellipsis; }`;
        }
        // ユーザーサムネ非表示
        if (c.isHideThumbnail) {
            stylesheet += "#author-photo { display: none !important; }";
        }
        // メンバーバッジ非表示
        if (c.isHideBadge) {
            stylesheet += "#chat-badges { display: none !important; }";
        }
        // フッター非表示
        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.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; }";
            }
        }
        // チャットウィンドウ左側
        if (c.windowIsFromLeft) {
            document.querySelector("#chat-left-button").setAttribute("on_", "");
            window.parent.document.querySelector("#chat").setAttribute("leftside_", "");
        }
        // チャットウィンドウ下側
        if (c.windowIsFromBottom) {
            document.querySelector("#chat-bottom-button").setAttribute("on_", "");
            window.parent.document.querySelector("#chat").setAttribute("bottomside_", "");
        }
        // チャットフィルタ
        if (c.enableFilter) {
            document.querySelector("#chat-filter-button").setAttribute("on_", "");
            isEnableFiltering = true;
        }

        // your name style
        stylesheet += "#input-container yt-live-chat-author-chip { display: none; }";

        // super chat background style
        stylesheet += "yt-live-chat-product-picker-renderer { background-color: #fff; }";

        // chat input field style
        stylesheet += `#input.yt-live-chat-message-input-renderer { background-color: #fffa; font-weight: bold; margin-top: -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; }`;

        // moderator/owner chats is fixed to bottom in chat window style
        stylesheet += `#highlight-chat-container {
                       position: absolute;
bottom: 0; left: 0; right: 0;
                       background: #fff;
                       z-index: 2020; }`;

        // toggle buttons style
        stylesheet += `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;
            }`;

        // apply style
        style.innerText = stylesheet;
        document.body.appendChild(style);
    }

    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) {
            chatframe.style.maxHeight = height + "px";
        }
        var chat = document.querySelector("#chat");
        if (chat) {
            chat.style.maxHeight = height + "px";
        }
    }

    function focusButtonOnClick() {
        this.toggleAttribute("on_");
    }

    function filterButtonOnClick() {
        isEnableFiltering = this.toggleAttribute("on_");
    }

    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);

        return false;
    }

    function reloadButtonOnClick() {
        location.reload();

        return false;
    }

    function toggleButtonOnClick() {
        window.top.document.querySelector("#show-hide-button #button").click();

        return false;
    }

    function highlightChat(chat) {
        document.querySelector("#highlight-chat-container").appendChild(chat);
        setTimeout(function () {
            chat.setAttribute("hidden_", "");
            document.querySelector("#chat #item-offset").appendChild(chat);
        }, c.moderatorChatTimeout * 1000);
    }

    /** *****************
     * 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 += `
        ytd-watch-flexy[flexy_][js-panel-height_][theater] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy {
            position: absolute; border: 0;
            right: 8px;
            width: ${c.windowWidth}${c.isWindowWidthRatio ? "%" : "px"};
            height: 100%;
            min-height: 0px;
            top: calc(var(--ytd-watch-flexy-masthead-height));
            z-index: 2019; /* #masthead is 2020 */
            box-sizing: border-box;
        }
        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_][theater] #chat.ytd-watch-flexy:not([collapsed]).ytd-watch-flexy #chatframe {
            position: absolute;
            top: 0;
            height: ${c.windowHeight}${c.isWindowHeightRatio ? "%" : "px"};
            padding: 8px;
            box-sizing: border-box;
        }
        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_][theater] #chat.ytd-watch-flexy[collapsed].ytd-watch-flexy #chatframe {
            height: 0;
        }
`;

        // 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 += `
#simple-chat-stylizer-config { max-height: 2rem; transition: max-height .25s ease-in; overflow: hidden; max-width: 1500px; margin: 0 auto; font-size: 12px; }
#simple-chat-stylizer-config:hover { max-height: 1000px; }
#simple-chat-stylizer-config input { width: 30px; text-align: center; margin-right: 8px; }`;

        // single border window
        stylesheet += `ytd-app[singleborderwindow_] { --ytd-app-fullerscreen-scrollbar-width: 17px; --ytd-masthead-height: 0px !important; }`;

        var oldStyle = document.querySelector("#yt-live-chat-float-on-screen-stylesheet");
        if (oldStyle) oldStyle.remove();

        var style = document.createElement("style");
        style.id = "yt-live-chat-float-on-screen-stylesheet";
        style.innerText = stylesheet;
        document.body.appendChild(style);
    }

    /** *****************
     * Single Bordered Window
     * ******************/
    var refreshSingleBorderSytleTimer = 0;
    function refreshSingleBorderStyle() {
        refreshSingleBorderSytleTimer = clearTimeout(refreshSingleBorderSytleTimer);
        refreshSingleBorderSytleTimer = setTimeout(function () {
            document.body.classList.add("no-scroll");

            var app = document.querySelector("ytd-app");
            app.setAttribute("masthead-hidden_", "");
            app.setAttribute("scrolling_", "");
            app.setAttribute("singleborderwindow_", "");

            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", "");
        }, 300);
    }

    function popupSingleBorderWindow(url) {
        url = url || location.href;
        window.open(url.split("#")[0] + (url.indexOf("&singleborderwindow_") >= 0 ? "" : "&singleborderwindow_="), url, SINGLE_WINDOW_PARAMS);
    }
})();