// ==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.dpdns.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 () {
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);
}
})();