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 = ''; 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", ''); 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, "'"); } 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, ''); } } } 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();