/**
* Generic javascript utilities.
*
* @module Utilities
*/
/**
* Find the longest common initial slice of a set of arrays.
*
* @function
* @param {...Array} arrays - The arrays to compare.
* @returns {Array} - The longest common initial slice of the arrays.
*/
const commonInitialSlice = (...arg) => {
if (!arg.length) return
let k=0; while (arg.every( A => A[k] === arg[0][k] && k<arg[0].length )) k++
return arg[0].slice(0,k)
}
/**
* Checks a filename to see if it has the right extension and adds the extension
* if it doesn't. The default extension is 'js'.
*
* @function
* @param {string} name - The filename to check.
* @param {string} [ext='js'] - The extension to add if missing.
* @returns {string} - The filename with the correct extension.
*/
const checkExtension = ( name , ext = 'js' ) => {
ext = (ext.startsWith('.')) ? ext : '.'+ext
return ( name.endsWith(ext)) ? name : name + ext
}
/**
* Checks a foldername to see if it has a trailing '/' and adds it if
* if it doesn't.
*
* @function
* @param {string} folder - The folder name to check.
* @returns {string} - The foldername with the correct extension.
*/
const checkFolder = ( folder ) =>
( folder.endsWith('/')) ? folder : folder+'/'
/**
* Return a string of $n$ spaces (or other character).
*
* @function
* @param {number} n - the length of the string
* @param {string} [char=' '] - the character to use
* @returns {string} the resulting string
*/
const tab = (n , char=' ') => { return Array.seq(()=>'',1,n+1).join(char) }
/**
* Indent string $s$ with a tab of $n$ spaces. If $s$ contains multiple lines,
* indent each line.
*
* @function
* @param {string} s - the string to indent
* @param {number} n - the number of spaces to indent
* @returns {string} the resulting string
*/
const indent = (s,n) => {
const t = tab(n)
return t+s.replaceAll(/\n(.)/g,'\n'+t+'$1')
}
// make a right justified line number consisting of a minimum width padded on
// the right with a given suffix
/**
* Returns a right justified line number consisting of a minimum width padded on
* the right with a given suffix.
*
* @function
* @param {number} n - The line number.
* @param {number} [width=4] - The minimum width of the line number.
* @param {string} [suffix=': '] - The suffix to be added to the line number.
* @returns {string} The right justified line number.
*/
const lineNum = (n,width=4,suffix=': ') => {
const num = String(n)
return String(n).padStart(width-num.length-1, ' ')+suffix
}
// The string of unicode numerical subscripts, '₀₁₂₃₄₅₆₇₈₉'.
const subscriptDigits = '₀₁₂₃₄₅₆₇₈₉'
/**
* Convert the integer $n$ to a string consisting of the corresponding unicode
* subscript.
*
* @function
* @param {number} n - The integer to be converted.
*/
const subscript = n => [...n.toString()].map(d => subscriptDigits[d]).join('')
/**
* Convert RGB to HEX
*/
const rgb2hex = (r, g, b) => {
const hexR = r.toString(16).padStart(2, '0').toUpperCase()
const hexG = g.toString(16).padStart(2, '0').toUpperCase()
const hexB = b.toString(16).padStart(2, '0').toUpperCase()
return `#${hexR}${hexG}${hexB}`;
}
const msToTime = ms => {
// Calculate hours, minutes, and seconds
const hours = Math.floor(ms / (1000 * 60 * 60))
const minutes = Math.floor((ms % (1000 * 60 * 60)) / (1000 * 60))
const seconds = Math.floor((ms % (1000 * 60))) / 1000
const Hours = (hours>0) ? `${hours} hr ` : ''
const Minutes = (minutes>0) ? `${minutes} min ` : ''
const Seconds = `${seconds.toFixed(3)} sec`
return Hours+Minutes+Seconds
}
/**
* Report the time it took to execute function `f`, passed as an argument.
*/
const timer = (f,msg='') => {
let start = Date.now()
f()
console.log(`${msg} ${(Date.now()-start)} ms`)
}
export default {
commonInitialSlice, checkExtension, checkFolder, tab, indent,
lineNum, subscript, rgb2hex, msToTime, timer
}
source