META-INF.resources.bower_components.globalize.dist.globalize.number.js Maven / Gradle / Ivy
/**
* Globalize v1.2.3
*
* http://github.com/jquery/globalize
*
* Copyright jQuery Foundation and other contributors
* Released under the MIT license
* http://jquery.org/license
*
* Date: 2017-03-17T01:41Z
*/
/*!
* Globalize v1.2.3 2017-03-17T01:41Z Released under the MIT license
* http://git.io/TrdQbw
*/
(function (root, factory) {
// UMD returnExports
if (typeof define === "function" && define.amd) {
// AMD
define([
"cldr",
"../globalize",
"cldr/event",
"cldr/supplemental"
], factory);
} else if (typeof exports === "object") {
// Node, CommonJS
module.exports = factory(require("cldrjs"), require("../globalize"));
} else {
// Global
factory(root.Cldr, root.Globalize);
}
}(this, function (Cldr, Globalize) {
var createError = Globalize._createError,
regexpEscape = Globalize._regexpEscape,
runtimeBind = Globalize._runtimeBind,
stringPad = Globalize._stringPad,
validateCldr = Globalize._validateCldr,
validateDefaultLocale = Globalize._validateDefaultLocale,
validateParameterPresence = Globalize._validateParameterPresence,
validateParameterRange = Globalize._validateParameterRange,
validateParameterType = Globalize._validateParameterType,
validateParameterTypePlainObject = Globalize._validateParameterTypePlainObject;
var createErrorUnsupportedFeature = function (feature) {
return createError("E_UNSUPPORTED", "Unsupported {feature}.", {
feature: feature
});
};
var validateParameterTypeNumber = function (value, name) {
validateParameterType(
value,
name,
value === undefined || typeof value === "number",
"Number"
);
};
var validateParameterTypeString = function (value, name) {
validateParameterType(
value,
name,
value === undefined || typeof value === "string",
"a string"
);
};
/**
* goupingSeparator( number, primaryGroupingSize, secondaryGroupingSize )
*
* @number [Number].
*
* @primaryGroupingSize [Number]
*
* @secondaryGroupingSize [Number]
*
* Return the formatted number with group separator.
*/
var numberFormatGroupingSeparator = function (number, primaryGroupingSize, secondaryGroupingSize) {
var index,
currentGroupingSize = primaryGroupingSize,
ret = "",
sep = ",",
switchToSecondary = secondaryGroupingSize ? true : false;
number = String(number).split(".");
index = number[0].length;
while (index > currentGroupingSize) {
ret = number[0].slice(index - currentGroupingSize, index) +
(ret.length ? sep : "") + ret;
index -= currentGroupingSize;
if (switchToSecondary) {
currentGroupingSize = secondaryGroupingSize;
switchToSecondary = false;
}
}
number[0] = number[0].slice(0, index) + (ret.length ? sep : "") + ret;
return number.join(".");
};
/**
* integerFractionDigits( number, minimumIntegerDigits, minimumFractionDigits,
* maximumFractionDigits, round, roundIncrement )
*
* @number [Number]
*
* @minimumIntegerDigits [Number]
*
* @minimumFractionDigits [Number]
*
* @maximumFractionDigits [Number]
*
* @round [Function]
*
* @roundIncrement [Function]
*
* Return the formatted integer and fraction digits.
*/
var numberFormatIntegerFractionDigits = function (number, minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, round,
roundIncrement) {
// Fraction
if (maximumFractionDigits) {
// Rounding
if (roundIncrement) {
number = round(number, roundIncrement);
// Maximum fraction digits
} else {
number = round(number, {exponent: -maximumFractionDigits});
}
// Minimum fraction digits
if (minimumFractionDigits) {
number = String(number).split(".");
number[1] = stringPad(number[1] || "", minimumFractionDigits, true);
number = number.join(".");
}
} else {
number = round(number);
}
number = String(number);
// Minimum integer digits
if (minimumIntegerDigits) {
number = number.split(".");
number[0] = stringPad(number[0], minimumIntegerDigits);
number = number.join(".");
}
return number;
};
/**
* toPrecision( number, precision, round )
*
* @number (Number)
*
* @precision (Number) significant figures precision (not decimal precision).
*
* @round (Function)
*
* Return number.toPrecision( precision ) using the given round function.
*/
var numberToPrecision = function (number, precision, round) {
var roundOrder;
// Get number at two extra significant figure precision.
number = number.toPrecision(precision + 2);
// Then, round it to the required significant figure precision.
roundOrder = Math.ceil(Math.log(Math.abs(number)) / Math.log(10));
roundOrder -= precision;
return round(number, {exponent: roundOrder});
};
/**
* toPrecision( number, minimumSignificantDigits, maximumSignificantDigits, round )
*
* @number [Number]
*
* @minimumSignificantDigits [Number]
*
* @maximumSignificantDigits [Number]
*
* @round [Function]
*
* Return the formatted significant digits number.
*/
var numberFormatSignificantDigits = function (number, minimumSignificantDigits, maximumSignificantDigits, round) {
var atMinimum, atMaximum;
// Sanity check.
if (minimumSignificantDigits > maximumSignificantDigits) {
maximumSignificantDigits = minimumSignificantDigits;
}
atMinimum = numberToPrecision(number, minimumSignificantDigits, round);
atMaximum = numberToPrecision(number, maximumSignificantDigits, round);
// Use atMaximum only if it has more significant digits than atMinimum.
number = +atMinimum === +atMaximum ? atMinimum : atMaximum;
// Expand integer numbers, eg. 123e5 to 12300.
number = (+number).toString(10);
if ((/e/).test(number)) {
throw createErrorUnsupportedFeature({
feature: "integers out of (1e21, 1e-7)"
});
}
// Add trailing zeros if necessary.
if (minimumSignificantDigits - number.replace(/^0+|\./g, "").length > 0) {
number = number.split(".");
number[1] = stringPad(number[1] || "", minimumSignificantDigits - number[0].replace(/^0+/, "").length, true);
number = number.join(".");
}
return number;
};
/**
* removeLiteralQuotes( string )
*
* Return:
* - `` if input string is `''`.
* - `o'clock` if input string is `'o''clock'`.
* - `foo` if input string is `foo`, i.e., return the same value in case it isn't a single-quoted
* string.
*/
var removeLiteralQuotes = function (string) {
if (string[0] + string[string.length - 1] !== "''") {
return string;
}
if (string === "''") {
return "";
}
return string.replace(/''/g, "'").slice(1, -1);
};
/**
* format( number, properties )
*
* @number [Number].
*
* @properties [Object] Output of number/format-properties.
*
* Return the formatted number.
* ref: http://www.unicode.org/reports/tr35/tr35-numbers.html
*/
var numberFormat = function (number, properties) {
var infinitySymbol, maximumFractionDigits, maximumSignificantDigits, minimumFractionDigits,
minimumIntegerDigits, minimumSignificantDigits, nanSymbol, nuDigitsMap, padding, prefix,
primaryGroupingSize, pattern, ret, round, roundIncrement, secondaryGroupingSize, suffix,
symbolMap;
padding = properties[1];
minimumIntegerDigits = properties[2];
minimumFractionDigits = properties[3];
maximumFractionDigits = properties[4];
minimumSignificantDigits = properties[5];
maximumSignificantDigits = properties[6];
roundIncrement = properties[7];
primaryGroupingSize = properties[8];
secondaryGroupingSize = properties[9];
round = properties[15];
infinitySymbol = properties[16];
nanSymbol = properties[17];
symbolMap = properties[18];
nuDigitsMap = properties[19];
// NaN
if (isNaN(number)) {
return nanSymbol;
}
if (number < 0) {
pattern = properties[12];
prefix = properties[13];
suffix = properties[14];
} else {
pattern = properties[11];
prefix = properties[0];
suffix = properties[10];
}
// Infinity
if (!isFinite(number)) {
return prefix + infinitySymbol + suffix;
}
ret = prefix;
// Percent
if (pattern.indexOf("%") !== -1) {
number *= 100;
// Per mille
} else if (pattern.indexOf("\u2030") !== -1) {
number *= 1000;
}
// Significant digit format
if (!isNaN(minimumSignificantDigits * maximumSignificantDigits)) {
number = numberFormatSignificantDigits(number, minimumSignificantDigits,
maximumSignificantDigits, round);
// Integer and fractional format
} else {
number = numberFormatIntegerFractionDigits(number, minimumIntegerDigits,
minimumFractionDigits, maximumFractionDigits, round, roundIncrement);
}
// Remove the possible number minus sign
number = number.replace(/^-/, "");
// Grouping separators
if (primaryGroupingSize) {
number = numberFormatGroupingSeparator(number, primaryGroupingSize,
secondaryGroupingSize);
}
ret += number;
// Scientific notation
// TODO implement here
// Padding/'([^']|'')+'|''|[.,\-+E%\u2030]/g
// TODO implement here
ret += suffix;
return ret.replace(/('([^']|'')+'|'')|./g, function (character, literal) {
// Literals
if (literal) {
return removeLiteralQuotes(literal);
}
// Symbols
character = character.replace(/[.,\-+E%\u2030]/, function (symbol) {
return symbolMap[symbol];
});
// Numbering system
if (nuDigitsMap) {
character = character.replace(/[0-9]/, function (digit) {
return nuDigitsMap[+digit];
});
}
return character;
});
};
var numberFormatterFn = function (properties) {
return function numberFormatter(value) {
validateParameterPresence(value, "value");
validateParameterTypeNumber(value, "value");
return numberFormat(value, properties);
};
};
/**
* NumberingSystem( cldr )
*
* - http://www.unicode.org/reports/tr35/tr35-numbers.html#otherNumberingSystems
* - http://cldr.unicode.org/index/bcp47-extension
* - http://www.unicode.org/reports/tr35/#u_Extension
*/
var numberNumberingSystem = function (cldr) {
var nu = cldr.attributes["u-nu"];
if (nu) {
if (nu === "traditio") {
nu = "traditional";
}
if (["native", "traditional", "finance"].indexOf(nu) !== -1) {
// Unicode locale extension `u-nu` is set using either (native, traditional or
// finance). So, lookup the respective locale's numberingSystem and return it.
return cldr.main(["numbers/otherNumberingSystems", nu]);
}
// Unicode locale extension `u-nu` is set with an explicit numberingSystem. Return it.
return nu;
}
// Return the default numberingSystem.
return cldr.main("numbers/defaultNumberingSystem");
};
/**
* nuMap( cldr )
*
* @cldr [Cldr instance].
*
* Return digits map if numbering system is different than `latn`.
*/
var numberNumberingSystemDigitsMap = function (cldr) {
var aux,
nu = numberNumberingSystem(cldr);
if (nu === "latn") {
return;
}
aux = cldr.supplemental(["numberingSystems", nu]);
if (aux._type !== "numeric") {
throw createErrorUnsupportedFeature("`" + aux._type + "` numbering system");
}
return aux._digits;
};
/**
* EBNF representation:
*
* number_pattern_re = prefix?
* padding?
* (integer_fraction_pattern | significant_pattern)
* scientific_notation?
* suffix?
*
* prefix = non_number_stuff
*
* padding = "*" regexp(.)
*
* integer_fraction_pattern = integer_pattern
* fraction_pattern?
*
* integer_pattern = regexp([#,]*[0,]*0+)
*
* fraction_pattern = "." regexp(0*[0-9]*#*)
*
* significant_pattern = regexp([#,]*@+#*)
*
* scientific_notation = regexp(E\+?0+)
*
* suffix = non_number_stuff
*
* non_number_stuff = regexp(('[^']+'|''|[^*#@0,.E])*)
*
*
* Regexp groups:
*
* 0: number_pattern_re
* 1: prefix
* 2: -
* 3: -
* 4: padding
* 5: (integer_fraction_pattern | significant_pattern)
* 6: integer_fraction_pattern
* 7: integer_pattern
* 8: fraction_pattern
* 9: significant_pattern
* 10: scientific_notation
* 11: suffix
* 12: -
*/
var numberPatternRe = (/^(('([^']|'')*'|[^*#@0,.E])*)(\*.)?((([#,]*[0,]*0+)(\.0*[0-9]*#*)?)|([#,]*@+#*))(E\+?0+)?(('[^']+'|''|[^*#@0,.E])*)$/);
/**
* format( number, pattern )
*
* @number [Number].
*
* @pattern [String] raw pattern for numbers.
*
* Return the formatted number.
* ref: http://www.unicode.org/reports/tr35/tr35-numbers.html
*/
var numberPatternProperties = function (pattern) {
var aux1, aux2, fractionPattern, integerFractionOrSignificantPattern, integerPattern,
maximumFractionDigits, maximumSignificantDigits, minimumFractionDigits,
minimumIntegerDigits, minimumSignificantDigits, padding, prefix, primaryGroupingSize,
roundIncrement, scientificNotation, secondaryGroupingSize, significantPattern, suffix;
pattern = pattern.match(numberPatternRe);
if (!pattern) {
throw new Error("Invalid pattern: " + pattern);
}
prefix = pattern[1];
padding = pattern[4];
integerFractionOrSignificantPattern = pattern[5];
significantPattern = pattern[9];
scientificNotation = pattern[10];
suffix = pattern[11];
// Significant digit format
if (significantPattern) {
significantPattern.replace(/(@+)(#*)/, function (match, minimumSignificantDigitsMatch, maximumSignificantDigitsMatch) {
minimumSignificantDigits = minimumSignificantDigitsMatch.length;
maximumSignificantDigits = minimumSignificantDigits +
maximumSignificantDigitsMatch.length;
});
// Integer and fractional format
} else {
fractionPattern = pattern[8];
integerPattern = pattern[7];
if (fractionPattern) {
// Minimum fraction digits, and rounding.
fractionPattern.replace(/[0-9]+/, function (match) {
minimumFractionDigits = match;
});
if (minimumFractionDigits) {
roundIncrement = +("0." + minimumFractionDigits);
minimumFractionDigits = minimumFractionDigits.length;
} else {
minimumFractionDigits = 0;
}
// Maximum fraction digits
// 1: ignore decimal character
maximumFractionDigits = fractionPattern.length - 1 /* 1 */;
}
// Minimum integer digits
integerPattern.replace(/0+$/, function (match) {
minimumIntegerDigits = match.length;
});
}
// Scientific notation
if (scientificNotation) {
throw createErrorUnsupportedFeature({
feature: "scientific notation (not implemented)"
});
}
// Padding
if (padding) {
throw createErrorUnsupportedFeature({
feature: "padding (not implemented)"
});
}
// Grouping
if ((aux1 = integerFractionOrSignificantPattern.lastIndexOf(",")) !== -1) {
// Primary grouping size is the interval between the last group separator and the end of
// the integer (or the end of the significant pattern).
aux2 = integerFractionOrSignificantPattern.split(".")[0];
primaryGroupingSize = aux2.length - aux1 - 1;
// Secondary grouping size is the interval between the last two group separators.
if ((aux2 = integerFractionOrSignificantPattern.lastIndexOf(",", aux1 - 1)) !== -1) {
secondaryGroupingSize = aux1 - 1 - aux2;
}
}
// Return:
// 0: @prefix String
// 1: @padding Array [ , ] TODO
// 2: @minimumIntegerDigits non-negative integer Number value indicating the minimum integer
// digits to be used. Numbers will be padded with leading zeroes if necessary.
// 3: @minimumFractionDigits and
// 4: @maximumFractionDigits are non-negative integer Number values indicating the minimum and
// maximum fraction digits to be used. Numbers will be rounded or padded with trailing
// zeroes if necessary.
// 5: @minimumSignificantDigits and
// 6: @maximumSignificantDigits are positive integer Number values indicating the minimum and
// maximum fraction digits to be shown. Either none or both of these properties are
// present; if they are, they override minimum and maximum integer and fraction digits
// – the formatter uses however many integer and fraction digits are required to display
// the specified number of significant digits.
// 7: @roundIncrement Decimal round increment or null
// 8: @primaryGroupingSize
// 9: @secondaryGroupingSize
// 10: @suffix String
return [
prefix,
padding,
minimumIntegerDigits,
minimumFractionDigits,
maximumFractionDigits,
minimumSignificantDigits,
maximumSignificantDigits,
roundIncrement,
primaryGroupingSize,
secondaryGroupingSize,
suffix
];
};
/**
* Symbol( name, cldr )
*
* @name [String] Symbol name.
*
* @cldr [Cldr instance].
*
* Return the localized symbol given its name.
*/
var numberSymbol = function (name, cldr) {
return cldr.main([
"numbers/symbols-numberSystem-" + numberNumberingSystem(cldr),
name
]);
};
var numberSymbolName = {
".": "decimal",
",": "group",
"%": "percentSign",
"+": "plusSign",
"-": "minusSign",
"E": "exponential",
"\u2030": "perMille"
};
/**
* symbolMap( cldr )
*
* @cldr [Cldr instance].
*
* Return the (localized symbol, pattern symbol) key value pair, eg. {
* ".": "٫",
* ",": "٬",
* "%": "٪",
* ...
* };
*/
var numberSymbolMap = function (cldr) {
var symbol,
symbolMap = {};
for (symbol in numberSymbolName) {
symbolMap[symbol] = numberSymbol(numberSymbolName[symbol], cldr);
}
return symbolMap;
};
var numberTruncate = function (value) {
if (isNaN(value)) {
return NaN;
}
return Math[value < 0 ? "ceil" : "floor"](value);
};
/**
* round( method )
*
* @method [String] with either "round", "ceil", "floor", or "truncate".
*
* Return function( value, incrementOrExp ):
*
* @value [Number] eg. 123.45.
*
* @incrementOrExp [Number] optional, eg. 0.1; or
* [Object] Either { increment: } or { exponent: }
*
* Return the rounded number, eg:
* - round( "round" )( 123.45 ): 123;
* - round( "ceil" )( 123.45 ): 124;
* - round( "floor" )( 123.45 ): 123;
* - round( "truncate" )( 123.45 ): 123;
* - round( "round" )( 123.45, 0.1 ): 123.5;
* - round( "round" )( 123.45, 10 ): 120;
*
* Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
* Ref: #376
*/
var numberRound = function (method) {
method = method || "round";
method = method === "truncate" ? numberTruncate : Math[method];
return function (value, incrementOrExp) {
var exp, increment;
value = +value;
// If the value is not a number, return NaN.
if (isNaN(value)) {
return NaN;
}
// Exponent given.
if (typeof incrementOrExp === "object" && incrementOrExp.exponent) {
exp = +incrementOrExp.exponent;
increment = 1;
if (exp === 0) {
return method(value);
}
// If the exp is not an integer, return NaN.
if (!(typeof exp === "number" && exp % 1 === 0)) {
return NaN;
}
// Increment given.
} else {
increment = +incrementOrExp || 1;
if (increment === 1) {
return method(value);
}
// If the increment is not a number, return NaN.
if (isNaN(increment)) {
return NaN;
}
increment = increment.toExponential().split("e");
exp = +increment[1];
increment = +increment[0];
}
// Shift & Round
value = value.toString().split("e");
value[0] = +value[0] / increment;
value[1] = value[1] ? (+value[1] - exp) : -exp;
value = method(+(value[0] + "e" + value[1]));
// Shift back
value = value.toString().split("e");
value[0] = +value[0] * increment;
value[1] = value[1] ? (+value[1] + exp) : exp;
return +(value[0] + "e" + value[1]);
};
};
/**
* formatProperties( pattern, cldr [, options] )
*
* @pattern [String] raw pattern for numbers.
*
* @cldr [Cldr instance].
*
* @options [Object]:
* - minimumIntegerDigits [Number]
* - minimumFractionDigits, maximumFractionDigits [Number]
* - minimumSignificantDigits, maximumSignificantDigits [Number]
* - round [String] "ceil", "floor", "round" (default), or "truncate".
* - useGrouping [Boolean] default true.
*
* Return the processed properties that will be used in number/format.
* ref: http://www.unicode.org/reports/tr35/tr35-numbers.html
*/
var numberFormatProperties = function (pattern, cldr, options) {
var negativePattern, negativePrefix, negativeProperties, negativeSuffix, positivePattern,
roundFn, properties;
function getOptions(attribute, propertyIndex) {
if (attribute in options) {
properties[propertyIndex] = options[attribute];
}
}
options = options || {};
pattern = pattern.split(";");
positivePattern = pattern[0];
negativePattern = pattern[1] || "-" + positivePattern;
negativeProperties = numberPatternProperties(negativePattern);
negativePrefix = negativeProperties[0];
negativeSuffix = negativeProperties[10];
// Have runtime code to refer to numberRound() instead of including it explicitly.
roundFn = numberRound(options.round);
roundFn.generatorString = function () {
return "numberRound(" + (options.round ? "\"" + options.round + "\"" : "") + ")";
};
properties = numberPatternProperties(positivePattern).concat([
positivePattern,
negativePrefix + positivePattern + negativeSuffix,
negativePrefix,
negativeSuffix,
roundFn,
numberSymbol("infinity", cldr),
numberSymbol("nan", cldr),
numberSymbolMap(cldr),
numberNumberingSystemDigitsMap(cldr)
]);
getOptions("minimumIntegerDigits", 2);
getOptions("minimumFractionDigits", 3);
getOptions("maximumFractionDigits", 4);
getOptions("minimumSignificantDigits", 5);
getOptions("maximumSignificantDigits", 6);
// Grouping separators
if (options.useGrouping === false) {
properties[8] = null;
}
// Normalize number of digits if only one of either minimumFractionDigits or
// maximumFractionDigits is passed in as an option
if ("minimumFractionDigits" in options && !("maximumFractionDigits" in options)) {
// maximumFractionDigits = Math.max( minimumFractionDigits, maximumFractionDigits );
properties[4] = Math.max(properties[3], properties[4]);
} else if (!("minimumFractionDigits" in options) &&
"maximumFractionDigits" in options) {
// minimumFractionDigits = Math.min( minimumFractionDigits, maximumFractionDigits );
properties[3] = Math.min(properties[3], properties[4]);
}
// Return:
// 0-10: see number/pattern-properties.
// 11: @positivePattern [String] Positive pattern.
// 12: @negativePattern [String] Negative pattern.
// 13: @negativePrefix [String] Negative prefix.
// 14: @negativeSuffix [String] Negative suffix.
// 15: @round [Function] Round function.
// 16: @infinitySymbol [String] Infinity symbol.
// 17: @nanSymbol [String] NaN symbol.
// 18: @symbolMap [Object] A bunch of other symbols.
// 19: @nuDigitsMap [Array] Digits map if numbering system is different than `latn`.
return properties;
};
/**
* Generated by:
*
* var regenerate = require( "regenerate" );
* var formatSymbols = require( * "unicode-8.0.0/General_Category/Format/symbols" );
* regenerate().add( formatSymbols ).toString();
*
* https://github.com/mathiasbynens/regenerate
* https://github.com/mathiasbynens/unicode-8.0.0
*/
var regexpCfG = /[\xAD\u0600-\u0605\u061C\u06DD\u070F\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804\uDCBD|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/g;
/**
* Generated by:
*
* var regenerate = require( "regenerate" );
* var dashSymbols = require( * "unicode-8.0.0/General_Category/Dash_Punctuation/symbols" );
* regenerate().add( dashSymbols ).toString();
*
* https://github.com/mathiasbynens/regenerate
* https://github.com/mathiasbynens/unicode-8.0.0
*
* NOTE: In addition to [:dash:], the below includes MINUS SIGN U+2212.
*/
var regexpDashG = /[\-\u058A\u05BE\u1400\u1806\u2010-\u2015\u2E17\u2E1A\u2E3A\u2E3B\u2E40\u301C\u3030\u30A0\uFE31\uFE32\uFE58\uFE63\uFF0D\u2212]/g;
/**
* Generated by:
*
* var regenerate = require( "regenerate" );
* var spaceSeparatorSymbols = require( "unicode-8.0.0/General_Category/Space_Separator/symbols" );
* regenerate().add( spaceSeparatorSymbols ).toString();
*
* https://github.com/mathiasbynens/regenerate
* https://github.com/mathiasbynens/unicode-8.0.0
*/
var regexpZsG = /[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/g;
/**
* parse( value, properties )
*
* @value [String].
*
* @properties [Object] Parser properties is a reduced pre-processed cldr
* data set returned by numberParserProperties().
*
* Return the parsed Number (including Infinity) or NaN when value is invalid.
* ref: http://www.unicode.org/reports/tr35/tr35-numbers.html
*/
var numberParse = function (value, properties) {
var grammar, invertedNuDigitsMap, invertedSymbolMap, negative, number, prefix, prefixNSuffix,
suffix, tokenizer, valid;
// Grammar:
// - Value <= NaN | PositiveNumber | NegativeNumber
// - PositiveNumber <= PositivePrefix NumberOrInf PositiveSufix
// - NegativeNumber <= NegativePrefix NumberOrInf
// - NumberOrInf <= Number | Inf
grammar = [
["nan"],
["prefix", "infinity", "suffix"],
["prefix", "number", "suffix"],
["negativePrefix", "infinity", "negativeSuffix"],
["negativePrefix", "number", "negativeSuffix"]
];
invertedSymbolMap = properties[0];
invertedNuDigitsMap = properties[1] || {};
tokenizer = properties[2];
// Loose Matching:
// - Ignore all format characters, which includes RLM, LRM or ALM used to control BIDI
// formatting.
// - Map all characters in [:Zs:] to U+0020 SPACE;
// - Map all characters in [:Dash:] to U+002D HYPHEN-MINUS;
value = value
.replace(regexpCfG, "")
.replace(regexpDashG, "-")
.replace(regexpZsG, " ");
function parse(type) {
return function (lexeme) {
// Reverse localized symbols and numbering system.
lexeme = lexeme.split("").map(function (character) {
return invertedSymbolMap[character] ||
invertedNuDigitsMap[character] ||
character;
}).join("");
switch (type) {
case "infinity":
number = Infinity;
break;
case "nan":
number = NaN;
break;
case "number":
// Remove grouping separators.
lexeme = lexeme.replace(/,/g, "");
number = +lexeme;
break;
case "prefix":
case "negativePrefix":
prefix = lexeme;
break;
case "suffix":
suffix = lexeme;
break;
case "negativeSuffix":
suffix = lexeme;
negative = true;
break;
// This should never be reached.
default:
throw new Error("Internal error");
}
return "";
};
}
function tokenizeNParse(_value, grammar) {
return grammar.some(function (statement) {
var value = _value;
// The whole grammar statement should be used (i.e., .every() return true) and value be
// entirely consumed (i.e., !value.length).
return statement.every(function (type) {
if (value.match(tokenizer[type]) === null) {
return false;
}
// Consume and parse it.
value = value.replace(tokenizer[type], parse(type));
return true;
}) && !value.length;
});
}
valid = tokenizeNParse(value, grammar);
// NaN
if (!valid || isNaN(number)) {
return NaN;
}
prefixNSuffix = "" + prefix + suffix;
// Percent
if (prefixNSuffix.indexOf("%") !== -1) {
number /= 100;
// Per mille
} else if (prefixNSuffix.indexOf("\u2030") !== -1) {
number /= 1000;
}
// Negative number
if (negative) {
number *= -1;
}
return number;
};
var numberParserFn = function (properties) {
return function numberParser(value) {
validateParameterPresence(value, "value");
validateParameterTypeString(value, "value");
return numberParse(value, properties);
};
};
/**
* symbolMap( cldr )
*
* @cldr [Cldr instance].
*
* Return the (localized symbol, pattern symbol) key value pair, eg. {
* "٫": ".",
* "٬": ",",
* "٪": "%",
* ...
* };
*/
var numberSymbolInvertedMap = function (cldr) {
var symbol,
symbolMap = {};
for (symbol in numberSymbolName) {
symbolMap[numberSymbol(numberSymbolName[symbol], cldr)] = symbol;
}
return symbolMap;
};
/**
* objectMap( object, fn)
*
* - object
*
* - fn( pair ) => pair
*/
var objectMap = function (object, fn) {
return Object.keys(object).map(function (key) {
return fn([key, object[key]]);
}).reduce(function (object, pair) {
object[pair[0]] = pair[1];
return object;
}, {});
};
/**
* parseProperties( pattern, cldr )
*
* @pattern [String] raw pattern for numbers.
*
* @cldr [Cldr instance].
*
* Return parser properties, used to feed parser function.
*
* TODO:
* - Scientific_notation;
* - Padding;
*/
var numberParseProperties = function (pattern, cldr, options) {
var aux, decimalSymbolRe, digitsRe, groupingSeparatorRe, infinitySymbol, invertedNuDigitsMap,
invertedSymbolMap, maximumFractionDigits, maximumSignificantDigits,
minimumSignificantDigits, nanSymbol, negativePrefix, negativeSuffix, nuDigitsMap,
numberTokenizer, prefix, primaryGroupingSize, secondaryGroupingSize, suffix, symbolMap,
formatProperties = numberFormatProperties(pattern, cldr, options);
// Loose Matching:
// - Ignore all format characters, which includes RLM, LRM or ALM used to control BIDI
// formatting.
// - Map all characters in [:Zs:] to U+0020 SPACE;
// - Map all characters in [:Dash:] to U+002D HYPHEN-MINUS;
function looseMatching(value) {
return value
.replace(regexpCfG, "")
.replace(regexpDashG, "-")
.replace(regexpZsG, " ");
}
prefix = looseMatching(formatProperties[0]);
maximumFractionDigits = formatProperties[4];
minimumSignificantDigits = formatProperties[5];
maximumSignificantDigits = formatProperties[6];
primaryGroupingSize = formatProperties[8];
secondaryGroupingSize = formatProperties[9];
suffix = looseMatching(formatProperties[10]);
negativePrefix = looseMatching(formatProperties[13]);
negativeSuffix = looseMatching(formatProperties[14]);
infinitySymbol = looseMatching(formatProperties[16]);
nanSymbol = looseMatching(formatProperties[17]);
symbolMap = objectMap(formatProperties[18], function (pair) {
return [pair[0], looseMatching(pair[1])];
});
nuDigitsMap = formatProperties[19];
invertedSymbolMap = objectMap(numberSymbolInvertedMap(cldr), function (pair) {
return [looseMatching(pair[0]), pair[1]];
});
digitsRe = nuDigitsMap ? "[" + nuDigitsMap + "]" : "\\d";
groupingSeparatorRe = regexpEscape(symbolMap[","]);
decimalSymbolRe = regexpEscape(symbolMap["."]);
if (nuDigitsMap) {
invertedNuDigitsMap = nuDigitsMap.split("").reduce(function (object, localizedDigit, i) {
object[localizedDigit] = String(i);
return object;
}, {});
}
aux = [prefix, suffix, negativePrefix, negativeSuffix].map(function (value) {
return value.replace(/('([^']|'')+'|'')|./g, function (character, literal) {
// Literals
if (literal) {
return removeLiteralQuotes(literal);
}
// Symbols
character = character.replace(/[\-+E%\u2030]/, function (symbol) {
return symbolMap[symbol];
});
return character;
});
});
prefix = aux[0];
suffix = aux[1];
negativePrefix = aux[2];
negativeSuffix = aux[3];
// Number
//
// number_re = integer fraction?
//
// integer = digits | digits_using_grouping_separators
//
// fraction = regexp((.\d+)?)
//
// digits = regexp(\d+)
//
// digits_w_grouping_separators = digits_w_1_grouping_separators |
// digits_w_2_grouping_separators
//
// digits_w_1_grouping_separators = regexp(\d{1,3}(,\d{3})+)
//
// digits_w_2_grouping_separators = regexp(\d{1,2}((,\d{2})*(,\d{3})))
// Integer part
numberTokenizer = digitsRe + "+";
// Grouping separators
if (primaryGroupingSize) {
if (secondaryGroupingSize) {
aux = digitsRe + "{1," + secondaryGroupingSize + "}((" + groupingSeparatorRe +
digitsRe + "{" + secondaryGroupingSize + "})*(" + groupingSeparatorRe +
digitsRe + "{" + primaryGroupingSize + "}))";
} else {
aux = digitsRe + "{1," + primaryGroupingSize + "}(" + groupingSeparatorRe +
digitsRe + "{" + primaryGroupingSize + "})+";
}
numberTokenizer = "(" + aux + "|" + numberTokenizer + ")";
}
// Fraction part? Only included if 1 or 2.
// 1: Using significant digit format.
// 2: Using integer and fractional format && it has a maximumFractionDigits.
if (!isNaN(minimumSignificantDigits * maximumSignificantDigits) || /* 1 */
maximumFractionDigits /* 2 */) {
aux = decimalSymbolRe + digitsRe + "+";
numberTokenizer = numberTokenizer + "(" + aux + ")?" +
// Handle non-padded decimals, e.g., `".12"` => `0.12` by making the integer part
// optional.
"|(" + numberTokenizer + ")?" + aux;
numberTokenizer = "(" + numberTokenizer + ")";
}
// 0: @invertedSymbolMap [Object] Inverted symbol map.
// 1: @invertedNuDigitsMap [Object] Inverted digits map if numbering system is different than
// `latn`.
// 2: @tokenizer [Object] Tokenizer map, used by parser to consume input.
return [
invertedSymbolMap,
invertedNuDigitsMap,
{
infinity: new RegExp("^" + regexpEscape(infinitySymbol)),
nan: new RegExp("^" + regexpEscape(nanSymbol)),
negativePrefix: new RegExp("^" + regexpEscape(negativePrefix)),
negativeSuffix: new RegExp("^" + regexpEscape(negativeSuffix)),
number: new RegExp("^" + numberTokenizer),
prefix: new RegExp("^" + regexpEscape(prefix)),
suffix: new RegExp("^" + regexpEscape(suffix))
}
];
};
/**
* Pattern( style )
*
* @style [String] "decimal" (default) or "percent".
*
* @cldr [Cldr instance].
*/
var numberPattern = function (style, cldr) {
if (style !== "decimal" && style !== "percent") {
throw new Error("Invalid style");
}
return cldr.main([
"numbers",
style + "Formats-numberSystem-" + numberNumberingSystem(cldr),
"standard"
]);
};
function validateDigits(properties) {
var minimumIntegerDigits = properties[2],
minimumFractionDigits = properties[3],
maximumFractionDigits = properties[4],
minimumSignificantDigits = properties[5],
maximumSignificantDigits = properties[6];
// Validate significant digit format properties
if (!isNaN(minimumSignificantDigits * maximumSignificantDigits)) {
validateParameterRange(minimumSignificantDigits, "minimumSignificantDigits", 1, 21);
validateParameterRange(maximumSignificantDigits, "maximumSignificantDigits",
minimumSignificantDigits, 21);
} else if (!isNaN(minimumSignificantDigits) || !isNaN(maximumSignificantDigits)) {
throw new Error("Neither or both the minimum and maximum significant digits must be " +
"present");
// Validate integer and fractional format
} else {
validateParameterRange(minimumIntegerDigits, "minimumIntegerDigits", 1, 21);
validateParameterRange(minimumFractionDigits, "minimumFractionDigits", 0, 20);
validateParameterRange(maximumFractionDigits, "maximumFractionDigits",
minimumFractionDigits, 20);
}
}
/**
* .numberFormatter( [options] )
*
* @options [Object]:
* - style: [String] "decimal" (default) or "percent".
* - see also number/format options.
*
* Return a function that formats a number according to the given options and default/instance
* locale.
*/
Globalize.numberFormatter =
Globalize.prototype.numberFormatter = function (options) {
var args, cldr, pattern, properties, returnFn;
validateParameterTypePlainObject(options, "options");
options = options || {};
cldr = this.cldr;
args = [options];
validateDefaultLocale(cldr);
cldr.on("get", validateCldr);
if (options.raw) {
pattern = options.raw;
} else {
pattern = numberPattern(options.style || "decimal", cldr);
}
properties = numberFormatProperties(pattern, cldr, options);
cldr.off("get", validateCldr);
validateDigits(properties);
returnFn = numberFormatterFn(properties);
runtimeBind(args, cldr, returnFn, [properties]);
return returnFn;
};
/**
* .numberParser( [options] )
*
* @options [Object]:
* - style: [String] "decimal" (default) or "percent".
*
* Return the number parser according to the default/instance locale.
*/
Globalize.numberParser =
Globalize.prototype.numberParser = function (options) {
var args, cldr, pattern, properties, returnFn;
validateParameterTypePlainObject(options, "options");
options = options || {};
cldr = this.cldr;
args = [options];
validateDefaultLocale(cldr);
cldr.on("get", validateCldr);
if (options.raw) {
pattern = options.raw;
} else {
pattern = numberPattern(options.style || "decimal", cldr);
}
properties = numberParseProperties(pattern, cldr, options);
cldr.off("get", validateCldr);
returnFn = numberParserFn(properties);
runtimeBind(args, cldr, returnFn, [properties]);
return returnFn;
};
/**
* .formatNumber( value [, options] )
*
* @value [Number] number to be formatted.
*
* @options [Object]: see number/format-properties.
*
* Format a number according to the given options and default/instance locale.
*/
Globalize.formatNumber =
Globalize.prototype.formatNumber = function (value, options) {
validateParameterPresence(value, "value");
validateParameterTypeNumber(value, "value");
return this.numberFormatter(options)(value);
};
/**
* .parseNumber( value [, options] )
*
* @value [String]
*
* @options [Object]: See numberParser().
*
* Return the parsed Number (including Infinity) or NaN when value is invalid.
*/
Globalize.parseNumber =
Globalize.prototype.parseNumber = function (value, options) {
validateParameterPresence(value, "value");
validateParameterTypeString(value, "value");
return this.numberParser(options)(value);
};
/**
* Optimization to avoid duplicating some internal functions across modules.
*/
Globalize._createErrorUnsupportedFeature = createErrorUnsupportedFeature;
Globalize._numberNumberingSystem = numberNumberingSystem;
Globalize._numberPattern = numberPattern;
Globalize._numberSymbol = numberSymbol;
Globalize._removeLiteralQuotes = removeLiteralQuotes;
Globalize._stringPad = stringPad;
Globalize._validateParameterTypeNumber = validateParameterTypeNumber;
Globalize._validateParameterTypeString = validateParameterTypeString;
return Globalize;
}));
© 2015 - 2025 Weber Informatics LLC | Privacy Policy