numbr
Version:
A fast number formatting library, based on Numeral.js
539 lines (459 loc) • 14 kB
HTML
<!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) < maxConsumes && 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 && 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 < len; ++i){
var range = byteRanges[i];
if(abs < 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 < len; ++i){
var range = abbrRanges[i];
if(abs < 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 && 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 < 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) && !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 < 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 < 10)? '0' + minutes : minutes) +
':' + ((seconds < 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) && !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 < 0 && 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 < 0 && 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] === '.' && 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 && len - i <= 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 && (!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) && !state.isConsumed(this.Id)){
var
precision,
optional = 0;
for(var i = pos, len = input.length; i < len; ++i){
var char = input[i];
if(char !== '0'){
if(char === '['){
for(var k = i+1; k < len; ++k){
char = input[k];
if(char !== '0'){
break;
}
}
if(char === ']' && 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) && !state.isConsumed(this.Id)){
return this.cachedStep;
}
},
step: function(state, output, pos){
if(state.num < 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) &&
!state.isConsumed(dotConsumer.Id) &&
!state.isConsumed(negConsumer.Id) &&
!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 < 0 && state.opts.currencyShouldNeg && !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 < 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>