1/** 2 * Globalize v1.7.0 3 * 4 * https://github.com/globalizejs/globalize 5 * 6 * Copyright OpenJS Foundation and other contributors 7 * Released under the MIT license 8 * https://jquery.org/license 9 * 10 * Date: 2021-08-02T11:53Z 11 */ 12/*! 13 * Globalize v1.7.0 2021-08-02T11:53Z Released under the MIT license 14 * http://git.io/TrdQbw
vendor: 38,525 bytes, lines 14-1454
14 15 */ 16(function( root, factory ) { 17 18 // UMD returnExports 19 if ( typeof define === "function" && define.amd ) { 20 21 // AMD 22 define([ 23 "cldr", 24 "../globalize", 25 "cldr/event", 26 "cldr/supplemental" 27 ], factory ); 28 } else if ( typeof exports === "object" ) { 29 30 // Node, CommonJS 31 module.exports = factory( require( "cldrjs" ), require( "../globalize" ) ); 32 } else { 33 34 // Global 35 factory( root.Cldr, root.Globalize ); 36 } 37}(this, function( Cldr, Globalize ) { 38 39var createError = Globalize._createError, 40 partsJoin = Globalize._partsJoin, 41 partsPush = Globalize._partsPush, 42 regexpEscape = Globalize._regexpEscape, 43 runtimeBind = Globalize._runtimeBind, 44 stringPad = Globalize._stringPad, 45 validateCldr = Globalize._validateCldr, 46 validateDefaultLocale = Globalize._validateDefaultLocale, 47 validateParameterPresence = Globalize._validateParameterPresence, 48 validateParameterRange = Globalize._validateParameterRange, 49 validateParameterType = Globalize._validateParameterType, 50 validateParameterTypePlainObject = Globalize._validateParameterTypePlainObject; 51 52 53var createErrorUnsupportedFeature = function( feature ) { 54 return createError( "E_UNSUPPORTED", "Unsupported {feature}.", { 55 feature: feature 56 }); 57}; 58 59 60 61 62var validateParameterTypeNumber = function( value, name ) { 63 validateParameterType( 64 value, 65 name, 66 value === undefined || typeof value === "number", 67 "Number" 68 ); 69}; 70 71 72 73 74var validateParameterTypeString = function( value, name ) { 75 validateParameterType( 76 value, 77 name, 78 value === undefined || typeof value === "string", 79 "a string" 80 ); 81}; 82 83 84 85 86var numberFormatterFn = function( numberToPartsFormatter ) { 87 return function numberFormatter( value ) { 88 return partsJoin( numberToPartsFormatter( value )); 89 }; 90}; 91 92 93 94 95/** 96 * NumberingSystem( cldr ) 97 * 98 * - http://www.unicode.org/reports/tr35/tr35-numbers.html#otherNumberingSystems 99 * - http://cldr.unicode.org/index/bcp47-extension 100 * - http://www.unicode.org/reports/tr35/#u_Extension 101 */ 102var numberNumberingSystem = function( cldr ) { 103 var nu = cldr.attributes[ "u-nu" ]; 104 105 if ( nu ) { 106 if ( nu === "traditio" ) { 107 nu = "traditional"; 108 } 109 if ( [ "native", "traditional", "finance" ].indexOf( nu ) !== -1 ) { 110 111 // Unicode locale extension `u-nu` is set using either (native, traditional or 112 // finance). So, lookup the respective locale's numberingSystem and return it. 113 return cldr.main([ "numbers/otherNumberingSystems", nu ]); 114 } 115 116 // Unicode locale extension `u-nu` is set with an explicit numberingSystem. Return it. 117 return nu; 118 } 119 120 // Return the default numberingSystem. 121 return cldr.main( "numbers/defaultNumberingSystem" ); 122}; 123 124 125 126 127/** 128 * Compact( name, cldr ) 129 * 130 * @compactType [String] Compact mode, `short` or `long`. 131 * 132 * @cldr [Cldr instance]. 133 * 134 * Return the localized compact map for the given compact mode. 135 */ 136var numberCompact = function( compactType, cldr ) { 137 var maxExponent = 0; 138 139 var object = cldr.main([ 140 "numbers/decimalFormats-numberSystem-" + numberNumberingSystem( cldr ), 141 compactType, 142 "decimalFormat" 143 ]); 144 145 object = Object.keys( object ).reduce(function( newObject, compactKey ) { 146 var numberExponent = compactKey.split( "0" ).length - 1; 147 var pluralForm = compactKey.split( "-" )[ 2 ]; 148 newObject[ numberExponent ] = newObject[ numberExponent ] || {}; 149 newObject[ numberExponent ][ pluralForm ] = object[ compactKey ]; 150 maxExponent = Math.max( numberExponent, maxExponent ); 151 return newObject; 152 }, {}); 153 154 object.maxExponent = maxExponent; 155 156 return object; 157}; 158 159 160 161 162/** 163 * nuMap( cldr ) 164 * 165 * @cldr [Cldr instance]. 166 * 167 * Return digits map if numbering system is different than `latn`. 168 */ 169var numberNumberingSystemDigitsMap = function( cldr ) { 170 var aux, 171 nu = numberNumberingSystem( cldr ); 172 173 if ( nu === "latn" ) { 174 return; 175 } 176 177 aux = cldr.supplemental([ "numberingSystems", nu ]); 178 179 if ( aux._type !== "numeric" ) { 180 throw createErrorUnsupportedFeature( "`" + aux._type + "` numbering system" ); 181 } 182 183 return aux._digits; 184}; 185 186 187 188 189/** 190 * EBNF representation: 191 * 192 * number_pattern_re = prefix? 193 * padding? 194 * (integer_fraction_pattern | significant_pattern) 195 * scientific_notation? 196 * suffix? 197 * 198 * prefix = non_number_stuff 199 * 200 * padding = "*" regexp(.) 201 * 202 * integer_fraction_pattern = integer_pattern 203 * fraction_pattern? 204 * 205 * integer_pattern = regexp([#,]*[0,]*0+) 206 * 207 * fraction_pattern = "." regexp(0*[0-9]*#*) 208 * 209 * significant_pattern = regexp([#,]*@+#*) 210 * 211 * scientific_notation = regexp(E\+?0+) 212 * 213 * suffix = non_number_stuff 214 * 215 * non_number_stuff = regexp(('[^']+'|''|[^*#@0,.E])*) 216 * 217 * 218 * Regexp groups: 219 * 220 * 0: number_pattern_re 221 * 1: prefix 222 * 2: - 223 * 3: - 224 * 4: padding 225 * 5: (integer_fraction_pattern | significant_pattern) 226 * 6: integer_fraction_pattern 227 * 7: integer_pattern 228 * 8: fraction_pattern 229 * 9: significant_pattern 230 * 10: scientific_notation 231 * 11: suffix 232 * 12: - 233 */ 234var numberPatternRe = ( /^(('([^']|'')*'|[^*#@0,.E])*)(\*.)?((([#,]*[0,]*0+)(\.0*[0-9]*#*)?)|([#,]*@+#*))(E\+?0+)?(('[^']+'|''|[^*#@0,.E])*)$/ ); 235 236 237 238 239/** 240 * format( number, pattern ) 241 * 242 * @number [Number]. 243 * 244 * @pattern [String] raw pattern for numbers. 245 * 246 * Return the formatted number. 247 * ref: http://www.unicode.org/reports/tr35/tr35-numbers.html 248 */ 249var numberPatternProperties = function( pattern ) { 250 var aux1, aux2, fractionPattern, integerFractionOrSignificantPattern, integerPattern, 251 maximumFractionDigits, maximumSignificantDigits, minimumFractionDigits, 252 minimumIntegerDigits, minimumSignificantDigits, padding, prefix, primaryGroupingSize, 253 roundIncrement, scientificNotation, secondaryGroupingSize, significantPattern, suffix; 254 255 pattern = pattern.match( numberPatternRe ); 256 if ( !pattern ) { 257 throw new Error( "Invalid pattern: " + pattern ); 258 } 259 260 prefix = pattern[ 1 ]; 261 padding = pattern[ 4 ]; 262 integerFractionOrSignificantPattern = pattern[ 5 ]; 263 significantPattern = pattern[ 9 ]; 264 scientificNotation = pattern[ 10 ]; 265 suffix = pattern[ 11 ]; 266 267 // Significant digit format 268 if ( significantPattern ) { 269 significantPattern.replace( /(@+)(#*)/, function( _match, minimumSignificantDigitsMatch, maximumSignificantDigitsMatch ) { 270 minimumSignificantDigits = minimumSignificantDigitsMatch.length; 271 maximumSignificantDigits = minimumSignificantDigits + 272 maximumSignificantDigitsMatch.length; 273 }); 274 275 // Integer and fractional format 276 } else { 277 fractionPattern = pattern[ 8 ]; 278 integerPattern = pattern[ 7 ]; 279 280 if ( fractionPattern ) { 281 282 // Minimum fraction digits, and rounding. 283 fractionPattern.replace( /[0-9]+/, function( match ) { 284 minimumFractionDigits = match; 285 }); 286 if ( minimumFractionDigits ) { 287 roundIncrement = +( "0." + minimumFractionDigits ); 288 minimumFractionDigits = minimumFractionDigits.length; 289 } else { 290 minimumFractionDigits = 0; 291 } 292 293 // Maximum fraction digits 294 // 1: ignore decimal character 295 maximumFractionDigits = fractionPattern.length - 1; /* 1 */ 296 } else { 297 minimumFractionDigits = 0; 298 maximumFractionDigits = 0; 299 } 300 301 // Minimum integer digits 302 integerPattern.replace( /0+$/, function( match ) { 303 minimumIntegerDigits = match.length; 304 }); 305 } 306 307 // Scientific notation 308 if ( scientificNotation ) { 309 throw createErrorUnsupportedFeature({ 310 feature: "scientific notation (not implemented)" 311 }); 312 } 313 314 // Padding 315 if ( padding ) { 316 throw createErrorUnsupportedFeature({ 317 feature: "padding (not implemented)" 318 }); 319 } 320 321 // Grouping 322 if ( ( aux1 = integerFractionOrSignificantPattern.lastIndexOf( "," ) ) !== -1 ) { 323 324 // Primary grouping size is the interval between the last group separator and the end of 325 // the integer (or the end of the significant pattern). 326 aux2 = integerFractionOrSignificantPattern.split( "." )[ 0 ]; 327 primaryGroupingSize = aux2.length - aux1 - 1; 328 329 // Secondary grouping size is the interval between the last two group separators. 330 if ( ( aux2 = integerFractionOrSignificantPattern.lastIndexOf( ",", aux1 - 1 ) ) !== -1 ) { 331 secondaryGroupingSize = aux1 - 1 - aux2; 332 } 333 } 334 335 // Return: 336 // 0: @prefix String 337 // 1: @padding Array [ <character>, <count> ] TODO 338 // 2: @minimumIntegerDigits non-negative integer Number value indicating the minimum integer 339 // digits to be used. Numbers will be padded with leading zeroes if necessary. 340 // 3: @minimumFractionDigits and 341 // 4: @maximumFractionDigits are non-negative integer Number values indicating the minimum and 342 // maximum fraction digits to be used. Numbers will be rounded or padded with trailing 343 // zeroes if necessary. 344 // 5: @minimumSignificantDigits and 345 // 6: @maximumSignificantDigits are positive integer Number values indicating the minimum and 346 // maximum fraction digits to be shown. Either none or both of these properties are 347 // present; if they are, they override minimum and maximum integer and fraction digits 348 // â the formatter uses however many integer and fraction digits are required to display 349 // the specified number of significant digits. 350 // 7: @roundIncrement Decimal round increment or null 351 // 8: @primaryGroupingSize 352 // 9: @secondaryGroupingSize 353 // 10: @suffix String 354 return [ 355 prefix, 356 padding, 357 minimumIntegerDigits, 358 minimumFractionDigits, 359 maximumFractionDigits, 360 minimumSignificantDigits, 361 maximumSignificantDigits, 362 roundIncrement, 363 primaryGroupingSize, 364 secondaryGroupingSize, 365 suffix 366 ]; 367}; 368 369 370 371 372/** 373 * Symbol( name, cldr ) 374 * 375 * @name [String] Symbol name. 376 * 377 * @cldr [Cldr instance]. 378 * 379 * Return the localized symbol given its name. 380 */ 381var numberSymbol = function( name, cldr ) { 382 return cldr.main([ 383 "numbers/symbols-numberSystem-" + numberNumberingSystem( cldr ), 384 name 385 ]); 386}; 387 388 389 390 391var numberSymbolName = { 392 ".": "decimal", 393 ",": "group", 394 "%": "percentSign", 395 "+": "plusSign", 396 "-": "minusSign", 397 "E": "exponential", 398 "\u2030": "perMille" 399}; 400 401 402 403 404/** 405 * symbolMap( cldr ) 406 * 407 * @cldr [Cldr instance]. 408 * 409 * Return the (localized symbol, pattern symbol) key value pair, eg. { 410 * ".": "Ù«", 411 * ",": "Ù¬", 412 * "%": "Ùª", 413 * ... 414 * }; 415 */ 416var numberSymbolMap = function( cldr ) { 417 var symbol, 418 symbolMap = {}; 419 420 for ( symbol in numberSymbolName ) { 421 symbolMap[ symbol ] = numberSymbol( numberSymbolName[ symbol ], cldr ); 422 } 423 424 return symbolMap; 425}; 426 427 428 429 430var numberTruncate = function( value ) { 431 if ( isNaN( value ) ) { 432 return NaN; 433 } 434 return Math[ value < 0 ? "ceil" : "floor" ]( value ); 435}; 436 437 438 439 440/** 441 * round( method ) 442 * 443 * @method [String] with either "round", "ceil", "floor", or "truncate". 444 * 445 * Return function( value, incrementOrExp ): 446 * 447 * @value [Number] eg. 123.45. 448 * 449 * @incrementOrExp [Number] optional, eg. 0.1; or 450 * [Object] Either { increment: <value> } or { exponent: <value> } 451 * 452 * Return the rounded number, eg: 453 * - round( "round" )( 123.45 ): 123; 454 * - round( "ceil" )( 123.45 ): 124; 455 * - round( "floor" )( 123.45 ): 123; 456 * - round( "truncate" )( 123.45 ): 123; 457 * - round( "round" )( 123.45, 0.1 ): 123.5; 458 * - round( "round" )( 123.45, 10 ): 120; 459 * 460 * Based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round 461 * Ref: #376 462 */ 463var numberRound = function( method ) { 464 method = method || "round"; 465 method = method === "truncate" ? numberTruncate : Math[ method ]; 466 467 return function( value, incrementOrExp ) { 468 var exp, increment; 469 470 value = +value; 471 472 // If the value is not a number, return NaN. 473 if ( isNaN( value ) ) { 474 return NaN; 475 } 476 477 // Exponent given. 478 if ( typeof incrementOrExp === "object" && incrementOrExp.exponent ) { 479 exp = +incrementOrExp.exponent; 480 increment = 1; 481 482 if ( exp === 0 ) { 483 return method( value ); 484 } 485 486 // If the exp is not an integer, return NaN. 487 if ( !( typeof exp === "number" && exp % 1 === 0 ) ) { 488 return NaN; 489 } 490 491 // Increment given. 492 } else { 493 increment = +incrementOrExp || 1; 494 495 if ( increment === 1 ) { 496 return method( value ); 497 } 498 499 // If the increment is not a number, return NaN. 500 if ( isNaN( increment ) ) { 501 return NaN; 502 } 503 504 increment = increment.toExponential().split( "e" ); 505 exp = +increment[ 1 ]; 506 increment = +increment[ 0 ]; 507 } 508 509 // Shift & Round 510 value = value.toString().split( "e" ); 511 value[ 0 ] = +value[ 0 ] / increment; 512 value[ 1 ] = value[ 1 ] ? ( +value[ 1 ] - exp ) : -exp; 513 value = method( +( value[ 0 ] + "e" + value[ 1 ] ) ); 514 515 // Shift back 516 value = value.toString().split( "e" ); 517 value[ 0 ] = +value[ 0 ] * increment; 518 value[ 1 ] = value[ 1 ] ? ( +value[ 1 ] + exp ) : exp; 519 return +( value[ 0 ] + "e" + value[ 1 ] ); 520 }; 521}; 522 523 524 525 526/** 527 * formatProperties( pattern, cldr [, options] ) 528 * 529 * @pattern [String] raw pattern for numbers. 530 * 531 * @cldr [Cldr instance]. 532 * 533 * @options [Object]: 534 * - minimumIntegerDigits [Number] 535 * - minimumFractionDigits, maximumFractionDigits [Number] 536 * - minimumSignificantDigits, maximumSignificantDigits [Number] 537 * - round [String] "ceil", "floor", "round" (default), or "truncate". 538 * - useGrouping [Boolean] default true. 539 * 540 * Return the processed properties that will be used in number/format. 541 * ref: http://www.unicode.org/reports/tr35/tr35-numbers.html 542 */ 543var numberFormatProperties = function( pattern, cldr, options ) { 544 var negativePattern, negativePrefix, negativeProperties, negativeSuffix, positivePattern, 545 roundFn, properties; 546 547 function getOptions( attribute, propertyIndex ) { 548 if ( attribute in options ) { 549 properties[ propertyIndex ] = options[ attribute ]; 550 } 551 } 552 553 options = options || {}; 554 pattern = pattern.split( ";" ); 555 556 positivePattern = pattern[ 0 ]; 557 558 negativePattern = pattern[ 1 ] || "-" + positivePattern; 559 negativeProperties = numberPatternProperties( negativePattern ); 560 negativePrefix = negativeProperties[ 0 ]; 561 negativeSuffix = negativeProperties[ 10 ]; 562 563 // Have runtime code to refer to numberRound() instead of including it explicitly. 564 roundFn = numberRound( options.round ); 565 roundFn.generatorString = function() { 566 return "numberRound(" + ( options.round ? "\"" + options.round + "\"" : "" ) + ")"; 567 }; 568 569 properties = numberPatternProperties( positivePattern ).concat([ 570 positivePattern, 571 negativePrefix + positivePattern + negativeSuffix, 572 negativePrefix, 573 negativeSuffix, 574 roundFn, 575 numberSymbol( "infinity", cldr ), 576 numberSymbol( "nan", cldr ), 577 numberSymbolMap( cldr ), 578 numberNumberingSystemDigitsMap( cldr ) 579 ]); 580 581 if ( options.compact ) { 582 583 // The compact digits number pattern is always `0+`, so override the following properties. 584 // Note: minimumIntegerDigits would actually range from `0` to `000` based on the scale of 585 // the value to be formatted, though we're always using 1 as a simplification, because the 586 // number won't be zero-padded since we chose the right format based on the scale, i.e., 587 // we'd never see something like `003M` anyway. 588 properties[ 2 ] = 1; // minimumIntegerDigits 589 properties[ 3 ] = 0; // minimumFractionDigits 590 properties[ 4 ] = 0; // maximumFractionDigits 591 properties[ 5 ] = // minimumSignificantDigits & 592 properties[ 6 ] = undefined; // maximumSignificantDigits 593 594 properties[ 20 ] = numberCompact( options.compact, cldr ); 595 } 596 597 getOptions( "minimumIntegerDigits", 2 ); 598 getOptions( "minimumFractionDigits", 3 ); 599 getOptions( "maximumFractionDigits", 4 ); 600 getOptions( "minimumSignificantDigits", 5 ); 601 getOptions( "maximumSignificantDigits", 6 ); 602 603 // Grouping separators 604 if ( options.useGrouping === false ) { 605 properties[ 8 ] = null; 606 } 607 608 // Normalize number of digits if only one of either minimumFractionDigits or 609 // maximumFractionDigits is passed in as an option 610 if ( "minimumFractionDigits" in options && !( "maximumFractionDigits" in options ) ) { 611 612 // maximumFractionDigits = Math.max( minimumFractionDigits, maximumFractionDigits ); 613 properties[ 4 ] = Math.max( properties[ 3 ], properties[ 4 ] ); 614 } else if ( !( "minimumFractionDigits" in options ) && 615 "maximumFractionDigits" in options ) { 616 617 // minimumFractionDigits = Math.min( minimumFractionDigits, maximumFractionDigits ); 618 properties[ 3 ] = Math.min( properties[ 3 ], properties[ 4 ] ); 619 } 620 621 // Return: 622 // 0-10: see number/pattern-properties. 623 // 11: @positivePattern [String] Positive pattern. 624 // 12: @negativePattern [String] Negative pattern. 625 // 13: @negativePrefix [String] Negative prefix. 626 // 14: @negativeSuffix [String] Negative suffix. 627 // 15: @round [Function] Round function. 628 // 16: @infinitySymbol [String] Infinity symbol. 629 // 17: @nanSymbol [String] NaN symbol. 630 // 18: @symbolMap [Object] A bunch of other symbols. 631 // 19: @nuDigitsMap [Array] Digits map if numbering system is different than `latn`. 632 // 20: @compactMap [Object] Map of per-digit-count format patterns for specified compact mode. 633 return properties; 634}; 635 636 637 638 639/** 640 * Generated by: 641 * 642 * var regenerate = require( "regenerate" ); 643 * var formatSymbols = require( "@unicode/unicode-13.0.0/General_Category/Format/symbols" ); 644 * regenerate().add( formatSymbols ).toString(); 645 * 646 * https://github.com/mathiasbynens/regenerate 647 * https://github.com/node-unicode/unicode-13.0.0 648 */ 649var 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; 650 651 652 653 654/** 655 * Generated by: 656 * 657 * var regenerate = require( "regenerate" ); 658 * var dashSymbols = require( "https://github.com/node-unicode/unicode-13.0.0/General_Category/Dash_Punctuation/symbols" ); 659 * regenerate().add( dashSymbols ).toString(); 660 * 661 * https://github.com/mathiasbynens/regenerate 662 * https://github.com/node-unicode/unicode-13.0.0 663 * 664 * NOTE: In addition to [:dash:], the below includes MINUS SIGN U+2212. 665 */ 666var 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; 667 668 669 670 671/** 672 * Generated by: 673 * 674 * var regenerate = require( "regenerate" ); 675 * var spaceSeparatorSymbols = require( "@unicode/unicode-13.0.0/General_Category/Space_Separator/symbols" ); 676 * regenerate().add( spaceSeparatorSymbols ).toString(); 677 * 678 * https://github.com/mathiasbynens/regenerate 679 * https://github.com/node-unicode/unicode-13.0.0 680 */ 681var regexpZsG = /[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000]/g; 682 683 684 685 686/** 687 * Loose Matching: 688 * - Ignore all format characters, which includes RLM, LRM or ALM used to control BIDI 689 * formatting. 690 * - Map all characters in [:Zs:] to U+0020 SPACE; 691 * - Map all characters in [:Dash:] to U+002D HYPHEN-MINUS; 692 */ 693var looseMatching = function( value ) { 694 return value 695 .replace( regexpCfG, "" ) 696 .replace( regexpDashG, "-" ) 697 .replace( regexpZsG, " " ); 698}; 699 700 701 702 703/** 704 * parse( value, properties ) 705 * 706 * @value [String]. 707 * 708 * @properties [Object] Parser properties is a reduced pre-processed cldr 709 * data set returned by numberParserProperties(). 710 * 711 * Return the parsed Number (including Infinity) or NaN when value is invalid. 712 * ref: http://www.unicode.org/reports/tr35/tr35-numbers.html 713 */ 714var numberParse = function( value, properties ) { 715 var grammar, invertedNuDigitsMap, invertedSymbolMap, negative, number, prefix, prefixNSuffix, 716 suffix, tokenizer, valid; 717 718 // Grammar: 719 // - Value <= NaN | PositiveNumber | NegativeNumber 720 // - PositiveNumber <= PositivePrefix NumberOrInf PositiveSufix 721 // - NegativeNumber <= NegativePrefix NumberOrInf 722 // - NumberOrInf <= Number | Inf 723 grammar = [ 724 [ "nan" ], 725 [ "prefix", "infinity", "suffix" ], 726 [ "prefix", "number", "suffix" ], 727 [ "negativePrefix", "infinity", "negativeSuffix" ], 728 [ "negativePrefix", "number", "negativeSuffix" ] 729 ]; 730 731 invertedSymbolMap = properties[ 0 ]; 732 invertedNuDigitsMap = properties[ 1 ] || {}; 733 tokenizer = properties[ 2 ]; 734 735 value = looseMatching( value ); 736 737 function parse( type ) { 738 return function( lexeme ) { 739 740 // Reverse localized symbols and numbering system. 741 lexeme = lexeme.split( "" ).map(function( character ) { 742 return invertedSymbolMap[ character ] || 743 invertedNuDigitsMap[ character ] || 744 character; 745 }).join( "" ); 746 747 switch ( type ) { 748 case "infinity": 749 number = Infinity; 750 break; 751 752 case "nan": 753 number = NaN; 754 break; 755 756 case "number": 757 758 // Remove grouping separators. 759 lexeme = lexeme.replace( /,/g, "" ); 760 761 number = +lexeme; 762 break; 763 764 case "prefix": 765 case "negativePrefix": 766 prefix = lexeme; 767 break; 768 769 case "suffix": 770 suffix = lexeme; 771 break; 772 773 case "negativeSuffix": 774 suffix = lexeme; 775 negative = true; 776 break; 777 778 // This should never be reached. 779 default: 780 throw new Error( "Internal error" ); 781 } 782 return ""; 783 }; 784 } 785 786 function tokenizeNParse( _value, grammar ) { 787 return grammar.some(function( statement ) { 788 var value = _value; 789 790 // The whole grammar statement should be used (i.e., .every() return true) and value be 791 // entirely consumed (i.e., !value.length). 792 return statement.every(function( type ) { 793 if ( value.match( tokenizer[ type ] ) === null ) { 794 return false; 795 } 796 797 // Consume and parse it. 798 value = value.replace( tokenizer[ type ], parse( type ) ); 799 return true; 800 }) && !value.length; 801 }); 802 } 803 804 valid = tokenizeNParse( value, grammar ); 805 806 // NaN 807 if ( !valid || isNaN( number ) ) { 808 return NaN; 809 } 810 811 prefixNSuffix = "" + prefix + suffix; 812 813 // Percent 814 if ( prefixNSuffix.indexOf( "%" ) !== -1 ) { 815 number /= 100; 816 817 // Per mille 818 } else if ( prefixNSuffix.indexOf( "\u2030" ) !== -1 ) { 819 number /= 1000; 820 } 821 822 // Negative number 823 if ( negative ) { 824 number *= -1; 825 } 826 827 return number; 828}; 829 830 831 832 833var numberParserFn = function( properties ) { 834 return function numberParser( value ) { 835 validateParameterPresence( value, "value" ); 836 validateParameterTypeString( value, "value" ); 837 838 return numberParse( value, properties ); 839 }; 840 841}; 842 843 844 845 846/** 847 * symbolMap( cldr ) 848 * 849 * @cldr [Cldr instance]. 850 * 851 * Return the (localized symbol, pattern symbol) key value pair, eg. { 852 * "Ù«": ".", 853 * "Ù¬": ",", 854 * "Ùª": "%", 855 * ... 856 * }; 857 */ 858var numberSymbolInvertedMap = function( cldr ) { 859 var symbol, 860 symbolMap = {}; 861 862 for ( symbol in numberSymbolName ) { 863 symbolMap[ numberSymbol( numberSymbolName[ symbol ], cldr ) ] = symbol; 864 } 865 866 return symbolMap; 867}; 868 869 870 871 872/** 873 * objectMap( object, fn) 874 * 875 * - object 876 * 877 * - fn( pair ) => pair 878 */ 879var objectMap = function( object, fn ) { 880 return Object.keys( object ).map(function( key ) { 881 return fn([ key, object[ key ] ]); 882 }).reduce(function( object, pair ) { 883 object[ pair[ 0 ] ] = pair[ 1 ]; 884 return object; 885 }, {}); 886}; 887 888 889 890 891/** 892 * removeLiteralQuotes( string ) 893 * 894 * Return: 895 * - `'` if input string is `''`. 896 * - `o'clock` if input string is `'o''clock'`. 897 * - `foo` if input string is `foo`, i.e., return the same value in case it isn't a single-quoted 898 * string. 899 */ 900var removeLiteralQuotes = function( string ) { 901 if ( string[ 0 ] + string[ string.length - 1 ] !== "''" ) { 902 return string; 903 } 904 if ( string === "''" ) { 905 return "'"; 906 } 907 return string.replace( /''/g, "'" ).slice( 1, -1 ); 908}; 909 910 911 912 913/** 914 * parseProperties( pattern, cldr ) 915 * 916 * @pattern [String] raw pattern for numbers. 917 * 918 * @cldr [Cldr instance]. 919 * 920 * Return parser properties, used to feed parser function. 921 * 922 * TODO: 923 * - Scientific_notation; 924 * - Padding; 925 */ 926var numberParseProperties = function( pattern, cldr, options ) { 927 var aux, decimalSymbolRe, digitsRe, groupingSeparatorRe, infinitySymbol, invertedNuDigitsMap, 928 invertedSymbolMap, maximumFractionDigits, maximumSignificantDigits, 929 minimumSignificantDigits, nanSymbol, negativePrefix, negativeSuffix, nuDigitsMap, 930 numberTokenizer, prefix, primaryGroupingSize, secondaryGroupingSize, suffix, symbolMap, 931 formatProperties = numberFormatProperties( pattern, cldr, options ); 932 933 prefix = looseMatching( formatProperties[ 0 ] ); 934 maximumFractionDigits = formatProperties[ 4 ]; 935 minimumSignificantDigits = formatProperties[ 5 ]; 936 maximumSignificantDigits = formatProperties[ 6 ]; 937 primaryGroupingSize = formatProperties[ 8 ]; 938 secondaryGroupingSize = formatProperties[ 9 ]; 939 suffix = looseMatching( formatProperties[ 10 ] ); 940 negativePrefix = looseMatching( formatProperties[ 13 ] ); 941 negativeSuffix = looseMatching( formatProperties[ 14 ] ); 942 infinitySymbol = looseMatching( formatProperties[ 16 ] ); 943 nanSymbol = looseMatching( formatProperties[ 17 ] ); 944 symbolMap = objectMap( formatProperties[ 18 ], function( pair ) { 945 return [ pair[ 0 ], looseMatching( pair[ 1 ] ) ]; 946 }); 947 nuDigitsMap = formatProperties[ 19 ]; 948 949 invertedSymbolMap = objectMap( numberSymbolInvertedMap( cldr ), function( pair ) { 950 return [ looseMatching( pair[ 0 ] ), pair[ 1 ] ]; 951 }); 952 953 digitsRe = nuDigitsMap ? "[" + nuDigitsMap + "]" : "\\d"; 954 groupingSeparatorRe = regexpEscape( symbolMap[ "," ] ); 955 decimalSymbolRe = regexpEscape( symbolMap[ "." ] ); 956 957 if ( nuDigitsMap ) { 958 invertedNuDigitsMap = nuDigitsMap.split( "" ).reduce(function( object, localizedDigit, i ) { 959 object[ localizedDigit ] = String( i ); 960 return object; 961 }, {} ); 962 } 963 964 aux = [ prefix, suffix, negativePrefix, negativeSuffix ].map(function( value ) { 965 return value.replace( /('([^']|'')+'|'')|./g, function( character, literal ) { 966 967 // Literals 968 if ( literal ) { 969 return removeLiteralQuotes( literal ); 970 } 971 972 // Symbols 973 character = character.replace( /[\-+E%\u2030]/, function( symbol ) { 974 return symbolMap[ symbol ]; 975 }); 976 977 return character; 978 }); 979 }); 980 981 prefix = aux[ 0 ]; 982 suffix = aux[ 1 ]; 983 negativePrefix = aux[ 2 ]; 984 negativeSuffix = aux[ 3 ]; 985 986 // Number 987 // 988 // number_re = integer fraction? 989 // 990 // integer = digits | digits_using_grouping_separators 991 // 992 // fraction = regexp((.\d+)?) 993 // 994 // digits = regexp(\d+) 995 // 996 // digits_w_grouping_separators = digits_w_1_grouping_separators | 997 // digits_w_2_grouping_separators 998 // 999 // digits_w_1_grouping_separators = regexp(\d{1,3}(,\d{3})+) 1000 // 1001 // digits_w_2_grouping_separators = regexp(\d{1,2}((,\d{2})*(,\d{3}))) 1002 1003 // Integer part 1004 numberTokenizer = digitsRe + "+"; 1005 1006 // Grouping separators 1007 if ( primaryGroupingSize ) { 1008 if ( secondaryGroupingSize ) { 1009 aux = digitsRe + "{1," + secondaryGroupingSize + "}((" + groupingSeparatorRe + 1010 digitsRe + "{" + secondaryGroupingSize + "})*(" + groupingSeparatorRe + 1011 digitsRe + "{" + primaryGroupingSize + "}))"; 1012 } else { 1013 aux = digitsRe + "{1," + primaryGroupingSize + "}(" + groupingSeparatorRe + 1014 digitsRe + "{" + primaryGroupingSize + "})+"; 1015 } 1016 numberTokenizer = "(" + aux + "|" + numberTokenizer + ")"; 1017 } 1018 1019 // Fraction part? Only included if 1 or 2. 1020 // 1: Using significant digit format. 1021 // 2: Using integer and fractional format && it has a maximumFractionDigits. 1022 if ( !isNaN( minimumSignificantDigits * maximumSignificantDigits ) || /* 1 */ 1023 maximumFractionDigits /* 2 */ ) { 1024 1025 // 1: Handle trailing decimal separator, e.g., `"1." => `1``. 1026 aux = decimalSymbolRe + digitsRe + "+"; 1027 numberTokenizer = numberTokenizer + "(" + aux + "|" + decimalSymbolRe /* 1 */ + ")?" + 1028 1029 // Handle non-padded decimals, e.g., `".12"` => `0.12` by making the integer part 1030 // optional. 1031 "|(" + numberTokenizer + ")?" + aux; 1032 1033 numberTokenizer = "(" + numberTokenizer + ")"; 1034 } 1035 1036 // 0: @invertedSymbolMap [Object] Inverted symbol map. 1037 // 1: @invertedNuDigitsMap [Object] Inverted digits map if numbering system is different than 1038 // `latn`. 1039 // 2: @tokenizer [Object] Tokenizer map, used by parser to consume input. 1040 return [ 1041 invertedSymbolMap, 1042 invertedNuDigitsMap, 1043 { 1044 infinity: new RegExp( "^" + regexpEscape( infinitySymbol ) ), 1045 nan: new RegExp( "^" + regexpEscape( nanSymbol ) ), 1046 negativePrefix: new RegExp( "^" + regexpEscape( negativePrefix ) ), 1047 negativeSuffix: new RegExp( "^" + regexpEscape( negativeSuffix ) ), 1048 number: new RegExp( "^" + numberTokenizer ), 1049 prefix: new RegExp( "^" + regexpEscape( prefix ) ), 1050 suffix: new RegExp( "^" + regexpEscape( suffix ) ) 1051 } 1052 ]; 1053 1054}; 1055 1056 1057 1058 1059/** 1060 * Pattern( style ) 1061 * 1062 * @style [String] "decimal" (default) or "percent". 1063 * 1064 * @cldr [Cldr instance]. 1065 */ 1066var numberPattern = function( style, cldr ) { 1067 if ( style !== "decimal" && style !== "percent" ) { 1068 throw new Error( "Invalid style" ); 1069 } 1070 1071 return cldr.main([ 1072 "numbers", 1073 style + "Formats-numberSystem-" + numberNumberingSystem( cldr ), 1074 "standard" 1075 ]); 1076}; 1077 1078 1079 1080 1081/** 1082 * EBNF representation: 1083 * 1084 * compact_pattern_re = prefix? 1085 * number_pattern_re 1086 * suffix? 1087 * 1088 * number_pattern_re = 0+ 1089 * 1090 * Regexp groups: 1091 * 1092 * 0: compact_pattern_re 1093 * 1: prefix 1094 * 2: number_pattern_re (the number pattern to use in compact mode) 1095 * 3: suffix 1096 */ 1097var numberCompactPatternRe = ( /^([^0]*)(0+)([^0]*)$/ ); 1098 1099 1100 1101 1102/** 1103 * goupingSeparator( number, primaryGroupingSize, secondaryGroupingSize ) 1104 * 1105 * @number [Number]. 1106 * 1107 * @primaryGroupingSize [Number] 1108 * 1109 * @secondaryGroupingSize [Number] 1110 * 1111 * Return the formatted number with group separator. 1112 */ 1113var numberFormatGroupingSeparator = function( number, primaryGroupingSize, secondaryGroupingSize ) { 1114 var index, 1115 currentGroupingSize = primaryGroupingSize, 1116 ret = "", 1117 sep = ",", 1118 switchToSecondary = secondaryGroupingSize ? true : false; 1119 1120 number = String( number ).split( "." ); 1121 index = number[ 0 ].length; 1122 1123 while ( index > currentGroupingSize ) { 1124 ret = number[ 0 ].slice( index - currentGroupingSize, index ) + 1125 ( ret.length ? sep : "" ) + ret; 1126 index -= currentGroupingSize; 1127 if ( switchToSecondary ) { 1128 currentGroupingSize = secondaryGroupingSize; 1129 switchToSecondary = false; 1130 } 1131 } 1132 1133 number[ 0 ] = number[ 0 ].slice( 0, index ) + ( ret.length ? sep : "" ) + ret; 1134 return number.join( "." ); 1135}; 1136 1137 1138 1139 1140/** 1141 * integerFractionDigits( number, minimumIntegerDigits, minimumFractionDigits, 1142 * maximumFractionDigits, round, roundIncrement ) 1143 * 1144 * @number [Number] 1145 * 1146 * @minimumIntegerDigits [Number] 1147 * 1148 * @minimumFractionDigits [Number] 1149 * 1150 * @maximumFractionDigits [Number] 1151 * 1152 * @round [Function] 1153 * 1154 * @roundIncrement [Function] 1155 * 1156 * Return the formatted integer and fraction digits. 1157 */ 1158var numberFormatIntegerFractionDigits = function( number, minimumIntegerDigits, minimumFractionDigits, maximumFractionDigits, round, 1159 roundIncrement ) { 1160 1161 // Fraction 1162 if ( maximumFractionDigits ) { 1163 1164 // Rounding 1165 if ( roundIncrement ) { 1166 number = round( number, roundIncrement ); 1167 1168 // Maximum fraction digits 1169 } else { 1170 number = round( number, { exponent: -maximumFractionDigits } ); 1171 } 1172 1173 } else { 1174 number = round( number ); 1175 } 1176 1177 number = String( number ); 1178 1179 // Maximum integer digits (post string phase) 1180 if ( maximumFractionDigits && /e-/.test( number ) ) { 1181 1182 // Use toFixed( maximumFractionDigits ) to make sure small numbers like 1e-7 are 1183 // displayed using plain digits instead of scientific notation. 1184 // 1: Remove leading decimal zeros. 1185 // 2: Remove leading decimal separator. 1186 // Note: String() is still preferred so it doesn't mess up with a number precision 1187 // unnecessarily, e.g., (123456789.123).toFixed(10) === "123456789.1229999959", 1188 // String(123456789.123) === "123456789.123". 1189 number = ( +number ).toFixed( maximumFractionDigits ) 1190 .replace( /0+$/, "" ) /* 1 */ 1191 .replace( /\.$/, "" ); /* 2 */ 1192 } 1193 1194 // Minimum fraction digits (post string phase) 1195 if ( minimumFractionDigits ) { 1196 number = number.split( "." ); 1197 number[ 1 ] = stringPad( number[ 1 ] || "", minimumFractionDigits, true ); 1198 number = number.join( "." ); 1199 } 1200 1201 // Minimum integer digits 1202 if ( minimumIntegerDigits ) { 1203 number = number.split( "." ); 1204 number[ 0 ] = stringPad( number[ 0 ], minimumIntegerDigits ); 1205 number = number.join( "." ); 1206 } 1207 1208 return number; 1209}; 1210 1211 1212 1213 1214/** 1215 * toPrecision( number, precision, round ) 1216 * 1217 * @number (Number) 1218 * 1219 * @precision (Number) significant figures precision (not decimal precision). 1220 * 1221 * @round (Function) 1222 * 1223 * Return number.toPrecision( precision ) using the given round function. 1224 */ 1225var numberToPrecision = function( number, precision, round ) { 1226 var roundOrder; 1227 1228 if ( number === 0 ) { // Fix #706 1229 return number; 1230 } 1231 1232 roundOrder = Math.ceil( Math.log( Math.abs( number ) ) / Math.log( 10 ) ); 1233 roundOrder -= precision; 1234 1235 return round( number, { exponent: roundOrder } ); 1236}; 1237 1238 1239 1240 1241/** 1242 * toPrecision( number, minimumSignificantDigits, maximumSignificantDigits, round ) 1243 * 1244 * @number [Number] 1245 * 1246 * @minimumSignificantDigits [Number] 1247 * 1248 * @maximumSignificantDigits [Number] 1249 * 1250 * @round [Function] 1251 * 1252 * Return the formatted significant digits number. 1253 */ 1254var numberFormatSignificantDigits = function( number, minimumSignificantDigits, maximumSignificantDigits, round ) { 1255 var atMinimum, atMaximum; 1256 1257 // Sanity check. 1258 if ( minimumSignificantDigits > maximumSignificantDigits ) { 1259 maximumSignificantDigits = minimumSignificantDigits; 1260 } 1261 1262 atMinimum = numberToPrecision( number, minimumSignificantDigits, round ); 1263 atMaximum = numberToPrecision( number, maximumSignificantDigits, round ); 1264 1265 // Use atMaximum only if it has more significant digits than atMinimum. 1266 number = +atMinimum === +atMaximum ? atMinimum : atMaximum; 1267 1268 // Expand integer numbers, eg. 123e5 to 12300. 1269 number = ( +number ).toString( 10 ); 1270 1271 if ( ( /e/ ).test( number ) ) { 1272 throw createErrorUnsupportedFeature({ 1273 feature: "integers out of (1e21, 1e-7)" 1274 }); 1275 } 1276 1277 // Add trailing zeros if necessary. 1278 if ( minimumSignificantDigits - number.replace( /^0+|\./g, "" ).length > 0 ) { 1279 number = number.split( "." ); 1280 number[ 1 ] = stringPad( number[ 1 ] || "", minimumSignificantDigits - number[ 0 ].replace( /^0+/, "" ).length, true ); 1281 number = number.join( "." ); 1282 } 1283 1284 return number; 1285}; 1286 1287 1288 1289 1290/** 1291 * format( number, properties ) 1292 * 1293 * @number [Number]. 1294 * 1295 * @properties [Object] Output of number/format-properties. 1296 * 1297 * Return the formatted number. 1298 * ref: http://www.unicode.org/reports/tr35/tr35-numbers.html 1299 */ 1300var numberFormat = function( number, properties, pluralGenerator ) { 1301 var aux, compactMap, infinitySymbol, maximumFractionDigits, maximumSignificantDigits, 1302 minimumFractionDigits, minimumIntegerDigits, minimumSignificantDigits, nanSymbol, 1303 nuDigitsMap, prefix, primaryGroupingSize, pattern, round, roundIncrement, 1304 secondaryGroupingSize, stringToParts, suffix, symbolMap; 1305 1306 minimumIntegerDigits = properties[ 2 ]; 1307 minimumFractionDigits = properties[ 3 ]; 1308 maximumFractionDigits = properties[ 4 ]; 1309 minimumSignificantDigits = properties[ 5 ]; 1310 maximumSignificantDigits = properties[ 6 ]; 1311 roundIncrement = properties[ 7 ]; 1312 primaryGroupingSize = properties[ 8 ]; 1313 secondaryGroupingSize = properties[ 9 ]; 1314 round = properties[ 15 ]; 1315 infinitySymbol = properties[ 16 ]; 1316 nanSymbol = properties[ 17 ]; 1317 symbolMap = properties[ 18 ]; 1318 nuDigitsMap = properties[ 19 ]; 1319 compactMap = properties[ 20 ]; 1320 1321 // NaN 1322 if ( isNaN( number ) ) { 1323 return [ { type: "nan", value: nanSymbol } ]; 1324 } 1325 1326 if ( number < 0 ) { 1327 pattern = properties[ 12 ]; 1328 prefix = properties[ 13 ]; 1329 suffix = properties[ 14 ]; 1330 } else { 1331 pattern = properties[ 11 ]; 1332 prefix = properties[ 0 ]; 1333 suffix = properties[ 10 ]; 1334 } 1335 1336 // For prefix, suffix, and number parts. 1337 stringToParts = function( string ) { 1338 var numberType = "integer", 1339 parts = []; 1340 1341 // TODO Move the tokenization of all parts that don't depend on number into 1342 // format-properties. 1343 string.replace( /('([^']|'')+'|'')|./g, function( character, literal ) { 1344 1345 // Literals 1346 if ( literal ) { 1347 partsPush( parts, "literal", removeLiteralQuotes( literal ) ); 1348 return; 1349 } 1350 1351 // Currency symbol 1352 if ( character === "\u00A4" ) { 1353 partsPush( parts, "currency", character ); 1354 return; 1355 } 1356 1357 // Symbols 1358 character = character.replace( /[.,\-+E%\u2030]/, function( symbol ) { 1359 if ( symbol === "." ) { 1360 numberType = "fraction"; 1361 } 1362 partsPush( parts, numberSymbolName[ symbol ], symbolMap[ symbol ] ); 1363 1364 // "Erase" handled character. 1365 return ""; 1366 }); 1367 1368 // Number 1369 character = character.replace( /[0-9]/, function( digit ) { 1370 1371 // Numbering system 1372 if ( nuDigitsMap ) { 1373 digit = nuDigitsMap[ +digit ]; 1374 } 1375 partsPush( parts, numberType, digit ); 1376 1377 // "Erase" handled character. 1378 return ""; 1379 }); 1380 1381 // Etc 1382 character.replace( /./, function( etc ) { 1383 partsPush( parts, "literal", etc ); 1384 }); 1385 }); 1386 return parts; 1387 }; 1388 1389 prefix = stringToParts( prefix ); 1390 suffix = stringToParts( suffix ); 1391 1392 // Infinity 1393 if ( !isFinite( number ) ) { 1394 return prefix.concat( 1395 { type: "infinity", value: infinitySymbol }, 1396 suffix 1397 ); 1398 } 1399 1400 // Percent 1401 if ( pattern.indexOf( "%" ) !== -1 ) { 1402 number *= 100; 1403 1404 // Per mille 1405 } else if ( pattern.indexOf( "\u2030" ) !== -1 ) { 1406 number *= 1000; 1407 } 1408 1409 var compactPattern, compactDigits, compactProperties, divisor, numberExponent, pluralForm; 1410 1411 // Compact mode: initial number digit processing 1412 if ( compactMap ) { 1413 numberExponent = Math.abs( Math.floor( number ) ).toString().length - 1; 1414 numberExponent = Math.min( numberExponent, compactMap.maxExponent ); 1415 1416 // Use default plural form to perform initial decimal shift 1417 if ( numberExponent >= 3 ) { 1418 compactPattern = compactMap[ numberExponent ] && compactMap[ numberExponent ].other; 1419 } 1420 1421 if ( compactPattern === "0" ) { 1422 compactPattern = null; 1423 } else if ( compactPattern ) { 1424 compactDigits = compactPattern.split( "0" ).length - 1; 1425 divisor = numberExponent - ( compactDigits - 1 ); 1426 number = number / Math.pow( 10, divisor ); 1427 } 1428 } 1429 1430 // Significant digit format 1431 if ( !isNaN( minimumSignificantDigits * maximumSignificantDigits ) ) { 1432 number = numberFormatSignificantDigits( number, minimumSignificantDigits, 1433 maximumSignificantDigits, round ); 1434 1435 // Integer and fractional format 1436 } else { 1437 number = numberFormatIntegerFractionDigits( number, minimumIntegerDigits, 1438 minimumFractionDigits, maximumFractionDigits, round, roundIncrement ); 1439 } 1440 1441 // Compact mode: apply formatting 1442 if ( compactMap && compactPattern ) { 1443 1444 // Get plural form after possible roundings 1445 pluralForm = pluralGenerator ? pluralGenerator( +number ) : "other"; 1446 1447 compactPattern = compactMap[ numberExponent ][ pluralForm ] || compactPattern; 1448 compactProperties = compactPattern.match( numberCompactPatternRe ); 1449 1450 // TODO Move the tokenization of all parts that don't depend on number into 1451 // format-properties. 1452 aux = function( string ) { 1453 var parts = []; 1454 string.replace( /(
1454\s+)|([^\s0]+)/g, function( _garbage, space, compact ) { 1455 1456 // Literals 1457 if ( space ) { 1458 partsPush( parts, "literal", space
vendor: 7,118 bytes, lines 1458-1727
1458 ); 1459 return; 1460 } 1461 1462 // Compact value 1463 if ( compact ) { 1464 partsPush( parts, "compact", compact ); 1465 return; 1466 } 1467 }); 1468 return parts; 1469 }; 1470 1471 // update prefix/suffix with compact prefix/suffix 1472 prefix = prefix.concat( aux( compactProperties[ 1 ] ) ); 1473 suffix = aux( compactProperties[ 3 ] ).concat( suffix ); 1474 } 1475 1476 // Remove the possible number minus sign 1477 number = number.replace( /^-/, "" ); 1478 1479 // Grouping separators 1480 if ( primaryGroupingSize ) { 1481 number = numberFormatGroupingSeparator( number, primaryGroupingSize, 1482 secondaryGroupingSize ); 1483 } 1484 1485 // Scientific notation 1486 // TODO implement here 1487 1488 // Padding/'([^']|'')+'|''|[.,\-+E%\u2030]/g 1489 // TODO implement here 1490 1491 return prefix.concat( 1492 stringToParts( number ), 1493 suffix 1494 ); 1495}; 1496 1497 1498 1499 1500var numberToPartsFormatterFn = function( properties, pluralGenerator ) { 1501 return function numberToPartsFormatter( value ) { 1502 validateParameterPresence( value, "value" ); 1503 validateParameterTypeNumber( value, "value" ); 1504 1505 return numberFormat( value, properties, pluralGenerator ); 1506 }; 1507}; 1508 1509 1510 1511 1512function validateDigits( properties ) { 1513 var minimumIntegerDigits = properties[ 2 ], 1514 minimumFractionDigits = properties[ 3 ], 1515 maximumFractionDigits = properties[ 4 ], 1516 minimumSignificantDigits = properties[ 5 ], 1517 maximumSignificantDigits = properties[ 6 ]; 1518 1519 // Validate significant digit format properties 1520 if ( !isNaN( minimumSignificantDigits * maximumSignificantDigits ) ) { 1521 validateParameterRange( minimumSignificantDigits, "minimumSignificantDigits", 1, 21 ); 1522 validateParameterRange( maximumSignificantDigits, "maximumSignificantDigits", 1523 minimumSignificantDigits, 21 ); 1524 1525 } else if ( !isNaN( minimumSignificantDigits ) || !isNaN( maximumSignificantDigits ) ) { 1526 throw new Error( "Neither or both the minimum and maximum significant digits must be " + 1527 "present" ); 1528 1529 // Validate integer and fractional format 1530 } else { 1531 validateParameterRange( minimumIntegerDigits, "minimumIntegerDigits", 1, 21 ); 1532 validateParameterRange( minimumFractionDigits, "minimumFractionDigits", 0, 20 ); 1533 validateParameterRange( maximumFractionDigits, "maximumFractionDigits", 1534 minimumFractionDigits, 20 ); 1535 } 1536} 1537 1538/** 1539 * .numberFormatter( [options] ) 1540 * 1541 * @options [Object]: 1542 * - style: [String] "decimal" (default) or "percent". 1543 * - see also number/format options. 1544 * 1545 * Return a function that formats a number according to the given options and default/instance 1546 * locale. 1547 */ 1548Globalize.numberFormatter = 1549Globalize.prototype.numberFormatter = function( options ) { 1550 var args, numberToPartsFormatter, returnFn; 1551 1552 validateParameterTypePlainObject( options, "options" ); 1553 1554 options = options || {}; 1555 args = [ options ]; 1556 1557 numberToPartsFormatter = this.numberToPartsFormatter( options ); 1558 returnFn = numberFormatterFn( numberToPartsFormatter ); 1559 runtimeBind( args, this.cldr, returnFn, [ numberToPartsFormatter ] ); 1560 1561 return returnFn; 1562}; 1563 1564/** 1565 * .numberToPartsFormatter( [options] ) 1566 * 1567 * @options [Object]: 1568 * - style: [String] "symbol" (default), "accounting", "code" or "name". 1569 * - see also number/format options. 1570 * 1571 * Return a function that formats a number to parts according to the given options and 1572 * default/instance locale. 1573 */ 1574Globalize.numberToPartsFormatter = 1575Globalize.prototype.numberToPartsFormatter = function( options ) { 1576 var args, cldr, fnArgs, pattern, properties, returnFn; 1577 1578 validateParameterTypePlainObject( options, "options" ); 1579 1580 options = options || {}; 1581 cldr = this.cldr; 1582 1583 args = [ options ]; 1584 1585 validateDefaultLocale( cldr ); 1586 1587 cldr.on( "get", validateCldr ); 1588 try { 1589 if ( options.raw ) { 1590 pattern = options.raw; 1591 } else { 1592 pattern = numberPattern( options.style || "decimal", cldr ); 1593 } 1594 1595 properties = numberFormatProperties( pattern, cldr, options ); 1596 fnArgs = [ properties ]; 1597 } finally { 1598 cldr.off( "get", validateCldr ); 1599 } 1600 1601 validateDigits( properties ); 1602 1603 if ( options.compact ) { 1604 fnArgs.push( this.pluralGenerator() ); 1605 } 1606 returnFn = numberToPartsFormatterFn.apply( null, fnArgs ); 1607 runtimeBind( args, cldr, returnFn, fnArgs ); 1608 1609 return returnFn; 1610}; 1611 1612/** 1613 * .numberParser( [options] ) 1614 * 1615 * @options [Object]: 1616 * - style: [String] "decimal" (default) or "percent". 1617 * 1618 * Return the number parser according to the default/instance locale. 1619 */ 1620Globalize.numberParser = 1621Globalize.prototype.numberParser = function( options ) { 1622 var args, cldr, pattern, properties, returnFn; 1623 1624 validateParameterTypePlainObject( options, "options" ); 1625 1626 options = options || {}; 1627 cldr = this.cldr; 1628 1629 args = [ options ]; 1630 1631 validateDefaultLocale( cldr ); 1632 if ( options.compact ) { 1633 throw createErrorUnsupportedFeature({ 1634 feature: "compact number parsing (not implemented)" 1635 }); 1636 } 1637 1638 cldr.on( "get", validateCldr ); 1639 1640 if ( options.raw ) { 1641 pattern = options.raw; 1642 } else { 1643 pattern = numberPattern( options.style || "decimal", cldr ); 1644 } 1645 1646 properties = numberParseProperties( pattern, cldr, options ); 1647 1648 cldr.off( "get", validateCldr ); 1649 1650 returnFn = numberParserFn( properties ); 1651 1652 runtimeBind( args, cldr, returnFn, [ properties ] ); 1653 1654 return returnFn; 1655}; 1656 1657/** 1658 * .formatNumber( value [, options] ) 1659 * 1660 * @value [Number] number to be formatted. 1661 * 1662 * @options [Object]: see number/format-properties. 1663 * 1664 * Format a number according to the given options and default/instance locale. 1665 */ 1666Globalize.formatNumber = 1667Globalize.prototype.formatNumber = function( value, options ) { 1668 validateParameterPresence( value, "value" ); 1669 validateParameterTypeNumber( value, "value" ); 1670 1671 return this.numberFormatter( options )( value ); 1672}; 1673 1674/** 1675 * .formatNumberToParts( value [, options] ) 1676 * 1677 * @value [Number] number to be formatted. 1678 * 1679 * @options [Object]: see number/format-properties. 1680 * 1681 * Format a number to pars according to the given options and default/instance locale. 1682 */ 1683Globalize.formatNumberToParts = 1684Globalize.prototype.formatNumberToParts = function( value, options ) { 1685 validateParameterPresence( value, "value" ); 1686 validateParameterTypeNumber( value, "value" ); 1687 1688 return this.numberToPartsFormatter( options )( value ); 1689}; 1690 1691/** 1692 * .parseNumber( value [, options] ) 1693 * 1694 * @value [String] 1695 * 1696 * @options [Object]: See numberParser(). 1697 * 1698 * Return the parsed Number (including Infinity) or NaN when value is invalid. 1699 */ 1700Globalize.parseNumber = 1701Globalize.prototype.parseNumber = function( value, options ) { 1702 validateParameterPresence( value, "value" ); 1703 validateParameterTypeString( value, "value" ); 1704 1705 return this.numberParser( options )( value ); 1706}; 1707 1708/** 1709 * Optimization to avoid duplicating some internal functions across modules. 1710 */ 1711Globalize._createErrorUnsupportedFeature = createErrorUnsupportedFeature; 1712Globalize._numberNumberingSystem = numberNumberingSystem; 1713Globalize._numberNumberingSystemDigitsMap = numberNumberingSystemDigitsMap; 1714Globalize._numberPattern = numberPattern; 1715Globalize._numberSymbol = numberSymbol; 1716Globalize._looseMatching = looseMatching; 1717Globalize._removeLiteralQuotes = removeLiteralQuotes; 1718Globalize._stringPad = stringPad; 1719Globalize._validateParameterTypeNumber = validateParameterTypeNumber; 1720Globalize._validateParameterTypeString = validateParameterTypeString; 1721 1722return Globalize; 1723 1724 1725 1726 1727}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.