Inline images (and other extras) for Twitch Chat
// ==UserScript==
// @name Twimages
// @namespace https://github.com/SammyIAm
// @version 0.4.5
// @description Inline images (and other extras) for Twitch Chat
// @author Sammy1Am
// @match http://www.twitch.tv/*
// @match https://www.twitch.tv/*
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
/* jshint -W097 */
'use strict';
//debugger;
var lastMessage = null; // Last message processed.
var imageRegex = /(https?:\/\/.*\.(?:png|jpg|jpeg|gif|bmp))/i; // Which image links to embed as an img tag
var twitchUsername = getTwitchUsername(); // Get username from GM value storage
var chatLines; // The chat-lines ul element (containing all the chatlines)
// Meta tag for iframes to prevent sending referrer (and fix imgur redirects)
var referrerBlock = document.createElement("meta");
referrerBlock.name = "referrer";
referrerBlock.content = "never";
// Attempt to get a stored username, or prompt for a new one.
function getTwitchUsername() {
if (GM_getValue("twimage_username",null) === null || GM_getValue("twimage_username", null) === ""){
GM_setValue("twimage_username", prompt("Twimages: Please enter your Twitch username for chat highlighting.", ""));
}
return GM_getValue("twimage_username",null);
}
// Gets all the new messages after the last one we've processed (or all the messages if we haven't processed any)
function getNewMessages(){
if (lastMessage === null){
lastMessage = chatLines.children[chatLines.children.length-1];
return Array.prototype.slice.call(chatLines.children);
} else {
var nextMessages = [];
while (lastMessage.nextElementSibling !== null){
nextMessages.push(lastMessage.nextElementSibling);
lastMessage = lastMessage.nextElementSibling;
}
return nextMessages;
}
}
// Searches for image links in the message and inserts the image below the message
function embedImages(messageDiv){
var message = messageDiv.getElementsByClassName("message")[0];
var imageUrlMatches = message.textContent.match(imageRegex);
if (imageUrlMatches !== null){
var imageUrl = imageUrlMatches[0];
// Create link so you can link to the URL
var imageInsert = document.createElement("a");
imageInsert.href = imageUrl;
imageInsert.target = "_blank";
imageInsert.style.display = "block";
// Create image that's reasonably sized
var newImage = document.createElement("img");
newImage.src = imageUrl;
newImage.style.border = "1px solid black";
newImage.style.maxHeight = "120px";
newImage.style.maxWidth = "100%";
imageInsert.appendChild(newImage);
message.appendChild(imageInsert);
}
}
// Searches for the configured username and highlights the message if it contains the name
function highlightUsername(messageDiv){
var message = messageDiv.getElementsByClassName("message")[0];
if (message.textContent.toLowerCase().indexOf(twitchUsername.toLowerCase()) > -1){
message.style.backgroundColor = "#C4B3DB";
}
}
// Adds a clickable '@' link next to each username
function addReplyLink(messageDiv){
var fromSpan = messageDiv.getElementsByClassName("from")[0];
var replyLink = document.createElement("a");
replyLink.innerHTML = "@";
replyLink.style.cursor = "pointer";
replyLink.addEventListener('click', function(){
onReplyLinkClick(fromSpan.textContent);
});
messageDiv.getElementsByTagName("span")[0].insertBefore(replyLink, fromSpan);
}
// Triggered when '@' link clicked on
function onReplyLinkClick(replyUsername){
var chatTextArea = document.getElementsByClassName("ember-text-area")[0];
chatTextArea.value = '@' + replyUsername + ': ';
chatTextArea.focus();
}
// Function to find and process new messages (triggered by update)
function processNewMessages(){
getNewMessages().forEach(function(newMessage){
embedImages(newMessage);
highlightUsername(newMessage);
//addReplyLink(newMessage); // Not working yet, something changed
});
}
// Find the chat-lines and set up observer or refresh timer
function initialize(){
chatLines = document.getElementsByClassName("chat-lines")[0];
// Firefox doesn't seem to like the observer, so we'll fallback to a safe polling-style for non-Chrome
if(navigator.userAgent.toLowerCase().indexOf('chrome') > -1){
// Set up observer to trigger processing each time a new message is added
var observer = new MutationObserver(processNewMessages);
var config = { attributes: false, childList: true, characterData: false };
observer.observe(document.getElementsByClassName("chat-lines")[0], config);
} else {
var refreshInterval = setInterval(function(){
chatLines = document.getElementsByClassName("chat-lines")[0];
processNewMessages();
}, 500);
}
}
// Wait for the chat-lines to show up, then initialize.
var waitForChatLines = setInterval(function(){
if (document.getElementsByClassName("chat-lines").length > 0){
initialize();
clearInterval(waitForChatLines);
}
}, 5000); // This has to be longer than 1 second for Firefox (for some reason?)