diff options
Diffstat (limited to 'external/globalize/globalize-runtime/number.js')
-rw-r--r-- | external/globalize/globalize-runtime/number.js | 682 |
1 files changed, 682 insertions, 0 deletions
diff --git a/external/globalize/globalize-runtime/number.js b/external/globalize/globalize-runtime/number.js new file mode 100644 index 000000000..b3031471a --- /dev/null +++ b/external/globalize/globalize-runtime/number.js @@ -0,0 +1,682 @@ +/** + * Globalize Runtime v1.1.0-rc.6 + * + * http://github.com/jquery/globalize + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-11-18T10:38Z + */ +/*! + * Globalize Runtime v1.1.0-rc.6 2015-11-18T10:38Z Released under the MIT license + * http://git.io/TrdQbw + */ +(function( root, factory ) { + + // 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, + 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" + ); +}; + + + + +/** + * 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; +}; + + + + +/** + * 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 ) { + literal = literal.replace( /''/, "'" ); + if ( literal.length > 2 ) { + literal = literal.slice( 1, -1 ); + } + return 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 ); + }; +}; + + + + +/** + * EBNF representation: + * + * number_pattern_re = prefix_including_padding? + * number + * scientific_notation? + * suffix? + * + * number = integer_including_group_separator fraction_including_decimal_separator + * + * integer_including_group_separator = + * regexp([0-9,]*[0-9]+) + * + * fraction_including_decimal_separator = + * regexp((\.[0-9]+)?) + + * prefix_including_padding = non_number_stuff + * + * scientific_notation = regexp(E[+-]?[0-9]+) + * + * suffix = non_number_stuff + * + * non_number_stuff = regexp([^0-9]*) + * + * + * Regexp groups: + * + * 0: number_pattern_re + * 1: prefix + * 2: integer_including_group_separator fraction_including_decimal_separator + * 3: integer_including_group_separator + * 4: fraction_including_decimal_separator + * 5: scientific_notation + * 6: suffix + */ +var numberNumberRe = ( /^([^0-9]*)(([0-9,]*[0-9]+)(\.[0-9]+)?)(E[+-]?[0-9]+)?([^0-9]*)$/ ); + + + + +/** + * 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 aux, infinitySymbol, invertedNuDigitsMap, invertedSymbolMap, localizedDigitRe, + localizedSymbolsRe, negativePrefix, negativeSuffix, number, prefix, suffix; + + infinitySymbol = properties[ 0 ]; + invertedSymbolMap = properties[ 1 ]; + negativePrefix = properties[ 2 ]; + negativeSuffix = properties[ 3 ]; + invertedNuDigitsMap = properties[ 4 ]; + + // Infinite number. + if ( aux = value.match( infinitySymbol ) ) { + + number = Infinity; + prefix = value.slice( 0, aux.length ); + suffix = value.slice( aux.length + 1 ); + + // Finite number. + } else { + + // TODO: Create it during setup, i.e., make it a property. + localizedSymbolsRe = new RegExp( + Object.keys( invertedSymbolMap ).map(function( localizedSymbol ) { + return regexpEscape( localizedSymbol ); + }).join( "|" ), + "g" + ); + + // Reverse localized symbols. + value = value.replace( localizedSymbolsRe, function( localizedSymbol ) { + return invertedSymbolMap[ localizedSymbol ]; + }); + + // Reverse localized numbering system. + if ( invertedNuDigitsMap ) { + + // TODO: Create it during setup, i.e., make it a property. + localizedDigitRe = new RegExp( + Object.keys( invertedNuDigitsMap ).map(function( localizedDigit ) { + return regexpEscape( localizedDigit ); + }).join( "|" ), + "g" + ); + value = value.replace( localizedDigitRe, function( localizedDigit ) { + return invertedNuDigitsMap[ localizedDigit ]; + }); + } + + // Add padding zero to leading decimal. + if ( value.charAt( 0 ) === "." ) { + value = "0" + value; + } + + // Is it a valid number? + value = value.match( numberNumberRe ); + if ( !value ) { + + // Invalid number. + return NaN; + } + + prefix = value[ 1 ]; + suffix = value[ 6 ]; + + // Remove grouping separators. + number = value[ 2 ].replace( /,/g, "" ); + + // Scientific notation + if ( value[ 5 ] ) { + number += value[ 5 ]; + } + + number = +number; + + // Is it a valid number? + if ( isNaN( number ) ) { + + // Invalid number. + return NaN; + } + + // Percent + if ( value[ 0 ].indexOf( "%" ) !== -1 ) { + number /= 100; + suffix = suffix.replace( "%", "" ); + + // Per mille + } else if ( value[ 0 ].indexOf( "\u2030" ) !== -1 ) { + number /= 1000; + suffix = suffix.replace( "\u2030", "" ); + } + } + + // Negative number + // "If there is an explicit negative subpattern, it serves only to specify the negative prefix + // and suffix. If there is no explicit negative subpattern, the negative subpattern is the + // localized minus sign prefixed to the positive subpattern" UTS#35 + if ( prefix === negativePrefix && suffix === negativeSuffix ) { + number *= -1; + } + + return number; +}; + + + + +var numberParserFn = function( properties ) { + return function numberParser( value ) { + validateParameterPresence( value, "value" ); + validateParameterTypeString( value, "value" ); + + return numberParse( value, properties ); + }; + +}; + + + + +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._numberFormat = numberFormat; +Globalize._numberFormatterFn = numberFormatterFn; +Globalize._numberParse = numberParse; +Globalize._numberParserFn = numberParserFn; +Globalize._numberRound = numberRound; +Globalize._validateParameterPresence = validateParameterPresence; +Globalize._validateParameterTypeNumber = validateParameterTypeNumber; +Globalize._validateParameterTypeString = validateParameterTypeString; + +Globalize.numberFormatter = +Globalize.prototype.numberFormatter = function( options ) { + options = options || {}; + return Globalize[ runtimeKey( "numberFormatter", 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.parseNumber = +Globalize.prototype.parseNumber = function( value, options ) { + validateParameterPresence( value, "value" ); + validateParameterTypeString( value, "value" ); + + return this.numberParser( options )( value ); +}; + +return Globalize; + + + + +})); |