/** * 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: } 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 ] ); }; }; 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; }));