/* Client-side part of StreamGoose */ /* escape HTML entities (for non-server-provided stuff) */ function esc(s) { return s.replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, ''') } /* handle Twitch, YouTube and Kick emotes */ function handleEmotes(text) { var twurl = 'https://static-cdn.jtvnw.net/emoticons/v2/$id/default/dark/1.0' var kickurl = 'https://files.kick.com/emotes/$id/fullsize' /* Handle Twitch emotes coming from backends */ text = text.replace(/#TWEMOTE-[^\s]+#/g, a => '') /* Handle Kick emotes coming from backends */ text = text.replace(/#KKEMOTE-[^\s]+#/g, a => '') /* Handle YouTube emotes */ text = text.replace(/:[^\s]+:/g, a => { if (YT_EMOTES.indexOf(a) > -1) /* found in the database */ return '' else return a }) return text } /* detect if there are any overridden styles */ function overrideStyles(text) { var styles = '', re if (messageHighlightRules) { for (re in messageHighlightRules) { if ((new RegExp(re, 'iu')).test(text)) styles += 'color:' + messageHighlightRules[re] + ';' } } if (styles.length > 0) styles = ' style="' + esc(styles) + '"' return styles } /* individual message appender */ function gooseAppendMessage(id, native_id, author_id, type, timestamp, username, text, icon) { var html = "" if (parseInt(id) > 0 && type.length > 0) { if (type == "delmsg") { /* message deletion event */ var el = document.querySelector('[data-native-id="' + esc(native_id) + '"]') if (el) el.parentNode.removeChild(el) } else if (type == "deluser") { /* bulk deletion event by username */ var els = document.querySelectorAll('[data-author-id="' + esc(author_id) + '"]') if (els && els.length > 0) { var i, l = els.length for (i = 0; i < l; i++) els[i].parentNode.removeChild(els[i]) } } else { /* message appending event */ var classes = type.split('^'), cl = classes.length, j, colorpref = null, badges = [], platform, bname for (j = 0; j < cl; j++) { /* detect color preference */ if (classes[j].startsWith('color')) colorpref = classes[j].slice(5) /* remove color word */ if (classes[j].startsWith('badge')) { /* remove the class but cache it */ badges.push(classes[j].slice(5)) /* remove badge word */ classes[j] = "" /* remove the class from the list */ } } html += '
' /* handle badges and icons */ html += '' for (j = 0, cl = badges.length; j < cl; j++) { /* iterate over badges */ platform = badges[j].slice(0, 2) /* first 2 characters denote the platform */ bname = badges[j].slice(2) /* the rest is the badge name itself */ html += '' .replace('{platform}', platform).replace('{badgename}', bname) } if (icon.length > 0) /* user icon, if present */ html += '' html += '' html += '' + timestamp + '' html += '' html += '' + handleEmotes(text) + '
' document.getElementById("msglist").innerHTML += html } } /* autoscroll to the end */ window.setTimeout(function () { window.scrollTo(0, document.body.scrollHeight) }, 150) } /* entry point, set up an event source */ window.addEventListener('DOMContentLoaded', function () { var msgSource = new EventSource('/sse') msgSource.onmessage = e => { var msg = JSON.parse(e.data) gooseAppendMessage(msg.id, msg.native_id, msg.author_id, msg.type, msg.timestamp, msg.username, msg.text, msg.icon) } }, false)