UNPKG

numbr

Version:

A fast number formatting library, based on Numeral.js

539 lines (459 loc) 14 kB
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>JSDoc: Source: lib/consumers.js</title> <script src="scripts/prettify/prettify.js"> </script> <script src="scripts/prettify/lang-css.js"> </script> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css"> <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css"> </head> <body> <div id="main"> <h1 class="page-title">Source: lib/consumers.js</h1> <section> <article> <pre class="prettyprint source linenums"><code>"use strict"; var _ = require('lodash'); var percentageConsumer, byteConsumer, abbrConsumer, leftParenthesisConsumer, plusConsumer, intConsumer, ordinalConsumer, timeConsumer, sepConsumer, dotConsumer, decimalConsumer, rightParenthesisConsumer, currencyConsumer, negConsumer; /** * * @alias numbr.Step * @param {function} step * @param {number} weight * @param {object} [opts] * @param {number }[consumed] * @constructor */ function Step(step, weight, opts, consumed){ this.step = step; this.weight = weight; this.opts = opts; this.consumed = consumed || 0; } /** * * @alias numbr.Consumer * @param {string} tokens A list of characters which will trigger this consumer * @param {object} def The consumer definition * @constructor */ function Consumer(tokens, def){ this.Id = -1; this.token = tokens; if(def.consume instanceof Step){ var step = def.consume, self = this; step.step = def.step; if(def.maxConsumes){ var maxConsumes = def.maxConsumes; def.consume = function(state){ return state.timesConsumed(self.Id) &lt; maxConsumes &amp;&amp; step; }; } else { def.consume = function(){ return step; }; } } if(def.cachedStep){ def.cachedStep.step = def.step; } this.consume = def.consume; this.step = def.step; this.cachedStep = def.cachedStep; this.priority = def.priority || 0; this.usedStepFns = _.compact([].concat(def.step, def.usedStepFns)); } /** * @private * @param {number} id */ Consumer.prototype.setId = function(id){ this.Id = id; this.usedStepFns.forEach(function(step){ step.consumerId = id; }); }; function toFixed(num, precision, roundFn){ var power = Math.pow(10, precision), fixed = (roundFn(num * power) / power).toFixed(precision), dot = fixed.lastIndexOf('.'); return [fixed.substring(0, dot), fixed.substring(dot+1)]; } function removeSign(pos, output){ var sign = pos !== -1 &amp;&amp; output[pos]; if(sign){ output[pos] = sign.substring(1); } } percentageConsumer = new Consumer('%', { consume: new Step(null, -900), maxConsumes: 1, step: function(state, output, pos){ output[pos] = '%'; state.num *= 100; state.right = state.num % 1; } }); var byteRanges = [ [ 0 , Math.pow(1024, 1), 'B' ], [ Math.pow(1024, 1), Math.pow(1024, 2), 'KB' ], [ Math.pow(1024, 2), Math.pow(1024, 3), 'MB' ], [ Math.pow(1024, 3), Math.pow(1024, 4), 'GB' ], [ Math.pow(1024, 4), Math.pow(1024, 5), 'TB' ], [ Math.pow(1024, 5), Math.pow(1024, 6), 'PB' ], [ Math.pow(1024, 6), Math.pow(1024, 7), 'EB' ], [ Math.pow(1024, 7), Math.pow(1024, 8), 'ZB' ], [ Math.pow(1024, 8), Infinity , 'YB' ] ]; byteConsumer = new Consumer('b', { consume: new Step(null, -900), step: function(state, output, pos){ var abs = Math.abs(state.num); for(var i = 0, len = byteRanges.length; i &lt; len; ++i){ var range = byteRanges[i]; if(abs &lt; range[1]){ output[pos] = range[2]; state.num /= range[0] || 1; state.right = state.num % 1; return; } } } }); var abbrRanges = [ [ Math.pow(10, 3), Math.pow(10, 6), 'k', 'thousand' ], [ Math.pow(10, 6), Math.pow(10, 9), 'm', 'million' ], [ Math.pow(10, 9), Math.pow(10, 12), 'b', 'billion' ], [ Math.pow(10, 12), Infinity, 't', 'trillion' ] ], abbrModifier = { 'K': 0, 'M': 1, 'B': 2, 'T': 3 }; function abbrSingleRangeStep(state, output, pos){ var range = state.opts.abbrRange; if(Math.abs(state.num) >= range[0]){ abbrDoOutput(range, state, output, pos); } } function abbrBestFitStep(state, output, pos){ var abs = Math.abs(state.num); for(var i = 0, len = abbrRanges.length; i &lt; len; ++i){ var range = abbrRanges[i]; if(abs &lt; range[1]){ abbrDoOutput(range, state, output, pos); return; } } } function abbrDoOutput(range, state, output, pos){ output[pos] = state.lang.abbreviations[range[3]]; state.num /= range[0]; state.right = state.num % 1; } abbrConsumer = new Consumer('a', { usedStepFns: [abbrSingleRangeStep, abbrBestFitStep], consume: function(state, input, pos){ var concrete = input[pos+1], range = concrete &amp;&amp; abbrRanges[abbrModifier[concrete]]; return range? new Step(abbrSingleRangeStep, -900, { abbrRange: range }, 1) : new Step(abbrBestFitStep, -900); } }); leftParenthesisConsumer = new Consumer('(', { consume: new Step(null, 0), step: function(state, output, pos){ if(state.num &lt; 0){ output[pos] = '('; } } }); plusConsumer = new Consumer('+', { consume: new Step(null, 0), step: function(state, output, pos){ if(state.num > 0){ state.singPos = pos; output[pos] = '+'; } } }); intConsumer = new Consumer('0', { cachedStep: new Step(null, 0), consume: function(state){ if(!state.isConsumed(dotConsumer.Id) &amp;&amp; !state.isConsumed(this.Id)){ return this.cachedStep; } }, step: function(state, output, pos){ var num = state.num, leftStr, decimalPrecision = state.opts.decimalPrecision; if(decimalPrecision){ var fixed = toFixed(num, decimalPrecision, state.roundFn); leftStr = fixed[0]; state.rightStr = fixed[1]; } else { leftStr = '' + state.roundFn(state.num); } if(num &lt; 0){ state.signPos = pos; } output[pos] = leftStr; } }); ordinalConsumer = new Consumer('o', { consume: new Step(null, 0), step: function(state, output, pos){ output[pos] = state.lang.ordinal(state.num); } }); var timeFullToken = '00:00:00'; timeConsumer = new Consumer('0', { cachedStep: new Step(null, 0, null, timeFullToken.length - 1), priority: -10, consume: function(state, input, pos){ if(input.indexOf(timeFullToken, pos) === pos){ return this.cachedStep; } }, step: function(state, output, pos){ var hours = Math.floor(state.num/60/60), minutes = Math.floor((state.num - (hours * 60 * 60))/60), seconds = Math.round(state.num - (hours * 60 * 60) - (minutes * 60)); output[pos] = hours + ':' + ((minutes &lt; 10)? '0' + minutes : minutes) + ':' + ((seconds &lt; 10) ? '0' + seconds : seconds); } }); var sepStep0 = new Step(null, 300, null, 0), sepStep1 = new Step(null, 300, null, 1); sepConsumer = new Consumer(',', { consume: function(state, input, pos){ if(state.isConsumed(intConsumer.Id) &amp;&amp; !state.isConsumed(this.Id)){ return input[pos+1] === '0'? sepStep1 : sepStep0; } }, step: function(state, output){ var intIndex = state.pos[intConsumer.Id]; output[intIndex] = output[intIndex].replace(/(\d)(?=(\d{3})+(?!\d))/g, '$1' + state.lang.delimiters.thousands); } }); sepStep0.step = sepStep1.step = sepConsumer.step; function dotFixedStep(state, output, pos){ var dot = state.lang.delimiters.decimal; if(state.num &lt; 0 &amp;&amp; state.signPos === -1){ output[pos] = '-' + dot; state.signPos = pos; } else { output[pos] = dot; } } function dotOptStep(state, output, pos){ if(state.right !== 0){ state.optionalDot = true; var dot = state.lang.delimiters.decimal; if(state.num &lt; 0 &amp;&amp; state.signPos === -1){ output[pos] = '-' + dot; state.signPos = pos; } else { output[pos] = dot; } } } var dotFixedStepObj = new Step(dotFixedStep, 300), dotOptStepObj = new Step(dotOptStep, 300, null, 2); dotConsumer = new Consumer('.[', { usedStepFns: [dotFixedStep, dotOptStep], consume: function(state, input, pos){ if(input[pos] === '.'){ return dotFixedStepObj; } if(input[pos+1] === '.' &amp;&amp; input[pos+2] === ']'){ return dotOptStepObj; } } }); function decimalFixedStep(state, output, pos){ var dotIndex = getDotIndex(state.pos, output); if(dotIndex !== -1){ var decimals = state.rightStr || toFixed(state.right, state.opts.decimalPrecision, state.roundFn)[1]; setDecimals(state.optionalDot, output, pos, dotIndex, decimals); } } function decimalOptStep(state, output, pos){ var dotIndex = getDotIndex(state.pos, output); if(dotIndex !== -1){ var decimals = state.rightStr || toFixed(state.right, state.opts.decimalPrecision, state.roundFn)[1], optional = state.opts.optionalPrecision; // Eliminate optional '0's from the right for(var len = decimals.length, i = len - 1; i >= 0 &amp;&amp; len - i &lt;= optional; --i){ if(decimals[i] !== '0'){ break; } } decimals = decimals.substring(0, i+1); setDecimals(state.optionalDot, output, pos, dotIndex, decimals); } } function getDotIndex(pos, output){ var dotIndex = pos[dotConsumer.Id]; return typeof output[dotIndex] === 'undefined'? -1 : dotIndex; } function setDecimals(optionalDot, output, pos, dotIndex, decimals){ if(decimals.length > 0 &amp;&amp; (!optionalDot|| Number('0.'+decimals) !== 0)){ output[pos] = decimals; } else { // Eliminate the decimal point output[dotIndex] = null; } } decimalConsumer = new Consumer('0[', { priority: 10, usedStepFns: [decimalOptStep, decimalFixedStep], consume: function(state, input, pos){ if(state.isConsumed(dotConsumer.Id) &amp;&amp; !state.isConsumed(this.Id)){ var precision, optional = 0; for(var i = pos, len = input.length; i &lt; len; ++i){ var char = input[i]; if(char !== '0'){ if(char === '['){ for(var k = i+1; k &lt; len; ++k){ char = input[k]; if(char !== '0'){ break; } } if(char === ']' &amp;&amp; k > i+1){ optional = k - i - 1; } } break; } } precision = i + optional - pos; var consumed = i - pos - 1, opts = { decimalPrecision: precision, optionalPrecision: optional }; if(optional > 0){ return new Step(decimalOptStep, 600, opts, consumed + optional + 2); } else { return new Step(decimalFixedStep, 600, opts, consumed); } } } }); rightParenthesisConsumer = new Consumer(')', { cachedStep: new Step(null, 600), consume: function(state){ if(state.isConsumed(leftParenthesisConsumer.Id) &amp;&amp; !state.isConsumed(this.Id)){ return this.cachedStep; } }, step: function(state, output, pos){ if(state.num &lt; 0){ removeSign(state.signPos, output); state.signPos = -1; output[pos] = ')'; } } }); currencyConsumer = new Consumer('$', { consume: function(state){ // Should only place negative sign before currency sign if we havent found any of '0', '.0', '-' or '(' before var shouldNeg = !state.isConsumed(intConsumer.Id) &amp;&amp; !state.isConsumed(dotConsumer.Id) &amp;&amp; !state.isConsumed(negConsumer.Id) &amp;&amp; !state.isConsumed(leftParenthesisConsumer.Id); return new Step(this.step, 900, { currencyShouldNeg: shouldNeg }); }, step: function(state, output, pos){ var symbol = state.lang.currency.symbol; if(state.num &lt; 0 &amp;&amp; state.opts.currencyShouldNeg &amp;&amp; !output[state.pos[rightParenthesisConsumer.Id]]){ removeSign(state.signPos, output); state.signPos = pos; output[pos] = '-' + symbol; } else { output[pos] = symbol; } } }); negConsumer = new Consumer('-', { consume: new Step(null, 1200), step: function(state, output, pos){ if(state.num &lt; 0){ removeSign(state.signPos, output); output[pos] = '-'; } } }); var exp = [ percentageConsumer, byteConsumer, abbrConsumer, leftParenthesisConsumer, plusConsumer, intConsumer, ordinalConsumer, timeConsumer, sepConsumer, dotConsumer, decimalConsumer, rightParenthesisConsumer, currencyConsumer, negConsumer ]; exp.Consumer = Consumer; exp.Step = Step; module.exports = exp;</code></pre> </article> </section> </div> <nav> <h2><a href="index.html">Index</a></h2><h3>Classes</h3><ul><li><a href="numbr.CompiledFormat.html">CompiledFormat</a></li><li><a href="numbr.Consumer.html">Consumer</a></li><li><a href="numbr.Numbr.html">Numbr</a></li><li><a href="numbr.Step.html">Step</a></li></ul><h3>Namespaces</h3><ul><li><a href="numbr.html">numbr</a></li></ul> </nav> <br clear="both"> <footer> Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.3.0-alpha9</a> on Tue Sep 30 2014 02:08:18 GMT-0500 (Central Daylight Time (Mexico)) </footer> <script> prettyPrint(); </script> <script src="scripts/linenumber.js"> </script> </body> </html>