UNPKG

@4players/odin-foundation

Version:

A set of classes defining a standard protocol for messaging and user data built on top of the Odin protocol.

167 lines (166 loc) 6.1 kB
/** * A set of utility functions used to serialize and deserialize data. */ export class Utilities { /** * @summary Capitalizes the first letter of a string * @param string The string to capitalize */ static capitalize(string) { return string.charAt(0).toUpperCase() + string.slice(1); } /** * @summary Converts a string to a base64url string * @param string The string to convert */ static base64url(string) { return btoa(string).replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); } /** * @summary URL encodes a string * @param string The string to encode */ static urlencode(string) { return encodeURIComponent(string) .replace(/['()]/g, escape) .replace(/\*/g, '%2A') .replace(/%(?:7C|60|5E)/g, unescape); } /** * @summary Converts a decimal number to a hex string * @param dec The number to convert to hex string */ static dec2hex(dec) { return dec.toString(16).padStart(2, '0'); } /** * Parses a JWT token and returns the payload * @param token The JWT token to parse */ static parseJwt(token) { var base64Url = token.split('.')[1]; var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/'); var jsonPayload = decodeURIComponent(atob(base64) .split('') .map(function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }) .join('')); return JSON.parse(jsonPayload); } /** * @summary Function to filter out duplicate values in an array * @param value * @param index * @param self */ static uniqueFilter(value, index, self) { return self.indexOf(value) === index; } /** * @summary Parses a string for URLs and returns an array of URLs * @param text The text to parse */ static parseLinks(text) { var urls = []; const matches = text.matchAll(/([*~_]+|\b)(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]])?(\1)?(?=\s|$)(?!["<>])/gi); for (const match of matches) { urls.push(match[0]); } return urls.filter(this.uniqueFilter); } /** * @summary Builds a chat message from a user and a message transfer message * @param user The user who sent the message * @param message The message transfer message */ static buildMessage(user, message) { if (user && message) { if (message.kind === 'poke' || message.kind === 'message') { const newMessage = { fromId: user.id, fromName: user.name, fromAvatar: user.avatar, kind: message.kind, text: message.payload.text, urls: this.parseLinks(message.payload.text), time: new Date() }; if (message.isPrivate) { newMessage.isPrivate = message.isPrivate; } return newMessage; } } else { console.warn('Cannot build message, user or message is null', user, message); } return null; } /** * @summary Creates a user from user data. * Deprecated fields are still filled in for backwards compatibility. Use `data` field for storing user data instead. * @param userData The user data to create a user from * @param peerId The peer ID of the user */ static createUserFromUserData(userData, peerId) { return { id: userData?.id ? userData.id : '', name: userData?.name ? userData.name : 'Unknown', avatar: userData?.avatar, peerId: peerId, status: userData?.status ? userData.status : 'online', outputMuted: userData?.outputMuted ? userData.outputMuted : 0, inputMuted: 1, renderer: userData?.renderer ? userData.renderer : '', platform: userData?.platform ? userData.platform : '', revision: userData?.revision ? userData.revision : '', version: userData?.version ? userData.version : '', buildNo: userData?.buildNo ? userData.buildNo : 0, data: userData }; } /** * @summary Creates user data from a user. * @deprecated Use IUser.data instead of duplicating data all the time back and forth * @param user The user to create user data from */ static userDataFromUser(user) { const userData = {}; userData.id = user?.id ? user.id : ''; userData.avatar = user?.avatar ? user.avatar : ''; userData.name = user?.name ? user.name : 'Unknown'; userData.status = user?.status ? user.status : 'online'; userData.outputMuted = user?.outputMuted ? user.outputMuted : 0; userData.renderer = user?.renderer ? user.renderer : ''; userData.revision = user?.revision ? user.revision : ''; userData.version = user?.version ? user.version : ''; userData.platform = user?.platform ? user.platform : ''; userData.buildNo = user?.buildNo ? user.buildNo : 0; return userData; } /** * @summary Encodes an object to a Uint8Array * Use this function to encode objects to send them over the network * @param data The object to encode */ static encodeObjToUint8Array(data) { return new TextEncoder().encode(JSON.stringify(data)); } /** * @summary Decodes a Uint8Array to an object * Use this function to encode objects to send them over the network * @param data */ static decodeUint8ArrayToObject(data) { if (data.length === 0) return {}; try { return JSON.parse(new TextDecoder().decode(data)); } catch (e) { console.warn('Error decoding Uint8Array to object:', e, data); return null; } } }