920 lines
23 KiB
JavaScript
920 lines
23 KiB
JavaScript
|
/**
|
||
|
* Globalize Runtime v1.7.0
|
||
|
*
|
||
|
* https://github.com/globalizejs/globalize
|
||
|
*
|
||
|
* Copyright OpenJS Foundation and other contributors
|
||
|
* Released under the MIT license
|
||
|
* https://jquery.org/license
|
||
|
*
|
||
|
* Date: 2021-08-02T11:53Z
|
||
|
*/
|
||
|
/*!
|
||
|
* Globalize Runtime v1.7.0 2021-08-02T11:53Z Released under the MIT license
|
||
|
* http://git.io/TrdQbw
|
||
|
*/
|
||
|
(function( root, factory ) {
|
||
|
|
||
|
"use strict";
|
||
|
|
||
|
// UMD returnExports
|
||
|
if ( typeof define === "function" && define.amd ) {
|
||
|
|
||
|
// AMD
|
||
|
define([
|
||
|
"../globalize-runtime"
|
||
|
], factory );
|
||
|
} else if ( typeof exports === "object" ) {
|
||
|
|
||
|
// Node, CommonJS
|
||
|
module.exports = factory( require( "../globalize-runtime" ) );
|
||
|
} else {
|
||
|
|
||
|
// Extend global
|
||
|
factory( root.Globalize );
|
||
|
}
|
||
|
}(this, function( Globalize ) {
|
||
|
|
||
|
|
||
|
|
||
|
var createError = Globalize._createError,
|
||
|
partsJoin = Globalize._partsJoin,
|
||
|
partsPush = Globalize._partsPush,
|
||
|
regexpEscape = Globalize._regexpEscape,
|
||
|
runtimeKey = Globalize._runtimeKey,
|
||
|
stringPad = Globalize._stringPad,
|
||
|
validateParameterType = Globalize._validateParameterType,
|
||
|
validateParameterPresence = Globalize._validateParameterPresence,
|
||
|
validateParameterTypeString = Globalize._validateParameterTypeString;
|
||
|
|
||
|
|
||
|
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"
|
||
|
);
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* EBNF representation:
|
||
|
*
|
||
|
* compact_pattern_re = prefix?
|
||
|
* number_pattern_re
|
||
|
* suffix?
|
||
|
*
|
||
|
* number_pattern_re = 0+
|
||
|
*
|
||
|
* Regexp groups:
|
||
|
*
|
||
|
* 0: compact_pattern_re
|
||
|
* 1: prefix
|
||
|
* 2: number_pattern_re (the number pattern to use in compact mode)
|
||
|
* 3: suffix
|
||
|
*/
|
||
|
var numberCompactPatternRe = ( /^([^0]*)(0+)([^0]*)$/ );
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 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 } );
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
number = round( number );
|
||
|
}
|
||
|
|
||
|
number = String( number );
|
||
|
|
||
|
// Maximum integer digits (post string phase)
|
||
|
if ( maximumFractionDigits && /e-/.test( number ) ) {
|
||
|
|
||
|
// Use toFixed( maximumFractionDigits ) to make sure small numbers like 1e-7 are
|
||
|
// displayed using plain digits instead of scientific notation.
|
||
|
// 1: Remove leading decimal zeros.
|
||
|
// 2: Remove leading decimal separator.
|
||
|
// Note: String() is still preferred so it doesn't mess up with a number precision
|
||
|
// unnecessarily, e.g., (123456789.123).toFixed(10) === "123456789.1229999959",
|
||
|
// String(123456789.123) === "123456789.123".
|
||
|
number = ( +number ).toFixed( maximumFractionDigits )
|
||
|
.replace( /0+$/, "" ) /* 1 */
|
||
|
.replace( /\.$/, "" ); /* 2 */
|
||
|
}
|
||
|
|
||
|
// Minimum fraction digits (post string phase)
|
||
|
if ( minimumFractionDigits ) {
|
||
|
number = number.split( "." );
|
||
|
number[ 1 ] = stringPad( number[ 1 ] || "", minimumFractionDigits, true );
|
||
|
number = number.join( "." );
|
||
|
}
|
||
|
|
||
|
// 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;
|
||
|
|
||
|
if ( number === 0 ) { // Fix #706
|
||
|
return number;
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
var numberSymbolName = {
|
||
|
".": "decimal",
|
||
|
",": "group",
|
||
|
"%": "percentSign",
|
||
|
"+": "plusSign",
|
||
|
"-": "minusSign",
|
||
|
"E": "exponential",
|
||
|
"\u2030": "perMille"
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 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, pluralGenerator ) {
|
||
|
var aux, compactMap, infinitySymbol, maximumFractionDigits, maximumSignificantDigits,
|
||
|
minimumFractionDigits, minimumIntegerDigits, minimumSignificantDigits, nanSymbol,
|
||
|
nuDigitsMap, prefix, primaryGroupingSize, pattern, round, roundIncrement,
|
||
|
secondaryGroupingSize, stringToParts, suffix, symbolMap;
|
||
|
|
||
|
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 ];
|
||
|
compactMap = properties[ 20 ];
|
||
|
|
||
|
// NaN
|
||
|
if ( isNaN( number ) ) {
|
||
|
return [ { type: "nan", value: nanSymbol } ];
|
||
|
}
|
||
|
|
||
|
if ( number < 0 ) {
|
||
|
pattern = properties[ 12 ];
|
||
|
prefix = properties[ 13 ];
|
||
|
suffix = properties[ 14 ];
|
||
|
} else {
|
||
|
pattern = properties[ 11 ];
|
||
|
prefix = properties[ 0 ];
|
||
|
suffix = properties[ 10 ];
|
||
|
}
|
||
|
|
||
|
// For prefix, suffix, and number parts.
|
||
|
stringToParts = function( string ) {
|
||
|
var numberType = "integer",
|
||
|
parts = [];
|
||
|
|
||
|
// TODO Move the tokenization of all parts that don't depend on number into
|
||
|
// format-properties.
|
||
|
string.replace( /('([^']|'')+'|'')|./g, function( character, literal ) {
|
||
|
|
||
|
// Literals
|
||
|
if ( literal ) {
|
||
|
partsPush( parts, "literal", removeLiteralQuotes( literal ) );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Currency symbol
|
||
|
if ( character === "\u00A4" ) {
|
||
|
partsPush( parts, "currency", character );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Symbols
|
||
|
character = character.replace( /[.,\-+E%\u2030]/, function( symbol ) {
|
||
|
if ( symbol === "." ) {
|
||
|
numberType = "fraction";
|
||
|
}
|
||
|
partsPush( parts, numberSymbolName[ symbol ], symbolMap[ symbol ] );
|
||
|
|
||
|
// "Erase" handled character.
|
||
|
return "";
|
||
|
});
|
||
|
|
||
|
// Number
|
||
|
character = character.replace( /[0-9]/, function( digit ) {
|
||
|
|
||
|
// Numbering system
|
||
|
if ( nuDigitsMap ) {
|
||
|
digit = nuDigitsMap[ +digit ];
|
||
|
}
|
||
|
partsPush( parts, numberType, digit );
|
||
|
|
||
|
// "Erase" handled character.
|
||
|
return "";
|
||
|
});
|
||
|
|
||
|
// Etc
|
||
|
character.replace( /./, function( etc ) {
|
||
|
partsPush( parts, "literal", etc );
|
||
|
});
|
||
|
});
|
||
|
return parts;
|
||
|
};
|
||
|
|
||
|
prefix = stringToParts( prefix );
|
||
|
suffix = stringToParts( suffix );
|
||
|
|
||
|
// Infinity
|
||
|
if ( !isFinite( number ) ) {
|
||
|
return prefix.concat(
|
||
|
{ type: "infinity", value: infinitySymbol },
|
||
|
suffix
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Percent
|
||
|
if ( pattern.indexOf( "%" ) !== -1 ) {
|
||
|
number *= 100;
|
||
|
|
||
|
// Per mille
|
||
|
} else if ( pattern.indexOf( "\u2030" ) !== -1 ) {
|
||
|
number *= 1000;
|
||
|
}
|
||
|
|
||
|
var compactPattern, compactDigits, compactProperties, divisor, numberExponent, pluralForm;
|
||
|
|
||
|
// Compact mode: initial number digit processing
|
||
|
if ( compactMap ) {
|
||
|
numberExponent = Math.abs( Math.floor( number ) ).toString().length - 1;
|
||
|
numberExponent = Math.min( numberExponent, compactMap.maxExponent );
|
||
|
|
||
|
// Use default plural form to perform initial decimal shift
|
||
|
if ( numberExponent >= 3 ) {
|
||
|
compactPattern = compactMap[ numberExponent ] && compactMap[ numberExponent ].other;
|
||
|
}
|
||
|
|
||
|
if ( compactPattern === "0" ) {
|
||
|
compactPattern = null;
|
||
|
} else if ( compactPattern ) {
|
||
|
compactDigits = compactPattern.split( "0" ).length - 1;
|
||
|
divisor = numberExponent - ( compactDigits - 1 );
|
||
|
number = number / Math.pow( 10, divisor );
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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 );
|
||
|
}
|
||
|
|
||
|
// Compact mode: apply formatting
|
||
|
if ( compactMap && compactPattern ) {
|
||
|
|
||
|
// Get plural form after possible roundings
|
||
|
pluralForm = pluralGenerator ? pluralGenerator( +number ) : "other";
|
||
|
|
||
|
compactPattern = compactMap[ numberExponent ][ pluralForm ] || compactPattern;
|
||
|
compactProperties = compactPattern.match( numberCompactPatternRe );
|
||
|
|
||
|
// TODO Move the tokenization of all parts that don't depend on number into
|
||
|
// format-properties.
|
||
|
aux = function( string ) {
|
||
|
var parts = [];
|
||
|
string.replace( /(\s+)|([^\s0]+)/g, function( _garbage, space, compact ) {
|
||
|
|
||
|
// Literals
|
||
|
if ( space ) {
|
||
|
partsPush( parts, "literal", space );
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// Compact value
|
||
|
if ( compact ) {
|
||
|
partsPush( parts, "compact", compact );
|
||
|
return;
|
||
|
}
|
||
|
});
|
||
|
return parts;
|
||
|
};
|
||
|
|
||
|
// update prefix/suffix with compact prefix/suffix
|
||
|
prefix = prefix.concat( aux( compactProperties[ 1 ] ) );
|
||
|
suffix = aux( compactProperties[ 3 ] ).concat( suffix );
|
||
|
}
|
||
|
|
||
|
// Remove the possible number minus sign
|
||
|
number = number.replace( /^-/, "" );
|
||
|
|
||
|
// Grouping separators
|
||
|
if ( primaryGroupingSize ) {
|
||
|
number = numberFormatGroupingSeparator( number, primaryGroupingSize,
|
||
|
secondaryGroupingSize );
|
||
|
}
|
||
|
|
||
|
// Scientific notation
|
||
|
// TODO implement here
|
||
|
|
||
|
// Padding/'([^']|'')+'|''|[.,\-+E%\u2030]/g
|
||
|
// TODO implement here
|
||
|
|
||
|
return prefix.concat(
|
||
|
stringToParts( number ),
|
||
|
suffix
|
||
|
);
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
var numberFormatterFn = function( numberToPartsFormatter ) {
|
||
|
return function numberFormatter( value ) {
|
||
|
return partsJoin( numberToPartsFormatter( value ));
|
||
|
};
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Generated by:
|
||
|
*
|
||
|
* var regenerate = require( "regenerate" );
|
||
|
* var formatSymbols = require( "@unicode/unicode-13.0.0/General_Category/Format/symbols" );
|
||
|
* regenerate().add( formatSymbols ).toString();
|
||
|
*
|
||
|
* https://github.com/mathiasbynens/regenerate
|
||
|
* https://github.com/node-unicode/unicode-13.0.0
|
||
|
*/
|
||
|
var regexpCfG = /[\xAD\u0600-\u0605\u061C\u06DD\u070F\u08E2\u180E\u200B-\u200F\u202A-\u202E\u2060-\u2064\u2066-\u206F\uFEFF\uFFF9-\uFFFB]|\uD804[\uDCBD\uDCCD]|\uD80D[\uDC30-\uDC38]|\uD82F[\uDCA0-\uDCA3]|\uD834[\uDD73-\uDD7A]|\uDB40[\uDC01\uDC20-\uDC7F]/g;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Generated by:
|
||
|
*
|
||
|
* var regenerate = require( "regenerate" );
|
||
|
* var dashSymbols = require( "https://github.com/node-unicode/unicode-13.0.0/General_Category/Dash_Punctuation/symbols" );
|
||
|
* regenerate().add( dashSymbols ).toString();
|
||
|
*
|
||
|
* https://github.com/mathiasbynens/regenerate
|
||
|
* https://github.com/node-unicode/unicode-13.0.0
|
||
|
*
|
||
|
* NOTE: In addition to [:dash:], the below includes MINUS SIGN U+2212.
|
||
|
*/
|
||
|
var regexpDashG = /[\x2D\u058A\u05BE\u1400\u1806\u2010-\u2015\u2E17\u2E1A\u2E3A\u2E3B\u2E40\u301C\u3030\u30A0\uFE31\uFE32\uFE58\uFE63\uFF0D\u2212]|\uD803\uDEAD/g;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Generated by:
|
||
|
*
|
||
|
* var regenerate = require( "regenerate" );
|
||
|
* var spaceSeparatorSymbols = require( "@unicode/unicode-13.0.0/General_Category/Space_Separator/symbols" );
|
||
|
* regenerate().add( spaceSeparatorSymbols ).toString();
|
||
|
*
|
||
|
* https://github.com/mathiasbynens/regenerate
|
||
|
* https://github.com/node-unicode/unicode-13.0.0
|
||
|
*/
|
||
|
var regexpZsG = /[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/g;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 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;
|
||
|
*/
|
||
|
var looseMatching = function( value ) {
|
||
|
return value
|
||
|
.replace( regexpCfG, "" )
|
||
|
.replace( regexpDashG, "-" )
|
||
|
.replace( regexpZsG, " " );
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
/**
|
||
|
* 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 ];
|
||
|
|
||
|
value = looseMatching( value );
|
||
|
|
||
|
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 );
|
||
|
};
|
||
|
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
var numberToPartsFormatterFn = function( properties, pluralGenerator ) {
|
||
|
return function numberToPartsFormatter( value ) {
|
||
|
validateParameterPresence( value, "value" );
|
||
|
validateParameterTypeNumber( value, "value" );
|
||
|
|
||
|
return numberFormat( value, properties, pluralGenerator );
|
||
|
};
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
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: <value> } or { exponent: <value> }
|
||
|
*
|
||
|
* 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 ] );
|
||
|
};
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
Globalize._createErrorUnsupportedFeature = createErrorUnsupportedFeature;
|
||
|
Globalize._looseMatching = looseMatching;
|
||
|
Globalize._numberFormat = numberFormat;
|
||
|
Globalize._numberFormatterFn = numberFormatterFn;
|
||
|
Globalize._numberParse = numberParse;
|
||
|
Globalize._numberParserFn = numberParserFn;
|
||
|
Globalize._numberRound = numberRound;
|
||
|
Globalize._numberToPartsFormatterFn = numberToPartsFormatterFn;
|
||
|
Globalize._removeLiteralQuotes = removeLiteralQuotes;
|
||
|
Globalize._validateParameterPresence = validateParameterPresence;
|
||
|
Globalize._validateParameterTypeNumber = validateParameterTypeNumber;
|
||
|
Globalize._validateParameterTypeString = validateParameterTypeString;
|
||
|
|
||
|
// Stamp runtimeKey and return cached fn.
|
||
|
// Note, this function isn't made common to all formatters and parsers, because in practice this is
|
||
|
// only used (at the moment) for numberFormatter used by unitFormatter.
|
||
|
// TODO: Move this function into a common place when this is used by different formatters.
|
||
|
function cached( runtimeKey ) {
|
||
|
Globalize[ runtimeKey ].runtimeKey = runtimeKey;
|
||
|
return Globalize[ runtimeKey ];
|
||
|
}
|
||
|
|
||
|
Globalize.numberFormatter =
|
||
|
Globalize.prototype.numberFormatter = function( options ) {
|
||
|
options = options || {};
|
||
|
return cached( runtimeKey( "numberFormatter", this._locale, [ options ] ) );
|
||
|
};
|
||
|
|
||
|
Globalize.numberToPartsFormatter =
|
||
|
Globalize.prototype.numberToPartsFormatter = function( options ) {
|
||
|
options = options || {};
|
||
|
return cached( runtimeKey( "numberToPartsFormatter", this._locale, [ options ] ) );
|
||
|
};
|
||
|
|
||
|
Globalize.numberParser =
|
||
|
Globalize.prototype.numberParser = function( options ) {
|
||
|
options = options || {};
|
||
|
return Globalize[ runtimeKey( "numberParser", this._locale, [ options ] ) ];
|
||
|
};
|
||
|
|
||
|
Globalize.formatNumber =
|
||
|
Globalize.prototype.formatNumber = function( value, options ) {
|
||
|
validateParameterPresence( value, "value" );
|
||
|
validateParameterTypeNumber( value, "value" );
|
||
|
|
||
|
return this.numberFormatter( options )( value );
|
||
|
};
|
||
|
|
||
|
Globalize.formatNumberToParts =
|
||
|
Globalize.prototype.formatNumberToParts = function( value, options ) {
|
||
|
validateParameterPresence( value, "value" );
|
||
|
validateParameterTypeNumber( value, "value" );
|
||
|
|
||
|
return this.numberFormatter( options )( value );
|
||
|
};
|
||
|
|
||
|
Globalize.parseNumber =
|
||
|
Globalize.prototype.parseNumber = function( value, options ) {
|
||
|
validateParameterPresence( value, "value" );
|
||
|
validateParameterTypeString( value, "value" );
|
||
|
|
||
|
return this.numberParser( options )( value );
|
||
|
};
|
||
|
|
||
|
return Globalize;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
}));
|