Tinychat Enhancement Suite

Fixes some Tinychat beta room annoyances and adds useful features.

Mint 2017.09.09.. Lásd a legutóbbi verzió

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 or Violentmonkey 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         Tinychat Enhancement Suite
// @namespace    https://greasyfork.org/en/users/27283-mutationobserver
// @version      2017-09-09--7
// @description  Fixes some Tinychat beta room annoyances and adds useful features.
// @author       MutationObserver
// @match        https://tinychat.com/room/*
// @exclude      https://tinychat.com/room/*?1
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_listValues

// ==/UserScript==
/* jshint -W097 */

var initInterval = setInterval(waitForInit, 500);
function waitForInit(){
	if (document.querySelector("tinychat-webrtc-app").shadowRoot) {
		clearInterval(initInterval);
		(function() {

// Actual userscript starts here //

browserFirefox = (navigator.userAgent.includes("Firefox/") ? true : false);

var webappElem = document.querySelector("tinychat-webrtc-app").shadowRoot;
var chatlogElem = webappElem.querySelector("tinychat-chatlog").shadowRoot;
var titleElem = webappElem.querySelector("tinychat-title").shadowRoot;
var sidemenuElem = webappElem.querySelector("tinychat-sidemenu").shadowRoot;
var videolistElem = webappElem.querySelector("tinychat-videolist").shadowRoot;

var chatlistElem = sidemenuElem.querySelector("tinychat-chatlist").shadowRoot;
var userlistElem = sidemenuElem.querySelector("tinychat-userlist").shadowRoot;

var chatlogCSS = chatlogElem.querySelector("#chat-wrapper");
var sidemenuCSS = sidemenuElem.querySelector("#sidemenu");
var webappCSS = webappElem.querySelector("#toast");
var chatlistCSS = chatlistElem.querySelector("#header");
var userlistCSS = userlistElem.querySelector("#header");
var titleCSS = titleElem.querySelector("#room-header");
var videolistCSS = videolistElem.querySelector("#videolist");
var bodyCSS = document.querySelector("body");

var audioPop = new Audio('/webrtc/2.0.20-420/sound/pop.mp3');

var giftsElemWidth = 150;

if (browserFirefox) {
	titleElem.querySelector("#room-header-info").insertAdjacentHTML("afterend", `
	<div id="tes-firefoxwarning" class="style-scope tinychat-title" 
	style="font-size: .75em; background: white; color: grey; width: 200px; padding: 5px; line-height: 13px;vertical-align: middle;border: #ddd 1px solid;border-width: 0px 1px 0px 1px;">
		<div class="style-scope tinychat-title" style="display: table;height: 100%;">
			<span style="display: table-cell; vertical-align: middle;top: 16%;" class="style-scope tinychat-title">
			Tinychat Enhancement Suite only supports Chrome. Support for Firefox is coming, but for now it only has autoscrolling.
			</span>
		</div>
	</div>
	`);
}

function waitForSettings() {
	if (browserFirefox) {
		clearInterval(settingsWaitInterval);
		return;
	}
	settingsVisible = false;
	if (titleElem.querySelector("#room-header-gifts") != null) {
		clearInterval(settingsWaitInterval);
		
		giftsElemWidth = titleElem.querySelector("#room-header-gifts").offsetWidth;
		if (titleElem.querySelector("#room-header-gifts-items") == null) {
			giftsElemWidth1000 = giftsElemWidth + 45;
		}
		else { giftsElemWidth1000 = giftsElemWidth; }
		if (titleCSS.querySelector("#titleCSS")) {
			titleCSS.querySelector("#titleCSS").innerHTML += `
				#tes-settings {
					right: ` + giftsElemWidth + `px;
				}
				@media screen and (max-width: 1000px) {
					#tes-settings {
						right: ` + giftsElemWidth1000 + `px;
					}
				}
			`;
		}
		
		settingsElem = titleElem.querySelector("#room-header-gifts").insertAdjacentHTML("beforebegin", `
		<div id="tes-settings">
			<div id="tes-settingsBox" class="hidden">
				<p id="title"><a href="https://greasyfork.org/en/scripts/32964-tinychat-enhancement-suite" target="_blank">Tinychat Enhancement Suite</a></p>
				<div id="tes-settings-mentions" class="tes-setting-container">
					<span class="label">
						Alert phrases
						<span class="info" data-title='A comma-separated list of phrases to alert/highlight for. Example of 3 phrases: "hello,Google Chrome,sky"'>?</span>
					</span>
					<input><button class="save blue-button">save</button>
				</div><!--
				<div id="tes-settings-font" class="tes-setting-container">
					<input type="checkbox">
					<span class="label">
						Improve font
						<span class="info" data-title='The default font is too thin in some browsers'>?</span>
					</span>
				</div>
				<div id="tes-settings-messageanim" class="tes-setting-container">
					<input type="checkbox">
					<span class="label">
						No message animation
					</span>
				</div>-->
			</div>
			<div id="tes-settingsGear" title="Tinychat Enhancement Suite settings"><span>✱</span></div>
		</div>
		`);
		
		titleElem.getElementById("tes-settingsGear").addEventListener("click", toggleSettings);
		titleElem.querySelector("#tes-settings #tes-settings-mentions button.save").addEventListener("click", function(){mentionsManager("save");} );
		settingMentions = [];
		!browserFirefox ? mentionsManager("load") : void(0);
	}
}

function toggleSettings() {
	if (settingsVisible == true) {
		titleElem.getElementById("tes-settingsBox").classList.add("hidden");
		titleElem.getElementById("tes-settings").classList.remove("tes-open");
		settingsVisible = false;
	}
	
	else {
		titleElem.getElementById("tes-settingsBox").classList.remove("hidden");
		titleElem.getElementById("tes-settings").classList.add("tes-open");
		settingsVisible = true;
	}
	
}

var settingsWaitInterval = setInterval(waitForSettings,1000);


function mentionsManager(mode) {
	var inputElem = titleElem.querySelector("#tes-settings #tes-settings-mentions input");
	// phrases = inputElem.value.split(",");
	var phrase = inputElem.value;
	if (phrase.endsWith(",")) { phrase = phrase.slice(0, -1); }
	if (phrase.startsWith(",")) { phrase = phrase.slice(1); }
	
	if (mode == "save") {
		GM_setValue("tes-Mentions", phrase);
		settingMentions = phrase.split(",");
		inputElem.value = phrase;
	}
	if (mode == "load") {
		var loadedMentions = GM_getValue("tes-Mentions");
		inputElem.value = loadedMentions;
		settingMentions = loadedMentions.split(",");
	}
						 return;
	
	var phrase = phrase.toString();
	if (mode == "save") {
		settingMentions.push(phrases);
		GM_setValue("tes-Mentions", JSON.stringify(setting_Mentions));
		console.log("Mention add:" + phrases);
	}
	if (mode == "load") {
		var mentions = JSON.parse(GM_getValue("tes-Mentions"));
		console.log("Mention load:" + mentions);
		settingMentions = mentions;
		inputElem.value = settingMentions.join();
	}
}

!browserFirefox ? injectCSS() : void(0);
function injectCSS(cssName=null) {
	// Indenting is purposely wrong, for readability
	var insertPosition = "beforeend";
	if (browserFirefox) {
		headElem = document.querySelector("head");
		titleCSS = videolistCSS = chatlistCSS = userlistCSS = bodyCSS = chatlogCSS = sidemenuCSS = webappCSS = headElem;
		headElem.querySelector('[scope="tinychat-title"]').innerHTML += `         #room-header {     min-height: 10%;     max-height: 10%; }      `;
	}
	
	if (cssName == "titleCSS" || cssName == null) {
	titleCSShtml = `
	<style id="titleCSS" scope="tinychat-title">
		@keyframes ease-to-left {
			0% {right: -50px; opacity: 0;}
			100% {right: 0; opacity: 1;}
		}
		@keyframes ease-to-right {
			0% {right:auto;}
			100% {right:0;}
		}
		@keyframes ease-to-bottom-21px {
			0% {top:-300px; opacity: 0;}
			100% {top:0; opacity: 1;}
		}
		input {
			border: 1px solid #c4d4dc;
			line-height: 16px;
			padding-left: 3px;
		}
		.label ~ input {
			border-bottom-left-radius: 6px;
			border-top-left-radius: 6px;
		}
		input ~ button {
			border-bottom-right-radius: 6px;
			border-top-right-radius: 6px;
		}
		input[type="button"], button {
			display: inline;
			padding: 0 15px;
			border: 0;
			box-sizing: border-box;
			letter-spacing: 1px;
			font-size: 12px;
			font-weight: bold;
			line-height: 20px;
			text-align: center;
			transition: .2s;
			outline: none;
		}
		.blue-button {
			color: #fff;
			background-color: #41b7ef;
		}
		.blue-button:hover {
		    background-color: #54ccf3;
		}
		.blue-button:active {
		    background-color: #38a8dd;
		}		
		#tes-settings > div {
			/*animation: ease-to-bottom-21px .2s ease 0s 1;*/
			position: relative;
			top: 0;
			height: 100px;
		}
		@media screen and (max-width: 1000px) {
			#tes-settings > div {
				height: 92px;
			}
		}
		#tes-settings .hidden { display: none; }
		#tes-settings #title { font-weight: bold; }
		#tes-settings {
			animation: ease-to-bottom-21px .2s ease 0s 1;
			/*max-height: 10%;*/
			margin-right: 15px;
			font-size: 11px;
			flex: none;
			overflow: hidden;
			z-index: 9;
			position: absolute;
			top: 13px;
			right: ` + (giftsElemWidth + 10).toString() + `px;
		}
		@media screen and (max-width: 1000px) {
			#tes-settings {
				right: ` + (giftsElemWidth + 40).toString() + `px;
			}
		}
		#tes-settings:hover {
			overflow: visible;
		}
		#tes-settingsGear {
			font-size: 70px;
			color: #38cd57;
			color: #53b6ef;
			float: right;
		}
		#tes-settingsGear:hover {
			cursor: pointer;
			color: #7ccefe;
		}
		.tes-open #tes-settingsGear {
			background: white;
			border-bottom-right-radius: 15px;
			border-top-right-radius: 15px;
			border: #ddd 1px solid;
			border-left: 0;
			/*transition: all .2s linear;*/
			}
		#tes-settingsGear span {
			display: block;
			transition: transform 0.4s ease-in-out;
		}
		.tes-open #tes-settingsGear span {
			transform: rotate(-90deg);
		}		
		#tes-settingsBox {
			background: white;
			padding: 0px 10px 0px 10px;
			float: left;
			border: #53b6ef 1px solid;
			border-top-left-radius: 12px;
			border-bottom-left-radius: 12px;
			animation: ease-to-left .2s ease 0s 1;
			right: 0;
		}
		#tes-settingsBox.hidden {
			animation: ease-to-right .2s ease 0s 1;
			display: visible;
			/*position: relative; right: -1000px;*/
		}
		
		/*** Inline with header ***/
		#tes-settingsBox {
			border-bottom: 0;
			border-top-left-radius: 0px;
			border-bottom-left-radius: 0px;
		}
		#tes-settings {
			top: -1px;
		}
		#tes-settingsGear {
			display: table;
		}
		#tes-settingsGear span {
			display: table-cell;
			vertical-align: middle;
		}
		/*** *************   ***/
		
		#tes-settings .tes-setting-container input[type=checkbox] {
			margin: 0;
			margin-right: 1px;
			float: left;
		}
		#tes-settings .label{
			margin-right: 4px;
		}
		#tes-settings .info{
			margin-left: 3px;
			color: #0d94e3;
			font-weight: bold;
			border: #0d94e3 1px solid;
			border-radius: 16px;
			height: 1em;
			width: 1em;
			text-align: center;
			display: inline-block;
		}
		#tes-settings .info:hover:after{
			font-weight: normal;
			padding: 4px 7px 4px 7px;
			border-radius: 7px;
			color: white;
			background: #61787f;
			content: attr(data-title);
			display: inline-block;
			position: absolute;
			top: 52px;
			left: 0;
		}
		/*#tes-settings .label:hover:before{
			border: solid;
			border-color: #61787f transparent;
			border-width: 0px 6px 6px 6px;
			top: 10px;
			content: "";
			left: 8%;
			position: relative;
			display: inline-block;
		}*/

		#tes-settings a:visited, #tes-settings a:link {
			text-decoration: none;
			color: inherit;
		}
		#tes-settings a:hover {
			color: #53b6ef;
		}

		#room-header {
			min-height: 10%; /* 105px */
			max-height: 10%;
		}
		@media screen and (max-width: 600px) {
			#room-header {
				min-height: inherit;
				max-height: inherit;
			}
		}
		#room-header-info {
			padding: 0;
		}
		#room-header-info-text {
			height: auto;
		}
		@media screen and (max-width: 600px) {
			#room-header-info-text {
				height: inherit;
			}
		}
		#room-header-avatar {
			margin: 2px 10px 0 35px;
			height: 90px;
			min-width: 90px;
			max-width: 90px;
		}
		#room-header-avatar:hover {
			border-radius: unset;
		}
		#room-header-gifts {
			padding: 10px 10px;
		}
	</style>
	`;
	titleCSS.insertAdjacentHTML(insertPosition, titleCSShtml);
	}

	if (cssName == "videolistCSS" || cssName == null) {
	videolistCSShtml = `
	<style id="videolistCSS" scope="tinychat-videolist">
		#videos-header {
			height: 10px;
			min-height: 10px;
		}
		#Fvideolist * {
			width: 75%!important;
			display: contents;
			float: right;
			flex-direction: column;
		}
		#Fvideos {
			flex-direction: unset;
			flex-wrap: unset;
		}
	</style>
	`;
	videolistCSS.insertAdjacentHTML(insertPosition, videolistCSShtml);
	}

	if (cssName == "chatlistCSS" || cssName == null) {
	chatlistCSShtml = `
	<style id="chatlistCSS" scope="tinychat-chatlist">
		#chatlist > div > span {
			padding-left: 1%;
		}
	</style>
	`;
	chatlistCSS.insertAdjacentHTML(insertPosition, chatlistCSShtml);
	}

	if (cssName == "userlistCSS" || cssName == null) {
	userlistCSShtml = `
	<style id="userlistCSS" scope="tinychat-userlist">
		#userlist > div > span {
			padding-left: 1%;
		}
		.list-item > span > span {
			right: auto;
			padding: 0 5px;
		}
		.list-item > span > .nickname {
			padding-right: 3px;
		}
		.list-item > span > img,
		.list-item > span[data-status="extreme"]:before,
		.list-item > span[data-status="gold"]:before,
		.list-item > span[data-status="pro"]:before {
			left: auto;
			right: 0;
		}
		.list-item > span > span[data-moderator="1"]:before {
			filter: hue-rotate(226deg) saturate(4000%);
	}
	</style>
	`;
	userlistCSS.insertAdjacentHTML(insertPosition, userlistCSShtml);
	}

	if (cssName == "bodyCSS" || cssName == null) {
	bodyCSShtml = `
	<style id="bodyCSS">
		#nav-static-wrapper {
			width: 2px;
			opacity: .7;
		}
		@media screen and (max-width: 1000px) {
			#nav-static-wrapper {
				width: 82px;
				opacity: 1;
			}
		}
	   #content {
		   padding: 0;
	   }
		body {
		  font-family: sans-serif;
		}
		#header-user {
			left: 115px;
		}
		@media screen and (max-width: 1000px) {
			#header-user {
				left: 21px;
			}
		}
		@media screen and (max-width: 600px) {
			#header-user {
				left: auto;
				right: 54px;
			}
		}
	</style>
	`;
	bodyCSS.insertAdjacentHTML(insertPosition, bodyCSShtml);
	}

	messageCSS = `
		.tes-mention-message { color: red; }
	`;

	if (cssName == "chatlogCSS" || cssName == null) {
	chatlogCSShtml = `
	<style id="chatlogCSS" scope="tinychat-chatlog">

		#chat-content > .message {
			padding-bottom: 0;
			padding-top: 0!important;
			margin-bottom: 0;
			min-height: 0px!important;
		}
		/*
		#chat-content > .message:hover {
			background: rgba(0, 0, 0, 0.03);
		}
		*/
		#chat-content > .message.common {
			margin-bottom: 5px;
		}
		#chat-content > .message.system {
			padding: 0;
		}
		#chat-instant > a:first-child,
		#chat-content > .message > a:first-child {
			top: auto;
		 }
		#chat-position #input:before {
			background: none;
		}
		#chat-instant > a > .avatar,
		#chat-content > .message > a > .avatar {
			border-radius: unset;
		}
		#timestamp {
			font-size: 11px;
			color: silver;
			float: right;
		}
		#chat-content > .message > .nickname {
			overflow: initial;
			line-height: initial;
		}
		#chat-content div.message.common:last-of-type {
			margin-bottom: 10px;
		}
	</style>
	`;
	chatlogCSS.insertAdjacentHTML(insertPosition, chatlogCSShtml);
	}

	if (cssName == "sidemenuCSS" || cssName == null) {
	sidemenuCSShtml = `
	<style id="sidemenuCSS" scope="tinychat-sidemenu">
		#sidemenu {
			min-width: 162px;
			max-width: 10%;
			left: auto;
		}
		@media screen and (max-width: 1000px) {
			#sidemenu {
				left: -188px;
			}
		}
		#sidemenu-content {
			padding-left: 1%;
		}
		#live-directory-wrapper {
			padding: 0;
		}
		#user-info {
			padding: 0 3px;
			height: auto;

		}
	</style>
	`;
	sidemenuCSS.insertAdjacentHTML(insertPosition, sidemenuCSShtml);
	}

	if (cssName == "webappCSS" || cssName == null) {
	webappCSShtml = `
	<style id="webappCSS" scope="tinychat-webrtc-app">
		#room {
			padding: 0;
			padding-left: 142px;
		}
		@media screen and (max-width: 1000px) {
			:host > #room {
				padding-left: 82px;
			}
		}
		@media screen and (max-width: 600px) {
			:host > #room {
				padding-left: 0;
			}
		}
	</style>
	`;
	webappCSS.insertAdjacentHTML(insertPosition, webappCSShtml);
	}
}

var scrollbox = chatlogElem.querySelector("#chat");
var unreadbubble = chatlogElem.querySelector("#input-unread");
var autoScrollStatus = true;

function updateScroll() {
	scrollbox.scrollTop = scrollbox.scrollHeight;
	scrollbox.scrollTop = scrollbox.scrollTop + 5;
}

function userHasScrolled(e) {
	var scrollwheelAmount = e.deltaY;

	if (scrollwheelAmount < 0) {
		autoScrollStatus = false;
	}
	if (autoScrollStatus === false && scrollbox.scrollHeight - scrollbox.scrollTop == scrollbox.offsetHeight) {
		autoScrollStatus = true;
	}
}

function newMessageAdded() {
	if (autoScrollStatus === true) { updateScroll(); }
	timestampAdd();
	messageParser();
}

messageCount = 0;
function messageParser() {
	latestMessageElem = chatlogElem.querySelector("#chat-content div.message.common:last-of-type");
	if (latestMessageElem != null) {
		latestMessageElem.setAttribute("id", "msg-"+messageCount);
		messageCount++;

		tcMessageHtmlElem = latestMessageElem.querySelector("tc-message-html").shadowRoot;
		latestMessageContentElem = tcMessageHtmlElem.querySelector("#html");
		
		if (!browserFirefox) {
			var node = document.createElement("style");
			var textnode = document.createTextNode(messageCSS);
			node.appendChild(textnode);
			tcMessageHtmlElem.appendChild(node);
		}
		
		
		latestMessageContent = latestMessageContentElem.innerHTML;
		
		if (!browserFirefox) {
			for (i=0; i < settingMentions.length; i++) {
				if (latestMessageContent.toLowerCase().includes(settingMentions[i].toLowerCase())) {
					latestMessageContentElem.classList.add("tes-mention-message");
					audioPop.play();
					break;
				}
			}
		}
	}
}

var x = new MutationObserver(function (e) {
  if (e[0].addedNodes) newMessageAdded();
});
x.observe(chatlogElem.querySelector("#chat-content"), { childList: true });


var x = new MutationObserver(function (e) {
  if (e[0].addedNodes) newCamAdded();
});
x.observe(videolistElem.querySelector(".videos-items:last-child"), { childList: true });


var scrollboxEvent = scrollbox.addEventListener("wheel", userHasScrolled);
var unreadbubble = unreadbubble.addEventListener("click", function(){autoScrollStatus = true;} );

function timestampAdd() {
	var queryString = "#chat-content div.message.common:last-of-type .nickname";
	
	var SHOW_SECONDS = true;

	var date = new Date(); 
	var hours = date.getHours();
	var minutes = date.getMinutes().toString();
	var secs = date.getSeconds().toString();

	if (hours > 12) {
		hours = (hours % 12 || 12);
		var period = "pm";
	}
	else { var period = "am"; }

	if (hours == "0") { hours = "12"; }
	if (minutes == "0") { minutes = "00"; }
	if (minutes.length == 1) { minutes = "0" + minutes; }
	if (secs.length == 1) { secs = "0" + secs; }

	if (SHOW_SECONDS == true) {
		var timestamp = hours + ":" + minutes + ":" + secs + "" + period;
	}
	else {
		var timestamp = hours + ":" + minutes + period;
	}
	
	if (chatlogElem.querySelector(queryString) != null) {
		var recentMsgNickname = chatlogElem.querySelector(queryString);
		recentMsgNickname.insertAdjacentHTML("afterend", "<span id='timestamp'> " + timestamp + "</span>");	
	}
}

// var newCamAddedInterval = setInterval(newCamAdded, 1000);
// var camAddedCounter = 0;
// newCamAdded();
function newCamAdded() {
	return;
	// camAddedCounter += 1;
	// if (camAddedCounter == 10) {
	// 	clearInterval(newCamAddedInterval);
	// }
	console.log("Cam added");
	var queryString = ".videos-items:last-child > .js-video";
	if (videolistElem.querySelector(queryString) != null) {
		var camElems = videolistElem.querySelectorAll(queryString);
		// console.log("found");
	}
	else {
		return;
	}
	for (i=0; i < camElems.length; i++) {
		if ( camElems[i].hasAttribute("id") == true ) { continue; }
		var camItem = camElems[i].querySelector("tc-video-item").shadowRoot;
		var camName = camItem.querySelector(".nickname").getAttribute("title");
		camElems[i].setAttribute("id", "camUser-"+camName);
		console.log("Cam: " + camName);
	}
}

// Userscript ends here //
		})();
	}
	else { console.log("Waiting for init..."); }
}