//@flow import { isError } from './errorUtils' export function getHash(str: string): number { let hash = 0, i, chr, len if (str.length === 0) return hash for (i = 0, len = str.length; i < len; i++) { chr = str.charCodeAt(i) hash = ((hash << 5) - hash) + chr hash |= 0 // Convert to 32bit integer } return hash } export function pad(n: any, width: number, padder: ?string) { padder = padder || '0' n = n + '' return n.length >= width ? n : new Array(width - n.length + 1).join(padder) + n } export function padRight(str: string, width: number, padder: string) { return str.length >= width ? str : str + new Array(width - str.length + 1).join(padder) } /** * Returns a short and rounded string representing the supplied MS timespan */ export function shortTimespan(totalMs: number): string { totalMs = Math.round(totalMs) const hours = totalMs / (60 * 60 * 1000) const flooredHours = Math.floor(hours) if (flooredHours >= 10) return `${Math.round(hours)}h` if (flooredHours > 0) return `${Math.round(hours * 10) / 10}h` totalMs -= (flooredHours * 60 * 60 * 1000) const min = totalMs / (60 * 1000) const flooredMin = Math.floor(min) if (min >= 10) return `${Math.round(min)}m` if (flooredMin > 0) return `${Math.round(min * 10) / 10}m` totalMs -= (flooredMin * 60 * 1000) const sec = totalMs / 1000 const flooredSec = Math.floor(sec) if (flooredSec >= 10) return `${Math.round(sec)}s` if (flooredSec >= 1) return `${Math.round(sec * 10) / 10}s` if (sec >= 0.1) return `${Math.round(sec * 100) / 100}s` return `${Math.round(sec * 1000) / 1000}s` } export function errorToString(err: any, maxLength: ?number = 100000) { let value = '' let type if (err === null || typeof err === 'undefined') { type = 'none' value = err === null ? 'null' : 'undefined' } else { type = (err.constructor && err.constructor.name) || 'unknown' if (err.stack) { value += err.stack // remove redundant Error prefix if (value.startsWith('Error\n')) value = value.substr('Error'.length) } else { const str = err.toString() if (str !== '[object Object]') value += str } // remove redundant Error type if (value.startsWith('Error: ')) value = value.substr('Error: '.length) // add defined properties if (typeof err === 'object') { for (const key in err) { if (err.hasOwnProperty(key) && err[key] !== undefined) { value += `\n ${key}: ${ isError(err[key]) ? errorToString(err[key], maxLength).replace(/\n/g, '\n ') : (err[key] != null ? err[key].toString() : err[key]) }` } } } } const str = `[${type}] ${value}` return maxLength && maxLength > 0 ? str.substr(0, maxLength) : str } export function errorToObj(err: Error): {} { const STACK_LINE_PREFIX = ' at ' const ret = { message: err.message, type: err.constructor && err.constructor.name || 'unknown', } for (const key in err) { // $FlowIgnore if (err.hasOwnProperty(key) && err[key] !== undefined) { const value = err[key] if (isError(value)) ret[key] = errorToObj(value) else if (Array.isArray(value)) ret[key] = value.map(item => isError(item) ? errorToObj(item) : item) else ret[key] = value } } if (typeof err.stack === 'string') { // $FlowIgnore ret.stack = err.stack .split('\n') .filter(l => l.startsWith(STACK_LINE_PREFIX)) .map(l => l.substr(STACK_LINE_PREFIX.length)) } return ret } export function splitString(str: string): Array { return str == null ? str : str.split(',').map(part => part.trim()).filter(part => !!part) } export function stripProtocolQueryAndIndex(url: string) { return url.split(/[?#]/)[0].split(/:\/\//).pop() } export function queryToObj(query: ?string, itemSep: ?string, keyValueSep: ?string): { [key: string]: string } { const finalItemSep = itemSep || '&' const finalKeyValueSep = keyValueSep || '=' const obj = {} if (query) { query .split(finalItemSep) .map( (item) => item .split(new RegExp(`${finalKeyValueSep}(.*)`)) .splice(0,2) ) .forEach(([ key, value ]) => obj[key] = decodeURIComponent(value)) } return obj } export function objToQuery(obj: ?Object, pairSep: ?string, keyValueSep: ?string): string { obj = obj || {} pairSep = typeof pairSep === 'string' ? pairSep : '&' keyValueSep = typeof keyValueSep === 'string' ? keyValueSep : '=' // $FlowIgnore return Object.keys(obj).map(k => `${k}${keyValueSep}${encodeURIComponent(obj[k])}`).join(pairSep) } export function generateGuid(): string { const chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' let result = '' for (let i = 6; i > 0; --i) result += chars[Math.round(Math.random() * (chars.length - 1))] return result + new Buffer(new Date().getTime().toString().substr(1)).toString('base64') } export function timeMSToStr(timeMS: number, percision: 'year' | 'month' | 'date' | 'hour' | 'minute' | 'second' | 'millisecond'): string { const date = new Date(timeMS) let str = pad(date.getUTCFullYear(), 4) if (percision === 'year') return str str += pad(date.getUTCMonth() + 1, 2) if (percision === 'month') return str str += pad(date.getUTCDate(), 2) if (percision === 'date') return str str += pad(date.getUTCHours(), 2) if (percision === 'hour') return str str += pad(date.getUTCMinutes(), 2) if (percision === 'minute') return str str += pad(date.getUTCSeconds(), 2) if (percision === 'second') return str str += pad(date.getUTCMilliseconds(), 2) if (percision === 'millisecond') return str else throw new Error('Unexpected date percision') }