mirror of https://github.com/jetkvm/kvm.git
235 lines
9.8 KiB
TypeScript
235 lines
9.8 KiB
TypeScript
export const formatters = {
|
|
date: (date: Date, options?: Intl.DateTimeFormatOptions) =>
|
|
new Intl.DateTimeFormat("en-US", {
|
|
year: "numeric",
|
|
month: "long",
|
|
day: "numeric",
|
|
...(options || {}),
|
|
}).format(date),
|
|
|
|
bytes: (bytes: number, decimals = 2) => {
|
|
if (!+bytes) return "0 Bytes";
|
|
|
|
const k = 1024;
|
|
const dm = decimals < 0 ? 0 : decimals;
|
|
const sizes = ["Bytes", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
|
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
},
|
|
|
|
hertz: (hz: number, decimals = 2) => {
|
|
if (!+hz) return "0 Hz";
|
|
|
|
const k = 1000; // The scaling factor for Hertz is 1000
|
|
const dm = decimals < 0 ? 0 : decimals;
|
|
const sizes = ["Hz", "kHz", "MHz", "GHz", "THz", "PHz", "EHz", "ZHz", "YHz"];
|
|
|
|
const i = Math.floor(Math.log(hz) / Math.log(k));
|
|
|
|
return `${parseFloat((hz / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`;
|
|
},
|
|
|
|
timeAgo: (date: Date, options?: Intl.RelativeTimeFormatOptions) => {
|
|
const relativeTimeFormat = new Intl.RelativeTimeFormat("en-US", {
|
|
numeric: "auto",
|
|
...(options || {}),
|
|
});
|
|
|
|
const DIVISIONS: {
|
|
amount: number;
|
|
name: Intl.RelativeTimeFormatUnit;
|
|
}[] = [
|
|
{ amount: 60, name: "seconds" },
|
|
{ amount: 60, name: "minutes" },
|
|
{ amount: 24, name: "hours" },
|
|
{ amount: 7, name: "days" },
|
|
{ amount: 4.34524, name: "weeks" },
|
|
{ amount: 12, name: "months" },
|
|
{ amount: Number.POSITIVE_INFINITY, name: "years" },
|
|
];
|
|
|
|
let duration = (date.valueOf() - new Date().valueOf()) / 1000;
|
|
|
|
for (const division of DIVISIONS) {
|
|
if (Math.abs(duration) < division.amount) {
|
|
return relativeTimeFormat.format(Math.round(duration), division.name);
|
|
}
|
|
duration /= division.amount;
|
|
}
|
|
},
|
|
|
|
price: (price: number | bigint | string, options?: Intl.NumberFormatOptions) => {
|
|
const opts: Intl.NumberFormatOptions = {
|
|
style: "currency",
|
|
currency: "USD",
|
|
...(options || {}),
|
|
};
|
|
|
|
// Convert the price to a number for comparison
|
|
const numericPrice = typeof price === "string" ? parseFloat(price) : Number(price);
|
|
|
|
// Check if the price is less than 1 and not zero, then adjust minimumFractionDigits
|
|
if (numericPrice > 0 && numericPrice < 1) {
|
|
opts.minimumFractionDigits = Math.max(2, -Math.floor(Math.log10(numericPrice)));
|
|
} else {
|
|
opts.minimumFractionDigits = 0;
|
|
}
|
|
|
|
return new Intl.NumberFormat("en-US", opts).format(numericPrice);
|
|
},
|
|
|
|
truncateMiddle: (str: string | null | undefined, maxLength: number): string => {
|
|
if (!str) return "";
|
|
if (str.length <= maxLength) {
|
|
return str;
|
|
}
|
|
|
|
const halfLength = Math.floor(maxLength / 2);
|
|
const firstPart = str.slice(0, halfLength - 1);
|
|
const lastPart = str.slice(-halfLength + 2);
|
|
|
|
return `${firstPart}...${lastPart}`;
|
|
},
|
|
};
|
|
|
|
export const VIDEO = new Blob(
|
|
[
|
|
new Uint8Array([
|
|
0, 0, 0, 28, 102, 116, 121, 112, 105, 115, 111, 109, 0, 0, 2, 0, 105, 115, 111, 109,
|
|
105, 115, 111, 50, 109, 112, 52, 49, 0, 0, 0, 8, 102, 114, 101, 101, 0, 0, 2, 239,
|
|
109, 100, 97, 116, 33, 16, 5, 32, 164, 27, 255, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 55, 167, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 33, 16, 5, 32, 164, 27, 255,
|
|
192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 55, 167, 128, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 112, 0, 0, 2, 194, 109, 111, 111, 118, 0, 0, 0, 108, 109, 118, 104, 100, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 232, 0, 0, 0, 47, 0, 1, 0, 0, 1, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 1, 236, 116, 114, 97, 107, 0,
|
|
0, 0, 92, 116, 107, 104, 100, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0,
|
|
0, 0, 0, 0, 0, 47, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 101, 100, 116, 115, 0, 0, 0, 28, 101,
|
|
108, 115, 116, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 47, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1,
|
|
100, 109, 100, 105, 97, 0, 0, 0, 32, 109, 100, 104, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 172, 68, 0, 0, 8, 0, 85, 196, 0, 0, 0, 0, 0, 45, 104, 100, 108, 114,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 115, 111, 117, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83,
|
|
111, 117, 110, 100, 72, 97, 110, 100, 108, 101, 114, 0, 0, 0, 1, 15, 109, 105, 110,
|
|
102, 0, 0, 0, 16, 115, 109, 104, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 36, 100, 105,
|
|
110, 102, 0, 0, 0, 28, 100, 114, 101, 102, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 117,
|
|
114, 108, 32, 0, 0, 0, 1, 0, 0, 0, 211, 115, 116, 98, 108, 0, 0, 0, 103, 115, 116,
|
|
115, 100, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 87, 109, 112, 52, 97, 0, 0, 0, 0, 0, 0,
|
|
0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 16, 0, 0, 0, 0, 172, 68, 0, 0, 0, 0, 0, 51,
|
|
101, 115, 100, 115, 0, 0, 0, 0, 3, 128, 128, 128, 34, 0, 2, 0, 4, 128, 128, 128, 20,
|
|
64, 21, 0, 0, 0, 0, 1, 244, 0, 0, 1, 243, 249, 5, 128, 128, 128, 2, 18, 16, 6, 128,
|
|
128, 128, 1, 2, 0, 0, 0, 24, 115, 116, 116, 115, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2,
|
|
0, 0, 4, 0, 0, 0, 0, 28, 115, 116, 115, 99, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0,
|
|
0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 28, 115, 116, 115, 122, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 2, 0, 0, 1, 115, 0, 0, 1, 116, 0, 0, 0, 20, 115, 116, 99, 111, 0, 0, 0, 0, 0, 0,
|
|
0, 1, 0, 0, 0, 44, 0, 0, 0, 98, 117, 100, 116, 97, 0, 0, 0, 90, 109, 101, 116, 97,
|
|
0, 0, 0, 0, 0, 0, 0, 33, 104, 100, 108, 114, 0, 0, 0, 0, 0, 0, 0, 0, 109, 100, 105,
|
|
114, 97, 112, 112, 108, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 45, 105, 108, 115, 116,
|
|
0, 0, 0, 37, 169, 116, 111, 111, 0, 0, 0, 29, 100, 97, 116, 97, 0, 0, 0, 1, 0, 0, 0,
|
|
0, 76, 97, 118, 102, 53, 54, 46, 52, 48, 46, 49, 48, 49,
|
|
]),
|
|
],
|
|
{ type: "video/mp4" },
|
|
);
|
|
|
|
export function canAutoPlayVideo(
|
|
muted = true,
|
|
timeout = 250,
|
|
inline = false,
|
|
): Promise<{ result: boolean; error: Error | null }> {
|
|
const videoElm = document.createElement("video");
|
|
videoElm.muted = muted;
|
|
if (muted) {
|
|
videoElm.setAttribute("muted", "muted");
|
|
}
|
|
|
|
if (inline) {
|
|
videoElm.setAttribute("playsinline", "playsinline");
|
|
}
|
|
|
|
videoElm.src = URL.createObjectURL(VIDEO);
|
|
|
|
return new Promise(resolve => {
|
|
const playResult = videoElm.play();
|
|
|
|
const timeoutId = setTimeout(() => {
|
|
sendOutput(false, new Error(`Timeout ${timeout} ms has been reached`));
|
|
}, timeout);
|
|
|
|
const sendOutput = (result: boolean, error: Error | null = null) => {
|
|
videoElm.remove();
|
|
videoElm.src = "";
|
|
|
|
clearTimeout(timeoutId);
|
|
resolve({ result, error });
|
|
};
|
|
|
|
if (playResult !== undefined) {
|
|
playResult
|
|
.then(() => {
|
|
sendOutput(true);
|
|
})
|
|
.catch(playError => {
|
|
sendOutput(false, playError);
|
|
});
|
|
} else {
|
|
sendOutput(true);
|
|
}
|
|
});
|
|
}
|
|
|
|
export function isMac() {
|
|
return !!/mac/i.exec(navigator.platform);
|
|
}
|
|
|
|
export function isWindows() {
|
|
return !!/win/i.exec(navigator.platform);
|
|
}
|
|
|
|
export function isIOS() {
|
|
return (
|
|
!!/ipad/i.exec(navigator.platform) ||
|
|
!!/iphone/i.exec(navigator.platform) ||
|
|
!!/ipod/i.exec(navigator.platform)
|
|
);
|
|
}
|
|
|
|
export function isAndroid() {
|
|
/* Android sets navigator.platform to Linux :/ */
|
|
return !!navigator.userAgent.match("Android ");
|
|
}
|
|
|
|
export function isChromeOS() {
|
|
/* ChromeOS sets navigator.platform to Linux :/ */
|
|
return !!navigator.userAgent.match(" CrOS ");
|
|
}
|