UNPKG

footinch

Version:

A minimalistic parser for imperial lengths - feet and inch. Does metric too.

169 lines (159 loc) 5.84 kB
// eslint-disable-next-line no-extra-semi ;(function() { "use strict"; const root = this; const previous_format = root.format; const METERS_PER_FOOT = 0.3048; const format = Object.create(null, { FEET : { get: function() { return _forward(METERS_PER_FOOT); }, enumerable: true }, FT : { get: function() { return _forward(METERS_PER_FOOT); }, enumerable: true }, F : { get: function() { return _forward(METERS_PER_FOOT); }, enumerable: true }, METERS : { get: function() { return _forward(1); }, enumerable: true }, M : { get: function() { return _forward(1); }, enumerable: true }, KM : { get: function() { return _forward(1e3); }, enumerable: true }, CM : { get: function() { return _forward(1e-2); }, enumerable: true }, }); function _forward(base) { return Object.create(null, { to : { get: function() { return _fmtFrom(base); } }, as : { get: function() { return _fmtFrom(base); } } }); } function _fmtFrom(base) { return Object.assign( Object.create(null, { FEET : { get : function() { return _fmtToFT(base); } }, FT : { get : function() { return _fmtToFT(base); } }, F : { get : function() { return _fmtToFT(base); } } }), { M : function(digits, sfx=' m') { return _fmtToSI(base, 'm', digits, sfx); }, METERS : function(digits, sfx=' m') { return _fmtToSI(base, 'm', digits, sfx); }, KM : function(digits, sfx=' km') { return _fmtToSI(base, 'km', digits, sfx); }, CM : function(digits, sfx=' cm') { return _fmtToSI(base, 'cm', digits, sfx); }, } ); } function _fmtToFT(base) { return Object.assign( Object.create(null, { IN : { get : function() { return _fmtToFT_IN(base); } }, INCHES: { get : function() { return _fmtToFT_IN(base); } } }), { DEC : function(digits, sfx=' ft') { return _fmtToFT_DEC(base, digits, sfx); }, DECIMAL : function(digits, sfx=' ft') { return _fmtToFT_DEC(base, digits, sfx); } } ); } function _sfxFTIN(sfx) { if (Array.isArray(sfx) && sfx.length == 2) { return sfx; } return ["' ", '"']; } function _fmtToFT_IN(base) { return { DEC : function(digits, sfx=_sfxFTIN()) { return _fmtToFT_IN_DEC(base, digits, sfx); }, DECIMAL : function(digits, sfx=_sfxFTIN()) { return _fmtToFT_IN_DEC(base, digits, sfx); }, FRAC : function(denom, sfx=_sfxFTIN()) { return _fmtToFT_IN_FRAC(base, denom, sfx); }, FRACTIONAL: function(denom, sfx=_sfxFTIN()) { return _fmtToFT_IN_FRAC(base, denom, sfx); } }; } function _fmtToFT_DEC(base, digits, sfx) { return _saneInput(function (valueIn) { const val = valueIn * (base / METERS_PER_FOOT); return 1*val.toFixed(digits) + sfx; }); } function _fmtToFT_IN_DEC(base, digits, sfx) { sfx = _sfxFTIN(sfx); return _saneInput(function (valueIn) { const val = valueIn * (base / METERS_PER_FOOT); let feet = Math.trunc(val); let inches = Math.abs((12 * (val - feet)).toFixed(digits)); if (inches === 12) { inches = 0; feet += Math.sign(val); } return feet + sfx[0] + Math.abs(inches.toFixed(digits)) + sfx[1]; }); } function _fmtToFT_IN_FRAC(base, denom, sfx) { sfx = _sfxFTIN(sfx); if (!denom || !isFinite(denom) || denom < 1) { denom = 16; } const denomConst = denom; return _saneInput(function (valueIn) { let denom = denomConst; // Because "denom" will change during fraction simplification! const val = valueIn * (base / METERS_PER_FOOT); let feet = Math.trunc(val); const inches = 12 * (val - feet); let wholeInches = Math.trunc(inches); let numerator = Math.round(denom * (inches - wholeInches)); if (Math.abs(numerator) === denom) { // "within epsilon", e.g. 7.999999999, -5.499999999 wholeInches += Math.sign(valueIn); numerator = 0; if (Math.abs(wholeInches) === 12) { feet += Math.sign(valueIn); wholeInches = 0; } } const sgn = (feet === 0 && valueIn < 0 ? '-' : ''); if (numerator === 0) { return sgn + feet + sfx[0] + Math.abs(wholeInches) + sfx[1]; } while (numerator % 2 === 0 && denom % 2 === 0) { numerator /= 2; denom /= 2; } return sgn + feet + sfx[0] + Math.abs(wholeInches) + ' ' + Math.abs(numerator) + '/' + denom + sfx[1]; }); } const _siUnits = { pm : 1e-12, nm : 1e-9, um : 1e-6, mm : 1e-3, cm : 1e-2, m : 1, km : 1e+3, }; function _fmtToSI(base, units, digits, sfx) { const unitSz = _siUnits[units]; if (unitSz === undefined) { // This cannot be triggered by the user. Only future bugs could lead us here. throw new Error('Unsupported SI length unit'); } return _saneInput(function (valueIn) { const val = valueIn * (base / unitSz); return 1*val.toFixed(digits) + sfx; }); } // Filter the input value - make sure it is a number function _saneInput(func) { return function (valueIn) { if (!Number.isFinite(valueIn)) { throw new Error('Value to format is not a number'); } return func(valueIn); }; } // ------------------- EXPORTS ---------------------- const toExport = format; // --- Node.js and Browser support shebang --- toExport.noConflict = function() { root.format = previous_format; return toExport; }; if( typeof exports !== 'undefined' ) { if( typeof module !== 'undefined' && module.exports ) { exports = module.exports = toExport; } exports.format = toExport; } else { root.format = toExport; } }).call(this);