Updated chat post-processor to handle embedded command examples (both ! and /), embedded usernames, and embedded channel names
This commit is contained in:
parent
42d306e1f2
commit
db5fac83ab
|
|
@ -125,7 +125,7 @@ class chatBox{
|
||||||
chatEntry.appendChild(chatBody);
|
chatEntry.appendChild(chatBody);
|
||||||
|
|
||||||
|
|
||||||
this.chatBuffer.appendChild(this.chatPostprocessor.preprocess(chatEntry, data));
|
this.chatBuffer.appendChild(this.chatPostprocessor.postprocess(chatEntry, data));
|
||||||
|
|
||||||
//Set size to aspect on launch
|
//Set size to aspect on launch
|
||||||
this.resizeAspect();
|
this.resizeAspect();
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ class chatPostprocessor{
|
||||||
this.client = client;
|
this.client = client;
|
||||||
}
|
}
|
||||||
|
|
||||||
preprocess(chatEntry, rawData){
|
postprocess(chatEntry, rawData){
|
||||||
this.rawData = rawData;
|
this.rawData = rawData;
|
||||||
//Set current chat nodes
|
//Set current chat nodes
|
||||||
this.chatEntry = chatEntry;
|
this.chatEntry = chatEntry;
|
||||||
|
|
@ -15,6 +15,15 @@ class chatPostprocessor{
|
||||||
//Inject links into un-processed placeholders
|
//Inject links into un-processed placeholders
|
||||||
this.processLinks();
|
this.processLinks();
|
||||||
|
|
||||||
|
//Inject clickable command examples
|
||||||
|
this.processCommandExamples();
|
||||||
|
|
||||||
|
//Inject clickable channel names
|
||||||
|
this.processChannelNames();
|
||||||
|
|
||||||
|
//Inject clickable usernames
|
||||||
|
this.processUsernames();
|
||||||
|
|
||||||
//Inject whitespace into un-processed words
|
//Inject whitespace into un-processed words
|
||||||
this.addWhitespace();
|
this.addWhitespace();
|
||||||
|
|
||||||
|
|
@ -30,14 +39,17 @@ class chatPostprocessor{
|
||||||
splitMessage(){
|
splitMessage(){
|
||||||
//Create an empty array to hold the body
|
//Create an empty array to hold the body
|
||||||
this.messageArray = [];
|
this.messageArray = [];
|
||||||
|
|
||||||
|
//First unescape forward-slashes to keep them from splitting, then..
|
||||||
//Split string by word-boundries, with negative lookaheads to exclude file seperators so we don't split link placeholders
|
//Split string by word-boundries, with negative lookaheads to exclude file seperators so we don't split link placeholders
|
||||||
const splitString = this.rawData.msg.split(/(?<!␜)\b/g);
|
const splitString = this.rawData.msg.replaceAll('/','/').split(/(?<!␜)\b/g);
|
||||||
|
|
||||||
//for each word in the splitstring
|
//for each word in the splitstring
|
||||||
splitString.forEach((string) => {
|
splitString.forEach((string) => {
|
||||||
//create a word object
|
//create a word object
|
||||||
const wordObj = {
|
const wordObj = {
|
||||||
string: string,
|
//re-escape slashes for safety
|
||||||
|
string: string.replaceAll('/','/'),
|
||||||
type: "word"
|
type: "word"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,15 +135,62 @@ class chatPostprocessor{
|
||||||
}
|
}
|
||||||
|
|
||||||
injectNode(wordObj, vid);
|
injectNode(wordObj, vid);
|
||||||
|
}else if(wordObj.type == 'command'){
|
||||||
|
//Create link node
|
||||||
|
const link = document.createElement('a');
|
||||||
|
//Set class
|
||||||
|
link.classList.add('chat-link');
|
||||||
|
//Set href and inner text
|
||||||
|
link.href = "javascript:";
|
||||||
|
link.innerText = wordObj.command;
|
||||||
|
|
||||||
|
//Add chatbox functionality
|
||||||
|
link.addEventListener('click', () => {this.client.chatBox.commandPreprocessor.preprocess(wordObj.command)});
|
||||||
|
|
||||||
|
//We don't have to worry about injecting this into whitespace since there shouldn't be any here.
|
||||||
|
injectionArray.push(link);
|
||||||
|
}else if(wordObj.type == "username"){
|
||||||
|
//Create link node
|
||||||
|
const link = document.createElement('a');
|
||||||
|
//set class
|
||||||
|
link.classList.add(wordObj.color);
|
||||||
|
//Set href and inner text
|
||||||
|
link.href = "javascript:";
|
||||||
|
link.innerText = wordObj.string;
|
||||||
|
|
||||||
|
//add chatbox functionality
|
||||||
|
link.addEventListener('click', () => {this.client.chatBox.chatPrompt.value += `${wordObj.string} `});
|
||||||
|
|
||||||
|
//We don't have to worry about injecting this into whitespace since there shouldn't be any here.
|
||||||
|
injectionArray.push(link);
|
||||||
|
}else if(wordObj.type == "channel"){
|
||||||
|
//Create link node
|
||||||
|
const link = document.createElement('a');
|
||||||
|
//set class
|
||||||
|
link.classList.add('chat-link');
|
||||||
|
//Set href and inner text
|
||||||
|
link.href = `/c/${wordObj.chan}`;
|
||||||
|
link.innerText = wordObj.string;
|
||||||
|
|
||||||
|
//We don't have to worry about injecting this into whitespace since there shouldn't be any here.
|
||||||
|
injectionArray.push(link);
|
||||||
|
}else{
|
||||||
|
console.warn("Unknown chat postprocessor word type:");
|
||||||
|
console.warn(wordObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//For each item found in the injection array
|
//For each item found in the injection array
|
||||||
injectionArray.forEach((item) => {
|
injectionArray.forEach((item) => {
|
||||||
//if it's a string
|
//if it's a string
|
||||||
if(typeof item == "string"){
|
if(typeof item == "string"){
|
||||||
//Add it to the chat's innerHTML (it should already be escaped by the server)
|
//Create span (can't add to innerHTML without clobbering sibling DOM nodes)
|
||||||
this.chatBody.innerHTML += item;
|
const text = document.createElement('span');
|
||||||
|
//Set text to innerHTML (can't just append as a text node since the message was escaped server-side)
|
||||||
|
text.innerHTML = item;
|
||||||
|
|
||||||
|
this.chatBody.appendChild(text);
|
||||||
//Otherwise it should be a DOM node
|
//Otherwise it should be a DOM node
|
||||||
}else{
|
}else{
|
||||||
//Append the node to our chat body
|
//Append the node to our chat body
|
||||||
|
|
@ -167,6 +226,88 @@ class chatPostprocessor{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
processCommandExamples(){
|
||||||
|
//for each word object in the body
|
||||||
|
this.messageArray.forEach((wordObj, wordIndex) => {
|
||||||
|
//if the word object hasn't been pre-processed elsewhere
|
||||||
|
if(wordObj.type == "word"){
|
||||||
|
//Get last char of current word with slashes unescaped
|
||||||
|
const unescaped = wordObj.string.replaceAll('/','/')
|
||||||
|
const lastChar = unescaped[unescaped.length - 1];
|
||||||
|
|
||||||
|
//if the last char is !
|
||||||
|
if(lastChar == '!' || lastChar == '/'){
|
||||||
|
//get next word
|
||||||
|
const nextWord = this.messageArray[wordIndex + 1];
|
||||||
|
//if we have another word
|
||||||
|
if(nextWord != null){
|
||||||
|
const command = lastChar + nextWord.string;
|
||||||
|
//Take out the command marker
|
||||||
|
this.messageArray[wordIndex].string = unescaped.slice(0,-1);
|
||||||
|
|
||||||
|
const commandObj = {
|
||||||
|
type: "command",
|
||||||
|
string: nextWord.string,
|
||||||
|
command: command
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messageArray[wordIndex + 1] = commandObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
processChannelNames(){
|
||||||
|
//for each word object in the body
|
||||||
|
this.messageArray.forEach((wordObj, wordIndex) => {
|
||||||
|
//if the word object hasn't been pre-processed elsewhere
|
||||||
|
if(wordObj.type == "word"){
|
||||||
|
//Get last char of current word with slashes unescaped
|
||||||
|
const lastChar = wordObj.string[wordObj.string.length - 1];
|
||||||
|
const secondLastChar = wordObj.string[wordObj.string.length - 2];
|
||||||
|
|
||||||
|
//if the last char is # and the second to last char isn't & (avoid escaped HTML char codes)
|
||||||
|
if(lastChar == '#' && secondLastChar != '&'){
|
||||||
|
//get next word
|
||||||
|
const nextWord = this.messageArray[wordIndex + 1];
|
||||||
|
//if we have another word
|
||||||
|
if(nextWord != null){
|
||||||
|
//Take out the chan marker
|
||||||
|
this.messageArray[wordIndex].string = wordObj.string.slice(0,-1);
|
||||||
|
|
||||||
|
const commandObj = {
|
||||||
|
type: "channel",
|
||||||
|
string: lastChar + nextWord.string,
|
||||||
|
chan: nextWord.string
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messageArray[wordIndex + 1] = commandObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
processUsernames(){
|
||||||
|
//for each word object in the body
|
||||||
|
this.messageArray.forEach((wordObj, wordIndex) => {
|
||||||
|
//if the word object hasn't been pre-processed elsewhere
|
||||||
|
if(wordObj.type == "word"){
|
||||||
|
//Check for user and get their color
|
||||||
|
const color = this.client.userList.colorMap.get(wordObj.string);
|
||||||
|
|
||||||
|
//If the current word is the username of a connected user
|
||||||
|
if(color != null){
|
||||||
|
//Mark it as so
|
||||||
|
this.messageArray[wordIndex].type = "username";
|
||||||
|
//Store their color
|
||||||
|
this.messageArray[wordIndex].color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
addWhitespace(){
|
addWhitespace(){
|
||||||
//for each word object in the body
|
//for each word object in the body
|
||||||
this.messageArray.forEach((wordObj, wordIndex) => {
|
this.messageArray.forEach((wordObj, wordIndex) => {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue