bring remote assets to local
This commit is contained in:
323
web/utils.js
Normal file
323
web/utils.js
Normal file
@ -0,0 +1,323 @@
|
||||
|
||||
/**
|
||||
* Generates a random string of a desired length, containing any of the desired characters.
|
||||
* @param {number} length The resulting string's length
|
||||
* @param {string|string[]} chars A list of possible characters to use
|
||||
* @returns {string} The resulting random string
|
||||
*/
|
||||
function randomString(length, chars = '0123456789abcdefghijklmnopqrstuvwxyz') {
|
||||
let str = '';
|
||||
for (let i = 0; i < length; i++) {
|
||||
str += chars[randomInt(0, chars.length-1)];
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a lowercase hexadecimal string of a desired length.
|
||||
* @param {number} length The length of the random
|
||||
* @returns {String} The resulting string
|
||||
*/
|
||||
function randomHex(length = 8) {
|
||||
return randomString(length, '0123456789abcdef');
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a pseudorandom integer between a minimum and maximum.
|
||||
* @param {number} min The minimum
|
||||
* @param {number} max The maximum
|
||||
* @returns {number} The resulting integer
|
||||
*/
|
||||
function randomInt(min, max) {
|
||||
return Math.round(min+(Math.random()*(max-min)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a pseudorandom float between a minimum and maximum.
|
||||
* @param {number} min The minimum
|
||||
* @param {number} max The maximum
|
||||
* @returns {number} The resulting float
|
||||
*/
|
||||
function randomFloat(min, max) {
|
||||
return min+(Math.random()*(max-min));
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a random element from an array.
|
||||
* @param {array} arr The input array
|
||||
* @returns {*} A randomly selected array element
|
||||
*/
|
||||
const getRandomElement = (arr) => arr[(Math.ceil(Math.random()*arr.length)-1)];
|
||||
|
||||
/**
|
||||
* @typedef itemWithWeight
|
||||
* @property {*} value The return value for this item
|
||||
* @property {number} weight The weight of this item
|
||||
*/
|
||||
/**
|
||||
* Selects a random item from a provided list, where each item has a specific weight.
|
||||
* @param {itemWithWeight[]} args An array of items and their weights
|
||||
* @returns A randomly selected value
|
||||
*/
|
||||
const getRandomWeighted = (args) => {
|
||||
// Get the total weight
|
||||
let total = 0;
|
||||
args.forEach((arg) => {
|
||||
total += arg.weight;
|
||||
});
|
||||
// Sort options from lightest to heaviest
|
||||
args.sort((a, b) => {
|
||||
return a.weight-b.weight;
|
||||
});
|
||||
// Get our random number
|
||||
const rand = randomFloat(0, total);
|
||||
// Iterate through options until the current weight (num)
|
||||
// is greater than our number (rand)
|
||||
let num = 0;
|
||||
for (const arg of args) {
|
||||
num += arg.weight;
|
||||
if (rand < num) return arg.value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps a number within a range by preventing it from going above/below its maximum/minimum.
|
||||
* @param {number} num The input number
|
||||
* @param {number} min The minimum number
|
||||
* @param {number} max The maximum number
|
||||
* @returns {number} The resulting number
|
||||
*/
|
||||
function clamp(num, min, max) {
|
||||
if (num < min) return min;
|
||||
if (num > max) return max;
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Keeps a number within a range by underflowing/overflowing.
|
||||
* @param {number} num The input number
|
||||
* @param {number} min The minimum number
|
||||
* @param {number} max The maximum number
|
||||
* @returns {number} The resulting number
|
||||
*/
|
||||
function overflow(num, min, max) {
|
||||
if (num < min) return max;
|
||||
if (num > max) return min;
|
||||
return num;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a float to the desired amount of decimal places, clipping any trailing zeros.
|
||||
* @param {number} number The input number
|
||||
* @param {number} decimalPlaces The maximum number of decimal places
|
||||
* @returns {number} The resulting number
|
||||
*/
|
||||
function roundSmart(number, decimalPlaces = 0) {
|
||||
const factorOfTen = Math.pow(10, decimalPlaces);
|
||||
return Math.round(number * factorOfTen) / factorOfTen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Separates a string into its words and returns an array of those words.
|
||||
* @param {string} s The input string
|
||||
* @returns {String[]} An array of words
|
||||
*/
|
||||
function getWords(s){
|
||||
s = s.replace(/(^\s*)|(\s*$)/gi, '');
|
||||
s = s.replace(/[ ]{2,}/gi, ' ');
|
||||
s = s.replace(/\n/g, ' ');
|
||||
return s.split(' ').filter(String);
|
||||
}
|
||||
/**
|
||||
* Returns the number of words in a string.
|
||||
* @param {string} s The input string
|
||||
* @returns {number} The number of words
|
||||
*/
|
||||
function countWords(s){
|
||||
return getWords(s).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pause execution for a desired amount of time. Use this in `async` functions with `await sleep(ms)`.
|
||||
* @param {Number} ms The number of milliseconds to sleep for
|
||||
* @returns {Promise<undefined>}
|
||||
*/
|
||||
function sleep(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles an array using `Math.random()` to swap elements and returns the result.
|
||||
* @param {array} arr The input array
|
||||
* @returns {array} The shuffled array
|
||||
*/
|
||||
function shuffle(arr) {
|
||||
let i = 0;
|
||||
while (i < arr.length) {
|
||||
let tmp = arr[i];
|
||||
let num = Math.round(Math.random()*(arr.length-1));
|
||||
arr[i] = arr[num];
|
||||
arr[num] = tmp;
|
||||
i++;
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number of seconds into `[hh]:[m]m:ss` format.
|
||||
* @param {number} s The number of seconds to format
|
||||
* @returns {String}
|
||||
*/
|
||||
function formatSeconds(s) {
|
||||
s = Math.floor(s || 0);
|
||||
let hours = 0;
|
||||
let minutes = 0;
|
||||
let seconds = s;
|
||||
if (s >= 3600) {
|
||||
hours = Math.floor(s / 3600);
|
||||
s = s % 3600;
|
||||
}
|
||||
if (s >= 60) {
|
||||
minutes = Math.floor(s / 60);
|
||||
s = s % 60;
|
||||
}
|
||||
seconds = s;
|
||||
let timeString = '';
|
||||
timeString += (hours > 0) ? hours.toString() + ':' : '';
|
||||
timeString += minutes.toString().padStart((hours > 0) ? 2 : 1, '0') + ':';
|
||||
timeString += seconds.toString().padStart(2, '0');
|
||||
return timeString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get age as an integer from a Date object.
|
||||
* @param {Date} date The target date
|
||||
* @returns {number}
|
||||
*/
|
||||
function getAgeFromDate(date) {
|
||||
const now = new Date();
|
||||
let age = now.getFullYear()-date.getFullYear();
|
||||
if (now.getMonth() < date.getMonth() || (now.getMonth() == date.getMonth() && now.getDate() < date.getDate())) {
|
||||
age--;
|
||||
}
|
||||
return age;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string describing the relative time between two dates, like "3 days ago" or "2 months from now".
|
||||
* @param {number} target A millisecond-timestamp of the target date.
|
||||
* @param {number} [anchor] A millisecond-timestamp of the date to look from. Defaults to `Date.now()`.
|
||||
* @returns {string} The resulting relative description.
|
||||
*/
|
||||
function getRelativeDate(target, anchor = Date.now()) {
|
||||
const isFuture = (anchor-target < 0) ? true : false;
|
||||
let diff = Math.abs(anchor-target);
|
||||
diff = Math.round(diff/1000);
|
||||
if (diff < 120) // Less than 120 seconds
|
||||
return (isFuture) ? `In a moment` : `Moments ago`;
|
||||
diff = Math.round(diff/60);
|
||||
if (diff < 120) // Less than 120 minutes
|
||||
return (isFuture) ? `${diff} mins from now` : `${diff} mins ago`;
|
||||
diff = Math.round(diff/60);
|
||||
if (diff < 72) // Less than 72 hours
|
||||
return (isFuture) ? `${diff} hours from now` : `${diff} hours ago`;
|
||||
diff = Math.round(diff/24);
|
||||
const days = diff;
|
||||
if (diff < 90) // Less than 90 days
|
||||
return (isFuture) ? `${diff} days from now` : `${diff} days ago`;
|
||||
diff = Math.round(diff/30.43685);
|
||||
if (diff < 36) // Less than 36 months
|
||||
return (isFuture) ? `${diff} months from now` : `${diff} months ago`;
|
||||
diff = Math.round(days/365.2422);
|
||||
return (isFuture) ? `${diff} years from now` : `${diff} years ago`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a number of bytes to a human-readable size, like "230.2 MB" or "7.27 GB".
|
||||
* @param {number} bytes A number of bytes to convert
|
||||
* @returns {string} The human-readable size string
|
||||
*/
|
||||
function formatSize(bytes) {
|
||||
if (bytes < 1000) return `${bytes} B`;
|
||||
bytes /= 1024;
|
||||
if (bytes < (1000)) return `${roundSmart(bytes, 0)} KB`;
|
||||
bytes /= 1024;
|
||||
if (bytes < (1000)) return `${roundSmart(bytes, 1)} MB`;
|
||||
bytes /= 1024;
|
||||
if (bytes < (1000)) return `${roundSmart(bytes, 2)} GB`;
|
||||
bytes /= 1024;
|
||||
if (bytes < (1000)) return `${roundSmart(bytes, 2)} TB`;
|
||||
return "-";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines of an input string is a valid URL.
|
||||
* @param {string} string The input string
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isValidUrl(string) {
|
||||
let url;
|
||||
try {
|
||||
url = new URL(string);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
return url.protocol === "http:" || url.protocol === "https:";
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a string is a valid hostname.
|
||||
* @param {string} string The input string
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isValidHostname(string) {
|
||||
return string.match(/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/) && !string.match(/^localhost$/);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if a string is a valid IPv4 or IPv6 address.
|
||||
* @param {string} string The input string
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
function isValidIp(string) {
|
||||
return string.match(/((^\s*((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))\s*$)|(^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$))/) && !string.match(/(^127\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^192\.168\.)/) && !string.match(/^::1$/);
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes HTML entities present in the input string and returns the result.
|
||||
* @param {string} text The input string
|
||||
* @returns {string} The escaped string
|
||||
*/
|
||||
function escapeHTML(text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
try {
|
||||
module.exports = {
|
||||
randomHex: randomHex,
|
||||
randomInt: randomInt,
|
||||
randomFloat: randomFloat,
|
||||
getRandomElement: getRandomElement,
|
||||
getRandomWeighted: getRandomWeighted,
|
||||
clamp: clamp,
|
||||
overflow: overflow,
|
||||
roundSmart: roundSmart,
|
||||
getWords: getWords,
|
||||
countWords: countWords,
|
||||
sleep: sleep,
|
||||
shuffle: shuffle,
|
||||
formatSeconds: formatSeconds,
|
||||
getAgeFromDate: getAgeFromDate,
|
||||
getRelativeDate: getRelativeDate,
|
||||
formatSize: formatSize,
|
||||
isValidUrl: isValidUrl,
|
||||
isValidHostname: isValidHostname,
|
||||
isValidIp: isValidIp,
|
||||
escapeHTML: escapeHTML
|
||||
};
|
||||
} catch (error) {}
|
Reference in New Issue
Block a user