Path: blob/trunk/third_party/closure/goog/i18n/numberformat.js
2868 views
// Copyright 2006 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Number format/parse library with locale support.16*/171819/**20* Namespace for locale number format functions21*/22goog.provide('goog.i18n.NumberFormat');23goog.provide('goog.i18n.NumberFormat.CurrencyStyle');24goog.provide('goog.i18n.NumberFormat.Format');2526goog.require('goog.asserts');27goog.require('goog.i18n.CompactNumberFormatSymbols');28goog.require('goog.i18n.NumberFormatSymbols');29goog.require('goog.i18n.currency');30goog.require('goog.math');31goog.require('goog.string');32333435/**36* Constructor of NumberFormat.37* @param {number|string} pattern The number that indicates a predefined38* number format pattern.39* @param {string=} opt_currency Optional international currency40* code. This determines the currency code/symbol used in format/parse. If41* not given, the currency code for current locale will be used.42* @param {number=} opt_currencyStyle currency style, value defined in43* goog.i18n.NumberFormat.CurrencyStyle.44* @constructor45*/46goog.i18n.NumberFormat = function(pattern, opt_currency, opt_currencyStyle) {47/** @private {string} */48this.intlCurrencyCode_ =49opt_currency || goog.i18n.NumberFormatSymbols.DEF_CURRENCY_CODE;5051/** @private {number} */52this.currencyStyle_ =53opt_currencyStyle || goog.i18n.NumberFormat.CurrencyStyle.LOCAL;5455/** @private {number} */56this.maximumIntegerDigits_ = 40;57/** @private {number} */58this.minimumIntegerDigits_ = 1;59/** @private {number} */60this.significantDigits_ = 0; // invariant, <= maximumFractionDigits61/** @private {number} */62this.maximumFractionDigits_ = 3; // invariant, >= minFractionDigits63/** @private {number} */64this.minimumFractionDigits_ = 0;65/** @private {number} */66this.minExponentDigits_ = 0;67/** @private {boolean} */68this.useSignForPositiveExponent_ = false;6970/**71* Whether to show trailing zeros in the fraction when significantDigits_ is72* positive.73* @private {boolean}74*/75this.showTrailingZeros_ = false;7677/** @private {string} */78this.positivePrefix_ = '';79/** @private {string} */80this.positiveSuffix_ = '';81/** @private {string} */82this.negativePrefix_ = '-';83/** @private {string} */84this.negativeSuffix_ = '';8586// The multiplier for use in percent, per mille, etc.87/** @private {number} */88this.multiplier_ = 1;8990/**91* True if the percent/permill sign of the negative pattern is expected.92* @private {!boolean}93*/94this.negativePercentSignExpected_ = false;9596/**97* The grouping array is used to store the values of each number group98* following left of the decimal place. For example, a number group with99* goog.i18n.NumberFormat('#,##,###') should have [3,2] where 2 is the100* repeated number group following a fixed number grouping of size 3.101* @private {!Array<number>}102*/103this.groupingArray_ = [];104105/** @private {boolean} */106this.decimalSeparatorAlwaysShown_ = false;107/** @private {boolean} */108this.useExponentialNotation_ = false;109/** @private {goog.i18n.NumberFormat.CompactStyle} */110this.compactStyle_ = goog.i18n.NumberFormat.CompactStyle.NONE;111112/**113* The number to base the formatting on when using compact styles, or null114* if formatting should not be based on another number.115* @type {?number}116* @private117*/118this.baseFormattingNumber_ = null;119120/** @private {string} */121this.pattern_;122123if (typeof pattern == 'number') {124this.applyStandardPattern_(pattern);125} else {126this.applyPattern_(pattern);127}128};129130131/**132* Standard number formatting patterns.133* @enum {number}134*/135goog.i18n.NumberFormat.Format = {136DECIMAL: 1,137SCIENTIFIC: 2,138PERCENT: 3,139CURRENCY: 4,140COMPACT_SHORT: 5,141COMPACT_LONG: 6142};143144145/**146* Currency styles.147* @enum {number}148*/149goog.i18n.NumberFormat.CurrencyStyle = {150LOCAL: 0, // currency style as it is used in its circulating country.151PORTABLE: 1, // currency style that differentiate it from other popular ones.152GLOBAL: 2 // currency style that is unique among all currencies.153};154155156/**157* Compacting styles.158* @enum {number}159*/160goog.i18n.NumberFormat.CompactStyle = {161NONE: 0, // Don't compact.162SHORT: 1, // Short compact form, such as 1.2B.163LONG: 2 // Long compact form, such as 1.2 billion.164};165166167/**168* If the usage of Ascii digits should be enforced.169* @type {boolean}170* @private171*/172goog.i18n.NumberFormat.enforceAsciiDigits_ = false;173174175/**176* Set if the usage of Ascii digits in formatting should be enforced.177* @param {boolean} doEnforce Boolean value about if Ascii digits should be178* enforced.179*/180goog.i18n.NumberFormat.setEnforceAsciiDigits = function(doEnforce) {181goog.i18n.NumberFormat.enforceAsciiDigits_ = doEnforce;182};183184185/**186* Return if Ascii digits is enforced.187* @return {boolean} If Ascii digits is enforced.188*/189goog.i18n.NumberFormat.isEnforceAsciiDigits = function() {190return goog.i18n.NumberFormat.enforceAsciiDigits_;191};192193194/**195* Sets minimum number of fraction digits.196* @param {number} min the minimum.197* @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.198*/199goog.i18n.NumberFormat.prototype.setMinimumFractionDigits = function(min) {200if (this.significantDigits_ > 0 && min > 0) {201throw Error(202'Can\'t combine significant digits and minimum fraction digits');203}204this.minimumFractionDigits_ = min;205return this;206};207208209/**210* Sets maximum number of fraction digits.211* @param {number} max the maximum.212* @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.213*/214goog.i18n.NumberFormat.prototype.setMaximumFractionDigits = function(max) {215if (max > 308) {216// Math.pow(10, 309) becomes Infinity which breaks the logic in this class.217throw Error('Unsupported maximum fraction digits: ' + max);218}219this.maximumFractionDigits_ = max;220return this;221};222223224/**225* Sets number of significant digits to show. Only fractions will be rounded.226* Regardless of the number of significant digits set, the number of fractional227* digits shown will always be capped by the maximum number of fractional digits228* set on {@link #setMaximumFractionDigits}.229* @param {number} number The number of significant digits to include.230* @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.231*/232goog.i18n.NumberFormat.prototype.setSignificantDigits = function(number) {233if (this.minimumFractionDigits_ > 0 && number >= 0) {234throw Error(235'Can\'t combine significant digits and minimum fraction digits');236}237this.significantDigits_ = number;238return this;239};240241242/**243* Gets number of significant digits to show. Only fractions will be rounded.244* @return {number} The number of significant digits to include.245*/246goog.i18n.NumberFormat.prototype.getSignificantDigits = function() {247return this.significantDigits_;248};249250251/**252* Sets whether trailing fraction zeros should be shown when significantDigits_253* is positive. If this is true and significantDigits_ is 2, 1 will be formatted254* as '1.0'.255* @param {boolean} showTrailingZeros Whether trailing zeros should be shown.256* @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.257*/258goog.i18n.NumberFormat.prototype.setShowTrailingZeros = function(259showTrailingZeros) {260this.showTrailingZeros_ = showTrailingZeros;261return this;262};263264265/**266* Sets a number to base the formatting on when compact style formatting is267* used. If this is null, the formatting should be based only on the number to268* be formatting.269*270* This base formatting number can be used to format the target number as271* another number would be formatted. For example, 100,000 is normally formatted272* as "100K" in the COMPACT_SHORT format. To instead format it as '0.1M', the273* base number could be set to 1,000,000 in order to force all numbers to be274* formatted in millions. Similarly, 1,000,000,000 would normally be formatted275* as '1B' and setting the base formatting number to 1,000,000, would cause it276* to be formatted instead as '1,000M'.277*278* @param {?number} baseFormattingNumber The number to base formatting on, or279* null if formatting should not be based on another number.280* @return {!goog.i18n.NumberFormat} Reference to this NumberFormat object.281*/282goog.i18n.NumberFormat.prototype.setBaseFormatting = function(283baseFormattingNumber) {284goog.asserts.assert(285goog.isNull(baseFormattingNumber) || isFinite(baseFormattingNumber));286this.baseFormattingNumber_ = baseFormattingNumber;287return this;288};289290291/**292* Gets the number on which compact formatting is currently based, or null if293* no such number is set. See setBaseFormatting() for more information.294* @return {?number}295*/296goog.i18n.NumberFormat.prototype.getBaseFormatting = function() {297return this.baseFormattingNumber_;298};299300301/**302* Apply provided pattern, result are stored in member variables.303*304* @param {string} pattern String pattern being applied.305* @private306*/307goog.i18n.NumberFormat.prototype.applyPattern_ = function(pattern) {308this.pattern_ = pattern.replace(/ /g, '\u00a0');309var pos = [0];310311this.positivePrefix_ = this.parseAffix_(pattern, pos);312var trunkStart = pos[0];313this.parseTrunk_(pattern, pos);314var trunkLen = pos[0] - trunkStart;315this.positiveSuffix_ = this.parseAffix_(pattern, pos);316if (pos[0] < pattern.length &&317pattern.charAt(pos[0]) == goog.i18n.NumberFormat.PATTERN_SEPARATOR_) {318pos[0]++;319if (this.multiplier_ != 1) this.negativePercentSignExpected_ = true;320this.negativePrefix_ = this.parseAffix_(pattern, pos);321// we assume this part is identical to positive part.322// user must make sure the pattern is correctly constructed.323pos[0] += trunkLen;324this.negativeSuffix_ = this.parseAffix_(pattern, pos);325} else {326// if no negative affix specified, they share the same positive affix327this.negativePrefix_ += this.positivePrefix_;328this.negativeSuffix_ += this.positiveSuffix_;329}330};331332333/**334* Apply a predefined pattern to NumberFormat object.335* @param {number} patternType The number that indicates a predefined number336* format pattern.337* @private338*/339goog.i18n.NumberFormat.prototype.applyStandardPattern_ = function(patternType) {340switch (patternType) {341case goog.i18n.NumberFormat.Format.DECIMAL:342this.applyPattern_(goog.i18n.NumberFormatSymbols.DECIMAL_PATTERN);343break;344case goog.i18n.NumberFormat.Format.SCIENTIFIC:345this.applyPattern_(goog.i18n.NumberFormatSymbols.SCIENTIFIC_PATTERN);346break;347case goog.i18n.NumberFormat.Format.PERCENT:348this.applyPattern_(goog.i18n.NumberFormatSymbols.PERCENT_PATTERN);349break;350case goog.i18n.NumberFormat.Format.CURRENCY:351this.applyPattern_(352goog.i18n.currency.adjustPrecision(353goog.i18n.NumberFormatSymbols.CURRENCY_PATTERN,354this.intlCurrencyCode_));355break;356case goog.i18n.NumberFormat.Format.COMPACT_SHORT:357this.applyCompactStyle_(goog.i18n.NumberFormat.CompactStyle.SHORT);358break;359case goog.i18n.NumberFormat.Format.COMPACT_LONG:360this.applyCompactStyle_(goog.i18n.NumberFormat.CompactStyle.LONG);361break;362default:363throw Error('Unsupported pattern type.');364}365};366367368/**369* Apply a predefined pattern for shorthand formats.370* @param {goog.i18n.NumberFormat.CompactStyle} style the compact style to371* set defaults for.372* @private373*/374goog.i18n.NumberFormat.prototype.applyCompactStyle_ = function(style) {375this.compactStyle_ = style;376this.applyPattern_(goog.i18n.NumberFormatSymbols.DECIMAL_PATTERN);377this.setMinimumFractionDigits(0);378this.setMaximumFractionDigits(2);379this.setSignificantDigits(2);380};381382383/**384* Parses text string to produce a Number.385*386* This method attempts to parse text starting from position "opt_pos" if it387* is given. Otherwise the parse will start from the beginning of the text.388* When opt_pos presents, opt_pos will be updated to the character next to where389* parsing stops after the call. If an error occurs, opt_pos won't be updated.390*391* @param {string} text The string to be parsed.392* @param {Array<number>=} opt_pos Position to pass in and get back.393* @return {number} Parsed number. This throws an error if the text cannot be394* parsed.395*/396goog.i18n.NumberFormat.prototype.parse = function(text, opt_pos) {397var pos = opt_pos || [0];398399if (this.compactStyle_ != goog.i18n.NumberFormat.CompactStyle.NONE) {400throw Error('Parsing of compact numbers is unimplemented');401}402403var ret = NaN;404405// we don't want to handle 2 kind of space in parsing, normalize it to nbsp406text = text.replace(/ /g, '\u00a0');407408var gotPositive = text.indexOf(this.positivePrefix_, pos[0]) == pos[0];409var gotNegative = text.indexOf(this.negativePrefix_, pos[0]) == pos[0];410411// check for the longest match412if (gotPositive && gotNegative) {413if (this.positivePrefix_.length > this.negativePrefix_.length) {414gotNegative = false;415} else if (this.positivePrefix_.length < this.negativePrefix_.length) {416gotPositive = false;417}418}419420if (gotPositive) {421pos[0] += this.positivePrefix_.length;422} else if (gotNegative) {423pos[0] += this.negativePrefix_.length;424}425426// process digits or Inf, find decimal position427if (text.indexOf(goog.i18n.NumberFormatSymbols.INFINITY, pos[0]) == pos[0]) {428pos[0] += goog.i18n.NumberFormatSymbols.INFINITY.length;429ret = Infinity;430} else {431ret = this.parseNumber_(text, pos);432}433434// check for suffix435if (gotPositive) {436if (!(text.indexOf(this.positiveSuffix_, pos[0]) == pos[0])) {437return NaN;438}439pos[0] += this.positiveSuffix_.length;440} else if (gotNegative) {441if (!(text.indexOf(this.negativeSuffix_, pos[0]) == pos[0])) {442return NaN;443}444pos[0] += this.negativeSuffix_.length;445}446447return gotNegative ? -ret : ret;448};449450451/**452* This function will parse a "localized" text into a Number. It needs to453* handle locale specific decimal, grouping, exponent and digits.454*455* @param {string} text The text that need to be parsed.456* @param {Array<number>} pos In/out parsing position. In case of failure,457* pos value won't be changed.458* @return {number} Number value, or NaN if nothing can be parsed.459* @private460*/461goog.i18n.NumberFormat.prototype.parseNumber_ = function(text, pos) {462var sawDecimal = false;463var sawExponent = false;464var sawDigit = false;465var scale = 1;466var decimal = goog.i18n.NumberFormatSymbols.DECIMAL_SEP;467var grouping = goog.i18n.NumberFormatSymbols.GROUP_SEP;468var exponentChar = goog.i18n.NumberFormatSymbols.EXP_SYMBOL;469470if (this.compactStyle_ != goog.i18n.NumberFormat.CompactStyle.NONE) {471throw Error('Parsing of compact style numbers is not implemented');472}473474var normalizedText = '';475for (; pos[0] < text.length; pos[0]++) {476var ch = text.charAt(pos[0]);477var digit = this.getDigit_(ch);478if (digit >= 0 && digit <= 9) {479normalizedText += digit;480sawDigit = true;481} else if (ch == decimal.charAt(0)) {482if (sawDecimal || sawExponent) {483break;484}485normalizedText += '.';486sawDecimal = true;487} else if (488ch == grouping.charAt(0) &&489('\u00a0' != grouping.charAt(0) ||490pos[0] + 1 < text.length &&491this.getDigit_(text.charAt(pos[0] + 1)) >= 0)) {492// Got a grouping character here. When grouping character is nbsp, need493// to make sure the character following it is a digit.494if (sawDecimal || sawExponent) {495break;496}497continue;498} else if (ch == exponentChar.charAt(0)) {499if (sawExponent) {500break;501}502normalizedText += 'E';503sawExponent = true;504} else if (ch == '+' || ch == '-') {505normalizedText += ch;506} else if (507this.multiplier_ == 1 &&508ch == goog.i18n.NumberFormatSymbols.PERCENT.charAt(0)) {509// Parse the percent character as part of the number only when it's510// not already included in the pattern.511if (scale != 1) {512break;513}514scale = 100;515if (sawDigit) {516pos[0]++; // eat this character if parse end here517break;518}519} else if (520this.multiplier_ == 1 &&521ch == goog.i18n.NumberFormatSymbols.PERMILL.charAt(0)) {522// Parse the permill character as part of the number only when it's523// not already included in the pattern.524if (scale != 1) {525break;526}527scale = 1000;528if (sawDigit) {529pos[0]++; // eat this character if parse end here530break;531}532} else {533break;534}535}536537// Scale the number when the percent/permill character was included in538// the pattern.539if (this.multiplier_ != 1) {540scale = this.multiplier_;541}542543return parseFloat(normalizedText) / scale;544};545546547/**548* Formats a Number to produce a string.549*550* @param {number} number The Number to be formatted.551* @return {string} The formatted number string.552*/553goog.i18n.NumberFormat.prototype.format = function(number) {554if (isNaN(number)) {555return goog.i18n.NumberFormatSymbols.NAN;556}557558var parts = [];559var baseFormattingNumber = goog.isNull(this.baseFormattingNumber_) ?560number :561this.baseFormattingNumber_;562var unit = this.getUnitAfterRounding_(baseFormattingNumber, number);563number /= Math.pow(10, unit.divisorBase);564565parts.push(unit.prefix);566567// in icu code, it is commented that certain computation need to keep the568// negative sign for 0.569var isNegative = number < 0.0 || number == 0.0 && 1 / number < 0.0;570571parts.push(isNegative ? this.negativePrefix_ : this.positivePrefix_);572573if (!isFinite(number)) {574parts.push(goog.i18n.NumberFormatSymbols.INFINITY);575} else {576// convert number to non-negative value577number *= isNegative ? -1 : 1;578579number *= this.multiplier_;580this.useExponentialNotation_ ?581this.subformatExponential_(number, parts) :582this.subformatFixed_(number, this.minimumIntegerDigits_, parts);583}584585parts.push(isNegative ? this.negativeSuffix_ : this.positiveSuffix_);586parts.push(unit.suffix);587588return parts.join('');589};590591592/**593* Round a number into an integer and fractional part594* based on the rounding rules for this NumberFormat.595* @param {number} number The number to round.596* @return {{intValue: number, fracValue: number}} The integer and fractional597* part after rounding.598* @private599*/600goog.i18n.NumberFormat.prototype.roundNumber_ = function(number) {601var power = Math.pow(10, this.maximumFractionDigits_);602var shiftedNumber = this.significantDigits_ <= 0 ?603Math.round(number * power) :604Math.round(605this.roundToSignificantDigits_(606number * power, this.significantDigits_,607this.maximumFractionDigits_));608609var intValue, fracValue;610if (isFinite(shiftedNumber)) {611intValue = Math.floor(shiftedNumber / power);612fracValue = Math.floor(shiftedNumber - intValue * power);613} else {614intValue = number;615fracValue = 0;616}617return {intValue: intValue, fracValue: fracValue};618};619620621/**622* Formats a number with the appropriate groupings when there are repeating623* digits present. Repeating digits exists when the length of the digits left624* of the decimal place exceeds the number of non-repeating digits.625*626* Formats a number by iterating through the integer number (intPart) from the627* most left of the decimal place by inserting the appropriate number grouping628* separator for the repeating digits until all of the repeating digits is629* iterated. Then iterate through the non-repeating digits by inserting the630* appropriate number grouping separator until all the non-repeating digits631* is iterated through.632*633* In the number grouping concept, anything left of the decimal634* place is followed by non-repeating digits and then repeating digits. If the635* pattern is #,##,###, then we first (from the left of the decimal place) have636* a non-repeating digit of size 3 followed by repeating digits of size 2637* separated by a thousand separator. If the length of the digits are six or638* more, there may be repeating digits required. For example, the value of639* 12345678 would format as 1,23,45,678 where the repeating digit is length 2.640*641* @param {!Array<string>} parts An array to build the 'parts' of the formatted642* number including the values and separators.643* @param {number} zeroCode The value of the zero digit whether or not644* goog.i18n.NumberFormat.enforceAsciiDigits_ is enforced.645* @param {string} intPart The integer representation of the number to be646* formatted and referenced.647* @param {!Array<number>} groupingArray The array of numbers to determine the648* grouping of repeated and non-repeated digits.649* @param {number} repeatedDigitLen The length of the repeated digits left of650* the non-repeating digits left of the decimal.651* @return {!Array<string>} Returns the resulting parts variable containing652* how numbers are to be grouped and appear.653* @private654*/655goog.i18n.NumberFormat.formatNumberGroupingRepeatingDigitsParts_ = function(656parts, zeroCode, intPart, groupingArray, repeatedDigitLen) {657// Keep track of how much has been completed on the non repeated groups658var nonRepeatedGroupCompleteCount = 0;659var currentGroupSizeIndex = 0;660var currentGroupSize = 0;661662var grouping = goog.i18n.NumberFormatSymbols.GROUP_SEP;663var digitLen = intPart.length;664665// There are repeating digits and non-repeating digits666for (var i = 0; i < digitLen; i++) {667parts.push(String.fromCharCode(zeroCode + Number(intPart.charAt(i)) * 1));668if (digitLen - i > 1) {669currentGroupSize = groupingArray[currentGroupSizeIndex];670if (i < repeatedDigitLen) {671// Process the left side (the repeated number groups)672var repeatedDigitIndex = repeatedDigitLen - i;673// Edge case if there's a number grouping asking for "1" group at674// a time; otherwise, if the remainder is 1, there's the separator675if (currentGroupSize === 1 ||676(currentGroupSize > 0 &&677(repeatedDigitIndex % currentGroupSize) === 1)) {678parts.push(grouping);679}680} else if (currentGroupSizeIndex < groupingArray.length) {681// Process the right side (the non-repeated fixed number groups)682if (i === repeatedDigitLen) {683// Increase the group index because a separator684// has previously added in the earlier logic685currentGroupSizeIndex += 1;686} else if (687currentGroupSize ===688i - repeatedDigitLen - nonRepeatedGroupCompleteCount + 1) {689// Otherwise, just iterate to the right side and690// add a separator once the length matches to the expected691parts.push(grouping);692// Keep track of what has been completed on the right693nonRepeatedGroupCompleteCount += currentGroupSize;694currentGroupSizeIndex += 1; // Get to the next number grouping695}696}697}698}699return parts;700};701702703/**704* Formats a number with the appropriate groupings when there are no repeating705* digits present. Non-repeating digits exists when the length of the digits706* left of the decimal place is equal or lesser than the length of707* non-repeating digits.708*709* Formats a number by iterating through the integer number (intPart) from the710* right most non-repeating number group of the decimal place. For each group,711* inserting the appropriate number grouping separator for the non-repeating712* digits until the number is completely iterated.713*714* In the number grouping concept, anything left of the decimal715* place is followed by non-repeating digits and then repeating digits. If the716* pattern is #,##,###, then we first (from the left of the decimal place) have717* a non-repeating digit of size 3 followed by repeating digits of size 2718* separated by a thousand separator. If the length of the digits are five or719* less, there won't be any repeating digits required. For example, the value720* of 12345 would be formatted as 12,345 where the non-repeating digit is of721* length 3.722*723* @param {!Array<string>} parts An array to build the 'parts' of the formatted724* number including the values and separators.725* @param {number} zeroCode The value of the zero digit whether or not726* goog.i18n.NumberFormat.enforceAsciiDigits_ is enforced.727* @param {string} intPart The integer representation of the number to be728* formatted and referenced.729* @param {!Array<number>} groupingArray The array of numbers to determine the730* grouping of repeated and non-repeated digits.731* @return {!Array<string>} Returns the resulting parts variable containing732* how numbers are to be grouped and appear.733* @private734*/735goog.i18n.NumberFormat.formatNumberGroupingNonRepeatingDigitsParts_ = function(736parts, zeroCode, intPart, groupingArray) {737// Keep track of how much has been completed on the non repeated groups738var grouping = goog.i18n.NumberFormatSymbols.GROUP_SEP;739var currentGroupSizeIndex;740var currentGroupSize = 0;741var digitLenLeft = intPart.length;742var rightToLeftParts = [];743744// Start from the right most non-repeating group and work inwards745for (currentGroupSizeIndex = groupingArray.length - 1;746currentGroupSizeIndex >= 0 && digitLenLeft > 0;747currentGroupSizeIndex--) {748currentGroupSize = groupingArray[currentGroupSizeIndex];749// Iterate from the right most digit750for (var rightDigitIndex = 0; rightDigitIndex < currentGroupSize &&751((digitLenLeft - rightDigitIndex - 1) >= 0);752rightDigitIndex++) {753rightToLeftParts.push(754String.fromCharCode(755zeroCode +756Number(intPart.charAt(digitLenLeft - rightDigitIndex - 1)) * 1));757}758// Update the number of digits left759digitLenLeft -= currentGroupSize;760if (digitLenLeft > 0) {761rightToLeftParts.push(grouping);762}763}764// Reverse and push onto the remaining parts765parts.push.apply(parts, rightToLeftParts.reverse());766767return parts;768};769770771/**772* Formats a Number in fraction format.773*774* @param {number} number775* @param {number} minIntDigits Minimum integer digits.776* @param {Array<string>} parts777* This array holds the pieces of formatted string.778* This function will add its formatted pieces to the array.779* @private780*/781goog.i18n.NumberFormat.prototype.subformatFixed_ = function(782number, minIntDigits, parts) {783if (this.minimumFractionDigits_ > this.maximumFractionDigits_) {784throw Error('Min value must be less than max value');785}786787if (!parts) {788parts = [];789}790791var rounded = this.roundNumber_(number);792var intValue = rounded.intValue;793var fracValue = rounded.fracValue;794795var numIntDigits = (intValue == 0) ? 0 : this.intLog10_(intValue) + 1;796var fractionPresent = this.minimumFractionDigits_ > 0 || fracValue > 0 ||797(this.showTrailingZeros_ && numIntDigits < this.significantDigits_);798var minimumFractionDigits = this.minimumFractionDigits_;799if (fractionPresent) {800if (this.showTrailingZeros_ && this.significantDigits_ > 0) {801minimumFractionDigits = this.significantDigits_ - numIntDigits;802} else {803minimumFractionDigits = this.minimumFractionDigits_;804}805}806807var intPart = '';808var translatableInt = intValue;809while (translatableInt > 1E20) {810// here it goes beyond double precision, add '0' make it look better811intPart = '0' + intPart;812translatableInt = Math.round(translatableInt / 10);813}814intPart = translatableInt + intPart;815816var decimal = goog.i18n.NumberFormatSymbols.DECIMAL_SEP;817var zeroCode = goog.i18n.NumberFormat.enforceAsciiDigits_ ?81848 /* ascii '0' */ :819goog.i18n.NumberFormatSymbols.ZERO_DIGIT.charCodeAt(0);820var digitLen = intPart.length;821var nonRepeatedGroupCount = 0;822823if (intValue > 0 || minIntDigits > 0) {824for (var i = digitLen; i < minIntDigits; i++) {825parts.push(String.fromCharCode(zeroCode));826}827828// If there's more than 1 number grouping,829// figure out the length of the non-repeated groupings (on the right)830if (this.groupingArray_.length >= 2) {831for (var j = 1; j < this.groupingArray_.length; j++) {832nonRepeatedGroupCount += this.groupingArray_[j];833}834}835836// Anything left of the fixed number grouping is repeated,837// figure out the length of repeated groupings (on the left)838var repeatedDigitLen = digitLen - nonRepeatedGroupCount;839if (repeatedDigitLen > 0) {840// There are repeating digits and non-repeating digits841parts = goog.i18n.NumberFormat.formatNumberGroupingRepeatingDigitsParts_(842parts, zeroCode, intPart, this.groupingArray_, repeatedDigitLen);843} else {844// There are no repeating digits and only non-repeating digits845parts =846goog.i18n.NumberFormat.formatNumberGroupingNonRepeatingDigitsParts_(847parts, zeroCode, intPart, this.groupingArray_);848}849} else if (!fractionPresent) {850// If there is no fraction present, and we haven't printed any851// integer digits, then print a zero.852parts.push(String.fromCharCode(zeroCode));853}854855// Output the decimal separator if we always do so.856if (this.decimalSeparatorAlwaysShown_ || fractionPresent) {857parts.push(decimal);858}859860var fracPart = String(fracValue);861// Handle case where fracPart is in scientific notation.862var fracPartSplit = fracPart.split('e+');863if (fracPartSplit.length == 2) {864// Only keep significant digits.865var floatFrac = parseFloat(fracPartSplit[0]);866fracPart = String(867this.roundToSignificantDigits_(floatFrac, this.significantDigits_, 1));868fracPart = fracPart.replace('.', '');869// Append zeroes based on the exponent.870var exp = parseInt(fracPartSplit[1], 10);871fracPart += goog.string.repeat('0', exp - fracPart.length + 1);872}873874// Add Math.pow(10, this.maximumFractionDigits) to fracPart. Uses string ops875// to avoid complexity with scientific notation and overflows.876if (this.maximumFractionDigits_ + 1 > fracPart.length) {877var zeroesToAdd = this.maximumFractionDigits_ - fracPart.length;878fracPart = '1' + goog.string.repeat('0', zeroesToAdd) + fracPart;879}880881var fracLen = fracPart.length;882while (fracPart.charAt(fracLen - 1) == '0' &&883fracLen > minimumFractionDigits + 1) {884fracLen--;885}886887for (var i = 1; i < fracLen; i++) {888parts.push(String.fromCharCode(zeroCode + Number(fracPart.charAt(i)) * 1));889}890};891892893/**894* Formats exponent part of a Number.895*896* @param {number} exponent Exponential value.897* @param {Array<string>} parts The array that holds the pieces of formatted898* string. This function will append more formatted pieces to the array.899* @private900*/901goog.i18n.NumberFormat.prototype.addExponentPart_ = function(exponent, parts) {902parts.push(goog.i18n.NumberFormatSymbols.EXP_SYMBOL);903904if (exponent < 0) {905exponent = -exponent;906parts.push(goog.i18n.NumberFormatSymbols.MINUS_SIGN);907} else if (this.useSignForPositiveExponent_) {908parts.push(goog.i18n.NumberFormatSymbols.PLUS_SIGN);909}910911var exponentDigits = '' + exponent;912var zeroChar = goog.i18n.NumberFormat.enforceAsciiDigits_ ?913'0' :914goog.i18n.NumberFormatSymbols.ZERO_DIGIT;915for (var i = exponentDigits.length; i < this.minExponentDigits_; i++) {916parts.push(zeroChar);917}918parts.push(exponentDigits);919};920921/**922* Returns the mantissa for the given value and its exponent.923*924* @param {number} value925* @param {number} exponent926* @return {number}927* @private928*/929goog.i18n.NumberFormat.prototype.getMantissa_ = function(value, exponent) {930var divisor = Math.pow(10, exponent);931if (isFinite(divisor) && divisor !== 0) {932return value / divisor;933} else {934// If the exponent is too big pow returns 0. In such a case we calculate935// half of the divisor and apply it twice.936divisor = Math.pow(10, Math.floor(exponent / 2));937var result = value / divisor / divisor;938if (exponent % 2 == 1) { // Correcting for odd exponents.939if (exponent > 0) {940result /= 10;941} else {942result *= 10;943}944}945return result;946}947};948949/**950* Formats Number in exponential format.951*952* @param {number} number Value need to be formatted.953* @param {Array<string>} parts The array that holds the pieces of formatted954* string. This function will append more formatted pieces to the array.955* @private956*/957goog.i18n.NumberFormat.prototype.subformatExponential_ = function(958number, parts) {959if (number == 0.0) {960this.subformatFixed_(number, this.minimumIntegerDigits_, parts);961this.addExponentPart_(0, parts);962return;963}964965var exponent = goog.math.safeFloor(Math.log(number) / Math.log(10));966number = this.getMantissa_(number, exponent);967968var minIntDigits = this.minimumIntegerDigits_;969if (this.maximumIntegerDigits_ > 1 &&970this.maximumIntegerDigits_ > this.minimumIntegerDigits_) {971// A repeating range is defined; adjust to it as follows.972// If repeat == 3, we have 6,5,4=>3; 3,2,1=>0; 0,-1,-2=>-3;973// -3,-4,-5=>-6, etc. This takes into account that the974// exponent we have here is off by one from what we expect;975// it is for the format 0.MMMMMx10^n.976while ((exponent % this.maximumIntegerDigits_) != 0) {977number *= 10;978exponent--;979}980minIntDigits = 1;981} else {982// No repeating range is defined; use minimum integer digits.983if (this.minimumIntegerDigits_ < 1) {984exponent++;985number /= 10;986} else {987exponent -= this.minimumIntegerDigits_ - 1;988number *= Math.pow(10, this.minimumIntegerDigits_ - 1);989}990}991this.subformatFixed_(number, minIntDigits, parts);992this.addExponentPart_(exponent, parts);993};994995996/**997* Returns the digit value of current character. The character could be either998* '0' to '9', or a locale specific digit.999*1000* @param {string} ch Character that represents a digit.1001* @return {number} The digit value, or -1 on error.1002* @private1003*/1004goog.i18n.NumberFormat.prototype.getDigit_ = function(ch) {1005var code = ch.charCodeAt(0);1006// between '0' to '9'1007if (48 <= code && code < 58) {1008return code - 48;1009} else {1010var zeroCode = goog.i18n.NumberFormatSymbols.ZERO_DIGIT.charCodeAt(0);1011return zeroCode <= code && code < zeroCode + 10 ? code - zeroCode : -1;1012}1013};101410151016// ----------------------------------------------------------------------1017// CONSTANTS1018// ----------------------------------------------------------------------1019// Constants for characters used in programmatic (unlocalized) patterns.1020/**1021* A zero digit character.1022* @type {string}1023* @private1024*/1025goog.i18n.NumberFormat.PATTERN_ZERO_DIGIT_ = '0';102610271028/**1029* A grouping separator character.1030* @type {string}1031* @private1032*/1033goog.i18n.NumberFormat.PATTERN_GROUPING_SEPARATOR_ = ',';103410351036/**1037* A decimal separator character.1038* @type {string}1039* @private1040*/1041goog.i18n.NumberFormat.PATTERN_DECIMAL_SEPARATOR_ = '.';104210431044/**1045* A per mille character.1046* @type {string}1047* @private1048*/1049goog.i18n.NumberFormat.PATTERN_PER_MILLE_ = '\u2030';105010511052/**1053* A percent character.1054* @type {string}1055* @private1056*/1057goog.i18n.NumberFormat.PATTERN_PERCENT_ = '%';105810591060/**1061* A digit character.1062* @type {string}1063* @private1064*/1065goog.i18n.NumberFormat.PATTERN_DIGIT_ = '#';106610671068/**1069* A separator character.1070* @type {string}1071* @private1072*/1073goog.i18n.NumberFormat.PATTERN_SEPARATOR_ = ';';107410751076/**1077* An exponent character.1078* @type {string}1079* @private1080*/1081goog.i18n.NumberFormat.PATTERN_EXPONENT_ = 'E';108210831084/**1085* A plus character.1086* @type {string}1087* @private1088*/1089goog.i18n.NumberFormat.PATTERN_PLUS_ = '+';109010911092/**1093* A generic currency sign character.1094* @type {string}1095* @private1096*/1097goog.i18n.NumberFormat.PATTERN_CURRENCY_SIGN_ = '\u00A4';109810991100/**1101* A quote character.1102* @type {string}1103* @private1104*/1105goog.i18n.NumberFormat.QUOTE_ = '\'';110611071108/**1109* Parses affix part of pattern.1110*1111* @param {string} pattern Pattern string that need to be parsed.1112* @param {Array<number>} pos One element position array to set and receive1113* parsing position.1114*1115* @return {string} Affix received from parsing.1116* @private1117*/1118goog.i18n.NumberFormat.prototype.parseAffix_ = function(pattern, pos) {1119var affix = '';1120var inQuote = false;1121var len = pattern.length;11221123for (; pos[0] < len; pos[0]++) {1124var ch = pattern.charAt(pos[0]);1125if (ch == goog.i18n.NumberFormat.QUOTE_) {1126if (pos[0] + 1 < len &&1127pattern.charAt(pos[0] + 1) == goog.i18n.NumberFormat.QUOTE_) {1128pos[0]++;1129affix += '\''; // 'don''t'1130} else {1131inQuote = !inQuote;1132}1133continue;1134}11351136if (inQuote) {1137affix += ch;1138} else {1139switch (ch) {1140case goog.i18n.NumberFormat.PATTERN_DIGIT_:1141case goog.i18n.NumberFormat.PATTERN_ZERO_DIGIT_:1142case goog.i18n.NumberFormat.PATTERN_GROUPING_SEPARATOR_:1143case goog.i18n.NumberFormat.PATTERN_DECIMAL_SEPARATOR_:1144case goog.i18n.NumberFormat.PATTERN_SEPARATOR_:1145return affix;1146case goog.i18n.NumberFormat.PATTERN_CURRENCY_SIGN_:1147if ((pos[0] + 1) < len &&1148pattern.charAt(pos[0] + 1) ==1149goog.i18n.NumberFormat.PATTERN_CURRENCY_SIGN_) {1150pos[0]++;1151affix += this.intlCurrencyCode_;1152} else {1153switch (this.currencyStyle_) {1154case goog.i18n.NumberFormat.CurrencyStyle.LOCAL:1155affix += goog.i18n.currency.getLocalCurrencySign(1156this.intlCurrencyCode_);1157break;1158case goog.i18n.NumberFormat.CurrencyStyle.GLOBAL:1159affix += goog.i18n.currency.getGlobalCurrencySign(1160this.intlCurrencyCode_);1161break;1162case goog.i18n.NumberFormat.CurrencyStyle.PORTABLE:1163affix += goog.i18n.currency.getPortableCurrencySign(1164this.intlCurrencyCode_);1165break;1166default:1167break;1168}1169}1170break;1171case goog.i18n.NumberFormat.PATTERN_PERCENT_:1172if (!this.negativePercentSignExpected_ && this.multiplier_ != 1) {1173throw Error('Too many percent/permill');1174} else if (1175this.negativePercentSignExpected_ && this.multiplier_ != 100) {1176throw Error('Inconsistent use of percent/permill characters');1177}1178this.multiplier_ = 100;1179this.negativePercentSignExpected_ = false;1180affix += goog.i18n.NumberFormatSymbols.PERCENT;1181break;1182case goog.i18n.NumberFormat.PATTERN_PER_MILLE_:1183if (!this.negativePercentSignExpected_ && this.multiplier_ != 1) {1184throw Error('Too many percent/permill');1185} else if (1186this.negativePercentSignExpected_ && this.multiplier_ != 1000) {1187throw Error('Inconsistent use of percent/permill characters');1188}1189this.multiplier_ = 1000;1190this.negativePercentSignExpected_ = false;1191affix += goog.i18n.NumberFormatSymbols.PERMILL;1192break;1193default:1194affix += ch;1195}1196}1197}11981199return affix;1200};120112021203/**1204* Parses the trunk part of a pattern.1205*1206* @param {string} pattern Pattern string that need to be parsed.1207* @param {Array<number>} pos One element position array to set and receive1208* parsing position.1209* @private1210*/1211goog.i18n.NumberFormat.prototype.parseTrunk_ = function(pattern, pos) {1212var decimalPos = -1;1213var digitLeftCount = 0;1214var zeroDigitCount = 0;1215var digitRightCount = 0;1216var groupingCount = -1;1217var len = pattern.length;1218for (var loop = true; pos[0] < len && loop; pos[0]++) {1219var ch = pattern.charAt(pos[0]);1220switch (ch) {1221case goog.i18n.NumberFormat.PATTERN_DIGIT_:1222if (zeroDigitCount > 0) {1223digitRightCount++;1224} else {1225digitLeftCount++;1226}1227if (groupingCount >= 0 && decimalPos < 0) {1228groupingCount++;1229}1230break;1231case goog.i18n.NumberFormat.PATTERN_ZERO_DIGIT_:1232if (digitRightCount > 0) {1233throw Error('Unexpected "0" in pattern "' + pattern + '"');1234}1235zeroDigitCount++;1236if (groupingCount >= 0 && decimalPos < 0) {1237groupingCount++;1238}1239break;1240case goog.i18n.NumberFormat.PATTERN_GROUPING_SEPARATOR_:1241if (groupingCount > 0) {1242this.groupingArray_.push(groupingCount);1243}1244groupingCount = 0;1245break;1246case goog.i18n.NumberFormat.PATTERN_DECIMAL_SEPARATOR_:1247if (decimalPos >= 0) {1248throw Error(1249'Multiple decimal separators in pattern "' + pattern + '"');1250}1251decimalPos = digitLeftCount + zeroDigitCount + digitRightCount;1252break;1253case goog.i18n.NumberFormat.PATTERN_EXPONENT_:1254if (this.useExponentialNotation_) {1255throw Error(1256'Multiple exponential symbols in pattern "' + pattern + '"');1257}1258this.useExponentialNotation_ = true;1259this.minExponentDigits_ = 0;12601261// exponent pattern can have a optional '+'.1262if ((pos[0] + 1) < len &&1263pattern.charAt(pos[0] + 1) ==1264goog.i18n.NumberFormat.PATTERN_PLUS_) {1265pos[0]++;1266this.useSignForPositiveExponent_ = true;1267}12681269// Use lookahead to parse out the exponential part1270// of the pattern, then jump into phase 2.1271while ((pos[0] + 1) < len &&1272pattern.charAt(pos[0] + 1) ==1273goog.i18n.NumberFormat.PATTERN_ZERO_DIGIT_) {1274pos[0]++;1275this.minExponentDigits_++;1276}12771278if ((digitLeftCount + zeroDigitCount) < 1 ||1279this.minExponentDigits_ < 1) {1280throw Error('Malformed exponential pattern "' + pattern + '"');1281}1282loop = false;1283break;1284default:1285pos[0]--;1286loop = false;1287break;1288}1289}12901291if (zeroDigitCount == 0 && digitLeftCount > 0 && decimalPos >= 0) {1292// Handle '###.###' and '###.' and '.###'1293var n = decimalPos;1294if (n == 0) { // Handle '.###'1295n++;1296}1297digitRightCount = digitLeftCount - n;1298digitLeftCount = n - 1;1299zeroDigitCount = 1;1300}13011302// Do syntax checking on the digits.1303if (decimalPos < 0 && digitRightCount > 0 ||1304decimalPos >= 0 && (decimalPos < digitLeftCount ||1305decimalPos > digitLeftCount + zeroDigitCount) ||1306groupingCount == 0) {1307throw Error('Malformed pattern "' + pattern + '"');1308}1309var totalDigits = digitLeftCount + zeroDigitCount + digitRightCount;13101311this.maximumFractionDigits_ = decimalPos >= 0 ? totalDigits - decimalPos : 0;1312if (decimalPos >= 0) {1313this.minimumFractionDigits_ = digitLeftCount + zeroDigitCount - decimalPos;1314if (this.minimumFractionDigits_ < 0) {1315this.minimumFractionDigits_ = 0;1316}1317}13181319// The effectiveDecimalPos is the position the decimal is at or would be at1320// if there is no decimal. Note that if decimalPos<0, then digitTotalCount ==1321// digitLeftCount + zeroDigitCount.1322var effectiveDecimalPos = decimalPos >= 0 ? decimalPos : totalDigits;1323this.minimumIntegerDigits_ = effectiveDecimalPos - digitLeftCount;1324if (this.useExponentialNotation_) {1325this.maximumIntegerDigits_ = digitLeftCount + this.minimumIntegerDigits_;13261327// in exponential display, we need to at least show something.1328if (this.maximumFractionDigits_ == 0 && this.minimumIntegerDigits_ == 0) {1329this.minimumIntegerDigits_ = 1;1330}1331}13321333// Add another number grouping at the end1334this.groupingArray_.push(Math.max(0, groupingCount));1335this.decimalSeparatorAlwaysShown_ =1336decimalPos == 0 || decimalPos == totalDigits;1337};133813391340/**1341* Alias for the compact format 'unit' object.1342* @typedef {{1343* prefix: string,1344* suffix: string,1345* divisorBase: number1346* }}1347*/1348goog.i18n.NumberFormat.CompactNumberUnit;134913501351/**1352* The empty unit, corresponding to a base of 0.1353* @private {!goog.i18n.NumberFormat.CompactNumberUnit}1354*/1355goog.i18n.NumberFormat.NULL_UNIT_ = {1356prefix: '',1357suffix: '',1358divisorBase: 01359};136013611362/**1363* Get compact unit for a certain number of digits1364*1365* @param {number} base The number of digits to get the unit for.1366* @param {string} plurality The plurality of the number.1367* @return {!goog.i18n.NumberFormat.CompactNumberUnit} The compact unit.1368* @private1369*/1370goog.i18n.NumberFormat.prototype.getUnitFor_ = function(base, plurality) {1371var table = this.compactStyle_ == goog.i18n.NumberFormat.CompactStyle.SHORT ?1372goog.i18n.CompactNumberFormatSymbols.COMPACT_DECIMAL_SHORT_PATTERN :1373goog.i18n.CompactNumberFormatSymbols.COMPACT_DECIMAL_LONG_PATTERN;13741375if (!goog.isDefAndNotNull(table)) {1376table = goog.i18n.CompactNumberFormatSymbols.COMPACT_DECIMAL_SHORT_PATTERN;1377}13781379if (base < 3) {1380return goog.i18n.NumberFormat.NULL_UNIT_;1381} else {1382base = Math.min(14, base);1383var patterns = table[Math.pow(10, base)];1384var previousNonNullBase = base - 1;1385while (!patterns && previousNonNullBase >= 3) {1386patterns = table[Math.pow(10, previousNonNullBase)];1387previousNonNullBase--;1388}1389if (!patterns) {1390return goog.i18n.NumberFormat.NULL_UNIT_;1391}13921393var pattern = patterns[plurality];1394if (!pattern || pattern == '0') {1395return goog.i18n.NumberFormat.NULL_UNIT_;1396}13971398var parts = /([^0]*)(0+)(.*)/.exec(pattern);1399if (!parts) {1400return goog.i18n.NumberFormat.NULL_UNIT_;1401}14021403return {1404prefix: parts[1],1405suffix: parts[3],1406divisorBase: (previousNonNullBase + 1) - (parts[2].length - 1)1407};1408}1409};141014111412/**1413* Get the compact unit divisor, accounting for rounding of the quantity.1414*1415* @param {number} formattingNumber The number to base the formatting on. The1416* unit will be calculated from this number.1417* @param {number} pluralityNumber The number to use for calculating the1418* plurality.1419* @return {!goog.i18n.NumberFormat.CompactNumberUnit} The unit after rounding.1420* @private1421*/1422goog.i18n.NumberFormat.prototype.getUnitAfterRounding_ = function(1423formattingNumber, pluralityNumber) {1424if (this.compactStyle_ == goog.i18n.NumberFormat.CompactStyle.NONE) {1425return goog.i18n.NumberFormat.NULL_UNIT_;1426}14271428formattingNumber = Math.abs(formattingNumber);1429pluralityNumber = Math.abs(pluralityNumber);14301431var initialPlurality = this.pluralForm_(formattingNumber);1432// Compute the exponent from the formattingNumber, to compute the unit.1433var base = formattingNumber <= 1 ? 0 : this.intLog10_(formattingNumber);1434var initialDivisor = this.getUnitFor_(base, initialPlurality).divisorBase;1435// Round both numbers based on the unit used.1436var pluralityAttempt = pluralityNumber / Math.pow(10, initialDivisor);1437var pluralityRounded = this.roundNumber_(pluralityAttempt);1438var formattingAttempt = formattingNumber / Math.pow(10, initialDivisor);1439var formattingRounded = this.roundNumber_(formattingAttempt);1440// Compute the plurality of the pluralityNumber when formatted using the name1441// units as the formattingNumber.1442var finalPlurality =1443this.pluralForm_(pluralityRounded.intValue + pluralityRounded.fracValue);1444// Get the final unit, using the rounded formatting number to get the correct1445// unit, and the plurality computed from the pluralityNumber.1446return this.getUnitFor_(1447initialDivisor + this.intLog10_(formattingRounded.intValue),1448finalPlurality);1449};145014511452/**1453* Get the integer base 10 logarithm of a number.1454*1455* @param {number} number The number to log.1456* @return {number} The lowest integer n such that 10^n >= number.1457* @private1458*/1459goog.i18n.NumberFormat.prototype.intLog10_ = function(number) {1460// Handle infinity.1461if (!isFinite(number)) {1462return number > 0 ? number : 0;1463}1464// Turns out Math.log(1000000)/Math.LN10 is strictly less than 6.1465var i = 0;1466while ((number /= 10) >= 1) i++;1467return i;1468};146914701471/**1472* Round to a certain number of significant digits.1473*1474* @param {number} number The number to round.1475* @param {number} significantDigits The number of significant digits1476* to round to.1477* @param {number} scale Treat number as fixed point times 10^scale.1478* @return {number} The rounded number.1479* @private1480*/1481goog.i18n.NumberFormat.prototype.roundToSignificantDigits_ = function(1482number, significantDigits, scale) {1483if (!number) return number;14841485var digits = this.intLog10_(number);1486var magnitude = significantDigits - digits - 1;14871488// Only round fraction, not (potentially shifted) integers.1489if (magnitude < -scale) {1490var point = Math.pow(10, scale);1491return Math.round(number / point) * point;1492}14931494var power = Math.pow(10, magnitude);1495var shifted = Math.round(number * power);1496return shifted / power;1497};149814991500/**1501* Get the plural form of a number.1502* @param {number} quantity The quantity to find plurality of.1503* @return {string} One of 'zero', 'one', 'two', 'few', 'many', 'other'.1504* @private1505*/1506goog.i18n.NumberFormat.prototype.pluralForm_ = function(quantity) {1507/* TODO: Implement */1508return 'other';1509};151015111512/**1513* Checks if the currency symbol comes before the value ($12) or after (12$)1514* Handy for applications that need to have separate UI fields for the currency1515* value and symbol, especially for input: Price: [USD] [123.45]1516* The currency symbol might be a combo box, or a label.1517*1518* @return {boolean} true if currency is before value.1519*/1520goog.i18n.NumberFormat.prototype.isCurrencyCodeBeforeValue = function() {1521var posCurrSymbol = this.pattern_.indexOf('\u00A4'); // 'ยค' Currency sign1522var posPound = this.pattern_.indexOf('#');1523var posZero = this.pattern_.indexOf('0');15241525// posCurrValue is the first '#' or '0' found.1526// If none of them is found (not possible, but still),1527// the result is true (postCurrSymbol < MAX_VALUE)1528// That is OK, matches the en_US and ROOT locales.1529var posCurrValue = Number.MAX_VALUE;1530if (posPound >= 0 && posPound < posCurrValue) {1531posCurrValue = posPound;1532}1533if (posZero >= 0 && posZero < posCurrValue) {1534posCurrValue = posZero;1535}15361537// No need to test, it is guaranteed that both these symbols exist.1538// If not, we have bigger problems than this.1539return posCurrSymbol < posCurrValue;1540};154115421543