UNPKG

pimatic

Version:

A home automation server and framework for the Raspberry PI running on node.js

345 lines (282 loc) 8.04 kB
// UMD: https://github.com/umdjs/umd/blob/master/returnExports.js (function (root, factory) { /* global define: false */ if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define([], factory) } else if (typeof exports === 'object') { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory() } else { // Browser globals (root is window) root.humanFormat = factory() } }(this, function () { 'use strict' // ================================================================= function assignBase (dst, src) { var prop for (prop in src) { if (has(src, prop)) { dst[prop] = src[prop] } } } function assign (dst, src) { var i, n for (i = 0, n = arguments.length; i < n; ++i) { src = arguments[i] if (src) { assignBase(dst, src) } } return dst } function compareLongestFirst (a, b) { return b.length - a.length } function compareSmallestFactorFirst (a, b) { return a.factor - b.factor } // https://www.npmjs.org/package/escape-regexp function escapeRegexp (str) { return str.replace(/([.*+?=^!:${}()|[\]\/\\])/g, '\\$1') } function forEach (arr, iterator) { var i, n for (i = 0, n = arr.length; i < n; ++i) { iterator(arr[i], i) } } function forOwn (obj, iterator) { var prop for (prop in obj) { if (has(obj, prop)) { iterator(obj[prop], prop) } } } var has = (function (hasOwnProperty) { return function has (obj, prop) { return obj && hasOwnProperty.call(obj, prop) } })(Object.prototype.hasOwnProperty) var toString = (function (toString_) { return function toString (val) { return toString_.call(val) } })(Object.prototype.toString) function isDefined (val) { /* jshint eqnull:true */ return val != null } var isNumber = (function (tag) { return function isNumber (value) { return (value === value) && (toString(value) === tag) // eslint-disable-line no-self-compare } })(toString(0)) var isString = (function (tag) { return function isString (value) { return (toString(value) === tag) } })(toString('')) function resolve (container, entry) { while (isString(entry)) { entry = container[entry] } return entry } function round (f, n) { if (!n) { return Math.round(f) } var p = Math.pow(10, n) return Math.round(f * p) / p } // ================================================================= function Scale (prefixes) { this._prefixes = prefixes var escapedPrefixes = [] var list = [] forOwn(prefixes, function (factor, prefix) { escapedPrefixes.push(escapeRegexp(prefix)) list.push({ factor: factor, prefix: prefix }) }) // Adds lower cased prefixes for case insensitive fallback. var lcPrefixes = this._lcPrefixes = {} forOwn(prefixes, function (factor, prefix) { var lcPrefix = prefix.toLowerCase() if (!has(prefixes, lcPrefix)) { lcPrefixes[lcPrefix] = prefix } }) list.sort(compareSmallestFactorFirst) this._list = list escapedPrefixes.sort(compareLongestFirst) this._regexp = new RegExp( '^\\s*(\\d+(?:\\.\\d+)?)\\s*(' + escapedPrefixes.join('|') + ')\\s*(.*)\\s*?$', 'i' ) } Scale.create = function Scale$create (prefixesList, base, initExp) { var prefixes = {} var factor = initExp ? Math.pow(base, initExp) : 1 forEach(prefixesList, function (prefix, i) { prefixes[prefix] = Math.pow(base, i + (initExp || 0)) factor *= base }) return new Scale(prefixes) } // Binary search to find the greatest index which has a value <=. Scale.prototype.findPrefix = function Scale$findPrefix (value) { /* jshint bitwise: false */ var list = this._list var low = 0 var high = list.length - 1 var mid, current while (low !== high) { mid = (low + high + 1) >> 1 current = list[mid].factor if (current > value) { high = mid - 1 } else { low = mid } } return list[low] } Scale.prototype.parse = function Scale$parse (str, strict) { var matches = str.match(this._regexp) if (!matches) { return null } var prefix = matches[2] var factor if (has(this._prefixes, prefix)) { factor = this._prefixes[prefix] } else if ( !strict && (prefix = prefix.toLowerCase(), has(this._lcPrefixes, prefix)) ) { prefix = this._lcPrefixes[prefix] factor = this._prefixes[prefix] } else { return null } return { factor: factor, prefix: prefix, unit: matches[3], value: +matches[1] } } // ================================================================= var scales = { // https://en.wikipedia.org/wiki/Binary_prefix binary: Scale.create( ',ki,Mi,Gi,Ti,Pi,Ei,Zi,Yi'.split(','), 1024 ), // https://en.wikipedia.org/wiki/Metric_prefix // // Not all prefixes are present, only those which are multiple of // 1000, because humans usually prefer to see close numbers using // the same unit to ease the comparison. SI: Scale.create( 'y,z,a,f,p,n,µ,m,,k,M,G,T,P,E,Z,Y'.split(','), 1000, -8 ) } var defaults = { scale: 'SI', // Strict mode prevents parsing of incorrectly cased prefixes. strict: false, // Unit to use for formatting. unit: '', // Decimal digits for formatting. decimals: 2, // separator to use between value and units separator: ' ' } function humanFormat (value, opts) { opts = assign({}, defaults, opts) var info = humanFormat$raw(value, opts) var suffix = info.prefix + opts.unit return round(info.value, opts.decimals) + (suffix ? opts.separator + suffix : '') } function humanFormat$parse (str, opts) { var info = humanFormat$parse$raw(str, opts) return info.value * info.factor } function humanFormat$parse$raw (str, opts) { if (!isString(str)) { throw new TypeError('str must be a string') } // Merge default options. opts = assign({}, defaults, opts) // Get current scale. var scale = resolve(scales, opts.scale) if (!scale) { throw new Error('missing scale') } // TODO: the unit should be checked: it might be absent but it // should not differ from the one expected. // // TODO: if multiple units are specified, at least must match and // the returned value should be: { value: <value>, unit: matchedUnit } var info = scale.parse(str, opts.strict) if (!info) { throw new Error('cannot parse str') } return info } function humanFormat$raw (value, opts) { // Zero is a special case, it never has any prefix. if (value === 0) { return { value: 0, prefix: '' } } if (!isNumber(value)) { throw new TypeError('value must be a number') } // Merge default options. opts = assign({}, defaults, opts) // Get current scale. var scale = resolve(scales, opts.scale) if (!scale) { throw new Error('missing scale') } var prefix = opts.prefix var factor if (isDefined(prefix)) { if (!has(scale._prefixes, prefix)) { throw new Error('invalid prefix') } factor = scale._prefixes[prefix] } else { var _ref = scale.findPrefix(value) prefix = _ref.prefix factor = _ref.factor } // Rebase using current factor. value /= factor return { prefix: prefix, value: value } } humanFormat.parse = humanFormat$parse humanFormat$parse.raw = humanFormat$parse$raw humanFormat.raw = humanFormat$raw humanFormat.Scale = Scale return humanFormat }))