plugins-evert/utility/convert.ts

283 lines
7.1 KiB
TypeScript

import configureMeasurements, { allMeasures } from 'convert-units';
export const convert = configureMeasurements(allMeasures);
export const bases: {[key: number]: string[]} = {
2: ['bin', 'binary'],
8: ['oct', 'octal'],
10: ['dec', 'decimal'],
16: ['hex', 'hexadecimal']
};
export function getBaseNum(base: string): number | null {
let result = null;
for (const i in bases) {
const defs = bases[i];
if (defs.indexOf(base) === -1) {
continue;
}
result = parseInt(i, 10);
}
if (result) {
return result;
}
if (base.indexOf('b') !== 0) {
return null;
}
const matcher = base.match(/b(?:ase)?-?(\d+)/);
if (!matcher || !matcher[1] || isNaN(parseInt(matcher[1], 10))) {
return null;
}
return parseInt(matcher[1], 10);
}
export function rgbToHex(r: number, g: number, b: number): string {
// tslint:disable-next-line: no-bitwise
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
export function RGBToHSL(r: number, g: number, b: number): {[key: string]: number} {
// Make r, g, and b fractions of 1
r /= 255;
g /= 255;
b /= 255;
// Find greatest and smallest channel values
const cmin = Math.min(r, g, b);
const cmax = Math.max(r, g, b);
const delta = cmax - cmin;
let h = 0;
let s = 0;
let l = 0;
// Calculate hue
// No difference
if (delta === 0) {
h = 0;
} // Red is max
else if (cmax === r) {
h = ((g - b) / delta) % 6;
} // Green is max
else if (cmax === g) {
h = (b - r) / delta + 2;
} // Blue is max
else {
h = (r - g) / delta + 4;
}
h = Math.round(h * 60);
// Make negative hues positive behind 360°
if (h < 0) {
h += 360;
}
// Calculate lightness
l = (cmax + cmin) / 2;
// Calculate saturation
s = delta === 0 ? 0 : delta / (1 - Math.abs(2 * l - 1));
// Multiply l and s by 100
s = +(s * 100).toFixed(1);
l = +(l * 100).toFixed(1);
return { h, s, l };
}
export function hexToRgb(hex: string): {[key: string]: number} | null {
// Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
hex = hex.replace(shorthandRegex, (m, r, g, b) => {
return r + r + g + g + b + b;
});
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
return result ? {
r: parseInt(result[1], 16),
g: parseInt(result[2], 16),
b: parseInt(result[3], 16)
} : null;
}
// Create an object to efficiently translate unit names to their abbreviations
let unitIndex: { [x: string]: string[] } = {};
let backupLowercaseUnitTable: { [x: string]: string } = {};
export function createUnitIndex(): void {
const fullList = convert().list();
fullList.forEach((unit) => {
const { abbr, singular, plural } = unit;
if (!unitIndex[abbr]) {
unitIndex[abbr] = [
abbr, singular.toLowerCase(), plural.toLowerCase()
];
// liter <-> litres
if (abbr === 'l') {
unitIndex[abbr].push('liter', 'liters');
}
// meter -> metre
if (singular.includes('meter')) {
unitIndex[abbr].push(
singular.replace('meter', 'metre').toLowerCase(),
plural.replace('meter', 'metre').toLowerCase(),
);
}
// metre -> meter
if (singular.includes('metre')) {
unitIndex[abbr].push(
singular.replace('metre', 'meter').toLowerCase(),
plural.replace('metre', 'meter').toLowerCase(),
);
}
// degree(s) Celsius -> celsius
if (singular.startsWith('degree ')) {
unitIndex[abbr].push(
singular.split(' ')[1].toLowerCase(),
);
}
// " per " -> "/"
const appendages: string[] = [];
unitIndex[abbr].forEach((entry) => {
if (entry.includes(' per ')) {
appendages.push(entry.replace(' per ', '/'));
}
});
unitIndex[abbr].push(...appendages);
backupLowercaseUnitTable[abbr.toLowerCase()] = abbr;
}
});
}
// Get a unit by it's full name or abbreviation
export function getUnitAbbreviation(fullText: string): string | null {
// Regular abbreviation
if (unitIndex[fullText]) {
return fullText;
}
// Lowercase abbreviation
const lowerText = fullText.toLowerCase();
if (backupLowercaseUnitTable[lowerText]) {
return backupLowercaseUnitTable[lowerText];
}
// Text search
const found = Object.keys(unitIndex).reduce((previous, current) => {
const arrayList = unitIndex[current];
if (arrayList.includes(lowerText)) {
return current;
}
return previous;
}, '');
return found || null;
}
export function wipeCaches(): void {
unitIndex = {};
backupLowercaseUnitTable = {};
}
export function ASCIIBinaryConverter(input: string, simplified: string[]): string {
let response = '';
let strArr;
let i;
let text = input.split(' ').slice(2).join(' ');
switch (simplified[0] ? simplified[0].toUpperCase() : null) {
case 'ENCODE':
strArr = text.split('');
for (i in strArr) {
response += ('0000000' +
parseInt(Buffer.from(strArr[i].toString(), 'utf8').toString('hex'), 16).toString(2)).slice(-8);
}
response = response.substring(1);
break;
case 'DECODE':
text = text.split(' ').join('');
i = 0;
while (8 * (i + 1) <= text.length) {
response += Buffer.from(parseInt(text.substring(8 * i, 8), 2).toString(16), 'hex').toString('utf8');
i++;
}
response = response.replace(/\n/g, '\\n').replace(/\r/g, '\\r');
}
return response;
}
export function ASCIIHexConverter(input: string, simplified: string[]): string {
let response = '';
let i;
let text = input.split(' ').slice(2).join(' ');
switch (simplified[0] ? simplified[0].toUpperCase() : null) {
case 'DECODE':
text = text.replace(/\s/g, '');
for (i = 0; i < text.length; i += 2) {
response += String.fromCharCode(parseInt(text.substring(i, 2), 16));
}
response = response.replace(/\n/g, '\\n').replace(/\r/g, '\\r');
break;
case 'ENCODE':
for (i = 0; i < text.length; i++) {
response += text.charCodeAt(i).toString(16) + ' ';
}
break;
}
return response;
}
export function base64Converter(input: string, simplified: string[]): string {
let response = '';
const text = input.split(' ').slice(2).join(' ');
switch (simplified[0] ? simplified[0].toUpperCase() : null) {
case 'DECODE':
response = (Buffer.from(text, 'base64').toString('ascii')).replace(/\n/g, '\\n').replace(/\r/g, '\\r');
break;
case 'ENCODE':
response = Buffer.from(text).toString('base64');
break;
}
return response;
}
export function baseConverter(simplified: string[]): string {
if (simplified.length < 3) {
throw new Error('Too few arguments!');
}
const input = simplified[0];
const src = simplified[1].toLowerCase();
const dst = simplified[2].toLowerCase();
const srcBase = getBaseNum(src);
const dstBase = getBaseNum(dst);
if (!srcBase || !dstBase || dstBase > 36 || dstBase < 2 || srcBase > 36 || srcBase < 2) {
throw new Error('Invalid conversion.');
}
const decimal = parseInt(input, srcBase);
return decimal.toString(dstBase);
}