/** Sanitizes HTML to display as plain-text. * This prevents some Cross Site Scripting (XSS). * This is handy when you are displaying user-made data, and you *must* use innerHTML. * @param {string} text - The text to sanitize * @returns {string} HTML escaped string * @since 0.44.2 * @example * const paragraph = document.createElement('p'); * paragraph.innerHTML = escapeHTML('Foobar.'); * // Output: * // (Does not include the paragraph element) * // (Output is not HTML formatted) *
* "Foobar." *
*/ export function escapeHTML(text) { const div = document.createElement('div'); // Creates a div div.textContent = text; // Puts the text in a PLAIN-TEXT property return div.innerHTML; // Returns the HTML property of the div } /** Converts the server tile-pixel coordinate system to the displayed tile-pixel coordinate system. * @param {string[]} tile - The tile to convert (as an array like ["12", "124"]) * @param {string[]} pixel - The pixel to convert (as an array like ["12", "124"]) * @returns {number[]} [tile, pixel] * @since 0.42.4 * @example * console.log(serverTPtoDisplayTP(['12', '123'], ['34', '567'])); // [34, 3567] */ export function serverTPtoDisplayTP(tile, pixel) { return [((parseInt(tile[0]) % 4) * 1000) + parseInt(pixel[0]), ((parseInt(tile[1]) % 4) * 1000) + parseInt(pixel[1])]; } /** Negative-Safe Modulo. You can pass negative numbers into this. * @param {number} a - The first number * @param {number} b - The second number * @returns {number} Result * @author osuplace * @since 0.55.8 */ export function negativeSafeModulo(a, b) { return (a % b + b) % b; } /** Bypasses terser's stripping of console function calls. * This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't. * However, the distributed version needs to call the console somehow, so this wrapper function is how. * This is the same as `console.log()`. * @param {...any} args - Arguments to be passed into the `log()` function of the Console * @since 0.58.9 */ export function consoleLog(...args) {((consoleLog) => consoleLog(...args))(console.log);} /** Bypasses terser's stripping of console function calls. * This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't. * However, the distributed version needs to call the console somehow, so this wrapper function is how. * This is the same as `console.error()`. * @param {...any} args - Arguments to be passed into the `error()` function of the Console * @since 0.58.13 */ export function consoleError(...args) {((consoleError) => consoleError(...args))(console.error);} /** Bypasses terser's stripping of console function calls. * This is so the non-obfuscated code will contain debugging console calls, but the distributed version won't. * However, the distributed version needs to call the console somehow, so this wrapper function is how. * This is the same as `console.warn()`. * @param {...any} args - Arguments to be passed into the `warn()` function of the Console * @since 0.58.13 */ export function consoleWarn(...args) {((consoleWarn) => consoleWarn(...args))(console.warn);} /** Encodes a number into a custom encoded string. * @param {number} number - The number to encode * @param {string} encoding - The characters to use when encoding * @since 0.65.2 * @returns {string} Encoded string * @example * const encode = '012abcABC'; // Base 9 * console.log(numberToEncoded(0, encode)); // 0 * console.log(numberToEncoded(5, encode)); // c * console.log(numberToEncoded(15, encode)); // 1A * console.log(numberToEncoded(12345, encode)); // 1BCaA */ export function numberToEncoded(number, encoding) { if (number === 0) return encoding[0]; // End quickly if number equals 0. No special calculation needed let result = ''; // The encoded string const base = encoding.length; // The number of characters used, which determines the base // Base conversion algorithm while (number > 0) { result = encoding[number % base] + result; // Find's the character's encoded value determined by the modulo of the base number = Math.floor(number / base); // Divides the number by the base so the next iteration can find the next modulo character } return result; // The final encoded string } /** Converts a Uint8 array to base64 using the browser's built-in binary to ASCII function * @param {Uint8Array} uint8 - The Uint8Array to convert * @returns {Uint8Array} The base64 encoded Uint8Array * @since 0.72.9 */ export function uint8ToBase64(uint8) { let binary = ''; for (let i = 0; i < uint8.length; i++) { binary += String.fromCharCode(uint8[i]); } return btoa(binary); // Binary to ASCII } /** Decodes a base 64 encoded Uint8 array using the browser's built-in ASCII to binary function * @param {Uint8Array} base64 - The base 64 encoded Uint8Array to convert * @returns {Uint8Array} The decoded Uint8Array * @since 0.72.9 */ export function base64ToUint8(base64) { const binary = atob(base64); // ASCII to Binary const array = new Uint8Array(binary.length); for (let i = 0; i < binary.length; i++) { array[i] = binary.charCodeAt(i); } return array; } /** Returns the coordinate input fields * @returns {Element[]} The 4 coordinate Inputs * @since 0.74.0 */ export function selectAllCoordinateInputs(document) { coords = []; coords.push(document.querySelector('#bm-input-tx')); coords.push(document.querySelector('#bm-input-ty')); coords.push(document.querySelector('#bm-input-px')); coords.push(document.querySelector('#bm-input-py')); return coords; } /** Processes the palette used for Blue Marble. * Each ID is sorted from smallest to largest. * Color ID's are integers, which can be negative. * Custom colors have been added for the Blue Marble purposes. * Wplace palette colors have not been modified. * @since 0.88.6 */ export function colorpaletteForBlueMarble(tolerance) { const colorpaletteBM = colorpalette; // Makes a copy // Adds the Blue Marble color for "erased" and "other" pixels to the palette list colorpaletteBM.unshift({ "id": -1, "premium": false, "name": "Erased", "rgb": [222, 250, 206] }); colorpaletteBM.unshift({ "id": -2, "premium": false, "name": "Other", "rgb": [ 0, 0, 0] }); const lookupTable = new Map(); // For each color in Blue Marble's palette... for (const color of colorpaletteBM) { if ((color.id == 0) || (color.id == -2)) continue; // skip Transparent or Other colors // Target RGB values. These are exactly correct. const targetRed = color.rgb[0]; const targetGreen = color.rgb[1]; const targetBlue = color.rgb[2]; // For each RGB value in the range of RGB values centered on the target RGB value for each channel... for (let deltaRedRange = -tolerance; deltaRedRange <= tolerance; deltaRedRange++) { for (let deltaGreenRange = -tolerance; deltaGreenRange <= tolerance; deltaGreenRange++) { for (let deltaBlueRange = -tolerance; deltaBlueRange <= tolerance; deltaBlueRange++) { // Basically, we are making a "cube" around each target value. // Say the tolerance is 3. The size of the cube will be ((3*2)+1)^3 which is 343 total. // This means 343 colors will be Mapped as associated to the target color ID because 343 colors are in the "cube" surrounding and including the target color // This specific deviation from the target RGB color values within the cube const derivativeRed = targetRed + deltaRedRange; const derivativeGreen = targetGreen + deltaGreenRange; const derivativeBlue = targetBlue + deltaBlueRange; // If it is impossible for the color to exist, then skip the color if (derivativeRed < 0 || derivativeRed > 255 || derivativeGreen < 0 || derivativeGreen > 255 || derivativeBlue < 0 || derivativeBlue > 255) continue; // Packed into 32-bit integer like RGBA = 0xAARRGGBB with the alpha channel forced to be 255 // Also, it is forced to be an unsigned 32-bit integer const derivativeColor32 = ((255 << 24) | (derivativeBlue << 16) | (derivativeGreen << 8) | derivativeRed) >>> 0; if (!lookupTable.has(derivativeColor32)) { lookupTable.set(derivativeColor32, color.id); } } } } } return {palette: colorpaletteBM, LUT: lookupTable} } /** The color palette used by wplace.live * @since 0.78.0 * @examples * import utils from 'src/utils.js'; * console.log(utils[5]?.name); // "White" * console.log(utils[5]?.rgb); // [255, 255, 255] */ export const colorpalette = [ { "id": 0, "premium": false, "name": "Transparent", "rgb": [ 0, 0, 0] }, { "id": 1, "premium": false, "name": "Black", "rgb": [ 0, 0, 0] }, { "id": 2, "premium": false, "name": "Dark Gray", "rgb": [ 60, 60, 60] }, { "id": 3, "premium": false, "name": "Gray", "rgb": [120, 120, 120] }, { "id": 4, "premium": false, "name": "Light Gray", "rgb": [210, 210, 210] }, { "id": 5, "premium": false, "name": "White", "rgb": [255, 255, 255] }, { "id": 6, "premium": false, "name": "Deep Red", "rgb": [ 96, 0, 24] }, { "id": 7, "premium": false, "name": "Red", "rgb": [237, 28, 36] }, { "id": 8, "premium": false, "name": "Orange", "rgb": [255, 127, 39] }, { "id": 9, "premium": false, "name": "Gold", "rgb": [246, 170, 9] }, { "id": 10, "premium": false, "name": "Yellow", "rgb": [249, 221, 59] }, { "id": 11, "premium": false, "name": "Light Yellow", "rgb": [255, 250, 188] }, { "id": 12, "premium": false, "name": "Dark Green", "rgb": [ 14, 185, 104] }, { "id": 13, "premium": false, "name": "Green", "rgb": [ 19, 230, 123] }, { "id": 14, "premium": false, "name": "Light Green", "rgb": [135, 255, 94] }, { "id": 15, "premium": false, "name": "Dark Teal", "rgb": [ 12, 129, 110] }, { "id": 16, "premium": false, "name": "Teal", "rgb": [ 16, 174, 166] }, { "id": 17, "premium": false, "name": "Light Teal", "rgb": [ 19, 225, 190] }, { "id": 18, "premium": false, "name": "Dark Blue", "rgb": [ 40, 80, 158] }, { "id": 19, "premium": false, "name": "Blue", "rgb": [ 64, 147, 228] }, { "id": 20, "premium": false, "name": "Cyan", "rgb": [ 96, 247, 242] }, { "id": 21, "premium": false, "name": "Indigo", "rgb": [107, 80, 246] }, { "id": 22, "premium": false, "name": "Light Indigo", "rgb": [153, 177, 251] }, { "id": 23, "premium": false, "name": "Dark Purple", "rgb": [120, 12, 153] }, { "id": 24, "premium": false, "name": "Purple", "rgb": [170, 56, 185] }, { "id": 25, "premium": false, "name": "Light Purple", "rgb": [224, 159, 249] }, { "id": 26, "premium": false, "name": "Dark Pink", "rgb": [203, 0, 122] }, { "id": 27, "premium": false, "name": "Pink", "rgb": [236, 31, 128] }, { "id": 28, "premium": false, "name": "Light Pink", "rgb": [243, 141, 169] }, { "id": 29, "premium": false, "name": "Dark Brown", "rgb": [104, 70, 52] }, { "id": 30, "premium": false, "name": "Brown", "rgb": [149, 104, 42] }, { "id": 31, "premium": false, "name": "Beige", "rgb": [248, 178, 119] }, { "id": 32, "premium": true, "name": "Medium Gray", "rgb": [170, 170, 170] }, { "id": 33, "premium": true, "name": "Dark Red", "rgb": [165, 14, 30] }, { "id": 34, "premium": true, "name": "Light Red", "rgb": [250, 128, 114] }, { "id": 35, "premium": true, "name": "Dark Orange", "rgb": [228, 92, 26] }, { "id": 36, "premium": true, "name": "Light Tan", "rgb": [214, 181, 148] }, { "id": 37, "premium": true, "name": "Dark Goldenrod", "rgb": [156, 132, 49] }, { "id": 38, "premium": true, "name": "Goldenrod", "rgb": [197, 173, 49] }, { "id": 39, "premium": true, "name": "Light Goldenrod", "rgb": [232, 212, 95] }, { "id": 40, "premium": true, "name": "Dark Olive", "rgb": [ 74, 107, 58] }, { "id": 41, "premium": true, "name": "Olive", "rgb": [ 90, 148, 74] }, { "id": 42, "premium": true, "name": "Light Olive", "rgb": [132, 197, 115] }, { "id": 43, "premium": true, "name": "Dark Cyan", "rgb": [ 15, 121, 159] }, { "id": 44, "premium": true, "name": "Light Cyan", "rgb": [187, 250, 242] }, { "id": 45, "premium": true, "name": "Light Blue", "rgb": [125, 199, 255] }, { "id": 46, "premium": true, "name": "Dark Indigo", "rgb": [ 77, 49, 184] }, { "id": 47, "premium": true, "name": "Dark Slate Blue", "rgb": [ 74, 66, 132] }, { "id": 48, "premium": true, "name": "Slate Blue", "rgb": [122, 113, 196] }, { "id": 49, "premium": true, "name": "Light Slate Blue", "rgb": [181, 174, 241] }, { "id": 50, "premium": true, "name": "Light Brown", "rgb": [219, 164, 99] }, { "id": 51, "premium": true, "name": "Dark Beige", "rgb": [209, 128, 81] }, { "id": 52, "premium": true, "name": "Light Beige", "rgb": [255, 197, 165] }, { "id": 53, "premium": true, "name": "Dark Peach", "rgb": [155, 82, 73] }, { "id": 54, "premium": true, "name": "Peach", "rgb": [209, 128, 120] }, { "id": 55, "premium": true, "name": "Light Peach", "rgb": [250, 182, 164] }, { "id": 56, "premium": true, "name": "Dark Tan", "rgb": [123, 99, 82] }, { "id": 57, "premium": true, "name": "Tan", "rgb": [156, 132, 107] }, { "id": 58, "premium": true, "name": "Dark Slate", "rgb": [ 51, 57, 65] }, { "id": 59, "premium": true, "name": "Slate", "rgb": [109, 117, 141] }, { "id": 60, "premium": true, "name": "Light Slate", "rgb": [179, 185, 209] }, { "id": 61, "premium": true, "name": "Dark Stone", "rgb": [109, 100, 63] }, { "id": 62, "premium": true, "name": "Stone", "rgb": [148, 140, 107] }, { "id": 63, "premium": true, "name": "Light Stone", "rgb": [205, 197, 158] } ]; // All entries include fixed id (index-based) and premium flag by design.