mirror of
https://github.com/overte-org/overte.git
synced 2025-06-15 04:25:59 +02:00
162 lines
5.6 KiB
JavaScript
162 lines
5.6 KiB
JavaScript
//
|
|
// formatting.js
|
|
//
|
|
// Created by Armored Dragon, 2024.
|
|
// Copyright 2024 Overte e.V.
|
|
//
|
|
// This just does some basic formatting and minor housekeeping for the domainChat.js application
|
|
//
|
|
// Distributed under the Apache License, Version 2.0.
|
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
|
|
|
const formatting = {
|
|
toJSON: function(data) {
|
|
if (typeof data == "object") return data; // Already JSON
|
|
|
|
try {
|
|
const parsedData = JSON.parse(data);
|
|
return parsedData;
|
|
} catch (e) {
|
|
console.log('Failed to convert data to JSON.')
|
|
return null; // Could not convert to json, some error;
|
|
}
|
|
},
|
|
addTimeAndDateStringToPacket: function(packet) {
|
|
// Gets the current time and adds it to a given packet
|
|
const timeArray = formatting.helpers._timestampArray(packet.timestamp);
|
|
packet.timeString = timeArray[0];
|
|
packet.dateString = timeArray[1];
|
|
return packet;
|
|
},
|
|
trimPacketToSave: function(packet) {
|
|
// Takes a packet, and returns a packet containing only what is needed to save.
|
|
let newPacket = {
|
|
channel: packet.channel || "",
|
|
displayName: packet.displayName || "",
|
|
message: packet.message || "",
|
|
timestamp: packet.timestamp || formatting.helpers.getTimestamp(),
|
|
};
|
|
return newPacket;
|
|
},
|
|
parseMessage: async function(message) {
|
|
const urlRegex = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
|
|
const overteLocationRegex = /hifi:\/\/[a-zA-Z0-9_-]+\/[-+]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+\/[-+]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+,[+-]?\d*\.?\d+/;
|
|
|
|
let runningMessage = message; // The remaining message that will be parsed
|
|
let messageArray = []; // An array of messages that are split up by the formatting functions
|
|
|
|
const regexPatterns = [
|
|
{ type: "url", regex: urlRegex },
|
|
{ type: "overteLocation", regex: overteLocationRegex }
|
|
]
|
|
|
|
while (true) {
|
|
let firstMatch = _findFirstMatch();
|
|
|
|
if (firstMatch == null) {
|
|
// If there is no more text to parse, break out of the loop and return the message array.
|
|
// Format any remaining text as a basic 'text' type.
|
|
messageArray.push({type: 'text', value: runningMessage});
|
|
|
|
// Append a final 'fill width' to the message text.
|
|
messageArray.push({type: 'messageEnd'});
|
|
break;
|
|
}
|
|
|
|
_formatMessage(firstMatch);
|
|
}
|
|
|
|
// Embed images in the message array.
|
|
for (dataChunk of messageArray){
|
|
if (dataChunk.type == 'url'){
|
|
let url = dataChunk.value;
|
|
|
|
const res = await formatting.helpers.fetch(url, {method: 'GET'}); // TODO: Replace with 'HEAD' method. https://github.com/overte-org/overte/issues/1273
|
|
const contentType = res.getResponseHeader("content-type");
|
|
|
|
// TODO: Add support for other media types
|
|
if (contentType.startsWith('image/')) {
|
|
messageArray.push({type: 'imageEmbed', value: url});
|
|
}
|
|
}
|
|
}
|
|
|
|
return messageArray;
|
|
|
|
function _formatMessage(firstMatch){
|
|
let indexOfFirstMatch = firstMatch[0];
|
|
let regex = regexPatterns[firstMatch[1]].regex;
|
|
|
|
let foundMatch = runningMessage.match(regex)[0];
|
|
|
|
messageArray.push({type: 'text', value: runningMessage.substring(0, indexOfFirstMatch)});
|
|
messageArray.push({type: regexPatterns[firstMatch[1]].type, value: runningMessage.substring(indexOfFirstMatch, indexOfFirstMatch + foundMatch.length)});
|
|
|
|
runningMessage = runningMessage.substring(indexOfFirstMatch + foundMatch.length); // Remove the part of the message we have worked with
|
|
}
|
|
|
|
function _findFirstMatch(){
|
|
let indexOfFirstMatch = Infinity;
|
|
let indexOfRegexPattern = Infinity;
|
|
|
|
for (let i = 0; regexPatterns.length > i; i++){
|
|
let indexOfMatch = runningMessage.search(regexPatterns[i].regex);
|
|
|
|
if (indexOfMatch == -1) continue; // No match found
|
|
|
|
if (indexOfMatch < indexOfFirstMatch) {
|
|
indexOfFirstMatch = indexOfMatch;
|
|
indexOfRegexPattern = i;
|
|
}
|
|
}
|
|
|
|
if (indexOfFirstMatch !== Infinity) return [indexOfFirstMatch, indexOfRegexPattern]; // If there was a found match
|
|
return null; // No found match
|
|
}
|
|
},
|
|
|
|
helpers: {
|
|
// Small functions that are used often in the other functions.
|
|
_timestampArray: function(timestamp) {
|
|
const currentDate = timestamp || formatting.helpers.getTimestamp();
|
|
let timeArray = [];
|
|
|
|
timeArray.push(new Date(currentDate).toLocaleTimeString(undefined, {
|
|
hour12: false,
|
|
}));
|
|
|
|
timeArray.push(new Date(currentDate).toLocaleDateString(undefined, {
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
}));
|
|
|
|
return timeArray;
|
|
},
|
|
getTimestamp: function(){
|
|
return Date.now();
|
|
},
|
|
fetch: function (url, options = {method: "GET"}) {
|
|
return new Promise((resolve, reject) => {
|
|
let req = new XMLHttpRequest();
|
|
|
|
req.onreadystatechange = function () {
|
|
|
|
if (req.readyState === req.DONE) {
|
|
if (req.status === 200) {
|
|
console.log("Content type:", req.getResponseHeader("content-type"));
|
|
resolve(req);
|
|
|
|
} else {
|
|
console.log("Error", req.status, req.statusText);
|
|
reject();
|
|
}
|
|
}
|
|
};
|
|
|
|
req.open(options.method, url);
|
|
req.send();
|
|
});
|
|
}
|
|
}
|
|
}
|