// ==UserScript==
// @name Flow Youtube Chat
// @namespace FlowYoutubeChatScript
// @version 1.12.2.emoji_fixed
// @description Youtubeのチャットをニコニコ風に画面上へ流すスクリプトです(再アップ,絵文字バグ修正済み)
// @author Emubure
// @name:en Flow Youtube Chat
// @description:en Flow the chat on Youtube
// @match https://www.youtube.com/*
// @require https://code.jquery.com/jquery-3.3.1.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js
// @resource toastrCSS https://cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_getResourceText
// @noframes
// @downloadURL none
// ==/UserScript==
(function(){
GM_addStyle(GM_getResourceText('toastrCSS'))
//---ユーザー設定||デフォルトの数値---
//---User Settings||Default Value---
let USER_CONFIG = {
Lang: GM_getValue('FYC_LANG')||'FYC_EN',
Font: GM_getValue('FYC_FONT')||'',
Opacity: GM_getValue('FYC_OPACITY')||1,
Color: GM_getValue('FYC_COLOR')||'#FFFFFF',
ColorOwner: GM_getValue('FYC_COLOR_OWNER')||'#ffd600',
ColorModerator: GM_getValue('FYC_COLOR_MODERATOR')||'#5e84f1',
ColorMember: GM_getValue('FYC_COLOR_MEMBER')||'#2ba640',
Size: GM_getValue('FYC_SIZE')||1,
SizeInChatField: GM_getValue('FYC_SIZE_IN_CHATFIELD')||13,
Weight: GM_getValue('FYC_WEIGHT')||730,
WeightShadow: GM_getValue('FYC_WEIGHT_SHADOW')||1,
Limit: GM_getValue('FYC_LIMIT')||25,
Speed: GM_getValue('FYC_SPEED')||18,
MaxLength: GM_getValue('FYC_MAX')||100,
LaneNum: GM_getValue('FYC_LANE_DIV')||12,
NGWords: GM_getValue('FYC_NG_WORDS')||'',
NGRegWords: GM_getValue('FYC_NG_REG_WORDS')||'',
NGUsers: GM_getValue('FYC_NG_USERS')||''
}
//ページの要素関連
let LIVE_PAGE = {
getPlayer: ()=>{return document.getElementById('movie_player')},
getMainVideo: ()=>{return document.getElementsByClassName('video-stream html5-main-video')[0]},
getUserIcon: ()=>{return document.getElementsByClassName('style-scope ytd-topbar-menu-button-renderer no-transition')[0].lastElementChild},
getChatField: ()=>{
//現在アカウントによってはchatframeが見つからないバグが有り
let chatField
if(document.getElementById('chatframe')!==null){
chatField = document.getElementById('chatframe').contentDocument.querySelector("#items.style-scope.yt-live-chat-item-list-renderer")
}else{
chatField = document.querySelector("#items.style-scope.yt-live-chat-item-list-renderer")
}
return chatField
}
}
//コメント関連
let COMMENTS = {
getAll: ()=>{return document.getElementsByClassName('fyc_comment')},
Visibility: 'visible',
AnimState: 'running'
}
//設定関連
let SETTINGS = {
CreateComments: true,
CreateNGButtons: true,
SimpleChatField: false,
DisplayOwner: true,
DisplayModerator: true,
DisplayMember: true,
DisplayModeratorName: true,
DisplaySettingPanel: false,
NGWords: [],
NGRegWords: [],
NGUsers: [],
UserID: '',
}
//-----------立ち上がり------------
//$(window).on('load', () => {
$(document).ready(() => {
//チャット欄とプレイヤーが出るまで待つ
findChatField()
})
//URL変更検知オブザーバー
let storedHref = location.href;
const URLObserver = new MutationObserver(function(mutations){
mutations.forEach(function(mutation){
if(storedHref !== location.href){
findChatField()
storedHref = location.href
log('URL Changed', storedHref, location.href)
}
})
})
//URL監視
URLObserver.disconnect()
URLObserver.observe(document, {childList: true, subtree: true})
//リサイズ検知オブザーバー
function playerResizeObserve(){
clearInterval(playerResizeInterval_id)
let storedSize = LIVE_PAGE.getPlayer().clientWidth + LIVE_PAGE.getPlayer().clientHeight
var playerResizeInterval_id = setInterval(() => {
if(LIVE_PAGE.getPlayer().clientWidth + LIVE_PAGE.getPlayer().clientHeight !== storedSize){
clearInterval(playerResizeInterval_id)
deleteAllComments()
initialize()
}
},1000)
}
var findInterval
function findChatField(){
let FindCount = 1
clearInterval(findInterval)
findInterval = setInterval(function(){
FindCount++
if(FindCount > 180){
log('The element cannot be found')
clearInterval(findInterval)
FindCount = 0
}
if(document.getElementById('chatframe')){
if(LIVE_PAGE.getChatField() !== null && LIVE_PAGE.getPlayer() !== null){// && LIVE_PAGE.getUserIcon() !== null){
log('Found the element: ')
console.log(LIVE_PAGE.getChatField())
console.log(LIVE_PAGE.getPlayer())
//console.log(LIVE_PAGE.getUserIcon())
setTimeout(function(){//少し待つ
initialize()
}, 1000)
clearInterval(findInterval)
FindCount = 0
}
}
}, 1000)
}
function initialize(){
log('initialize...')
//変数初期化
initializeParameter()
//チャット欄監視
if(LIVE_PAGE.getChatField() !== null){
ChatFieldObserver.disconnect()
ChatFieldObserver.observe(LIVE_PAGE.getChatField(), {childList: true})
}
//プレイヤーサイズ監視
playerResizeObserve()
//CSS配置
createScriptCSS()
//コメント描画レイヤー配置
if(!document.getElementById('fyc_comment_screen')){
LIVE_PAGE.getPlayer().insertAdjacentHTML('afterbegin', '
')
}
//コメント表示切り替えボタン配置
createToggleCommentDisplayButton()
//コメント送信フォーム設置
//createCommentSubmitForm()
//イベント設置
putEvents()
//設定パネル配置
createSettingPanel()
}
function initializeParameter(){
if(GM_getValue('FYC_TOGGLE_CREATE_COMMENTS') === true || GM_getValue('FYC_TOGGLE_CREATE_COMMENTS') === undefined){
SETTINGS.CreateComments = true
}else{
SETTINGS.CreateComments = false
}
if(GM_getValue('FYC_NG_BUTTON') === true || GM_getValue('FYC_NG_BUTTON') === undefined){
SETTINGS.CreateNGButtons = true
}else{
SETTINGS.CreateNGButtons = false
}
if(GM_getValue('FYC_SIMPLE_CHAT_FIELD') === false || GM_getValue('FYC_SIMPLE_CHAT_FIELD') === undefined){
SETTINGS.SimpleChatField = false
}else{
SETTINGS.SimpleChatField = true
}
if(GM_getValue('FYC_DISPLAY_MODERATOR_NAME') === true || GM_getValue('FYC_DISPLAY_MODERATOR_NAME') === undefined){
SETTINGS.DisplayModeratorName = true
}else{
SETTINGS.DisplayModeratorName = false
}
if(GM_getValue('FYC_DISPLAY_COMMENTS') === true || GM_getValue('FYC_DISPLAY_COMMENTS') === undefined){
COMMENTS.Visibility = 'visible'
}else{
COMMENTS.Visibility = 'hidden'
}
//NG更新
if(GM_getValue('FYC_NG_WORDS') !== undefined){
SETTINGS.NGWords = USER_CONFIG.NGWords.split(/\r\n|\n/)
}
if(GM_getValue('FYC_NG_REG_WORDS') !== undefined){
SETTINGS.NGRegWords = USER_CONFIG.NGRegWords.split(/\r\n|\n/)
}
if(GM_getValue('FYC_NG_USERS') !== undefined){
SETTINGS.NGUsers = USER_CONFIG.NGUsers.split(/\r\n|\n/)
}
}
function createScriptCSS(){
//const Player = LIVE_PAGE.getPlayer()
const screenWidth = LIVE_PAGE.getPlayer().clientWidth
const screenHeight = LIVE_PAGE.getMainVideo().clientHeight
const screenY = (LIVE_PAGE.getPlayer().clientHeight - screenHeight)/2 //プレイヤー全体の高さ-実際の動画の高さ
const screenWidthLimit = 0 - screenWidth * 4//コメントを 横幅の-4倍 まで流す
const ScriptCSS = document.getElementById('fyc_style')
//既にCSSがあれば消す
if(ScriptCSS){
ScriptCSS.parentNode.removeChild(ScriptCSS)
}
let ScriptCSS_HTML = ''
ScriptCSS_HTML +=`'
document.body.insertAdjacentHTML('beforeend', ScriptCSS_HTML)
//youtubeのCSSの設定
document.getElementById('chatframe').contentDocument.querySelector('#item-scroller.animated.yt-live-chat-item-list-renderer #item-offset.yt-live-chat-item-list-renderer').style.overflow = 'unset'
//toastrの設定
toastr.options = {
'positionClass': 'toast-bottom-left',
'timeOut': '2500',
'progressBar': true,
'newestOnTop': true,
'extendedTimeOut': '1000'
}
}
//--------------コメント関連--------------
const ChatFieldObserver = new MutationObserver(function(mutations){
mutations.forEach(function(e){
let addedChats = e.addedNodes
if(addedChats.length <= 0){
return
}
for(let i = 0; i < addedChats.length; i++){
//.yt-live-chat-placeholder-item-rendererを避ける
if(addedChats[i].children.length <= 0){
continue
}
const commentData = convertChat(addedChats[i])
//const commentLaneNum = judgeLaneNum(commentData)
if(checkBannedWords(commentData) || checkBannedRegexpWords(commentData) || checkBannedUsers(commentData)){
addedChats[i].style.display='none'
continue
}else{
//youtubeのチャットは同じ要素を使い回すので、NG以外のときに表示させる処理も必要
if(addedChats[i].getElementsByClassName('style-scope yt-live-chat-paid-message-renderer').length > 0){
//スパチャの場合
addedChats[i].style.display='block'
}else{
//通常の場合
addedChats[i].style.display='flex'
}
}
if(SETTINGS.CreateComments){
findCommentsOutOfScreen()
createComment(commentData)
}
if(SETTINGS.CreateNGButtons&&addedChats[i].getElementsByClassName('owner')[0]===undefined){
createNGButton(addedChats[i], commentData.authorID)
}
simplificationCommentField(addedChats[i], SETTINGS.SimpleChatField)
deleteOldComments()
}
})
})
function calcSpeed(length){
const MAX_LENGTH = USER_CONFIG.MaxLength
let speed = 0
speed = 720/(length+30)
//最高文字数以上では速さ固定
if(length >= MAX_LENGTH){
speed = 720/(MAX_LENGTH+30)
}
//最大速度以上では速さ固定
if(speed < 720/(MAX_LENGTH+30)){
speed = 720/(MAX_LENGTH+30)
}
//ユーザー設定適用
speed = speed * (20/USER_CONFIG.Speed)
return speed
}
function createComment(commentData){
const screenHeight = LIVE_PAGE.getMainVideo().clientHeight
const commentHTML = commentData.html
const commentLength = commentData.length
const commentColor = commentData.color
const commentAuthorID = commentData.authorID
const commentIsMine = commentData.isMine
const commentIsMember = commentData.isMember
const commentSize = Math.round(((USER_CONFIG.Size-0.2) * (screenHeight/USER_CONFIG.LaneNum))*100)/100
const commentSpeed = calcSpeed(commentLength)
const commentLaneNum = judgeLaneNum(commentData)
let html = ''
html += ''
if(document.getElementsByClassName('fyc_comment_usable').length > 0){
const element = document.getElementsByClassName('fyc_comment_usable')
element[element.length-1].outerHTML = html
}else{
document.getElementById('fyc_comment_screen').insertAdjacentHTML('beforeend', html)
}
}
function createNGButton(chat, id){
//既にボタンがあれば無視
if(chat.children['content'] && chat.children['content'].children['fyc_ngbutton'])return
//スパチャは無視
if(chat.children['card'])return
let button = document.createElement('button')
button.className = 'style-scope yt-icon-button fyc_button'
button.id = 'fyc_ngbutton'
button.style = 'padding: 0px;width: 20px; height: 20px;'
button.setAttribute('aria-label','NGに入れる')
button.innerHTML =
''
button.onclick = () =>{
try{
console.log('【FYC】Added to Banned Users: '+id)
if(USER_CONFIG.NGUsers !== null){
USER_CONFIG.NGUsers += '\n'+id
}else{
USER_CONFIG.NGUsers += id
}
GM_setValue('FYC_NG_USERS', GM_getValue('FYC_NG_USERS')+'\n'+id)
document.getElementById('fyc_ngusers').value = USER_CONFIG.NGUsers
SETTINGS.NGUsers = USER_CONFIG.NGUsers.split(/\r\n|\n/)
chat.style.display='none'
toastr.success('Added Banned User: '+id)
}catch(e){
toastr.error('Error: '+e.message)
}
}
chat.children['content'].children['message'].appendChild(button)
}
//チャット欄に追加されたチャットから必要なものを抽出する
function convertChat(chat){
const MAX_LENGTH = USER_CONFIG.MaxLength
let html = ''
let length = 0
let color = USER_CONFIG.Color
let isMine = false
let isMember = false
let authorID = null
//オーナーチップの有無
if(chat.getElementsByClassName('owner')[0]){
color = USER_CONFIG.ColorOwner
}
//モデレーターチップの有無
if(chat.getElementsByClassName('moderator')[0]){
color = USER_CONFIG.ColorModerator
}
//メンバーチップの有無
if(chat.getElementsByClassName('member')[0]){
isMember = true
color = USER_CONFIG.ColorMember
}
//チャットの子要素を見ていく
let children = Array.from(chat.children)
children.some(_chat =>{
let childID = _chat.id
//テキストの場合
if(childID === 'content'){
//モデレーターの名前表示
if(chat.getElementsByClassName('moderator')[0]){
if(SETTINGS.DisplayModeratorName === true){
html += _chat.querySelector('#author-name').innerText + ': '
}
}
let text = Array.from(_chat.children).find((v) => v.id === 'message')
//正規表現で文字と画像要素を分ける
let textChildren = text.innerHTML.split(/
/g)
textChildren.some(_text => {
//絵文字の場合
if(_text.match('emoji yt-formatted-string style-scope yt-live-chat-text-message-renderer')){
let src = _text.match(/src="(\\.|[^"\\])*"/g)//
let size = Math.round(((USER_CONFIG.Size-0.2) * (LIVE_PAGE.getMainVideo().clientHeight/USER_CONFIG.LaneNum))*100)/100
html += '
'
length++
}else{//テキストの場合
html += (_text.length >= MAX_LENGTH)? _text.substr(0, MAX_LENGTH) : _text//最大文字数以上なら切り捨て
length += _text.length
}
if(length >= MAX_LENGTH)return true
})
}
//アイコンの場合(アイコンの画像URLはアカウントによって違うためこれでNGユーザー処理が出来そうだ)
if(childID === 'author-photo'){
let str = _chat.lastElementChild.getAttribute('src')||''
let result = str.split('/')
//yt3.ggpht.com/【-xxxxxxxxxxx】/AAAAAAAAAAI/AAAAAAAAAAA/【xxxxxxxxxxx】/s32-c-k-no-mo-rj-c0xffffff/photo.jpg
authorID = result[3]+result[6]
}
//スパチャの場合
if(childID === 'card'){
//通常
if(_chat.className === 'style-scope yt-live-chat-paid-message-renderer'){
let header = _chat.children[0]
let content = _chat.children[1]
let text = content.children[0].innerText
let textColor = window.getComputedStyle(header, null).getPropertyValue('background-color');//文字の色
let amountColor = window.getComputedStyle(content, null).getPropertyValue('background-color');//金額の色
let authorName = _chat.querySelector('#author-name')
let paidAmount = _chat.querySelector('#purchase-amount')||_chat.querySelector('#purchase-amount-chip')
text = (text.length >= MAX_LENGTH)? text.substr(0,MAX_LENGTH) : text
html += ''+authorName.innerText+': '
html += ''+text+''
html += ''+paidAmount.innerText+''
length += text.length + paidAmount.innerText.length
authorID = null
}
//ステッカー
if(_chat.className === 'style-scope yt-live-chat-paid-sticker-renderer'){
let textColor = window.getComputedStyle(chat, null).getPropertyValue('--yt-live-chat-paid-sticker-chip-background-color')
let amountColor = window.getComputedStyle(chat, null).getPropertyValue('--yt-live-chat-paid-sticker-background-color')
//本当ならステッカーも表示させたいが、srcが取得出来るのはimg要素が出てから0.1秒遅延があり、無理
//let sticker = _chat.querySelector('#sticker')
let authorName = _chat.querySelector('#author-name')
let paidAmount = _chat.querySelector('#purchase-amount')||_chat.querySelector('#purchase-amount-chip')
html += ''+authorName.innerText+': '
html += ''+paidAmount.innerText+''
length += paidAmount.innerText.length
authorID = null
}
}
})
let convertedComment={
html: html,
length: length,
color: color,
isMine: isMine,
isMember: isMember,
authorID: authorID
}
return convertedComment
}
//レーン判定
function judgeLaneNum(commentData){
/*
*1. 描画されてるコメントを全部見る
*2. コメントの右端がスクリーンからはみ出てたらそのコメントのdata-lane属性のレーン番号をfalseにする そうでないならtrue
*(2.5). 一つ前のコメントとの文字数差が10文字以上、かつ、一つ前のコメントのX座標がプレイヤーの横幅*0.4より右にある、という場合、コメントの判定ボックスを画面外まで引き伸ばして強制的にfalseにする(コメントが追い越して被るのを防ぐため)
*3. レーンの真偽を頭から順番に見る。falseだったら次のレーンを見て、trueだったらそのレーンに設定してbreak
*/
const screenWidth = LIVE_PAGE.getPlayer().clientWidth
const comments = COMMENTS.getAll()
const latestCommentLength = commentData.length
let acceptableLane = new Array(USER_CONFIG.LaneNum*2-1).fill(true)
//1と2
//console.log('judge: ' + commentData.html)
for(let i = 0; i <= comments.length - 1; i++){
const commentLane = comments[i].getAttribute('data-lane')
const commentX = comments[i].getBoundingClientRect().x + comments[i].getBoundingClientRect().width//コメントの右端のx座標
const commentLength = comments[i].innerText.length
//2.5(なんともアナログな調整法だけど)
let commentBoxAdjustment = 0
if(latestCommentLength - commentLength >= 3 && commentX > screenWidth * 0.8 && commentX > 0){
commentBoxAdjustment = screenWidth - (commentX) + 70
}
if(latestCommentLength - commentLength >= 10 && commentX > screenWidth * 0.4 && commentX > 0){
commentBoxAdjustment = screenWidth - (commentX) + 70
}
//コメントの右端がスクリーンからはみ出ていたらfalse
if(acceptableLane[commentLane]!==false){
acceptableLane[commentLane] = commentX + commentBoxAdjustment> screenWidth ? false : true
//console.log('acceptableLane['+commentLane+']: '+acceptableLane[commentLane])
//console.log(': '+commentX+' + '+commentBoxAdjustment+' > '+screenWidth)
}
}
//3
let resultLaneNum = 0
let i = 0
while(resultLaneNum === 0){
if(acceptableLane[i]==true){
resultLaneNum = i
i = 0
break
}else if(i > USER_CONFIG.LaneNum*3-1){//無限ループ対策
resultLaneNum = 0
i = 0
break
}else if(acceptableLane[i]==false){
resultLaneNum = 0
}
i++
}
return resultLaneNum
}
function checkBannedWords(commentData){
if(!USER_CONFIG.NGWords||!commentData.html){
return false
}
let target = commentData.html
for(let i = 0; i < SETTINGS.NGWords.length; i++){
if(target.indexOf(SETTINGS.NGWords[i]) > -1){
//console.log('Banned Words: '+commentData.html)
return true
break
}
}
return false
}
function checkBannedRegexpWords(commentData){
if(!USER_CONFIG.NGRegWords||!commentData.html){
return false
}
let target = commentData.html
for(let i = 0; i < SETTINGS.NGRegWords.length; i++){
if(SETTINGS.NGRegWords[i] === ''){continue}
let result = target.match(SETTINGS.NGWords[i])
if(result !== null){
//console.log('Banned Words(Regexp): '+commentData.html)
return true
break
}
}
return false
}
function checkBannedUsers(commentData){
if(!USER_CONFIG.NGUsers||!commentData.authorID){
return false
}
let target = commentData.authorID
for(let i = 0; i < SETTINGS.NGUsers.length; i++){
if(SETTINGS.NGUsers[i]!==''){
let result = target.match(SETTINGS.NGUsers[i])
if(result !== null){
//log(commentData.authorID+': '+commentData.html)
return true
break
}
}
}
return false
}
function findCommentsOutOfScreen(){
const comments = COMMENTS.getAll()
for(let i = comments.length-1; i >= 0; i--){
if(comments[i].getBoundingClientRect().x + comments[i].getBoundingClientRect().width <= 0){//なんかしらんけど70足りねえから足してる
//再利用
if(comments[i].className !== 'fyc_comment fyc_comment_usable'){
comments[i].className += ' fyc_comment_usable'
//console.log(comments[i])
}
}
}
}
function deleteOldComments(){
const comments = COMMENTS.getAll()
if(comments.length<=USER_CONFIG.Limit)return;
while(comments.length>USER_CONFIG.Limit){
comments[0].parentNode.removeChild(comments[0])
}
}
function deleteAllComments(){
const comments = COMMENTS.getAll()
for(let i = comments.length-1; i >= 0; i--){
comments[i].parentNode.removeChild(comments[i])
}
}
function createCommentSubmitForm(){
//既にあればやらない
if(document.getElementById('fyc_input_comment') !== null)return
//送信フォーム設置
let parent = document.getElementsByClassName('ytp-chrome-controls')[0]
let inputArea = document.createElement('div')
inputArea.id = 'fyc_input_comment'
inputArea.style = 'display: inline-block;'
inputArea.innerHTML =
`
`
parent.appendChild(inputArea)
//Enterキー押下時
$('#fyc_input_comment_textbox').on('keydown',(e)=>{
if(e.keyCode === 13){
const message = document.querySelector('#fyc_input_comment_textbox').value
document.getElementById('chatframe').contentDocument.querySelector('#input.style-scope.yt-live-chat-text-input-field-renderer').innerHTML = message
document.getElementById('chatframe').contentDocument.querySelector('#send-button.style-scope.yt-live-chat-message-input-renderer').querySelector('#button.style-scope.yt-icon-button').click()
}
})
//ショートカットキー無効化
var ignoreShortcut = (e) => {
//J,K,L,F,M,C
var list = [74,75,76,70,77,67];
if(list.indexOf(e.keyCode) != -1){
e.stopPropagation();
}
}
document.querySelector('#fyc_input_comment_textbox').onfocus = () => {
log('focus')
window.addEventListener("keydown", ignoreShortcut, true);
}
document.querySelector('#fyc_input_comment_textbox').onblur = () => {
log('blur')
window.removeEventListener("keydown", ignoreShortcut, true)
}
}
function simplificationCommentField(chat, setting){
if(setting === true){
chat.style.borderBottom='1px solid var(--yt-spec-text-secondary)'
if(chat.getElementsByClassName('style-scope yt-live-chat-paid-message-renderer').length>0)return
if(chat.getElementsByClassName('owner')[0])return
chat.children['author-photo'].style.display = 'none'
chat.querySelector('yt-live-chat-author-chip.style-scope.yt-live-chat-text-message-renderer').style.display = 'none'
}else{
chat.style.borderBottom='none'
if(chat.getElementsByClassName('style-scope yt-live-chat-paid-message-renderer').length>0)return
if(chat.getElementsByClassName('owner')[0])return
chat.children['author-photo'].style.display = 'block'
chat.querySelector('yt-live-chat-author-chip.style-scope.yt-live-chat-text-message-renderer').style.display = 'inline-flex'
chat.setAttribute('style', 'border-bottom: none;')
}
}
function changeSizeInChatField(size){
document.getElementsByClassName('yt-live-chat-renderer').fontSize = size+'px'
}
//-----------設定パネル関連-----------------
function putEvents(){
const video = document.getElementsByTagName('video')[0]
video.addEventListener('playing', changeCommentAnimState)
video.addEventListener('pause', changeCommentAnimState)
}
const changeCommentAnimState = () => {
if(document.getElementsByTagName('video')[0].paused){
document.styleSheets.item(document.styleSheets.length-1).cssRules.item(0).style.animationPlayState = 'paused'
COMMENTS.AnimState = 'paused'
}else{
document.styleSheets.item(document.styleSheets.length-1).cssRules.item(0).style.animationPlayState = 'running'
COMMENTS.AnimState = 'running'
}
}
//コメント表示切り替えボタン設置
function createToggleCommentDisplayButton(){
//既にあればやらない
if(document.getElementById('fyc_comment_visibility_button') !== null){
return
}
const parent = document.querySelector('.ytp-right-controls')
let button = document.createElement('button')
button.className = 'ytp-button fyc-comment-button'
button.id = 'fyc_comment_visibility_button'
button.type = 'button'
if(COMMENTS.Visibility === 'visible'){//コメントが表示設定だったとき
button.setAttribute('aria-label', 'コメント非表示')
button.setAttribute('title', 'コメント非表示')
button.innerHTML =
``
}else{//コメントが非表示設定だったとき
button.setAttribute('aria-label', 'コメント表示')
button.setAttribute('title', 'コメント表示')
button.innerHTML =
``
}
button.onclick = changeCommentDisplay
//挿入
parent.appendChild(button)
}
//表示切り替え処理
const changeCommentDisplay = () => {
const comments = COMMENTS.getAll()
if(COMMENTS.Visibility==='visible'){//コメントを非表示にするとき
GM_setValue('FYC_DISPLAY_COMMENTS', false)
COMMENTS.Visibility='hidden'
if(comments.length){
for(let i = comments.length-1; i >= 0; i--){
comments[i].style.visibility = 'hidden'
}
}
document.getElementById('fyc_comment_visibility_button').setAttribute('aria-label', 'コメント表示')
document.getElementById('fyc_comment_visibility_button').setAttribute('title', 'コメント表示')
document.getElementById('comment_button_path').setAttribute('fill-opacity', '0')
}else{//コメントを表示させるとき
GM_setValue('FYC_DISPLAY_COMMENTS', true)
COMMENTS.Visibility='visible'
if(comments.length){
for(let i = comments.length-1; i >= 0; i--){
comments[i].style.visibility = 'visible'
}
}
document.getElementById('fyc_comment_visibility_button').setAttribute('aria-label', 'コメント非表示')
document.getElementById('fyc_comment_visibility_button').setAttribute('title', 'コメント非表示')
document.getElementById('comment_button_path').setAttribute('fill-opacity', '1')
}
}
//設定パネル
function createSettingPanel(){
if(document.getElementsByClassName('fyc_settings')[0]!==undefined)return
const HTML_EN = `
Language(Refresh after change)
Font
Aa1あア亜
* requires [Reload]
`
const HTML_JA = `
言語(要ページ再読み込み)
フォント
Aa1あア亜
*は要[再読み込み]
`
//言語設定
let HTML = ''
if(USER_CONFIG.Lang === 'FYC_EN'){
HTML = HTML_EN
}
if(USER_CONFIG.Lang === 'FYC_JA'){
HTML = HTML_JA
}
else{
HTML = HTML_EN
}
const menuElement = document.getElementById('menu-container').getElementsByClassName('dropdown-trigger style-scope ytd-menu-renderer')[0]
menuElement.insertAdjacentHTML('beforebegin', HTML);
//設定ボタン押下時のバルーン開閉
let HIDE_OR_BLOCK_JUDGE_ELEMENT = document.getElementById('fyc-setting-panel-block-or-hide')
document.getElementById('fyc-setting-panel-button').onclick = function(){
if(SETTINGS.DisplaySettingPanel==false){
HIDE_OR_BLOCK_JUDGE_ELEMENT.style.visibility = 'visible'
SETTINGS.DisplaySettingPanel=true
}else if(SETTINGS.DisplaySettingPanel==true){
HIDE_OR_BLOCK_JUDGE_ELEMENT.style.visibility = 'hidden'
SETTINGS.DisplaySettingPanel=false
}
}
//同期
document.getElementById('fyc_check_button_to_ban').checked = SETTINGS.CreateNGButtons
document.getElementById('fyc_check_display_moderator_name').checked = SETTINGS.DisplayModeratorName
document.getElementById('fyc_button_toggle_create_comments').checked = SETTINGS.CreateComments
document.getElementById('fyc_toggle_simple_chat_field').checked = SETTINGS.SimpleChatField
document.getElementById('fyc_input_font').value = USER_CONFIG.Font
document.getElementById('fyc_input_lang').value = USER_CONFIG.Lang
//設定保存
document.getElementById('fyc_input_save_button').onclick = function(){
try{
let val = document.getElementById('fyc_input_color').value
USER_CONFIG.Color = val
GM_setValue('FYC_COLOR', val)
val = document.getElementById('fyc_input_color_owner').value
USER_CONFIG.ColorOwner = val
GM_setValue('FYC_COLOR_OWNER', val)
val = document.getElementById('fyc_input_color_moderator').value
USER_CONFIG.ColorModerator = val
GM_setValue('FYC_COLOR_MODERATOR', val)
val = document.getElementById('fyc_input_color_member').value
USER_CONFIG.ColorMember = val
GM_setValue('FYC_COLOR_MEMBER', val)
val = document.getElementById('fyc_ngwords').value
USER_CONFIG.NGWords = val
GM_setValue('FYC_NG_WORDS', val)
val = document.getElementById('fyc_ngwords_reg').value
USER_CONFIG.NGRegWords = val
GM_setValue('FYC_NG_REG_WORDS', val)
val = document.getElementById('fyc_ngusers').value
USER_CONFIG.NGUsers = val
GM_setValue('FYC_NG_USERS', val)
val = document.getElementById('fyc_toggle_simple_chat_field').checked
SETTINGS.SimpleChatField = val
GM_setValue('FYC_SIMPLE_CHAT_FIELD', val)
val = document.getElementById('fyc_check_button_to_ban').checked
SETTINGS.CreateNGButtons = val
GM_setValue('FYC_NG_BUTTON', val)
val = document.getElementById('fyc_check_display_moderator_name').checked
SETTINGS.DisplayModeratorName = val
GM_setValue('FYC_DISPLAY_MODERATOR_NAME', val)
val = document.getElementById('fyc_button_toggle_create_comments').checked
SETTINGS.CreateComments = val
GM_setValue('FYC_TOGGLE_CREATE_COMMENTS', val)
SETTINGS.NGWords = USER_CONFIG.NGWords.split(/\r\n|\n/)
SETTINGS.NGRegWords = USER_CONFIG.NGRegWords.split(/\r\n|\n/)
SETTINGS.NGUsers = USER_CONFIG.NGUsers.split(/\r\n|\n/)
toastr.success('Saved.')
}catch(e){
toastr.error('Error: '+e.message)
}
}
document.getElementById('fyc_input_font').onchange = function(){
let val = document.getElementById('fyc_input_font').value
if(val === 'Default')val=''
document.getElementById('fyc_font_sample_text').style.fontFamily = val
USER_CONFIG.Font = val
GM_setValue('FYC_FONT', val)
}
document.getElementById('fyc_input_lang').onchange = function(){
let val = document.getElementById('fyc_input_lang').value
USER_CONFIG.Lang = val
GM_setValue('FYC_LANG', val)
}
document.getElementById('fyc_range_opacity').oninput = function(val){
val=this.value/10
document.getElementById('output_opacity').value = val
USER_CONFIG.Opacity = val
GM_setValue('FYC_OPACITY', val)
}
document.getElementById('fyc_range_size').oninput = function(val){
val=this.value/10
document.getElementById('output_size').value = val
USER_CONFIG.Size = val
GM_setValue('FYC_SIZE', val)
}
document.getElementById('fyc_range_weight').oninput = function(val){
val=this.value*10
document.getElementById('output_weight').value = val
USER_CONFIG.Weight = val
GM_setValue('FYC_WEIGHT', val)
}
document.getElementById('fyc_range_weight_shadow').oninput = function(val){
val=this.value/10
document.getElementById('output_weight_shadow').value = val
USER_CONFIG.WeightShadow = val
GM_setValue('FYC_WEIGHT_SHADOW', val)
}
document.getElementById('fyc_range_speed').oninput = function(val){
val=this.value
document.getElementById('output_speed').value = val
USER_CONFIG.Speed = val
GM_setValue('FYC_SPEED', val)
}
document.getElementById('fyc_range_limit').oninput = function(val){
val=this.value*5
document.getElementById('output_limit').value = val
USER_CONFIG.Limit = val
GM_setValue('FYC_LIMIT', val)
}
document.getElementById('fyc_range_max').oninput = function(val){
val=this.value*5
document.getElementById('output_max').value = val
USER_CONFIG.Max = val
GM_setValue('FYC_MAX', val)
}
document.getElementById('fyc_range_line').oninput = function(val){
val=this.value
document.getElementById('output_line').value = val
USER_CONFIG.LaneNum = val
GM_setValue('FYC_LANE_DIV', val)
}
document.getElementById('fyc_reload_button').onclick = function(){
try{
console.log(LIVE_PAGE.getChatField())
document.getElementById('fyc_comment_screen').parentNode.removeChild(document.getElementById('fyc_comment_screen'))
document.getElementsByClassName('ytp-button fyc-comment-button')[0].parentNode.removeChild(document.getElementsByClassName('ytp-button fyc-comment-button')[0])
initialize()
ChatFieldObserver.disconnect()
ChatFieldObserver.observe(LIVE_PAGE.getChatField(), {childList: true})
toastr.success('Reloaded.')
}catch(e){
toastr.error('Error: '+e.message)
}
}
}
//------------------------------------------
function log(mes){console.log('【FYC】'+mes)}
})()