221 lines
6.3 KiB
JavaScript
221 lines
6.3 KiB
JavaScript
const timeout = 120000;
|
|
|
|
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";
|
|
|
|
let badges = document.createElement('span');
|
|
badges.className = 'badges';
|
|
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);
|
|
}
|
|
|
|
let name = document.createElement('span');
|
|
name.className = "text name";
|
|
name.style.color = readableColor(this.context.color || findColor(this.context["display-name"]));
|
|
name.innerHTML = escapeHtml(this.context["display-name"]);
|
|
badges.appendChild(name);
|
|
|
|
this.element.appendChild(badges);
|
|
|
|
let messageSpan = document.createElement('span');
|
|
let {message, emotes} = fixEmotes(this.message, this.context.emotes);
|
|
let text = formatEmotes(message, emotes);
|
|
text = replaceAll(text, "DragonRck", '<img class="emoticon" src="/static/dragonrck.png">');
|
|
messageSpan.innerHTML = text;
|
|
messageSpan.className = "text message";
|
|
this.element.appendChild(messageSpan);
|
|
|
|
setTimeout(() => this.destroy(), timeout);
|
|
return this.element;
|
|
}
|
|
|
|
destroy() {
|
|
this.element.className += ' destroy';
|
|
}
|
|
}
|
|
|
|
function escapeHtml(unsafe) {
|
|
return unsafe
|
|
.replace(/&/g, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """)
|
|
.replace(/'/g, "'");
|
|
}
|
|
|
|
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 += '&';
|
|
currentOffset += 4;
|
|
offsets.push([currentCharIndex, currentOffset]);
|
|
break;
|
|
case '<':
|
|
newMessage += '<';
|
|
currentOffset += 3;
|
|
offsets.push([currentCharIndex, currentOffset]);
|
|
break;
|
|
case '>':
|
|
newMessage += '>';
|
|
currentOffset += 3;
|
|
offsets.push([currentCharIndex, currentOffset]);
|
|
break;
|
|
case '"':
|
|
newMessage += '"';
|
|
currentOffset += 5;
|
|
offsets.push([currentCharIndex, currentOffset]);
|
|
break;
|
|
case "'":
|
|
newMessage += ''';
|
|
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};
|
|
|
|
|
|
}
|
|
|
|
function formatEmotes(text, emotes) {
|
|
var splitText = text.split('');
|
|
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));
|
|
splitText.splice(mote[0], 1, '<img class="emoticon" src="http://static-cdn.jtvnw.net/emoticons/v1/' + i + '/4.0">');
|
|
}
|
|
}
|
|
}
|
|
return splitText.join('');
|
|
}
|
|
|
|
function readableColor(color) {
|
|
switch (color.toLowerCase()) {
|
|
case "#0000ff": return "#8b58ff";
|
|
case "#008000": return "#319a24";
|
|
case "#b22222": return "#db4a3f";
|
|
default: return color;
|
|
}
|
|
}
|
|
|
|
function findColor(_username) {
|
|
const colors = [
|
|
"#ff0000",
|
|
"#0000ff",
|
|
"#008000",
|
|
"#b22222",
|
|
"#ff7f50",
|
|
"#99cd32",
|
|
"#ff4400",
|
|
"#2e8b56",
|
|
"#daa520",
|
|
"#d2691e",
|
|
"#5f9ea0",
|
|
"#1e8fff",
|
|
"#ff69b4",
|
|
"#892be2",
|
|
"#00ff80"
|
|
];
|
|
|
|
let username = _username.toLowerCase();
|
|
let c = 0;
|
|
for (let i = 0; i < username.length; i++) {
|
|
c += username.charCodeAt(i);
|
|
}
|
|
|
|
return colors[c % colors.length];
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
function addMessage(object) {
|
|
console.log(object);
|
|
messages .className = "";
|
|
let message = new Message(object);
|
|
messages.appendChild(message.createEl());
|
|
messages.style.bottom = '-' + message.element.offsetHeight + 'px';
|
|
messages.className = "slide";
|
|
};
|
|
|
|
function onWindowResize() {
|
|
messages.style.width = window.innerWidth + 'px';
|
|
}
|
|
|
|
let messages = document.getElementById('messages');
|
|
let socket = io();
|
|
socket.on('message', addMessage);
|
|
window.addEventListener('resize', onWindowResize, false);
|
|
onWindowResize();
|