twitch-chat/static/main.js

193 lines
5.7 KiB
JavaScript
Raw Normal View History

2020-03-20 16:01:29 +01:00
const timeout = 120000;
2020-03-20 12:23:27 +01:00
class Message {
constructor(object, onDestroy) {
this.target = object.target;
this.context = object.context;
this.message = object.message;
this.self = object.self;
}
createEl() {
this.element = document.createElement('li');
this.element.className = "item";
this.element.style.position = "relative";
2020-03-20 16:01:29 +01:00
let badges = document.createElement('span');
2020-04-04 14:30:06 +02:00
badges.className = 'badges';
2020-03-20 16:01:29 +01:00
for (let key in this.context.badges) {
let badge = document.createElement('span');
badge.className = "badge";
badge.innerHTML = '<img src="/static/' + key + '.png">';
badges.appendChild(badge);
}
2020-03-20 12:23:27 +01:00
let name = document.createElement('span');
name.className = "text name";
2020-03-20 17:19:16 +01:00
name.style.color = readableColor(this.context.color);
2020-03-20 12:23:27 +01:00
name.innerHTML = escapeHtml(this.context["display-name"]);
2020-04-04 14:30:06 +02:00
badges.appendChild(name);
this.element.appendChild(badges);
2020-03-20 12:23:27 +01:00
2020-04-04 14:30:06 +02:00
let messageSpan = document.createElement('span');
let {message, emotes} = fixEmotes(this.message, this.context.emotes);
let text = formatEmotes(message, emotes);
2020-03-20 16:30:31 +01:00
text = replaceAll(text, "DragonRck", '<img class="emoticon" src="/static/dragonrck.png">');
2020-04-04 14:30:06 +02:00
messageSpan.innerHTML = text;
messageSpan.className = "text message";
this.element.appendChild(messageSpan);
2020-03-20 12:23:27 +01:00
setTimeout(() => this.destroy(), timeout);
return this.element;
}
destroy() {
this.element.className += ' destroy';
}
}
function escapeHtml(unsafe) {
return unsafe
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
2020-04-04 14:30:06 +02:00
function fixEmotes(message, emotes) {
let offsets = [[0, 0]];
let len = message.length;
let newMessage = '';
let currentCharIndex = 0;
let currentOffset = 0;
while (currentCharIndex < len) {
let character = message.charAt(currentCharIndex);
switch (character) {
case '&':
newMessage += '&amp;';
currentOffset += 4;
offsets.push([currentCharIndex, currentOffset]);
break;
case '<':
newMessage += '&lt;';
currentOffset += 3;
offsets.push([currentCharIndex, currentOffset]);
break;
case '>':
newMessage += '&gt;';
currentOffset += 3;
offsets.push([currentCharIndex, currentOffset]);
break;
case '"':
newMessage += '&quot;';
currentOffset += 5;
offsets.push([currentCharIndex, currentOffset]);
break;
case "'":
newMessage += '&#039;';
currentOffset += 5;
offsets.push([currentCharIndex, currentOffset]);
break;
default:
newMessage += character;
}
currentCharIndex++;
}
for (let emoteType in emotes) {
let emoteSet = emotes[emoteType];
for (let emoteKey in emoteSet) {
let boundaries = emoteSet[emoteKey].split('-').map(x => parseInt(x, 10));
for (let i = 0; i < 2; i++) {
// Find floor entry
let entryIndex = 0;
while (entryIndex + 1 < offsets.length && offsets[entryIndex + 1][0] < boundaries[i]) {
entryIndex++;
}
boundaries[i] += offsets[entryIndex][1];
}
emoteSet[emoteKey] = boundaries.map(x => x + "").join('-');
}
}
return {message: newMessage, emotes};
}
2020-03-20 16:30:31 +01:00
function formatEmotes(text, emotes) {
2020-04-04 14:30:06 +02:00
var splitText = text.split('');
2020-03-20 16:30:31 +01:00
for(var i in emotes) {
var e = emotes[i];
for(var j in e) {
var mote = e[j];
if(typeof mote == 'string') {
mote = mote.split('-');
mote = [parseInt(mote[0]), parseInt(mote[1])];
var length = mote[1] - mote[0],
empty = Array.apply(null, new Array(length + 1)).map(function() { return '' });
splitText = splitText.slice(0, mote[0]).concat(empty).concat(splitText.slice(mote[1] + 1, splitText.length));
2020-03-20 16:38:37 +01:00
splitText.splice(mote[0], 1, '<img class="emoticon" src="http://static-cdn.jtvnw.net/emoticons/v1/' + i + '/4.0">');
2020-03-20 16:30:31 +01:00
}
}
}
return splitText.join('');
}
2020-03-20 17:19:16 +01:00
function readableColor(color) {
switch (color.toLowerCase()) {
case "#0000ff": return "#8b58ff";
case "#008000": return "#319a24";
case "#b22222": return "#db4a3f";
default: return color;
}
}
2020-03-20 16:01:29 +01:00
function replaceAll(input, from, to) {
const fromLen = from.length;
let output = "";
let pos = 0;
for (;;) {
let matchPos = input.indexOf(from, pos);
if (matchPos === -1) {
output += input.slice(pos);
break;
}
output += input.slice(pos, matchPos);
output += to;
pos = matchPos + fromLen;
}
return output;
}
2020-03-20 12:23:27 +01:00
function addMessage(object) {
2020-03-20 16:38:37 +01:00
console.log(object);
2020-03-20 12:23:27 +01:00
messages .className = "";
let message = new Message(object);
messages.appendChild(message.createEl());
messages.style.bottom = '-' + message.element.offsetHeight + 'px';
messages.className = "slide";
};
2020-04-04 14:30:06 +02:00
function onWindowResize() {
messages.style.width = window.innerWidth + 'px';
}
2020-03-20 12:23:27 +01:00
let messages = document.getElementById('messages');
let socket = io();
socket.on('message', addMessage);
2020-04-04 14:30:06 +02:00
window.addEventListener('resize', onWindowResize, false);
onWindowResize();