1/** 2 * @license 3 * Copyright (C) 2010 The Libphonenumber Authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18/** 19 * @fileoverview Utility for international phone numbers. 20 * Functionality includes formatting, parsing and validation. 21 * (based on the java implementation). 22 * 23 * NOTE: A lot of methods in this class require Region Code strings. These must 24 * be provided using CLDR two-letter region-code format. These should be in 25 * upper-case. The list of the codes can be found here: 26 * http://www.unicode.org/cldr/charts/30/supplemental/territory_information.html 27 */ 28 29goog.provide('i18n.phonenumbers.Error'); 30goog.provide('i18n.phonenumbers.PhoneNumberFormat'); 31goog.provide('i18n.phonenumbers.PhoneNumberType'); 32goog.provide('i18n.phonenumbers.PhoneNumberUtil'); 33goog.provide('i18n.phonenumbers.PhoneNumberUtil.MatchType'); 34goog.provide('i18n.phonenumbers.PhoneNumberUtil.ValidationResult'); 35 36goog.require('goog.object'); 37goog.require('goog.proto2.PbLiteSerializer'); 38goog.require('goog.string'); 39goog.require('goog.string.StringBuffer'); 40goog.require('i18n.phonenumbers.NumberFormat'); 41goog.require('i18n.phonenumbers.PhoneMetadata'); 42goog.require('i18n.phonenumbers.PhoneNumber'); 43goog.require('i18n.phonenumbers.PhoneNumber.CountryCodeSource'); 44goog.require('i18n.phonenumbers.PhoneNumberDesc'); 45goog.require('i18n.phonenumbers.metadata'); 46 47 48 49/** 50 * @constructor 51 * @private 52 */ 53i18n.phonenumbers.PhoneNumberUtil = function() { 54 /** 55 * A mapping from a region code to the PhoneMetadata for that region. 56 * @type {Object.<string, i18n.phonenumbers.PhoneMetadata>} 57 */ 58 this.regionToMetadataMap = {}; 59}; 60goog.addSingletonGetter(i18n.phonenumbers.PhoneNumberUtil); 61 62 63/** 64 * Errors encountered when parsing phone numbers. 65 * 66 * @enum {string} 67 */ 68i18n.phonenumbers.Error = { 69 INVALID_COUNTRY_CODE: 'Invalid country calling code', 70 // This indicates the string passed is not a valid number. Either the string 71 // had less than 3 digits in it or had an invalid phone-context parameter. 72 // More specifically, the number failed to match the regular expression 73 // VALID_PHONE_NUMBER, RFC3966_GLOBAL_NUMBER_DIGITS, or RFC3966_DOMAINNAME. 74 NOT_A_NUMBER: 'The string supplied did not seem to be a phone number', 75 // This indicates the string started with an international dialing prefix, but 76 // after this was stripped from the number, had less digits than any valid 77 // phone number (including country calling code) could have. 78 TOO_SHORT_AFTER_IDD: 'Phone number too short after IDD', 79 // This indicates the string, after any country calling code has been 80 // stripped, had less digits than any valid phone number could have. 81 TOO_SHORT_NSN: 'The string supplied is too short to be a phone number', 82 // This indicates the string had more digits than any valid phone number could 83 // have. 84 TOO_LONG: 'The string supplied is too long to be a phone number' 85}; 86 87 88/** 89 * @const 90 * @type {number} 91 * @private 92 */ 93i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_ = 1; 94 95 96/** 97 * The minimum length of the national significant number. 98 * 99 * @const 100 * @type {number} 101 * @private 102 */ 103i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 2; 104 105 106/** 107 * The ITU says the maximum length should be 15, but we have found longer 108 * numbers in Germany. 109 * 110 * @const 111 * @type {number} 112 * @private 113 */ 114i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_ = 17; 115 116 117/** 118 * The maximum length of the country calling code. 119 * 120 * @const 121 * @type {number} 122 * @private 123 */ 124i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_COUNTRY_CODE_ = 3; 125 126 127/** 128 * We don't allow input strings for parsing to be longer than 250 chars. This 129 * prevents malicious input from consuming CPU. 130 * 131 * @const 132 * @type {number} 133 * @private 134 */ 135i18n.phonenumbers.PhoneNumberUtil.MAX_INPUT_STRING_LENGTH_ = 250; 136 137 138/** 139 * Region-code for the unknown region. 140 * 141 * @const 142 * @type {string} 143 * @private 144 */ 145i18n.phonenumbers.PhoneNumberUtil.UNKNOWN_REGION_ = 'ZZ'; 146 147 148/** 149 * Map of country calling codes that use a mobile token before the area code. 150 * One example of when this is relevant is when determining the length of the 151 * national destination code, which should be the length of the area code plus 152 * the length of the mobile token. 153 * 154 * @const 155 * @type {!Object.<number, string>} 156 * @private 157 */ 158i18n.phonenumbers.PhoneNumberUtil.MOBILE_TOKEN_MAPPINGS_ = { 159 54: '9' 160}; 161 162 163/** 164 * Set of country calling codes that have geographically assigned mobile 165 * numbers. This may not be complete; we add calling codes case by case, as we 166 * find geographical mobile numbers or hear from user reports. 167 * 168 * @const 169 * @type {!Array.<number>} 170 * @private 171 */ 172i18n.phonenumbers.PhoneNumberUtil.GEO_MOBILE_COUNTRIES_ = [ 173 52, // Mexico 174 54, // Argentina 175 55 // Brazil 176]; 177 178 179/** 180 * The PLUS_SIGN signifies the international prefix. 181 * 182 * @const 183 * @type {string} 184 */ 185i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN = '+'; 186 187 188/** 189 * @const 190 * @type {string} 191 * @private 192 */ 193i18n.phonenumbers.PhoneNumberUtil.STAR_SIGN_ = '*'; 194 195 196/** 197 * The RFC 3966 format for extensions. 198 * 199 * @const 200 * @type {string} 201 * @private 202 */ 203i18n.phonenumbers.PhoneNumberUtil.RFC3966_EXTN_PREFIX_ = ';ext='; 204 205 206/** 207 * @const 208 * @type {string} 209 * @private 210 */ 211i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_ = 'tel:'; 212 213 214/** 215 * @const 216 * @type {string} 217 * @private 218 */ 219i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_ = ';phone-context='; 220 221 222/** 223 * @const 224 * @type {string} 225 * @private 226 */ 227i18n.phonenumbers.PhoneNumberUtil.RFC3966_ISDN_SUBADDRESS_ = ';isub='; 228 229 230/** 231 * These mappings map a character (key) to a specific digit that should replace 232 * it for normalization purposes. Non-European digits that may be used in phone 233 * numbers are mapped to a European equivalent. 234 * 235 * @const 236 * @type {!Object.<string, string>} 237 */ 238i18n.phonenumbers.PhoneNumberUtil.DIGIT_MAPPINGS = { 239 '0': '0', 240 '1': '1', 241 '2': '2', 242 '3': '3', 243 '4': '4', 244 '5': '5', 245 '6': '6', 246 '7': '7', 247 '8': '8', 248 '9': '9', 249 '\uFF10': '0', // Fullwidth digit 0 250 '\uFF11': '1', // Fullwidth digit 1 251 '\uFF12': '2', // Fullwidth digit 2 252 '\uFF13': '3', // Fullwidth digit 3 253 '\uFF14': '4', // Fullwidth digit 4 254 '\uFF15': '5', // Fullwidth digit 5 255 '\uFF16': '6', // Fullwidth digit 6 256 '\uFF17': '7', // Fullwidth digit 7 257 '\uFF18': '8', // Fullwidth digit 8 258 '\uFF19': '9', // Fullwidth digit 9 259 '\u0660': '0', // Arabic-indic digit 0 260 '\u0661': '1', // Arabic-indic digit 1 261 '\u0662': '2', // Arabic-indic digit 2 262 '\u0663': '3', // Arabic-indic digit 3 263 '\u0664': '4', // Arabic-indic digit 4 264 '\u0665': '5', // Arabic-indic digit 5 265 '\u0666': '6', // Arabic-indic digit 6 266 '\u0667': '7', // Arabic-indic digit 7 267 '\u0668': '8', // Arabic-indic digit 8 268 '\u0669': '9', // Arabic-indic digit 9 269 '\u06F0': '0', // Eastern-Arabic digit 0 270 '\u06F1': '1', // Eastern-Arabic digit 1 271 '\u06F2': '2', // Eastern-Arabic digit 2 272 '\u06F3': '3', // Eastern-Arabic digit 3 273 '\u06F4': '4', // Eastern-Arabic digit 4 274 '\u06F5': '5', // Eastern-Arabic digit 5 275 '\u06F6': '6', // Eastern-Arabic digit 6 276 '\u06F7': '7', // Eastern-Arabic digit 7 277 '\u06F8': '8', // Eastern-Arabic digit 8 278 '\u06F9': '9' // Eastern-Arabic digit 9 279}; 280 281 282/** 283 * A map that contains characters that are essential when dialling. That means 284 * any of the characters in this map must not be removed from a number when 285 * dialling, otherwise the call will not reach the intended destination. 286 * 287 * @const 288 * @type {!Object.<string, string>} 289 * @private 290 */ 291i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_ = { 292 '0': '0', 293 '1': '1', 294 '2': '2', 295 '3': '3', 296 '4': '4', 297 '5': '5', 298 '6': '6', 299 '7': '7', 300 '8': '8', 301 '9': '9', 302 '+': i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN, 303 '*': '*', 304 '#': '#' 305}; 306 307 308/** 309 * Only upper-case variants of alpha characters are stored. 310 * 311 * @const 312 * @type {!Object.<string, string>} 313 * @private 314 */ 315i18n.phonenumbers.PhoneNumberUtil.ALPHA_MAPPINGS_ = { 316 'A': '2', 317 'B': '2', 318 'C': '2', 319 'D': '3', 320 'E': '3', 321 'F': '3', 322 'G': '4', 323 'H': '4', 324 'I': '4', 325 'J': '5', 326 'K': '5', 327 'L': '5', 328 'M': '6', 329 'N': '6', 330 'O': '6', 331 'P': '7', 332 'Q': '7', 333 'R': '7', 334 'S': '7', 335 'T': '8', 336 'U': '8', 337 'V': '8', 338 'W': '9', 339 'X': '9', 340 'Y': '9', 341 'Z': '9' 342}; 343 344 345/** 346 * For performance reasons, amalgamate both into one map. 347 * 348 * @const 349 * @type {!Object.<string, string>} 350 * @private 351 */ 352i18n.phonenumbers.PhoneNumberUtil.ALL_NORMALIZATION_MAPPINGS_ = { 353 '0': '0', 354 '1': '1', 355 '2': '2', 356 '3': '3', 357 '4': '4', 358 '5': '5', 359 '6': '6', 360 '7': '7', 361 '8': '8', 362 '9': '9', 363 '\uFF10': '0', // Fullwidth digit 0 364 '\uFF11': '1', // Fullwidth digit 1 365 '\uFF12': '2', // Fullwidth digit 2 366 '\uFF13': '3', // Fullwidth digit 3 367 '\uFF14': '4', // Fullwidth digit 4 368 '\uFF15': '5', // Fullwidth digit 5 369 '\uFF16': '6', // Fullwidth digit 6 370 '\uFF17': '7', // Fullwidth digit 7 371 '\uFF18': '8', // Fullwidth digit 8 372 '\uFF19': '9', // Fullwidth digit 9 373 '\u0660': '0', // Arabic-indic digit 0 374 '\u0661': '1', // Arabic-indic digit 1 375 '\u0662': '2', // Arabic-indic digit 2 376 '\u0663': '3', // Arabic-indic digit 3 377 '\u0664': '4', // Arabic-indic digit 4 378 '\u0665': '5', // Arabic-indic digit 5 379 '\u0666': '6', // Arabic-indic digit 6 380 '\u0667': '7', // Arabic-indic digit 7 381 '\u0668': '8', // Arabic-indic digit 8 382 '\u0669': '9', // Arabic-indic digit 9 383 '\u06F0': '0', // Eastern-Arabic digit 0 384 '\u06F1': '1', // Eastern-Arabic digit 1 385 '\u06F2': '2', // Eastern-Arabic digit 2 386 '\u06F3': '3', // Eastern-Arabic digit 3 387 '\u06F4': '4', // Eastern-Arabic digit 4 388 '\u06F5': '5', // Eastern-Arabic digit 5 389 '\u06F6': '6', // Eastern-Arabic digit 6 390 '\u06F7': '7', // Eastern-Arabic digit 7 391 '\u06F8': '8', // Eastern-Arabic digit 8 392 '\u06F9': '9', // Eastern-Arabic digit 9 393 'A': '2', 394 'B': '2', 395 'C': '2', 396 'D': '3', 397 'E': '3', 398 'F': '3', 399 'G': '4', 400 'H': '4', 401 'I': '4', 402 'J': '5', 403 'K': '5', 404 'L': '5', 405 'M': '6', 406 'N': '6', 407 'O': '6', 408 'P': '7', 409 'Q': '7', 410 'R': '7', 411 'S': '7', 412 'T': '8', 413 'U': '8', 414 'V': '8', 415 'W': '9', 416 'X': '9', 417 'Y': '9', 418 'Z': '9' 419}; 420 421 422/** 423 * Separate map of all symbols that we wish to retain when formatting alpha 424 * numbers. This includes digits, ASCII letters and number grouping symbols such 425 * as '-' and ' '. 426 * 427 * @const 428 * @type {!Object.<string, string>} 429 * @private 430 */ 431i18n.phonenumbers.PhoneNumberUtil.ALL_PLUS_NUMBER_GROUPING_SYMBOLS_ = { 432 '0': '0', 433 '1': '1', 434 '2': '2', 435 '3': '3', 436 '4': '4', 437 '5': '5', 438 '6': '6', 439 '7': '7', 440 '8': '8', 441 '9': '9', 442 'A': 'A', 443 'B': 'B', 444 'C': 'C', 445 'D': 'D', 446 'E': 'E', 447 'F': 'F', 448 'G': 'G', 449 'H': 'H', 450 'I': 'I', 451 'J': 'J', 452 'K': 'K', 453 'L': 'L', 454 'M': 'M', 455 'N': 'N', 456 'O': 'O', 457 'P': 'P', 458 'Q': 'Q', 459 'R': 'R', 460 'S': 'S', 461 'T': 'T', 462 'U': 'U', 463 'V': 'V', 464 'W': 'W', 465 'X': 'X', 466 'Y': 'Y', 467 'Z': 'Z', 468 'a': 'A', 469 'b': 'B', 470 'c': 'C', 471 'd': 'D', 472 'e': 'E', 473 'f': 'F', 474 'g': 'G', 475 'h': 'H', 476 'i': 'I', 477 'j': 'J', 478 'k': 'K', 479 'l': 'L', 480 'm': 'M', 481 'n': 'N', 482 'o': 'O', 483 'p': 'P', 484 'q': 'Q', 485 'r': 'R', 486 's': 'S', 487 't': 'T', 488 'u': 'U', 489 'v': 'V', 490 'w': 'W', 491 'x': 'X', 492 'y': 'Y', 493 'z': 'Z', 494 '-': '-', 495 '\uFF0D': '-', 496 '\u2010': '-', 497 '\u2011': '-', 498 '\u2012': '-', 499 '\u2013': '-', 500 '\u2014': '-', 501 '\u2015': '-', 502 '\u2212': '-', 503 '/': '/', 504 '\uFF0F': '/', 505 ' ': ' ', 506 '\u3000': ' ', 507 '\u2060': ' ', 508 '.': '.', 509 '\uFF0E': '.' 510}; 511 512 513/** 514 * Pattern that makes it easy to distinguish whether a region has a single 515 * international dialing prefix or not. If a region has a single international 516 * prefix (e.g. 011 in USA), it will be represented as a string that contains 517 * a sequence of ASCII digits, and possibly a tilde, which signals waiting for 518 * the tone. If there are multiple available international prefixes in a 519 * region, they will be represented as a regex string that always contains one 520 * or more characters that are not ASCII digits or a tilde. 521 * 522 * @const 523 * @type {!RegExp} 524 * @private 525 */ 526i18n.phonenumbers.PhoneNumberUtil.SINGLE_INTERNATIONAL_PREFIX_ = 527 /[\d]+(?:[~\u2053\u223C\uFF5E][\d]+)?/; 528 529 530/** 531 * Regular expression of acceptable punctuation found in phone numbers, used to 532 * find numbers in text and to decide what is a viable phone number. This 533 * excludes diallable characters. 534 * This consists of dash characters, white space characters, full stops, 535 * slashes, square brackets, parentheses and tildes. It also includes the letter 536 * 'x' as that is found as a placeholder for carrier information in some phone 537 * numbers. Full-width variants are also present. 538 * 539 * @const 540 * @type {string} 541 */ 542i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION = 543 '-x\u2010-\u2015\u2212\u30FC\uFF0D-\uFF0F \u00A0\u00AD\u200B\u2060\u3000' + 544 '()\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E'; 545 546 547/** 548 * Digits accepted in phone numbers (ascii, fullwidth, arabic-indic, and eastern 549 * arabic digits). 550 * 551 * @const 552 * @type {string} 553 * @private 554 */ 555i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ = 556 '0-9\uFF10-\uFF19\u0660-\u0669\u06F0-\u06F9'; 557 558 559/** 560 * We accept alpha characters in phone numbers, ASCII only, upper and lower 561 * case. 562 * 563 * @const 564 * @type {string} 565 * @private 566 */ 567i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_ = 'A-Za-z'; 568 569 570/** 571 * @const 572 * @type {string} 573 * @private 574 */ 575i18n.phonenumbers.PhoneNumberUtil.PLUS_CHARS_ = '+\uFF0B'; 576 577 578/** 579 * @const 580 * @type {!RegExp} 581 */ 582i18n.phonenumbers.PhoneNumberUtil.PLUS_CHARS_PATTERN = 583 new RegExp('[' + i18n.phonenumbers.PhoneNumberUtil.PLUS_CHARS_ + ']+'); 584 585 586/** 587 * @const 588 * @type {!RegExp} 589 * @package 590 */ 591i18n.phonenumbers.PhoneNumberUtil.LEADING_PLUS_CHARS_PATTERN = 592 new RegExp('^[' + i18n.phonenumbers.PhoneNumberUtil.PLUS_CHARS_ + ']+'); 593 594 595/** 596 * @const 597 * @type {string} 598 * @private 599 */ 600i18n.phonenumbers.PhoneNumberUtil.SEPARATOR_PATTERN_ = 601 '[' + i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION + ']+'; 602 603 604/** 605 * @const 606 * @type {!RegExp} 607 */ 608i18n.phonenumbers.PhoneNumberUtil.CAPTURING_DIGIT_PATTERN = 609 new RegExp('([' + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + '])'); 610 611 612/** 613 * Regular expression of acceptable characters that may start a phone number for 614 * the purposes of parsing. This allows us to strip away meaningless prefixes to 615 * phone numbers that may be mistakenly given to us. This consists of digits, 616 * the plus symbol and arabic-indic digits. This does not contain alpha 617 * characters, although they may be used later in the number. It also does not 618 * include other punctuation, as this will be stripped later during parsing and 619 * is of no information value when parsing a number. 620 * 621 * @const 622 * @type {!RegExp} 623 * @private 624 */ 625i18n.phonenumbers.PhoneNumberUtil.VALID_START_CHAR_PATTERN_ = 626 new RegExp('[' + i18n.phonenumbers.PhoneNumberUtil.PLUS_CHARS_ + 627 i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']'); 628 629 630/** 631 * Regular expression of characters typically used to start a second phone 632 * number for the purposes of parsing. This allows us to strip off parts of the 633 * number that are actually the start of another number, such as for: 634 * (530) 583-6985 x302/x2303 -> the second extension here makes this actually 635 * two phone numbers, (530) 583-6985 x302 and (530) 583-6985 x2303. We remove 636 * the second extension so that the first number is parsed correctly. 637 * 638 * @const 639 * @type {!RegExp} 640 * @private 641 */ 642i18n.phonenumbers.PhoneNumberUtil.SECOND_NUMBER_START_PATTERN_ = /[\\\/] *x/; 643 644 645/** 646 * Regular expression of trailing characters that we want to remove. We remove 647 * all characters that are not alpha or numerical characters. The hash character 648 * is retained here, as it may signify the previous block was an extension. 649 * 650 * @const 651 * @type {!RegExp} 652 * @private 653 */ 654i18n.phonenumbers.PhoneNumberUtil.UNWANTED_END_CHAR_PATTERN_ = 655 new RegExp('[^' + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + 656 i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_ + '#]+$'); 657 658 659/** 660 * We use this pattern to check if the phone number has at least three letters 661 * in it - if so, then we treat it as a number where some phone-number digits 662 * are represented by letters. 663 * 664 * @const 665 * @type {!RegExp} 666 * @private 667 */ 668i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_PHONE_PATTERN_ = 669 /(?:.*?[A-Za-z]){3}.*/; 670 671 672/** 673 * Regular expression of viable phone numbers. This is location independent. 674 * Checks we have at least three leading digits, and only valid punctuation, 675 * alpha characters and digits in the phone number. Does not include extension 676 * data. The symbol 'x' is allowed here as valid punctuation since it is often 677 * used as a placeholder for carrier codes, for example in Brazilian phone 678 * numbers. We also allow multiple '+' characters at the start. 679 * Corresponds to the following: 680 * [digits]{minLengthNsn}| 681 * plus_sign* 682 * (([punctuation]|[star])*[digits]){3,}([punctuation]|[star]|[digits]|[alpha])* 683 * 684 * The first reg-ex is to allow short numbers (two digits long) to be parsed if 685 * they are entered as "15" etc, but only if there is no punctuation in them. 686 * The second expression restricts the number of digits to three or more, but 687 * then allows them to be in international form, and to have alpha-characters 688 * and punctuation. We split up the two reg-exes here and combine them when 689 * creating the reg-ex VALID_PHONE_NUMBER_PATTERN_ itself so we can prefix it 690 * with ^ and append $ to each branch. 691 * 692 * Note VALID_PUNCTUATION starts with a -, so must be the first in the range. 693 * 694 * @const 695 * @type {string} 696 * @private 697 */ 698i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_PHONE_NUMBER_PATTERN_ = 699 '[' + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']{' + 700 i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ + '}'; 701 702 703/** 704 * See MIN_LENGTH_PHONE_NUMBER_PATTERN_ for a full description of this reg-exp. 705 * 706 * @const 707 * @type {string} 708 * @private 709 */ 710i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_ = 711 '[' + i18n.phonenumbers.PhoneNumberUtil.PLUS_CHARS_ + ']*(?:[' + 712 i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION + 713 i18n.phonenumbers.PhoneNumberUtil.STAR_SIGN_ + ']*[' + 714 i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']){3,}[' + 715 i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION + 716 i18n.phonenumbers.PhoneNumberUtil.STAR_SIGN_ + 717 i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_ + 718 i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']*'; 719 720 721/** 722 * Default extension prefix to use when formatting. This will be put in front of 723 * any extension component of the number, after the main national number is 724 * formatted. For example, if you wish the default extension formatting to be 725 * ' extn: 3456', then you should specify ' extn: ' here as the default 726 * extension prefix. This can be overridden by region-specific preferences. 727 * 728 * @const 729 * @type {string} 730 * @private 731 */ 732i18n.phonenumbers.PhoneNumberUtil.DEFAULT_EXTN_PREFIX_ = ' ext. '; 733 734/** 735 * @const 736 * @type {string} 737 * @private 738 */ 739i18n.phonenumbers.PhoneNumberUtil.RFC3966_VISUAL_SEPARATOR_ = '[\\-\\.\\(\\)]?'; 740 741/** 742 * @const 743 * @type {string} 744 * @private 745 */ 746i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_DIGIT_ = '([' 747 + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']|' 748 + i18n.phonenumbers.PhoneNumberUtil.RFC3966_VISUAL_SEPARATOR_ + ')'; 749 750/** 751 * @const 752 * @type {string} 753 * @private 754 */ 755i18n.phonenumbers.PhoneNumberUtil.RFC3966_GLOBAL_NUMBER_DIGITS_ = '^\\' 756 + i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN 757 + i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_DIGIT_ + '*[' 758 + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']' 759 + i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_DIGIT_ + '*$'; 760 761/** 762 * Regular expression of valid global-number-digits for the phone-context 763 * parameter, following the syntax defined in RFC3966. 764 * 765 * @const 766 * @type {RegExp} 767 * @private 768 */ 769i18n.phonenumbers.PhoneNumberUtil.RFC3966_GLOBAL_NUMBER_DIGITS_PATTERN_ = 770 new RegExp(i18n.phonenumbers.PhoneNumberUtil.RFC3966_GLOBAL_NUMBER_DIGITS_); 771 772/** 773 * @const 774 * @type {string} 775 * @private 776 */ 777i18n.phonenumbers.PhoneNumberUtil.ALPHANUM_ = 778 i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_ 779 + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_; 780 781/** 782 * @const 783 * @type {string} 784 * @private 785 */ 786i18n.phonenumbers.PhoneNumberUtil.RFC3966_DOMAINLABEL_ = '[' 787 + i18n.phonenumbers.PhoneNumberUtil.ALPHANUM_ + ']+((\\-)*[' 788 + i18n.phonenumbers.PhoneNumberUtil.ALPHANUM_ + '])*'; 789 790/** 791 * @const 792 * @type {string} 793 * @private 794 */ 795i18n.phonenumbers.PhoneNumberUtil.RFC3966_TOPLABEL_ = '[' 796 + i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_ + ']+((\\-)*[' 797 + i18n.phonenumbers.PhoneNumberUtil.ALPHANUM_ + '])*'; 798 799/** 800 * @const 801 * @type {string} 802 * @private 803 */ 804i18n.phonenumbers.PhoneNumberUtil.RFC3966_DOMAINNAME_ = '^(' 805 + i18n.phonenumbers.PhoneNumberUtil.RFC3966_DOMAINLABEL_ + '\\.)*' 806 + i18n.phonenumbers.PhoneNumberUtil.RFC3966_TOPLABEL_ + '\\.?$'; 807 808/** 809 * Regular expression of valid domainname for the phone-context parameter, 810 * following the syntax defined in RFC3966. 811 * 812 * @const 813 * @type {RegExp} 814 * @private 815 */ 816i18n.phonenumbers.PhoneNumberUtil.RFC3966_DOMAINNAME_PATTERN_ = 817 new RegExp(i18n.phonenumbers.PhoneNumberUtil.RFC3966_DOMAINNAME_); 818 819/** 820 * Helper method for constructing regular expressions for parsing. Creates 821 * an expression that captures up to max_length digits. 822 * 823 * @return {string} RegEx pattern to capture extension digits. 824 * @private 825 */ 826i18n.phonenumbers.PhoneNumberUtil.extnDigits_ = 827 function(maxLength) { 828 return ('([' + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']' 829 + '{1,' + maxLength + '})'); 830}; 831 832/** 833 * Helper initialiser method to create the regular-expression pattern to match 834 * extensions. 835 * 836 * @return {string} RegEx pattern to capture extensions. 837 * @private 838 */ 839i18n.phonenumbers.PhoneNumberUtil.createExtnPattern_ = 840 function() { 841 // We cap the maximum length of an extension based on the ambiguity of the way 842 // the extension is prefixed. As per ITU, the officially allowed length for 843 // extensions is actually 40, but we don't support this since we haven't seen real 844 // examples and this introduces many false interpretations as the extension labels 845 // are not standardized. 846 /** @type {string} */ 847 var extLimitAfterExplicitLabel = '20'; 848 /** @type {string} */ 849 var extLimitAfterLikelyLabel = '15'; 850 /** @type {string} */ 851 var extLimitAfterAmbiguousChar = '9'; 852 /** @type {string} */ 853 var extLimitWhenNotSure = '6'; 854 855 /** @type {string} */ 856 var possibleSeparatorsBetweenNumberAndExtLabel = "[ \u00A0\\t,]*"; 857 // Optional full stop (.) or colon, followed by zero or more spaces/tabs/commas. 858 /** @type {string} */ 859 var possibleCharsAfterExtLabel = "[:\\.\uFF0E]?[ \u00A0\\t,-]*"; 860 /** @type {string} */ 861 var optionalExtnSuffix = "#?"; 862 863 // Here the extension is called out in more explicit way, i.e mentioning it obvious 864 // patterns like "ext.". 865 /** @type {string} */ 866 var explicitExtLabels = 867 "(?:e?xt(?:ensi(?:o\u0301?|\u00F3))?n?|\uFF45?\uFF58\uFF54\uFF4E?|\u0434\u043E\u0431|anexo)"; 868 // One-character symbols that can be used to indicate an extension, and less 869 // commonly used or more ambiguous extension labels. 870 /** @type {string} */ 871 var ambiguousExtLabels = "(?:[x\uFF58#\uFF03~\uFF5E]|int|\uFF49\uFF4E\uFF54)"; 872 // When extension is not separated clearly. 873 /** @type {string} */ 874 var ambiguousSeparator = "[- ]+"; 875 // This is the same as possibleSeparatorsBetweenNumberAndExtLabel, but not matching 876 // comma as extension label may have it. 877 /** @type {string} */ 878 var possibleSeparatorsNumberExtLabelNoComma = "[ \u00A0\\t]*"; 879 // ",," is commonly used for auto dialling the extension when connected. First 880 // comma is matched through possibleSeparatorsBetweenNumberAndExtLabel, so we do 881 // not repeat it here. Semi-colon works in Iphone and Android also to pop up a 882 // button with the extension number following. 883 /** @type {string} */ 884 var autoDiallingAndExtLabelsFound = "(?:,{2}|;)"; 885 886 /** @type {string} */ 887 var rfcExtn = i18n.phonenumbers.PhoneNumberUtil.RFC3966_EXTN_PREFIX_ 888 + i18n.phonenumbers.PhoneNumberUtil.extnDigits_(extLimitAfterExplicitLabel); 889 /** @type {string} */ 890 var explicitExtn = possibleSeparatorsBetweenNumberAndExtLabel + explicitExtLabels 891 + possibleCharsAfterExtLabel 892 + i18n.phonenumbers.PhoneNumberUtil.extnDigits_(extLimitAfterExplicitLabel) 893 + optionalExtnSuffix; 894 /** @type {string} */ 895 var ambiguousExtn = possibleSeparatorsBetweenNumberAndExtLabel + ambiguousExtLabels 896 + possibleCharsAfterExtLabel 897 + i18n.phonenumbers.PhoneNumberUtil.extnDigits_(extLimitAfterAmbiguousChar) 898 + optionalExtnSuffix; 899 /** @type {string} */ 900 var americanStyleExtnWithSuffix = ambiguousSeparator 901 + i18n.phonenumbers.PhoneNumberUtil.extnDigits_(extLimitWhenNotSure) + "#"; 902 903 /** @type {string} */ 904 var autoDiallingExtn = possibleSeparatorsNumberExtLabelNoComma 905 + autoDiallingAndExtLabelsFound + possibleCharsAfterExtLabel 906 + i18n.phonenumbers.PhoneNumberUtil.extnDigits_(extLimitAfterLikelyLabel) 907 + optionalExtnSuffix; 908 /** @type {string} */ 909 var onlyCommasExtn = possibleSeparatorsNumberExtLabelNoComma 910 + "(?:,)+" + possibleCharsAfterExtLabel 911 + i18n.phonenumbers.PhoneNumberUtil.extnDigits_(extLimitAfterAmbiguousChar) 912 + optionalExtnSuffix; 913 914 // The first regular expression covers RFC 3966 format, where the extension is added 915 // using ";ext=". The second more generic where extension is mentioned with explicit 916 // labels like "ext:". In both the above cases we allow more numbers in extension than 917 // any other extension labels. The third one captures when single character extension 918 // labels or less commonly used labels are used. In such cases we capture fewer 919 // extension digits in order to reduce the chance of falsely interpreting two 920 // numbers beside each other as a number + extension. The fourth one covers the 921 // special case of American numbers where the extension is written with a hash 922 // at the end, such as "- 503#". The fifth one is exclusively for extension 923 // autodialling formats which are used when dialling and in this case we accept longer 924 // extensions. The last one is more liberal on the number of commas that acts as 925 // extension labels, so we have a strict cap on the number of digits in such extensions. 926 return rfcExtn + "|" 927 + explicitExtn + "|" 928 + ambiguousExtn + "|" 929 + americanStyleExtnWithSuffix + "|" 930 + autoDiallingExtn + "|" 931 + onlyCommasExtn; 932}; 933 934 935/** 936 * Regexp of all known extension prefixes used by different regions followed by 937 * 1 or more valid digits, for use when parsing. 938 * 939 * @const 940 * @type {!RegExp} 941 * @private 942 */ 943i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERN_ = 944 new RegExp('(?:' + 945 i18n.phonenumbers.PhoneNumberUtil.createExtnPattern_() + 946 ')$', 'i'); 947 948 949/** 950 * We append optionally the extension pattern to the end here, as a valid phone 951 * number may have an extension prefix appended, followed by 1 or more digits. 952 * 953 * @const 954 * @type {!RegExp} 955 * @private 956 */ 957i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_PATTERN_ = 958 new RegExp( 959 '^' + 960 i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_PHONE_NUMBER_PATTERN_ + 961 '$|' + 962 '^' + i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_ + 963 '(?:' + i18n.phonenumbers.PhoneNumberUtil.createExtnPattern_() + 964 ')?' + '$', 'i'); 965 966 967/** 968 * @const 969 * @type {!RegExp} 970 * @private 971 */ 972i18n.phonenumbers.PhoneNumberUtil.NON_DIGITS_PATTERN_ = /\D+/; 973 974 975/** 976 * This was originally set to $1 but there are some countries for which the 977 * first group is not used in the national pattern (e.g. Argentina) so the $1 978 * group does not match correctly. Therefore, we use \d, so that the first 979 * group actually used in the pattern will be matched. 980 * @const 981 * @type {!RegExp} 982 * @private 983 */ 984i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_PATTERN_ = /(\$\d)/; 985 986 987/** 988 * @const 989 * @type {!RegExp} 990 * @private 991 */ 992i18n.phonenumbers.PhoneNumberUtil.NP_PATTERN_ = /\$NP/; 993 994 995/** 996 * @const 997 * @type {!RegExp} 998 * @private 999 */ 1000i18n.phonenumbers.PhoneNumberUtil.FG_PATTERN_ = /\$FG/; 1001 1002 1003/** 1004 * @const 1005 * @type {!RegExp} 1006 * @private 1007 */ 1008i18n.phonenumbers.PhoneNumberUtil.CC_PATTERN_ = /\$CC/; 1009 1010 1011/** 1012 * A pattern that is used to determine if the national prefix formatting rule 1013 * has the first group only, i.e., does not start with the national prefix. 1014 * Note that the pattern explicitly allows for unbalanced parentheses. 1015 * @const 1016 * @type {!RegExp} 1017 * @private 1018 */ 1019i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_ONLY_PREFIX_PATTERN_ = 1020 /^\(?\$1\)?$/; 1021 1022 1023/** 1024 * @const 1025 * @type {string} 1026 */ 1027i18n.phonenumbers.PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY = '001'; 1028 1029 1030/** 1031 * INTERNATIONAL and NATIONAL formats are consistent with the definition in 1032 * ITU-T Recommendation E123. However we follow local conventions such as 1033 * using '-' instead of whitespace as separators. For example, the number of the 1034 * Google Switzerland office will be written as '+41 44 668 1800' in 1035 * INTERNATIONAL format, and as '044 668 1800' in NATIONAL format. E164 format 1036 * is as per INTERNATIONAL format but with no formatting applied, e.g. 1037 * '+41446681800'. RFC3966 is as per INTERNATIONAL format, but with all spaces 1038 * and other separating symbols replaced with a hyphen, and with any phone 1039 * number extension appended with ';ext='. It also will have a prefix of 'tel:' 1040 * added, e.g. 'tel:+41-44-668-1800'. 1041 * 1042 * Note: If you are considering storing the number in a neutral format, you are 1043 * highly advised to use the PhoneNumber class. 1044 * @enum {number} 1045 */ 1046i18n.phonenumbers.PhoneNumberFormat = { 1047 E164: 0, 1048 INTERNATIONAL: 1, 1049 NATIONAL: 2, 1050 RFC3966: 3 1051}; 1052 1053 1054/** 1055 * Type of phone numbers. 1056 * 1057 * @enum {number} 1058 */ 1059i18n.phonenumbers.PhoneNumberType = { 1060 FIXED_LINE: 0, 1061 MOBILE: 1, 1062 // In some regions (e.g. the USA), it is impossible to distinguish between 1063 // fixed-line and mobile numbers by looking at the phone number itself. 1064 FIXED_LINE_OR_MOBILE: 2, 1065 // Freephone lines 1066 TOLL_FREE: 3, 1067 PREMIUM_RATE: 4, 1068 // The cost of this call is shared between the caller and the recipient, and 1069 // is hence typically less than PREMIUM_RATE calls. See 1070 // http://en.wikipedia.org/wiki/Shared_Cost_Service for more information. 1071 SHARED_COST: 5, 1072 // Voice over IP numbers. This includes TSoIP (Telephony Service over IP). 1073 VOIP: 6, 1074 // A personal number is associated with a particular person, and may be routed 1075 // to either a MOBILE or FIXED_LINE number. Some more information can be found 1076 // here: http://en.wikipedia.org/wiki/Personal_Numbers 1077 PERSONAL_NUMBER: 7, 1078 PAGER: 8, 1079 // Used for 'Universal Access Numbers' or 'Company Numbers'. They may be 1080 // further routed to specific offices, but allow one number to be used for a 1081 // company. 1082 UAN: 9, 1083 // Used for 'Voice Mail Access Numbers'. 1084 VOICEMAIL: 10, 1085 // A phone number is of type UNKNOWN when it does not fit any of the known 1086 // patterns for a specific region. 1087 UNKNOWN: -1 1088}; 1089 1090 1091/** 1092 * Types of phone number matches. See detailed description beside the 1093 * isNumberMatch() method. 1094 * 1095 * @enum {number} 1096 */ 1097i18n.phonenumbers.PhoneNumberUtil.MatchType = { 1098 NOT_A_NUMBER: 0, 1099 NO_MATCH: 1, 1100 SHORT_NSN_MATCH: 2, 1101 NSN_MATCH: 3, 1102 EXACT_MATCH: 4 1103}; 1104 1105 1106/** 1107 * Possible outcomes when testing if a PhoneNumber is possible. 1108 * 1109 * @enum {number} 1110 */ 1111i18n.phonenumbers.PhoneNumberUtil.ValidationResult = { 1112 /** The number length matches that of valid numbers for this region. */ 1113 IS_POSSIBLE: 0, 1114 /** 1115 * The number length matches that of local numbers for this region only (i.e. 1116 * numbers that may be able to be dialled within an area, but do not have all 1117 * the information to be dialled from anywhere inside or outside the country). 1118 */ 1119 IS_POSSIBLE_LOCAL_ONLY: 4, 1120 /** The number has an invalid country calling code. */ 1121 INVALID_COUNTRY_CODE: 1, 1122 /** The number is shorter than all valid numbers for this region. */ 1123 TOO_SHORT: 2, 1124 /** 1125 * The number is longer than the shortest valid numbers for this region, 1126 * shorter than the longest valid numbers for this region, and does not itself 1127 * have a number length that matches valid numbers for this region. 1128 * This can also be returned in the case where 1129 * isPossibleNumberForTypeWithReason was called, and there are no numbers of 1130 * this type at all for this region. 1131 */ 1132 INVALID_LENGTH: 5, 1133 /** The number is longer than all valid numbers for this region. */ 1134 TOO_LONG: 3 1135}; 1136 1137 1138/** 1139 * Attempts to extract a possible number from the string passed in. This 1140 * currently strips all leading characters that cannot be used to start a phone 1141 * number. Characters that can be used to start a phone number are defined in 1142 * the VALID_START_CHAR_PATTERN. If none of these characters are found in the 1143 * number passed in, an empty string is returned. This function also attempts to 1144 * strip off any alternative extensions or endings if two or more are present, 1145 * such as in the case of: (530) 583-6985 x302/x2303. The second extension here 1146 * makes this actually two phone numbers, (530) 583-6985 x302 and (530) 583-6985 1147 * x2303. We remove the second extension so that the first number is parsed 1148 * correctly. 1149 * 1150 * @param {string} number the string that might contain a phone number. 1151 * @return {string} the number, stripped of any non-phone-number prefix (such as 1152 * 'Tel:') or an empty string if no character used to start phone numbers 1153 * (such as + or any digit) is found in the number. 1154 */ 1155i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber = function(number) { 1156 /** @type {string} */ 1157 var possibleNumber; 1158 1159 /** @type {number} */ 1160 var start = number 1161 .search(i18n.phonenumbers.PhoneNumberUtil.VALID_START_CHAR_PATTERN_); 1162 if (start >= 0) { 1163 possibleNumber = number.substring(start); 1164 // Remove trailing non-alpha non-numerical characters. 1165 possibleNumber = possibleNumber.replace( 1166 i18n.phonenumbers.PhoneNumberUtil.UNWANTED_END_CHAR_PATTERN_, ''); 1167 1168 // Check for extra numbers at the end. 1169 /** @type {number} */ 1170 var secondNumberStart = possibleNumber 1171 .search(i18n.phonenumbers.PhoneNumberUtil.SECOND_NUMBER_START_PATTERN_); 1172 if (secondNumberStart >= 0) { 1173 possibleNumber = possibleNumber.substring(0, secondNumberStart); 1174 } 1175 } else { 1176 possibleNumber = ''; 1177 } 1178 return possibleNumber; 1179}; 1180 1181 1182/** 1183 * Checks to see if the string of characters could possibly be a phone number at 1184 * all. At the moment, checks to see that the string begins with at least 2 1185 * digits, ignoring any punctuation commonly found in phone numbers. This method 1186 * does not require the number to be normalized in advance - but does assume 1187 * that leading non-number symbols have been removed, such as by the method 1188 * extractPossibleNumber. 1189 * 1190 * @param {string} number string to be checked for viability as a phone number. 1191 * @return {boolean} true if the number could be a phone number of some sort, 1192 * otherwise false. 1193 */ 1194i18n.phonenumbers.PhoneNumberUtil.isViablePhoneNumber = function(number) { 1195 if (number.length < i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) { 1196 return false; 1197 } 1198 return i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 1199 i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_PATTERN_, number); 1200}; 1201 1202 1203/** 1204 * Normalizes a string of characters representing a phone number. This performs 1205 * the following conversions: 1206 * Punctuation is stripped. 1207 * For ALPHA/VANITY numbers: 1208 * Letters are converted to their numeric representation on a telephone 1209 * keypad. The keypad used here is the one defined in ITU Recommendation 1210 * E.161. This is only done if there are 3 or more letters in the number, 1211 * to lessen the risk that such letters are typos. 1212 * For other numbers: 1213 * Wide-ascii digits are converted to normal ASCII (European) digits. 1214 * Arabic-Indic numerals are converted to European numerals. 1215 * Spurious alpha characters are stripped. 1216 * 1217 * @param {string} number a string of characters representing a phone number. 1218 * @return {string} the normalized string version of the phone number. 1219 */ 1220i18n.phonenumbers.PhoneNumberUtil.normalize = function(number) { 1221 if (i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 1222 i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_PHONE_PATTERN_, number)) { 1223 return i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(number, 1224 i18n.phonenumbers.PhoneNumberUtil.ALL_NORMALIZATION_MAPPINGS_, true); 1225 } else { 1226 return i18n.phonenumbers.PhoneNumberUtil.normalizeDigitsOnly(number); 1227 } 1228}; 1229 1230 1231/** 1232 * Normalizes a string of characters representing a phone number. This is a 1233 * wrapper for normalize(String number) but does in-place normalization of the 1234 * StringBuffer provided. 1235 * 1236 * @param {!goog.string.StringBuffer} number a StringBuffer of characters 1237 * representing a phone number that will be normalized in place. 1238 * @private 1239 */ 1240i18n.phonenumbers.PhoneNumberUtil.normalizeSB_ = function(number) { 1241 /** @type {string} */ 1242 var normalizedNumber = i18n.phonenumbers.PhoneNumberUtil.normalize(number 1243 .toString()); 1244 number.clear(); 1245 number.append(normalizedNumber); 1246}; 1247 1248 1249/** 1250 * Normalizes a string of characters representing a phone number. This converts 1251 * wide-ascii and arabic-indic numerals to European numerals, and strips 1252 * punctuation and alpha characters. 1253 * 1254 * @param {string} number a string of characters representing a phone number. 1255 * @return {string} the normalized string version of the phone number. 1256 */ 1257i18n.phonenumbers.PhoneNumberUtil.normalizeDigitsOnly = function(number) { 1258 return i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(number, 1259 i18n.phonenumbers.PhoneNumberUtil.DIGIT_MAPPINGS, true); 1260}; 1261 1262 1263/** 1264 * Normalizes a string of characters representing a phone number. This strips 1265 * all characters which are not diallable on a mobile phone keypad (including 1266 * all non-ASCII digits). 1267 * 1268 * @param {string} number a string of characters representing a phone number. 1269 * @return {string} the normalized string version of the phone number. 1270 */ 1271i18n.phonenumbers.PhoneNumberUtil.normalizeDiallableCharsOnly = 1272 function(number) { 1273 1274 return i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(number, 1275 i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_, 1276 true /* remove non matches */); 1277}; 1278 1279 1280/** 1281 * Converts all alpha characters in a number to their respective digits on a 1282 * keypad, but retains existing formatting. Also converts wide-ascii digits to 1283 * normal ascii digits, and converts Arabic-Indic numerals to European numerals. 1284 * 1285 * @param {string} number a string of characters representing a phone number. 1286 * @return {string} the normalized string version of the phone number. 1287 */ 1288i18n.phonenumbers.PhoneNumberUtil.convertAlphaCharactersInNumber = 1289 function(number) { 1290 1291 return i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(number, 1292 i18n.phonenumbers.PhoneNumberUtil.ALL_NORMALIZATION_MAPPINGS_, false); 1293}; 1294 1295 1296/** 1297 * Gets the length of the geographical area code from the 1298 * {@code national_number} field of the PhoneNumber object passed in, so that 1299 * clients could use it to split a national significant number into geographical 1300 * area code and subscriber number. It works in such a way that the resultant 1301 * subscriber number should be diallable, at least on some devices. An example 1302 * of how this could be used: 1303 * 1304 * <pre> 1305 * var phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance(); 1306 * var number = phoneUtil.parse('16502530000', 'US'); 1307 * var nationalSignificantNumber = 1308 * phoneUtil.getNationalSignificantNumber(number); 1309 * var areaCode; 1310 * var subscriberNumber; 1311 * 1312 * var areaCodeLength = phoneUtil.getLengthOfGeographicalAreaCode(number); 1313 * if (areaCodeLength > 0) { 1314 * areaCode = nationalSignificantNumber.substring(0, areaCodeLength); 1315 * subscriberNumber = nationalSignificantNumber.substring(areaCodeLength); 1316 * } else { 1317 * areaCode = ''; 1318 * subscriberNumber = nationalSignificantNumber; 1319 * } 1320 * </pre> 1321 * 1322 * N.B.: area code is a very ambiguous concept, so the I18N team generally 1323 * recommends against using it for most purposes, but recommends using the more 1324 * general {@code national_number} instead. Read the following carefully before 1325 * deciding to use this method: 1326 * <ul> 1327 * <li> geographical area codes change over time, and this method honors those 1328 * changes; therefore, it doesn't guarantee the stability of the result it 1329 * produces. 1330 * <li> subscriber numbers may not be diallable from all devices (notably 1331 * mobile devices, which typically requires the full national_number to be 1332 * dialled in most regions). 1333 * <li> most non-geographical numbers have no area codes, including numbers 1334 * from non-geographical entities. 1335 * <li> some geographical numbers have no area codes. 1336 * </ul> 1337 * 1338 * @param {i18n.phonenumbers.PhoneNumber} number the PhoneNumber object for 1339 * which clients want to know the length of the area code. 1340 * @return {number} the length of area code of the PhoneNumber object passed in. 1341 */ 1342i18n.phonenumbers.PhoneNumberUtil.prototype.getLengthOfGeographicalAreaCode = 1343 function(number) { 1344 /** @type {i18n.phonenumbers.PhoneMetadata} */ 1345 var metadata = this.getMetadataForRegion(this.getRegionCodeForNumber(number)); 1346 if (metadata == null) { 1347 return 0; 1348 } 1349 // If a country doesn't use a national prefix, and this number doesn't have 1350 // an Italian leading zero, we assume it is a closed dialling plan with no 1351 // area codes. 1352 if (!metadata.hasNationalPrefix() && !number.hasItalianLeadingZero()) { 1353 return 0; 1354 } 1355 1356 if (!this.isNumberGeographical(number)) { 1357 return 0; 1358 } 1359 1360 return this.getLengthOfNationalDestinationCode(number); 1361}; 1362 1363 1364/** 1365 * Gets the length of the national destination code (NDC) from the PhoneNumber 1366 * object passed in, so that clients could use it to split a national 1367 * significant number into NDC and subscriber number. The NDC of a phone number 1368 * is normally the first group of digit(s) right after the country calling code 1369 * when the number is formatted in the international format, if there is a 1370 * subscriber number part that follows. 1371 * 1372 * N.B.: similar to an area code, not all numbers have an NDC! 1373 * 1374 * An example of how this could be used: 1375 * 1376 * <pre> 1377 * var phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance(); 1378 * var number = phoneUtil.parse('18002530000', 'US'); 1379 * var nationalSignificantNumber = 1380 * phoneUtil.getNationalSignificantNumber(number); 1381 * var nationalDestinationCode; 1382 * var subscriberNumber; 1383 * 1384 * var nationalDestinationCodeLength = 1385 * phoneUtil.getLengthOfNationalDestinationCode(number); 1386 * if (nationalDestinationCodeLength > 0) { 1387 * nationalDestinationCode = 1388 * nationalSignificantNumber.substring(0, nationalDestinationCodeLength); 1389 * subscriberNumber = 1390 * nationalSignificantNumber.substring(nationalDestinationCodeLength); 1391 * } else { 1392 * nationalDestinationCode = ''; 1393 * subscriberNumber = nationalSignificantNumber; 1394 * } 1395 * </pre> 1396 * 1397 * Refer to the unittests to see the difference between this function and 1398 * {@link #getLengthOfGeographicalAreaCode}. 1399 * 1400 * @param {i18n.phonenumbers.PhoneNumber} number the PhoneNumber object for 1401 * which clients want to know the length of the NDC. 1402 * @return {number} the length of NDC of the PhoneNumber object passed in, which 1403 * could be zero. 1404 */ 1405i18n.phonenumbers.PhoneNumberUtil.prototype.getLengthOfNationalDestinationCode = 1406 function(number) { 1407 1408 /** @type {i18n.phonenumbers.PhoneNumber} */ 1409 var copiedProto; 1410 if (number.hasExtension()) { 1411 // We don't want to alter the proto given to us, but we don't want to 1412 // include the extension when we format it, so we copy it and clear the 1413 // extension here. 1414 copiedProto = number.clone(); 1415 copiedProto.clearExtension(); 1416 } else { 1417 copiedProto = number; 1418 } 1419 1420 /** @type {string} */ 1421 var nationalSignificantNumber = this.format(copiedProto, 1422 i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL); 1423 /** @type {!Array.<string>} */ 1424 var numberGroups = nationalSignificantNumber.split( 1425 i18n.phonenumbers.PhoneNumberUtil.NON_DIGITS_PATTERN_); 1426 // The pattern will start with '+COUNTRY_CODE ' so the first group will always 1427 // be the empty string (before the + symbol) and the second group will be the 1428 // country calling code. The third group will be area code if it is not the 1429 // last group. 1430 // NOTE: On IE the first group that is supposed to be the empty string does 1431 // not appear in the array of number groups... so make the result on non-IE 1432 // browsers to be that of IE. 1433 if (numberGroups[0].length == 0) { 1434 numberGroups.shift(); 1435 } 1436 if (numberGroups.length <= 2) { 1437 return 0; 1438 } 1439 1440 if (this.getNumberType(number) == i18n.phonenumbers.PhoneNumberType.MOBILE) { 1441 // For example Argentinian mobile numbers, when formatted in the 1442 // international format, are in the form of +54 9 NDC XXXX.... As a result, 1443 // we take the length of the third group (NDC) and add the length of the 1444 // mobile token, which also forms part of the national significant number. 1445 // This assumes that the mobile token is always formatted separately from 1446 // the rest of the phone number. 1447 /** @type {string} */ 1448 var mobileToken = i18n.phonenumbers.PhoneNumberUtil.getCountryMobileToken( 1449 number.getCountryCodeOrDefault()); 1450 if (mobileToken != '') { 1451 return numberGroups[2].length + mobileToken.length; 1452 } 1453 } 1454 return numberGroups[1].length; 1455}; 1456 1457 1458/** 1459 * Returns the mobile token for the provided country calling code if it has 1460 * one, otherwise returns an empty string. A mobile token is a number inserted 1461 * before the area code when dialing a mobile number from that country from 1462 * abroad. 1463 * 1464 * @param {number} countryCallingCode the country calling code for which we 1465 * want the mobile token. 1466 * @return {string} the mobile token for the given country calling code. 1467 */ 1468i18n.phonenumbers.PhoneNumberUtil.getCountryMobileToken = 1469 function(countryCallingCode) { 1470 return i18n.phonenumbers.PhoneNumberUtil.MOBILE_TOKEN_MAPPINGS_[ 1471 countryCallingCode] || ''; 1472}; 1473 1474 1475/** 1476 * Returns all regions the library has metadata for. 1477 * 1478 * @return {!Array.<string>} the two-letter region codes for every geographical 1479 * region the library supports. 1480 */ 1481i18n.phonenumbers.PhoneNumberUtil.prototype.getSupportedRegions = function() { 1482 return Object.keys(i18n.phonenumbers.metadata.countryToMetadata) 1483 .filter(function(regionCode) { 1484 return isNaN(regionCode); 1485 }); 1486}; 1487 1488 1489/** 1490 * Returns all global network calling codes the library has metadata for. 1491 * 1492 * @return {!Array.<number>} the country calling codes for every 1493 * non-geographical entity the library supports. 1494 */ 1495i18n.phonenumbers.PhoneNumberUtil.prototype 1496 .getSupportedGlobalNetworkCallingCodes = function() { 1497 var callingCodesAsStrings = 1498 Object.keys(i18n.phonenumbers.metadata.countryToMetadata) 1499 .filter(function(regionCode) { 1500 return !isNaN(regionCode); 1501 }); 1502 return callingCodesAsStrings.map(function(callingCode) { 1503 return parseInt(callingCode, 10); 1504 }); 1505}; 1506 1507 1508/** 1509 * Returns all country calling codes the library has metadata for, covering 1510 * both non-geographical entities (global network calling codes) and those used 1511 * for geographical entities. This could be used to populate a drop-down box of 1512 * country calling codes for a phone-number widget, for instance. 1513 * 1514 * @return {!Array.<number>} the country calling codes for every geographical 1515 * and non-geographical entity the library supports. 1516 */ 1517i18n.phonenumbers.PhoneNumberUtil.prototype.getSupportedCallingCodes = 1518 function() { 1519 var countryCodesAsStrings = 1520 Object.keys(i18n.phonenumbers.metadata.countryCodeToRegionCodeMap); 1521 return [ 1522 ...this.getSupportedGlobalNetworkCallingCodes(), 1523 ...countryCodesAsStrings.map(function(callingCode) { 1524 return parseInt(callingCode, 10); 1525 }) 1526 ]; 1527}; 1528 1529 1530/** 1531 * Returns true if there is any possibleLength data set for a particular 1532 * PhoneNumberDesc. 1533 * 1534 * @param {i18n.phonenumbers.PhoneNumberDesc} desc 1535 * @return {boolean} 1536 * @private 1537 */ 1538i18n.phonenumbers.PhoneNumberUtil.descHasPossibleNumberData_ = function(desc) { 1539 // If this is empty, it means numbers of this type inherit from the "general 1540 // desc" -> the value "-1" means that no numbers exist for this type. 1541 return desc != null && 1542 (desc.possibleLengthCount() != 1 || desc.possibleLengthArray()[0] != -1); 1543}; 1544 1545 1546/** 1547 * Returns true if there is any data set for a particular PhoneNumberDesc. 1548 * 1549 * @param {i18n.phonenumbers.PhoneNumberDesc} desc 1550 * @return {boolean} 1551 * @private 1552 */ 1553i18n.phonenumbers.PhoneNumberUtil.descHasData_ = function(desc) { 1554 // Checking most properties since we don't know what's present, since a 1555 // custom build may have stripped just one of them (e.g. liteBuild strips 1556 // exampleNumber). We don't bother checking the possibleLengthsLocalOnly, 1557 // since if this is the only thing that's present we don't really support the 1558 // type at all: no type-specific methods will work with only this data. 1559 return desc != null && (desc.hasExampleNumber() || 1560 i18n.phonenumbers.PhoneNumberUtil.descHasPossibleNumberData_(desc) || 1561 desc.hasNationalNumberPattern()); 1562}; 1563 1564 1565/** 1566 * Returns the types we have metadata for based on the PhoneMetadata object 1567 * passed in. 1568 * 1569 * @param {!i18n.phonenumbers.PhoneMetadata} metadata 1570 * @return {!Array.<i18n.phonenumbers.PhoneNumberType>} the types supported 1571 * based on the metadata object passed in. 1572 * @private 1573 */ 1574i18n.phonenumbers.PhoneNumberUtil.getSupportedTypesForMetadata_ = 1575 function(metadata) { 1576 /** @type {!Array.<i18n.phonenumbers.PhoneNumberType>} */ 1577 var types = []; 1578 goog.object.forEach(i18n.phonenumbers.PhoneNumberType, 1579 function(type) { 1580 if (type == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE || 1581 type == i18n.phonenumbers.PhoneNumberType.UNKNOWN) { 1582 // Never return FIXED_LINE_OR_MOBILE (it is a convenience type, and 1583 // represents that a particular number type can't be determined) or 1584 // UNKNOWN (the non-type). 1585 return; 1586 } 1587 /** @type {i18n.phonenumbers.PhoneNumberDesc} */ 1588 var desc = i18n.phonenumbers.PhoneNumberUtil.getNumberDescByType_( 1589 metadata, type); 1590 if (i18n.phonenumbers.PhoneNumberUtil.descHasData_(desc)) { 1591 types.push(type); 1592 } 1593 }); 1594 return types; 1595}; 1596 1597 1598/** 1599 * Returns the types for a given region which the library has metadata for. 1600 * Will not include FIXED_LINE_OR_MOBILE (if numbers for this non-geographical 1601 * entity could be classified as FIXED_LINE_OR_MOBILE, both FIXED_LINE and 1602 * MOBILE would be present) and UNKNOWN. 1603 * 1604 * No types will be returned for invalid or unknown region codes. 1605 * 1606 * @param {?string} regionCode 1607 * @return {!Array.<i18n.phonenumbers.PhoneNumberType>} the types for every 1608 * region the library supports. 1609 */ 1610i18n.phonenumbers.PhoneNumberUtil.prototype.getSupportedTypesForRegion = 1611 function(regionCode) { 1612 if (!this.isValidRegionCode_(regionCode)) { 1613 return []; 1614 } 1615 return i18n.phonenumbers.PhoneNumberUtil.getSupportedTypesForMetadata_( 1616 /** @type {!i18n.phonenumbers.PhoneMetadata} */ ( 1617 this.getMetadataForRegion(regionCode))); 1618}; 1619 1620 1621/** 1622 * Returns the types for a country-code belonging to a non-geographical entity 1623 * which the library has metadata for. Will not include FIXED_LINE_OR_MOBILE 1624 * (instead both FIXED_LINE and FIXED_LINE_OR_MOBILE (if numbers for this 1625 * non-geographical entity could be classified as FIXED_LINE_OR_MOBILE, both 1626 * FIXED_LINE and MOBILE would be present) and UNKNOWN. 1627 * 1628 * No types will be returned for country calling codes that do not map to a 1629 * known non-geographical entity. 1630 * 1631 * @param {number} countryCallingCode 1632 * @return {!Array.<i18n.phonenumbers.PhoneNumberType>} the types for every 1633 * non-geographical entity the library supports. 1634 */ 1635i18n.phonenumbers.PhoneNumberUtil.prototype.getSupportedTypesForNonGeoEntity = 1636 function(countryCallingCode) { 1637 /** @type {i18n.phonenumbers.PhoneMetadata} */ 1638 var metadata = this.getMetadataForNonGeographicalRegion(countryCallingCode); 1639 if (metadata == null) { 1640 return []; 1641 } 1642 return i18n.phonenumbers.PhoneNumberUtil.getSupportedTypesForMetadata_( 1643 /** @type {!i18n.phonenumbers.PhoneMetadata} */ (metadata)); 1644}; 1645 1646 1647/** 1648 * Normalizes a string of characters representing a phone number by replacing 1649 * all characters found in the accompanying map with the values therein, and 1650 * stripping all other characters if removeNonMatches is true. 1651 * 1652 * @param {string} number a string of characters representing a phone number. 1653 * @param {!Object.<string, string>} normalizationReplacements a mapping of 1654 * characters to what they should be replaced by in the normalized version 1655 * of the phone number. 1656 * @param {boolean} removeNonMatches indicates whether characters that are not 1657 * able to be replaced should be stripped from the number. If this is false, 1658 * they will be left unchanged in the number. 1659 * @return {string} the normalized string version of the phone number. 1660 * @private 1661 */ 1662i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_ = 1663 function(number, normalizationReplacements, removeNonMatches) { 1664 1665 /** @type {!goog.string.StringBuffer} */ 1666 var normalizedNumber = new goog.string.StringBuffer(); 1667 /** @type {string} */ 1668 var character; 1669 /** @type {string} */ 1670 var newDigit; 1671 /** @type {number} */ 1672 var numberLength = number.length; 1673 for (var i = 0; i < numberLength; ++i) { 1674 character = number.charAt(i); 1675 newDigit = normalizationReplacements[character.toUpperCase()]; 1676 if (newDigit != null) { 1677 normalizedNumber.append(newDigit); 1678 } else if (!removeNonMatches) { 1679 normalizedNumber.append(character); 1680 } 1681 // If neither of the above are true, we remove this character. 1682 } 1683 return normalizedNumber.toString(); 1684}; 1685 1686 1687/** 1688 * Helper function to check if the national prefix formatting rule has the first 1689 * group only, i.e., does not start with the national prefix. 1690 * 1691 * @param {string} nationalPrefixFormattingRule The formatting rule for the 1692 * national prefix. 1693 * @return {boolean} true if the national prefix formatting rule has the first 1694 * group only. 1695 */ 1696i18n.phonenumbers.PhoneNumberUtil.prototype.formattingRuleHasFirstGroupOnly = 1697 function(nationalPrefixFormattingRule) { 1698 return nationalPrefixFormattingRule.length == 0 || 1699 i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_ONLY_PREFIX_PATTERN_. 1700 test(nationalPrefixFormattingRule); 1701}; 1702 1703 1704/** 1705 * Tests whether a phone number has a geographical association. It checks if the 1706 * number is associated with a certain region in the country to which it 1707 * belongs. Note that this doesn't verify if the number is actually in use. 1708 * 1709 * @param {i18n.phonenumbers.PhoneNumber} phoneNumber The phone number to test. 1710 * @return {boolean} true if the phone number has a geographical association. 1711 */ 1712i18n.phonenumbers.PhoneNumberUtil.prototype.isNumberGeographical = 1713 function(phoneNumber) { 1714 /** @type {i18n.phonenumbers.PhoneNumberType} */ 1715 var numberType = this.getNumberType(phoneNumber); 1716 1717 return numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE || 1718 numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE || 1719 (i18n.phonenumbers.PhoneNumberUtil.GEO_MOBILE_COUNTRIES_.includes( 1720 phoneNumber.getCountryCodeOrDefault()) && 1721 numberType == i18n.phonenumbers.PhoneNumberType.MOBILE); 1722}; 1723 1724 1725/** 1726 * Helper function to check region code is not unknown or null. 1727 * 1728 * @param {?string} regionCode the CLDR two-letter region code. 1729 * @return {boolean} true if region code is valid. 1730 * @private 1731 */ 1732i18n.phonenumbers.PhoneNumberUtil.prototype.isValidRegionCode_ = 1733 function(regionCode) { 1734 1735 // In Java we check whether the regionCode is contained in supportedRegions 1736 // that is built out of all the values of countryCallingCodeToRegionCodeMap 1737 // (countryCodeToRegionCodeMap in JS) minus REGION_CODE_FOR_NON_GEO_ENTITY. 1738 // In JS we check whether the regionCode is contained in the keys of 1739 // countryToMetadata but since for non-geographical country calling codes 1740 // (e.g. +800) we use the country calling codes instead of the region code as 1741 // key in the map we have to make sure regionCode is not a number to prevent 1742 // returning true for non-geographical country calling codes. 1743 return regionCode != null && 1744 isNaN(regionCode) && 1745 regionCode.toUpperCase() in i18n.phonenumbers.metadata.countryToMetadata; 1746}; 1747 1748 1749/** 1750 * Helper function to check the country calling code is valid. 1751 * 1752 * @param {number} countryCallingCode the country calling code. 1753 * @return {boolean} true if country calling code code is valid. 1754 * @private 1755 */ 1756i18n.phonenumbers.PhoneNumberUtil.prototype.hasValidCountryCallingCode_ = 1757 function(countryCallingCode) { 1758 1759 return countryCallingCode in 1760 i18n.phonenumbers.metadata.countryCodeToRegionCodeMap; 1761}; 1762 1763 1764/** 1765 * Formats a phone number in the specified format using default rules. Note that 1766 * this does not promise to produce a phone number that the user can dial from 1767 * where they are - although we do format in either 'national' or 1768 * 'international' format depending on what the client asks for, we do not 1769 * currently support a more abbreviated format, such as for users in the same 1770 * 'area' who could potentially dial the number without area code. Note that if 1771 * the phone number has a country calling code of 0 or an otherwise invalid 1772 * country calling code, we cannot work out which formatting rules to apply so 1773 * we return the national significant number with no formatting applied. 1774 * 1775 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be 1776 * formatted. 1777 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the 1778 * phone number should be formatted into. 1779 * @return {string} the formatted phone number. 1780 */ 1781i18n.phonenumbers.PhoneNumberUtil.prototype.format = 1782 function(number, numberFormat) { 1783 1784 if (number.getNationalNumber() == 0 && number.hasRawInput()) { 1785 // Unparseable numbers that kept their raw input just use that. 1786 // This is the only case where a number can be formatted as E164 without a 1787 // leading '+' symbol (but the original number wasn't parseable anyway). 1788 // TODO: Consider removing the 'if' above so that unparseable strings 1789 // without raw input format to the empty string instead of "+00" 1790 /** @type {string} */ 1791 var rawInput = number.getRawInputOrDefault(); 1792 if (rawInput.length > 0) { 1793 return rawInput; 1794 } 1795 } 1796 /** @type {number} */ 1797 var countryCallingCode = number.getCountryCodeOrDefault(); 1798 /** @type {string} */ 1799 var nationalSignificantNumber = this.getNationalSignificantNumber(number); 1800 if (numberFormat == i18n.phonenumbers.PhoneNumberFormat.E164) { 1801 // Early exit for E164 case (even if the country calling code is invalid) 1802 // since no formatting of the national number needs to be applied. 1803 // Extensions are not formatted. 1804 return this.prefixNumberWithCountryCallingCode_( 1805 countryCallingCode, i18n.phonenumbers.PhoneNumberFormat.E164, 1806 nationalSignificantNumber, ''); 1807 } 1808 if (!this.hasValidCountryCallingCode_(countryCallingCode)) { 1809 return nationalSignificantNumber; 1810 } 1811 // Note getRegionCodeForCountryCode() is used because formatting information 1812 // for regions which share a country calling code is contained by only one 1813 // region for performance reasons. For example, for NANPA regions it will be 1814 // contained in the metadata for US. 1815 /** @type {string} */ 1816 var regionCode = this.getRegionCodeForCountryCode(countryCallingCode); 1817 1818 // Metadata cannot be null because the country calling code is valid (which 1819 // means that the region code cannot be ZZ and must be one of our supported 1820 // region codes). 1821 /** @type {i18n.phonenumbers.PhoneMetadata} */ 1822 var metadata = 1823 this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode); 1824 /** @type {string} */ 1825 var formattedExtension = 1826 this.maybeGetFormattedExtension_(number, metadata, numberFormat); 1827 /** @type {string} */ 1828 var formattedNationalNumber = 1829 this.formatNsn_(nationalSignificantNumber, metadata, numberFormat); 1830 return this.prefixNumberWithCountryCallingCode_(countryCallingCode, 1831 numberFormat, 1832 formattedNationalNumber, 1833 formattedExtension); 1834}; 1835 1836 1837/** 1838 * Formats a phone number in the specified format using client-defined 1839 * formatting rules. Note that if the phone number has a country calling code of 1840 * zero or an otherwise invalid country calling code, we cannot work out things 1841 * like whether there should be a national prefix applied, or how to format 1842 * extensions, so we return the national significant number with no formatting 1843 * applied. 1844 * 1845 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be 1846 * formatted. 1847 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the 1848 * phone number should be formatted into. 1849 * @param {Array.<i18n.phonenumbers.NumberFormat>} userDefinedFormats formatting 1850 * rules specified by clients. 1851 * @return {string} the formatted phone number. 1852 */ 1853i18n.phonenumbers.PhoneNumberUtil.prototype.formatByPattern = 1854 function(number, numberFormat, userDefinedFormats) { 1855 1856 /** @type {number} */ 1857 var countryCallingCode = number.getCountryCodeOrDefault(); 1858 /** @type {string} */ 1859 var nationalSignificantNumber = this.getNationalSignificantNumber(number); 1860 if (!this.hasValidCountryCallingCode_(countryCallingCode)) { 1861 return nationalSignificantNumber; 1862 } 1863 // Note getRegionCodeForCountryCode() is used because formatting information 1864 // for regions which share a country calling code is contained by only one 1865 // region for performance reasons. For example, for NANPA regions it will be 1866 // contained in the metadata for US. 1867 /** @type {string} */ 1868 var regionCode = this.getRegionCodeForCountryCode(countryCallingCode); 1869 // Metadata cannot be null because the country calling code is valid 1870 /** @type {i18n.phonenumbers.PhoneMetadata} */ 1871 var metadata = 1872 this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode); 1873 1874 /** @type {string} */ 1875 var formattedNumber = ''; 1876 1877 /** @type {i18n.phonenumbers.NumberFormat} */ 1878 var formattingPattern = this.chooseFormattingPatternForNumber_( 1879 userDefinedFormats, nationalSignificantNumber); 1880 if (formattingPattern == null) { 1881 // If no pattern above is matched, we format the number as a whole. 1882 formattedNumber = nationalSignificantNumber; 1883 } else { 1884 // Before we do a replacement of the national prefix pattern $NP with the 1885 // national prefix, we need to copy the rule so that subsequent replacements 1886 // for different numbers have the appropriate national prefix. 1887 /** @type {i18n.phonenumbers.NumberFormat} */ 1888 var numFormatCopy = formattingPattern.clone(); 1889 /** @type {string} */ 1890 var nationalPrefixFormattingRule = 1891 formattingPattern.getNationalPrefixFormattingRuleOrDefault(); 1892 if (nationalPrefixFormattingRule.length > 0) { 1893 /** @type {string} */ 1894 var nationalPrefix = metadata.getNationalPrefixOrDefault(); 1895 if (nationalPrefix.length > 0) { 1896 // Replace $NP with national prefix and $FG with the first group ($1). 1897 nationalPrefixFormattingRule = nationalPrefixFormattingRule 1898 .replace(i18n.phonenumbers.PhoneNumberUtil.NP_PATTERN_, 1899 nationalPrefix) 1900 .replace(i18n.phonenumbers.PhoneNumberUtil.FG_PATTERN_, '$1'); 1901 numFormatCopy.setNationalPrefixFormattingRule( 1902 nationalPrefixFormattingRule); 1903 } else { 1904 // We don't want to have a rule for how to format the national prefix if 1905 // there isn't one. 1906 numFormatCopy.clearNationalPrefixFormattingRule(); 1907 } 1908 } 1909 formattedNumber = this.formatNsnUsingPattern_( 1910 nationalSignificantNumber, numFormatCopy, numberFormat); 1911 } 1912 1913 /** @type {string} */ 1914 var formattedExtension = 1915 this.maybeGetFormattedExtension_(number, metadata, numberFormat); 1916 return this.prefixNumberWithCountryCallingCode_(countryCallingCode, 1917 numberFormat, 1918 formattedNumber, 1919 formattedExtension); 1920}; 1921 1922 1923/** 1924 * Formats a phone number in national format for dialing using the carrier as 1925 * specified in the {@code carrierCode}. The {@code carrierCode} will always be 1926 * used regardless of whether the phone number already has a preferred domestic 1927 * carrier code stored. If {@code carrierCode} contains an empty string, returns 1928 * the number in national format without any carrier code. 1929 * 1930 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be 1931 * formatted. 1932 * @param {string} carrierCode the carrier selection code to be used. 1933 * @return {string} the formatted phone number in national format for dialing 1934 * using the carrier as specified in the {@code carrierCode}. 1935 */ 1936i18n.phonenumbers.PhoneNumberUtil.prototype. 1937 formatNationalNumberWithCarrierCode = function(number, carrierCode) { 1938 1939 /** @type {number} */ 1940 var countryCallingCode = number.getCountryCodeOrDefault(); 1941 /** @type {string} */ 1942 var nationalSignificantNumber = this.getNationalSignificantNumber(number); 1943 if (!this.hasValidCountryCallingCode_(countryCallingCode)) { 1944 return nationalSignificantNumber; 1945 } 1946 1947 // Note getRegionCodeForCountryCode() is used because formatting information 1948 // for regions which share a country calling code is contained by only one 1949 // region for performance reasons. For example, for NANPA regions it will be 1950 // contained in the metadata for US. 1951 /** @type {string} */ 1952 var regionCode = this.getRegionCodeForCountryCode(countryCallingCode); 1953 // Metadata cannot be null because the country calling code is valid. 1954 /** @type {i18n.phonenumbers.PhoneMetadata} */ 1955 var metadata = 1956 this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode); 1957 /** @type {string} */ 1958 var formattedExtension = this.maybeGetFormattedExtension_( 1959 number, metadata, i18n.phonenumbers.PhoneNumberFormat.NATIONAL); 1960 /** @type {string} */ 1961 var formattedNationalNumber = this.formatNsn_( 1962 nationalSignificantNumber, metadata, 1963 i18n.phonenumbers.PhoneNumberFormat.NATIONAL, carrierCode); 1964 return this.prefixNumberWithCountryCallingCode_( 1965 countryCallingCode, i18n.phonenumbers.PhoneNumberFormat.NATIONAL, 1966 formattedNationalNumber, formattedExtension); 1967}; 1968 1969 1970/** 1971 * @param {number} countryCallingCode 1972 * @param {?string} regionCode 1973 * @return {i18n.phonenumbers.PhoneMetadata} 1974 * @private 1975 */ 1976i18n.phonenumbers.PhoneNumberUtil.prototype.getMetadataForRegionOrCallingCode_ = 1977 function(countryCallingCode, regionCode) { 1978 return i18n.phonenumbers.PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY == 1979 regionCode ? 1980 this.getMetadataForNonGeographicalRegion(countryCallingCode) : 1981 this.getMetadataForRegion(regionCode); 1982}; 1983 1984 1985/** 1986 * Formats a phone number in national format for dialing using the carrier as 1987 * specified in the preferred_domestic_carrier_code field of the PhoneNumber 1988 * object passed in. If that is missing, use the {@code fallbackCarrierCode} 1989 * passed in instead. If there is no {@code preferred_domestic_carrier_code}, 1990 * and the {@code fallbackCarrierCode} contains an empty string, return the 1991 * number in national format without any carrier code. 1992 * 1993 * <p>Use {@link #formatNationalNumberWithCarrierCode} instead if the carrier 1994 * code passed in should take precedence over the number's 1995 * {@code preferred_domestic_carrier_code} when formatting. 1996 * 1997 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be 1998 * formatted. 1999 * @param {string} fallbackCarrierCode the carrier selection code to be used, if 2000 * none is found in the phone number itself. 2001 * @return {string} the formatted phone number in national format for dialing 2002 * using the number's preferred_domestic_carrier_code, or the 2003 * {@code fallbackCarrierCode} passed in if none is found. 2004 */ 2005i18n.phonenumbers.PhoneNumberUtil.prototype. 2006 formatNationalNumberWithPreferredCarrierCode = function( 2007 number, fallbackCarrierCode) { 2008 return this.formatNationalNumberWithCarrierCode( 2009 number, 2010 // Historically, we set this to an empty string when parsing with raw 2011 // input if none was found in the input string. However, this doesn't 2012 // result in a number we can dial. For this reason, we treat the empty 2013 // string the same as if it isn't set at all. 2014 number.getPreferredDomesticCarrierCodeOrDefault().length > 0 ? 2015 number.getPreferredDomesticCarrierCodeOrDefault() : 2016 fallbackCarrierCode); 2017}; 2018 2019 2020/** 2021 * Returns a number formatted in such a way that it can be dialed from a mobile 2022 * phone in a specific region. If the number cannot be reached from the region 2023 * (e.g. some countries block toll-free numbers from being called outside of the 2024 * country), the method returns an empty string. 2025 * 2026 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be 2027 * formatted. 2028 * @param {string} regionCallingFrom the region where the call is being placed. 2029 * @param {boolean} withFormatting whether the number should be returned with 2030 * formatting symbols, such as spaces and dashes. 2031 * @return {string} the formatted phone number. 2032 */ 2033i18n.phonenumbers.PhoneNumberUtil.prototype.formatNumberForMobileDialing = 2034 function(number, regionCallingFrom, withFormatting) { 2035 2036 /** @type {number} */ 2037 var countryCallingCode = number.getCountryCodeOrDefault(); 2038 if (!this.hasValidCountryCallingCode_(countryCallingCode)) { 2039 return number.hasRawInput() ? number.getRawInputOrDefault() : ''; 2040 } 2041 2042 /** @type {string} */ 2043 var formattedNumber = ''; 2044 // Clear the extension, as that part cannot normally be dialed together with 2045 // the main number. 2046 /** @type {i18n.phonenumbers.PhoneNumber} */ 2047 var numberNoExt = number.clone(); 2048 numberNoExt.clearExtension(); 2049 /** @type {string} */ 2050 var regionCode = this.getRegionCodeForCountryCode(countryCallingCode); 2051 /** @type {i18n.phonenumbers.PhoneNumberType} */ 2052 var numberType = this.getNumberType(numberNoExt); 2053 /** @type {boolean} */ 2054 var isValidNumber = (numberType != i18n.phonenumbers.PhoneNumberType.UNKNOWN); 2055 if (regionCallingFrom == regionCode) { 2056 /** @type {boolean} */ 2057 var isFixedLineOrMobile = 2058 (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) || 2059 (numberType == i18n.phonenumbers.PhoneNumberType.MOBILE) || 2060 (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE); 2061 // Carrier codes may be needed in some countries. We handle this here. 2062 if (regionCode == 'BR' && isFixedLineOrMobile) { 2063 formattedNumber = 2064 // Historically, we set this to an empty string when parsing with raw 2065 // input if none was found in the input string. However, this doesn't 2066 // result in a number we can dial. For this reason, we treat the empty 2067 // string the same as if it isn't set at all. 2068 numberNoExt.getPreferredDomesticCarrierCodeOrDefault().length > 0 ? 2069 this.formatNationalNumberWithPreferredCarrierCode(numberNoExt, '') : 2070 // Brazilian fixed line and mobile numbers need to be dialed with a 2071 // carrier code when called within Brazil. Without that, most of the 2072 // carriers won't connect the call. Because of that, we return an 2073 // empty string here. 2074 ''; 2075 } else if (countryCallingCode == 2076 i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_) { 2077 // For NANPA countries, we output international format for numbers that 2078 // can be dialed internationally, since that always works, except for 2079 // numbers which might potentially be short numbers, which are always 2080 // dialled in national format. 2081 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2082 var regionMetadata = this.getMetadataForRegion(regionCallingFrom); 2083 if (this.canBeInternationallyDialled(numberNoExt) && 2084 this.testNumberLength_(this.getNationalSignificantNumber(numberNoExt), 2085 regionMetadata) != 2086 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT) { 2087 formattedNumber = this.format( 2088 numberNoExt, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL); 2089 } else { 2090 formattedNumber = this.format( 2091 numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL); 2092 } 2093 } else { 2094 // For non-geographical countries, and Mexican, Chilean and Uzbek fixed 2095 // line and mobile numbers, we output international format for numbers 2096 // that can be dialed internationally as that always works. 2097 if ((regionCode == 2098 i18n.phonenumbers.PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY || 2099 // MX fixed line and mobile numbers should always be formatted in 2100 // international format, even when dialed within MX. For national 2101 // format to work, a carrier code needs to be used, and the correct 2102 // carrier code depends on if the caller and callee are from the 2103 // same local area. It is trickier to get that to work correctly than 2104 // using international format, which is tested to work fine on all 2105 // carriers. 2106 // CL fixed line numbers need the national prefix when dialing in the 2107 // national format, but don't have it when used for display. The 2108 // reverse is true for mobile numbers. As a result, we output them in 2109 // the international format to make it work. 2110 // UZ mobile and fixed-line numbers have to be formatted in 2111 // international format or prefixed with special codes like 03, 04 2112 // (for fixed-line) and 05 (for mobile) for dialling successfully 2113 // from mobile devices. As we do not have complete information on 2114 // special codes and to be consistent with formatting across all 2115 // phone types we return the number in international format here. 2116 ((regionCode == 'MX' || regionCode == 'CL' || regionCode == 'UZ') && 2117 isFixedLineOrMobile)) && 2118 this.canBeInternationallyDialled(numberNoExt)) { 2119 formattedNumber = this.format( 2120 numberNoExt, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL); 2121 } else { 2122 formattedNumber = this.format( 2123 numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL); 2124 } 2125 } 2126 } else if (isValidNumber && this.canBeInternationallyDialled(numberNoExt)) { 2127 // We assume that short numbers are not diallable from outside their region, 2128 // so if a number is not a valid regular length phone number, we treat it as 2129 // if it cannot be internationally dialled. 2130 return withFormatting ? 2131 this.format(numberNoExt, 2132 i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL) : 2133 this.format(numberNoExt, i18n.phonenumbers.PhoneNumberFormat.E164); 2134 } 2135 return withFormatting ? 2136 formattedNumber : 2137 i18n.phonenumbers.PhoneNumberUtil.normalizeDiallableCharsOnly( 2138 formattedNumber); 2139}; 2140 2141 2142/** 2143 * Formats a phone number for out-of-country dialing purposes. If no 2144 * regionCallingFrom is supplied, we format the number in its INTERNATIONAL 2145 * format. If the country calling code is the same as that of the region where 2146 * the number is from, then NATIONAL formatting will be applied. 2147 * 2148 * <p>If the number itself has a country calling code of zero or an otherwise 2149 * invalid country calling code, then we return the number with no formatting 2150 * applied. 2151 * 2152 * <p>Note this function takes care of the case for calling inside of NANPA and 2153 * between Russia and Kazakhstan (who share the same country calling code). In 2154 * those cases, no international prefix is used. For regions which have multiple 2155 * international prefixes, the number in its INTERNATIONAL format will be 2156 * returned instead. 2157 * 2158 * @param {i18n.phonenumbers.PhoneNumber} number the phone number to be 2159 * formatted. 2160 * @param {string} regionCallingFrom the region where the call is being placed. 2161 * @return {string} the formatted phone number. 2162 */ 2163i18n.phonenumbers.PhoneNumberUtil.prototype.formatOutOfCountryCallingNumber = 2164 function(number, regionCallingFrom) { 2165 2166 if (!this.isValidRegionCode_(regionCallingFrom)) { 2167 return this.format(number, 2168 i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL); 2169 } 2170 /** @type {number} */ 2171 var countryCallingCode = number.getCountryCodeOrDefault(); 2172 /** @type {string} */ 2173 var nationalSignificantNumber = this.getNationalSignificantNumber(number); 2174 if (!this.hasValidCountryCallingCode_(countryCallingCode)) { 2175 return nationalSignificantNumber; 2176 } 2177 if (countryCallingCode == 2178 i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_) { 2179 if (this.isNANPACountry(regionCallingFrom)) { 2180 // For NANPA regions, return the national format for these regions but 2181 // prefix it with the country calling code. 2182 return countryCallingCode + ' ' + 2183 this.format(number, i18n.phonenumbers.PhoneNumberFormat.NATIONAL); 2184 } 2185 } else if (countryCallingCode == 2186 this.getCountryCodeForValidRegion_(regionCallingFrom)) { 2187 // If regions share a country calling code, the country calling code need 2188 // not be dialled. This also applies when dialling within a region, so this 2189 // if clause covers both these cases. Technically this is the case for 2190 // dialling from La Reunion to other overseas departments of France (French 2191 // Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover 2192 // this edge case for now and for those cases return the version including 2193 // country calling code. Details here: 2194 // http://www.petitfute.com/voyage/225-info-pratiques-reunion 2195 return this.format(number, 2196 i18n.phonenumbers.PhoneNumberFormat.NATIONAL); 2197 } 2198 // Metadata cannot be null because we checked 'isValidRegionCode()' above. 2199 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2200 var metadataForRegionCallingFrom = 2201 this.getMetadataForRegion(regionCallingFrom); 2202 /** @type {string} */ 2203 var internationalPrefix = 2204 metadataForRegionCallingFrom.getInternationalPrefixOrDefault(); 2205 2206 // For regions that have multiple international prefixes, the international 2207 // format of the number is returned, unless there is a preferred international 2208 // prefix. 2209 /** @type {string} */ 2210 var internationalPrefixForFormatting = ''; 2211 if (metadataForRegionCallingFrom.hasPreferredInternationalPrefix()) { 2212 internationalPrefixForFormatting = 2213 metadataForRegionCallingFrom.getPreferredInternationalPrefixOrDefault(); 2214 } else if (i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 2215 i18n.phonenumbers.PhoneNumberUtil.SINGLE_INTERNATIONAL_PREFIX_, 2216 internationalPrefix)) { 2217 internationalPrefixForFormatting = internationalPrefix; 2218 } 2219 2220 /** @type {string} */ 2221 var regionCode = this.getRegionCodeForCountryCode(countryCallingCode); 2222 // Metadata cannot be null because the country calling code is valid. 2223 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2224 var metadataForRegion = 2225 this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode); 2226 /** @type {string} */ 2227 var formattedNationalNumber = this.formatNsn_( 2228 nationalSignificantNumber, metadataForRegion, 2229 i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL); 2230 /** @type {string} */ 2231 var formattedExtension = this.maybeGetFormattedExtension_(number, 2232 metadataForRegion, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL); 2233 return internationalPrefixForFormatting.length > 0 ? 2234 internationalPrefixForFormatting + ' ' + countryCallingCode + ' ' + 2235 formattedNationalNumber + formattedExtension : 2236 this.prefixNumberWithCountryCallingCode_( 2237 countryCallingCode, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL, 2238 formattedNationalNumber, formattedExtension); 2239}; 2240 2241 2242/** 2243 * Formats a phone number using the original phone number format that the number 2244 * is parsed from. The original format is embedded in the country_code_source 2245 * field of the PhoneNumber object passed in. If such information is missing, 2246 * the number will be formatted into the NATIONAL format by default. When the 2247 * number contains a leading zero and this is unexpected for this country, or 2248 * we don't have a formatting pattern for the number, the method returns the 2249 * raw input when it is available. 2250 * 2251 * Note this method guarantees no digit will be inserted, removed or modified as 2252 * a result of formatting. 2253 * 2254 * @param {i18n.phonenumbers.PhoneNumber} number the phone number that needs to 2255 * be formatted in its original number format. 2256 * @param {string} regionCallingFrom the region whose IDD needs to be prefixed 2257 * if the original number has one. 2258 * @return {string} the formatted phone number in its original number format. 2259 */ 2260i18n.phonenumbers.PhoneNumberUtil.prototype.formatInOriginalFormat = 2261 function(number, regionCallingFrom) { 2262 2263 if (number.hasRawInput() && !this.hasFormattingPatternForNumber_(number)) { 2264 // We check if we have the formatting pattern because without that, we might 2265 // format the number as a group without national prefix. 2266 return number.getRawInputOrDefault(); 2267 } 2268 if (!number.hasCountryCodeSource()) { 2269 return this.format(number, i18n.phonenumbers.PhoneNumberFormat.NATIONAL); 2270 } 2271 /** @type {string} */ 2272 var formattedNumber; 2273 switch (number.getCountryCodeSource()) { 2274 case i18n.phonenumbers.PhoneNumber.CountryCodeSource 2275 .FROM_NUMBER_WITH_PLUS_SIGN: 2276 formattedNumber = this.format(number, 2277 i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL); 2278 break; 2279 case i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_IDD: 2280 formattedNumber = 2281 this.formatOutOfCountryCallingNumber(number, regionCallingFrom); 2282 break; 2283 case i18n.phonenumbers.PhoneNumber.CountryCodeSource 2284 .FROM_NUMBER_WITHOUT_PLUS_SIGN: 2285 formattedNumber = this.format(number, 2286 i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL).substring(1); 2287 break; 2288 case i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_DEFAULT_COUNTRY: 2289 // Fall-through to default case. 2290 default: 2291 /** @type {string} */ 2292 var regionCode = 2293 this.getRegionCodeForCountryCode(number.getCountryCodeOrDefault()); 2294 // We strip non-digits from the NDD here, and from the raw input later, 2295 // so that we can compare them easily. 2296 /** @type {?string} */ 2297 var nationalPrefix = this.getNddPrefixForRegion(regionCode, true); 2298 /** @type {string} */ 2299 var nationalFormat = 2300 this.format(number, i18n.phonenumbers.PhoneNumberFormat.NATIONAL); 2301 if (nationalPrefix == null || nationalPrefix.length == 0) { 2302 // If the region doesn't have a national prefix at all, we can safely 2303 // return the national format without worrying about a national prefix 2304 // being added. 2305 formattedNumber = nationalFormat; 2306 break; 2307 } 2308 // Otherwise, we check if the original number was entered with a national 2309 // prefix. 2310 if (this.rawInputContainsNationalPrefix_( 2311 number.getRawInputOrDefault(), nationalPrefix, regionCode)) { 2312 // If so, we can safely return the national format. 2313 formattedNumber = nationalFormat; 2314 break; 2315 } 2316 // Metadata cannot be null here because getNddPrefixForRegion() (above) 2317 // returns null if there is no metadata for the region. 2318 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2319 var metadata = this.getMetadataForRegion(regionCode); 2320 /** @type {string} */ 2321 var nationalNumber = this.getNationalSignificantNumber(number); 2322 /** @type {i18n.phonenumbers.NumberFormat} */ 2323 var formatRule = this.chooseFormattingPatternForNumber_( 2324 metadata.numberFormatArray(), nationalNumber); 2325 // The format rule could still be null here if the national number was 0 2326 // and there was no raw input (this should not be possible for numbers 2327 // generated by the phonenumber library as they would also not have a 2328 // country calling code and we would have exited earlier). 2329 if (formatRule == null) { 2330 formattedNumber = nationalFormat; 2331 break; 2332 } 2333 // When the format we apply to this number doesn't contain national 2334 // prefix, we can just return the national format. 2335 // TODO: Refactor the code below with the code in 2336 // isNationalPrefixPresentIfRequired. 2337 /** @type {string} */ 2338 var candidateNationalPrefixRule = 2339 formatRule.getNationalPrefixFormattingRuleOrDefault(); 2340 // We assume that the first-group symbol will never be _before_ the 2341 // national prefix. 2342 /** @type {number} */ 2343 var indexOfFirstGroup = candidateNationalPrefixRule.indexOf('$1'); 2344 if (indexOfFirstGroup <= 0) { 2345 formattedNumber = nationalFormat; 2346 break; 2347 } 2348 candidateNationalPrefixRule = 2349 candidateNationalPrefixRule.substring(0, indexOfFirstGroup); 2350 candidateNationalPrefixRule = i18n.phonenumbers.PhoneNumberUtil 2351 .normalizeDigitsOnly(candidateNationalPrefixRule); 2352 if (candidateNationalPrefixRule.length == 0) { 2353 // National prefix not used when formatting this number. 2354 formattedNumber = nationalFormat; 2355 break; 2356 } 2357 // Otherwise, we need to remove the national prefix from our output. 2358 /** @type {i18n.phonenumbers.NumberFormat} */ 2359 var numFormatCopy = formatRule.clone(); 2360 numFormatCopy.clearNationalPrefixFormattingRule(); 2361 formattedNumber = this.formatByPattern(number, 2362 i18n.phonenumbers.PhoneNumberFormat.NATIONAL, [numFormatCopy]); 2363 break; 2364 } 2365 /** @type {string} */ 2366 var rawInput = number.getRawInputOrDefault(); 2367 // If no digit is inserted/removed/modified as a result of our formatting, we 2368 // return the formatted phone number; otherwise we return the raw input the 2369 // user entered. 2370 if (formattedNumber != null && rawInput.length > 0) { 2371 /** @type {string} */ 2372 var normalizedFormattedNumber = 2373 i18n.phonenumbers.PhoneNumberUtil.normalizeDiallableCharsOnly( 2374 formattedNumber); 2375 /** @type {string} */ 2376 var normalizedRawInput = 2377 i18n.phonenumbers.PhoneNumberUtil.normalizeDiallableCharsOnly(rawInput); 2378 if (normalizedFormattedNumber != normalizedRawInput) { 2379 formattedNumber = rawInput; 2380 } 2381 } 2382 return formattedNumber; 2383}; 2384 2385 2386/** 2387 * Check if rawInput, which is assumed to be in the national format, has a 2388 * national prefix. The national prefix is assumed to be in digits-only form. 2389 * @param {string} rawInput 2390 * @param {string} nationalPrefix 2391 * @param {string} regionCode 2392 * @return {boolean} 2393 * @private 2394 */ 2395i18n.phonenumbers.PhoneNumberUtil.prototype.rawInputContainsNationalPrefix_ = 2396 function(rawInput, nationalPrefix, regionCode) { 2397 2398 /** @type {string} */ 2399 var normalizedNationalNumber = 2400 i18n.phonenumbers.PhoneNumberUtil.normalizeDigitsOnly(rawInput); 2401 if (goog.string.startsWith(normalizedNationalNumber, nationalPrefix)) { 2402 try { 2403 // Some Japanese numbers (e.g. 00777123) might be mistaken to contain the 2404 // national prefix when written without it (e.g. 0777123) if we just do 2405 // prefix matching. To tackle that, we check the validity of the number if 2406 // the assumed national prefix is removed (777123 won't be valid in 2407 // Japan). 2408 return this.isValidNumber( 2409 this.parse(normalizedNationalNumber.substring(nationalPrefix.length), 2410 regionCode)); 2411 } catch (e) { 2412 return false; 2413 } 2414 } 2415 return false; 2416}; 2417 2418 2419/** 2420 * @param {i18n.phonenumbers.PhoneNumber} number 2421 * @return {boolean} 2422 * @private 2423 */ 2424i18n.phonenumbers.PhoneNumberUtil.prototype.hasFormattingPatternForNumber_ = 2425 function(number) { 2426 2427 /** @type {number} */ 2428 var countryCallingCode = number.getCountryCodeOrDefault(); 2429 /** @type {string} */ 2430 var phoneNumberRegion = this.getRegionCodeForCountryCode(countryCallingCode); 2431 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2432 var metadata = this.getMetadataForRegionOrCallingCode_( 2433 countryCallingCode, phoneNumberRegion); 2434 if (metadata == null) { 2435 return false; 2436 } 2437 /** @type {string} */ 2438 var nationalNumber = this.getNationalSignificantNumber(number); 2439 /** @type {i18n.phonenumbers.NumberFormat} */ 2440 var formatRule = this.chooseFormattingPatternForNumber_( 2441 metadata.numberFormatArray(), nationalNumber); 2442 return formatRule != null; 2443}; 2444 2445 2446/** 2447 * Formats a phone number for out-of-country dialing purposes. 2448 * 2449 * Note that in this version, if the number was entered originally using alpha 2450 * characters and this version of the number is stored in raw_input, this 2451 * representation of the number will be used rather than the digit 2452 * representation. Grouping information, as specified by characters such as '-' 2453 * and ' ', will be retained. 2454 * 2455 * <p><b>Caveats:</b></p> 2456 * <ul> 2457 * <li>This will not produce good results if the country calling code is both 2458 * present in the raw input _and_ is the start of the national number. This is 2459 * not a problem in the regions which typically use alpha numbers. 2460 * <li>This will also not produce good results if the raw input has any grouping 2461 * information within the first three digits of the national number, and if the 2462 * function needs to strip preceding digits/words in the raw input before these 2463 * digits. Normally people group the first three digits together so this is not 2464 * a huge problem - and will be fixed if it proves to be so. 2465 * </ul> 2466 * 2467 * @param {i18n.phonenumbers.PhoneNumber} number the phone number that needs to 2468 * be formatted. 2469 * @param {string} regionCallingFrom the region where the call is being placed. 2470 * @return {string} the formatted phone number. 2471 */ 2472i18n.phonenumbers.PhoneNumberUtil.prototype. 2473 formatOutOfCountryKeepingAlphaChars = function(number, regionCallingFrom) { 2474 /** @type {string} */ 2475 var rawInput = number.getRawInputOrDefault(); 2476 // If there is no raw input, then we can't keep alpha characters because there 2477 // aren't any. In this case, we return formatOutOfCountryCallingNumber. 2478 if (rawInput.length == 0) { 2479 return this.formatOutOfCountryCallingNumber(number, regionCallingFrom); 2480 } 2481 /** @type {number} */ 2482 var countryCode = number.getCountryCodeOrDefault(); 2483 if (!this.hasValidCountryCallingCode_(countryCode)) { 2484 return rawInput; 2485 } 2486 // Strip any prefix such as country calling code, IDD, that was present. We do 2487 // this by comparing the number in raw_input with the parsed number. To do 2488 // this, first we normalize punctuation. We retain number grouping symbols 2489 // such as ' ' only. 2490 rawInput = i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_( 2491 rawInput, 2492 i18n.phonenumbers.PhoneNumberUtil.ALL_PLUS_NUMBER_GROUPING_SYMBOLS_, 2493 true); 2494 // Now we trim everything before the first three digits in the parsed number. 2495 // We choose three because all valid alpha numbers have 3 digits at the start 2496 // - if it does not, then we don't trim anything at all. Similarly, if the 2497 // national number was less than three digits, we don't trim anything at all. 2498 /** @type {string} */ 2499 var nationalNumber = this.getNationalSignificantNumber(number); 2500 if (nationalNumber.length > 3) { 2501 /** @type {number} */ 2502 var firstNationalNumberDigit = 2503 rawInput.indexOf(nationalNumber.substring(0, 3)); 2504 if (firstNationalNumberDigit != -1) { 2505 rawInput = rawInput.substring(firstNationalNumberDigit); 2506 } 2507 } 2508 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2509 var metadataForRegionCallingFrom = 2510 this.getMetadataForRegion(regionCallingFrom); 2511 if (countryCode == i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_) { 2512 if (this.isNANPACountry(regionCallingFrom)) { 2513 return countryCode + ' ' + rawInput; 2514 } 2515 } else if (metadataForRegionCallingFrom != null && 2516 countryCode == this.getCountryCodeForValidRegion_(regionCallingFrom)) { 2517 /** @type {i18n.phonenumbers.NumberFormat} */ 2518 var formattingPattern = this.chooseFormattingPatternForNumber_( 2519 metadataForRegionCallingFrom.numberFormatArray(), nationalNumber); 2520 if (formattingPattern == null) { 2521 // If no pattern above is matched, we format the original input. 2522 return rawInput; 2523 } 2524 /** @type {i18n.phonenumbers.NumberFormat} */ 2525 var newFormat = formattingPattern.clone(); 2526 // The first group is the first group of digits that the user wrote 2527 // together. 2528 newFormat.setPattern('(\\d+)(.*)'); 2529 // Here we just concatenate them back together after the national prefix 2530 // has been fixed. 2531 newFormat.setFormat('$1$2'); 2532 // Now we format using this pattern instead of the default pattern, but 2533 // with the national prefix prefixed if necessary. 2534 // This will not work in the cases where the pattern (and not the leading 2535 // digits) decide whether a national prefix needs to be used, since we have 2536 // overridden the pattern to match anything, but that is not the case in the 2537 // metadata to date. 2538 return this.formatNsnUsingPattern_(rawInput, newFormat, 2539 i18n.phonenumbers.PhoneNumberFormat.NATIONAL); 2540 } 2541 /** @type {string} */ 2542 var internationalPrefixForFormatting = ''; 2543 // If an unsupported region-calling-from is entered, or a country with 2544 // multiple international prefixes, the international format of the number is 2545 // returned, unless there is a preferred international prefix. 2546 if (metadataForRegionCallingFrom != null) { 2547 /** @type {string} */ 2548 var internationalPrefix = 2549 metadataForRegionCallingFrom.getInternationalPrefixOrDefault(); 2550 internationalPrefixForFormatting = 2551 i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 2552 i18n.phonenumbers.PhoneNumberUtil.SINGLE_INTERNATIONAL_PREFIX_, 2553 internationalPrefix) ? 2554 internationalPrefix : 2555 metadataForRegionCallingFrom.getPreferredInternationalPrefixOrDefault(); 2556 } 2557 /** @type {string} */ 2558 var regionCode = this.getRegionCodeForCountryCode(countryCode); 2559 // Metadata cannot be null because the country calling code is valid. 2560 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2561 var metadataForRegion = 2562 this.getMetadataForRegionOrCallingCode_(countryCode, regionCode); 2563 /** @type {string} */ 2564 var formattedExtension = this.maybeGetFormattedExtension_( 2565 number, metadataForRegion, 2566 i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL); 2567 if (internationalPrefixForFormatting.length > 0) { 2568 return internationalPrefixForFormatting + ' ' + countryCode + ' ' + 2569 rawInput + formattedExtension; 2570 } else { 2571 // Invalid region entered as country-calling-from (so no metadata was found 2572 // for it) or the region chosen has multiple international dialling 2573 // prefixes. 2574 return this.prefixNumberWithCountryCallingCode_( 2575 countryCode, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL, 2576 rawInput, formattedExtension); 2577 } 2578}; 2579 2580 2581/** 2582 * Gets the national significant number of a phone number. Note a national 2583 * significant number doesn't contain a national prefix or any formatting. 2584 * 2585 * @param {i18n.phonenumbers.PhoneNumber} number the phone number for which the 2586 * national significant number is needed. 2587 * @return {string} the national significant number of the PhoneNumber object 2588 * passed in. 2589 */ 2590i18n.phonenumbers.PhoneNumberUtil.prototype.getNationalSignificantNumber = 2591 function(number) { 2592 2593 if (!number.hasNationalNumber()) { 2594 return ''; 2595 } 2596 /** @type {string} */ 2597 var nationalNumber = '' + number.getNationalNumber(); 2598 // If leading zero(s) have been set, we prefix this now. Note that a single 2599 // leading zero is not the same as a national prefix; leading zeros should be 2600 // dialled no matter whether you are dialling from within or outside the 2601 // country, national prefixes are added when formatting nationally if 2602 // applicable. 2603 if (number.hasItalianLeadingZero() && number.getItalianLeadingZero() && 2604 number.getNumberOfLeadingZerosOrDefault() > 0) { 2605 return Array(number.getNumberOfLeadingZerosOrDefault() + 1).join('0') + 2606 nationalNumber; 2607 } 2608 return nationalNumber; 2609}; 2610 2611 2612/** 2613 * A helper function that is used by format and formatByPattern. 2614 * 2615 * @param {number} countryCallingCode the country calling code. 2616 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the 2617 * phone number should be formatted into. 2618 * @param {string} formattedNationalNumber 2619 * @param {string} formattedExtension 2620 * @return {string} the formatted phone number. 2621 * @private 2622 */ 2623i18n.phonenumbers.PhoneNumberUtil.prototype. 2624 prefixNumberWithCountryCallingCode_ = function(countryCallingCode, 2625 numberFormat, 2626 formattedNationalNumber, 2627 formattedExtension) { 2628 2629 switch (numberFormat) { 2630 case i18n.phonenumbers.PhoneNumberFormat.E164: 2631 return i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN + countryCallingCode + 2632 formattedNationalNumber + formattedExtension; 2633 case i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL: 2634 return i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN + countryCallingCode + 2635 ' ' + formattedNationalNumber + formattedExtension; 2636 case i18n.phonenumbers.PhoneNumberFormat.RFC3966: 2637 return i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_ + 2638 i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN + countryCallingCode + 2639 '-' + formattedNationalNumber + formattedExtension; 2640 case i18n.phonenumbers.PhoneNumberFormat.NATIONAL: 2641 default: 2642 return formattedNationalNumber + formattedExtension; 2643 } 2644}; 2645 2646 2647/** 2648 * Note in some regions, the national number can be written in two completely 2649 * different ways depending on whether it forms part of the NATIONAL format or 2650 * INTERNATIONAL format. The numberFormat parameter here is used to specify 2651 * which format to use for those cases. If a carrierCode is specified, this will 2652 * be inserted into the formatted string to replace $CC. 2653 * 2654 * @param {string} number a string of characters representing a phone number. 2655 * @param {i18n.phonenumbers.PhoneMetadata} metadata the metadata for the 2656 * region that we think this number is from. 2657 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the 2658 * phone number should be formatted into. 2659 * @param {string=} opt_carrierCode 2660 * @return {string} the formatted phone number. 2661 * @private 2662 */ 2663i18n.phonenumbers.PhoneNumberUtil.prototype.formatNsn_ = 2664 function(number, metadata, numberFormat, opt_carrierCode) { 2665 2666 /** @type {Array.<i18n.phonenumbers.NumberFormat>} */ 2667 var intlNumberFormats = metadata.intlNumberFormatArray(); 2668 // When the intlNumberFormats exists, we use that to format national number 2669 // for the INTERNATIONAL format instead of using the numberDesc.numberFormats. 2670 /** @type {Array.<i18n.phonenumbers.NumberFormat>} */ 2671 var availableFormats = 2672 (intlNumberFormats.length == 0 || 2673 numberFormat == i18n.phonenumbers.PhoneNumberFormat.NATIONAL) ? 2674 metadata.numberFormatArray() : metadata.intlNumberFormatArray(); 2675 /** @type {i18n.phonenumbers.NumberFormat} */ 2676 var formattingPattern = this.chooseFormattingPatternForNumber_( 2677 availableFormats, number); 2678 return (formattingPattern == null) ? 2679 number : 2680 this.formatNsnUsingPattern_(number, formattingPattern, 2681 numberFormat, opt_carrierCode); 2682}; 2683 2684 2685/** 2686 * @param {Array.<i18n.phonenumbers.NumberFormat>} availableFormats the 2687 * available formats the phone number could be formatted into. 2688 * @param {string} nationalNumber a string of characters representing a phone 2689 * number. 2690 * @return {i18n.phonenumbers.NumberFormat} 2691 * @private 2692 */ 2693i18n.phonenumbers.PhoneNumberUtil.prototype.chooseFormattingPatternForNumber_ = 2694 function(availableFormats, nationalNumber) { 2695 2696 /** @type {i18n.phonenumbers.NumberFormat} */ 2697 var numFormat; 2698 /** @type {number} */ 2699 var l = availableFormats.length; 2700 for (var i = 0; i < l; ++i) { 2701 numFormat = availableFormats[i]; 2702 /** @type {number} */ 2703 var size = numFormat.leadingDigitsPatternCount(); 2704 if (size == 0 || 2705 // We always use the last leading_digits_pattern, as it is the most 2706 // detailed. 2707 nationalNumber 2708 .search(numFormat.getLeadingDigitsPattern(size - 1)) == 0) { 2709 /** @type {!RegExp} */ 2710 var patternToMatch = new RegExp(numFormat.getPattern()); 2711 if (i18n.phonenumbers.PhoneNumberUtil.matchesEntirely(patternToMatch, 2712 nationalNumber)) { 2713 return numFormat; 2714 } 2715 } 2716 } 2717 return null; 2718}; 2719 2720 2721/** 2722 * Note that carrierCode is optional - if null or an empty string, no carrier 2723 * code replacement will take place. 2724 * 2725 * @param {string} nationalNumber a string of characters representing a phone 2726 * number. 2727 * @param {i18n.phonenumbers.NumberFormat} formattingPattern the formatting rule 2728 * the phone number should be formatted into. 2729 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the 2730 * phone number should be formatted into. 2731 * @param {string=} opt_carrierCode 2732 * @return {string} the formatted phone number. 2733 * @private 2734 */ 2735i18n.phonenumbers.PhoneNumberUtil.prototype.formatNsnUsingPattern_ = 2736 function(nationalNumber, formattingPattern, numberFormat, opt_carrierCode) { 2737 2738 /** @type {string} */ 2739 var numberFormatRule = formattingPattern.getFormatOrDefault(); 2740 /** @type {!RegExp} */ 2741 var patternToMatch = new RegExp(formattingPattern.getPattern()); 2742 /** @type {string} */ 2743 var domesticCarrierCodeFormattingRule = 2744 formattingPattern.getDomesticCarrierCodeFormattingRuleOrDefault(); 2745 /** @type {string} */ 2746 var formattedNationalNumber = ''; 2747 if (numberFormat == i18n.phonenumbers.PhoneNumberFormat.NATIONAL && 2748 opt_carrierCode != null && opt_carrierCode.length > 0 && 2749 domesticCarrierCodeFormattingRule.length > 0) { 2750 // Replace the $CC in the formatting rule with the desired carrier code. 2751 /** @type {string} */ 2752 var carrierCodeFormattingRule = domesticCarrierCodeFormattingRule 2753 .replace(i18n.phonenumbers.PhoneNumberUtil.CC_PATTERN_, 2754 opt_carrierCode); 2755 // Now replace the $FG in the formatting rule with the first group and 2756 // the carrier code combined in the appropriate way. 2757 numberFormatRule = numberFormatRule.replace( 2758 i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_PATTERN_, 2759 carrierCodeFormattingRule); 2760 formattedNationalNumber = 2761 nationalNumber.replace(patternToMatch, numberFormatRule); 2762 } else { 2763 // Use the national prefix formatting rule instead. 2764 /** @type {string} */ 2765 var nationalPrefixFormattingRule = 2766 formattingPattern.getNationalPrefixFormattingRuleOrDefault(); 2767 if (numberFormat == i18n.phonenumbers.PhoneNumberFormat.NATIONAL && 2768 nationalPrefixFormattingRule != null && 2769 nationalPrefixFormattingRule.length > 0) { 2770 formattedNationalNumber = nationalNumber.replace(patternToMatch, 2771 numberFormatRule.replace( 2772 i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_PATTERN_, 2773 nationalPrefixFormattingRule)); 2774 } else { 2775 formattedNationalNumber = 2776 nationalNumber.replace(patternToMatch, numberFormatRule); 2777 } 2778 } 2779 if (numberFormat == i18n.phonenumbers.PhoneNumberFormat.RFC3966) { 2780 // Strip any leading punctuation. 2781 formattedNationalNumber = formattedNationalNumber.replace( 2782 new RegExp('^' + i18n.phonenumbers.PhoneNumberUtil.SEPARATOR_PATTERN_), 2783 ''); 2784 // Replace the rest with a dash between each number group. 2785 formattedNationalNumber = formattedNationalNumber.replace( 2786 new RegExp(i18n.phonenumbers.PhoneNumberUtil.SEPARATOR_PATTERN_, 'g'), 2787 '-'); 2788 } 2789 return formattedNationalNumber; 2790}; 2791 2792 2793/** 2794 * Gets a valid number for the specified region. 2795 * 2796 * @param {string} regionCode the region for which an example number is needed. 2797 * @return {i18n.phonenumbers.PhoneNumber} a valid fixed-line number for the 2798 * specified region. Returns null when the metadata does not contain such 2799 * information, or the region 001 is passed in. For 001 (representing non- 2800 * geographical numbers), call {@link #getExampleNumberForNonGeoEntity} 2801 * instead. 2802 */ 2803i18n.phonenumbers.PhoneNumberUtil.prototype.getExampleNumber = 2804 function(regionCode) { 2805 2806 return this.getExampleNumberForType(regionCode, 2807 i18n.phonenumbers.PhoneNumberType.FIXED_LINE); 2808}; 2809 2810 2811/** 2812 * Gets a valid number for the specified region and number type. 2813 * 2814 * @param {string} regionCode the region for which an example number is needed. 2815 * @param {i18n.phonenumbers.PhoneNumberType} type the type of number that is 2816 * needed. 2817 * @return {i18n.phonenumbers.PhoneNumber} a valid number for the specified 2818 * region and type. Returns null when the metadata does not contain such 2819 * information or if an invalid region or region 001 was entered. 2820 * For 001 (representing non-geographical numbers), call 2821 * {@link #getExampleNumberForNonGeoEntity} instead. 2822 */ 2823i18n.phonenumbers.PhoneNumberUtil.prototype.getExampleNumberForType = 2824 function(regionCode, type) { 2825 2826 // Check the region code is valid. 2827 if (!this.isValidRegionCode_(regionCode)) { 2828 return null; 2829 } 2830 /** @type {i18n.phonenumbers.PhoneNumberDesc} */ 2831 var desc = i18n.phonenumbers.PhoneNumberUtil.getNumberDescByType_( 2832 this.getMetadataForRegion(regionCode), type); 2833 try { 2834 if (desc.hasExampleNumber()) { 2835 return this.parse(desc.getExampleNumber(), regionCode); 2836 } 2837 } catch (e) { 2838 } 2839 return null; 2840}; 2841 2842 2843/** 2844 * Gets a valid number for the specified country calling code for a 2845 * non-geographical entity. 2846 * 2847 * @param {number} countryCallingCode the country calling code for a 2848 * non-geographical entity. 2849 * @return {i18n.phonenumbers.PhoneNumber} a valid number for the 2850 * non-geographical entity. Returns null when the metadata does not contain 2851 * such information, or the country calling code passed in does not belong 2852 * to a non-geographical entity. 2853 */ 2854i18n.phonenumbers.PhoneNumberUtil.prototype.getExampleNumberForNonGeoEntity = 2855 function(countryCallingCode) { 2856 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2857 var metadata = 2858 this.getMetadataForNonGeographicalRegion(countryCallingCode); 2859 if (metadata != null) { 2860 /** @type {!i18n.phonenumbers.PhoneNumberDesc|undefined} */ 2861 var numberTypeWithExampleNumber = [ 2862 metadata.getMobile(), metadata.getTollFree(), metadata.getSharedCost(), 2863 metadata.getVoip(), metadata.getVoicemail(), metadata.getUan(), 2864 metadata.getPremiumRate() 2865 ].find(function(desc, index) { 2866 return desc.hasExampleNumber(); 2867 }); 2868 if (numberTypeWithExampleNumber !== undefined) { 2869 try { 2870 return this.parse('+' + countryCallingCode + 2871 numberTypeWithExampleNumber.getExampleNumber(), 'ZZ'); 2872 } catch (e) { 2873 } 2874 } 2875 } 2876 return null; 2877}; 2878 2879 2880/** 2881 * Gets the formatted extension of a phone number, if the phone number had an 2882 * extension specified. If not, it returns an empty string. 2883 * 2884 * @param {i18n.phonenumbers.PhoneNumber} number the PhoneNumber that might have 2885 * an extension. 2886 * @param {i18n.phonenumbers.PhoneMetadata} metadata the metadata for the 2887 * region that we think this number is from. 2888 * @param {i18n.phonenumbers.PhoneNumberFormat} numberFormat the format the 2889 * phone number should be formatted into. 2890 * @return {string} the formatted extension if any. 2891 * @private 2892 */ 2893i18n.phonenumbers.PhoneNumberUtil.prototype.maybeGetFormattedExtension_ = 2894 function(number, metadata, numberFormat) { 2895 2896 if (!number.hasExtension() || number.getExtension().length == 0) { 2897 return ''; 2898 } else { 2899 if (numberFormat == i18n.phonenumbers.PhoneNumberFormat.RFC3966) { 2900 return i18n.phonenumbers.PhoneNumberUtil.RFC3966_EXTN_PREFIX_ + 2901 number.getExtension(); 2902 } else { 2903 if (metadata.hasPreferredExtnPrefix()) { 2904 return metadata.getPreferredExtnPrefix() + 2905 number.getExtensionOrDefault(); 2906 } else { 2907 return i18n.phonenumbers.PhoneNumberUtil.DEFAULT_EXTN_PREFIX_ + 2908 number.getExtensionOrDefault(); 2909 } 2910 } 2911 } 2912}; 2913 2914 2915/** 2916 * @param {i18n.phonenumbers.PhoneMetadata} metadata 2917 * @param {i18n.phonenumbers.PhoneNumberType} type 2918 * @return {i18n.phonenumbers.PhoneNumberDesc} 2919 * @private 2920 */ 2921i18n.phonenumbers.PhoneNumberUtil.getNumberDescByType_ = 2922 function(metadata, type) { 2923 2924 switch (type) { 2925 case i18n.phonenumbers.PhoneNumberType.PREMIUM_RATE: 2926 return metadata.getPremiumRate(); 2927 case i18n.phonenumbers.PhoneNumberType.TOLL_FREE: 2928 return metadata.getTollFree(); 2929 case i18n.phonenumbers.PhoneNumberType.MOBILE: 2930 return metadata.getMobile(); 2931 case i18n.phonenumbers.PhoneNumberType.FIXED_LINE: 2932 case i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE: 2933 return metadata.getFixedLine(); 2934 case i18n.phonenumbers.PhoneNumberType.SHARED_COST: 2935 return metadata.getSharedCost(); 2936 case i18n.phonenumbers.PhoneNumberType.VOIP: 2937 return metadata.getVoip(); 2938 case i18n.phonenumbers.PhoneNumberType.PERSONAL_NUMBER: 2939 return metadata.getPersonalNumber(); 2940 case i18n.phonenumbers.PhoneNumberType.PAGER: 2941 return metadata.getPager(); 2942 case i18n.phonenumbers.PhoneNumberType.UAN: 2943 return metadata.getUan(); 2944 case i18n.phonenumbers.PhoneNumberType.VOICEMAIL: 2945 return metadata.getVoicemail(); 2946 default: 2947 return metadata.getGeneralDesc(); 2948 } 2949}; 2950 2951 2952/** 2953 * Gets the type of a valid phone number. 2954 * 2955 * @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want 2956 * to know the type. 2957 * @return {i18n.phonenumbers.PhoneNumberType} the type of the phone number, or 2958 * UNKNOWN if it is invalid. 2959 */ 2960i18n.phonenumbers.PhoneNumberUtil.prototype.getNumberType = 2961 function(number) { 2962 2963 /** @type {?string} */ 2964 var regionCode = this.getRegionCodeForNumber(number); 2965 /** @type {i18n.phonenumbers.PhoneMetadata} */ 2966 var metadata = this.getMetadataForRegionOrCallingCode_( 2967 number.getCountryCodeOrDefault(), regionCode); 2968 if (metadata == null) { 2969 return i18n.phonenumbers.PhoneNumberType.UNKNOWN; 2970 } 2971 /** @type {string} */ 2972 var nationalSignificantNumber = this.getNationalSignificantNumber(number); 2973 return this.getNumberTypeHelper_(nationalSignificantNumber, metadata); 2974}; 2975 2976 2977/** 2978 * @param {string} nationalNumber 2979 * @param {i18n.phonenumbers.PhoneMetadata} metadata 2980 * @return {i18n.phonenumbers.PhoneNumberType} 2981 * @private 2982 */ 2983i18n.phonenumbers.PhoneNumberUtil.prototype.getNumberTypeHelper_ = 2984 function(nationalNumber, metadata) { 2985 2986 if (!this.isNumberMatchingDesc_(nationalNumber, metadata.getGeneralDesc())) { 2987 return i18n.phonenumbers.PhoneNumberType.UNKNOWN; 2988 } 2989 2990 if (this.isNumberMatchingDesc_(nationalNumber, metadata.getPremiumRate())) { 2991 return i18n.phonenumbers.PhoneNumberType.PREMIUM_RATE; 2992 } 2993 if (this.isNumberMatchingDesc_(nationalNumber, metadata.getTollFree())) { 2994 return i18n.phonenumbers.PhoneNumberType.TOLL_FREE; 2995 } 2996 if (this.isNumberMatchingDesc_(nationalNumber, metadata.getSharedCost())) { 2997 return i18n.phonenumbers.PhoneNumberType.SHARED_COST; 2998 } 2999 if (this.isNumberMatchingDesc_(nationalNumber, metadata.getVoip())) { 3000 return i18n.phonenumbers.PhoneNumberType.VOIP; 3001 } 3002 if (this.isNumberMatchingDesc_(nationalNumber, 3003 metadata.getPersonalNumber())) { 3004 return i18n.phonenumbers.PhoneNumberType.PERSONAL_NUMBER; 3005 } 3006 if (this.isNumberMatchingDesc_(nationalNumber, metadata.getPager())) { 3007 return i18n.phonenumbers.PhoneNumberType.PAGER; 3008 } 3009 if (this.isNumberMatchingDesc_(nationalNumber, metadata.getUan())) { 3010 return i18n.phonenumbers.PhoneNumberType.UAN; 3011 } 3012 if (this.isNumberMatchingDesc_(nationalNumber, metadata.getVoicemail())) { 3013 return i18n.phonenumbers.PhoneNumberType.VOICEMAIL; 3014 } 3015 3016 /** @type {boolean} */ 3017 var isFixedLine = this.isNumberMatchingDesc_(nationalNumber, metadata 3018 .getFixedLine()); 3019 if (isFixedLine) { 3020 if (metadata.getSameMobileAndFixedLinePattern()) { 3021 return i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE; 3022 } else if (this.isNumberMatchingDesc_(nationalNumber, 3023 metadata.getMobile())) { 3024 return i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE; 3025 } 3026 return i18n.phonenumbers.PhoneNumberType.FIXED_LINE; 3027 } 3028 // Otherwise, test to see if the number is mobile. Only do this if certain 3029 // that the patterns for mobile and fixed line aren't the same. 3030 if (!metadata.getSameMobileAndFixedLinePattern() && 3031 this.isNumberMatchingDesc_(nationalNumber, metadata.getMobile())) { 3032 return i18n.phonenumbers.PhoneNumberType.MOBILE; 3033 } 3034 return i18n.phonenumbers.PhoneNumberType.UNKNOWN; 3035}; 3036 3037 3038/** 3039 * Returns the metadata for the given region code or {@code null} if the region 3040 * code is invalid or unknown. 3041 * 3042 * @param {?string} regionCode 3043 * @return {?i18n.phonenumbers.PhoneMetadata} 3044 */ 3045i18n.phonenumbers.PhoneNumberUtil.prototype.getMetadataForRegion = 3046 function(regionCode) { 3047 3048 if (regionCode == null) { 3049 return null; 3050 } 3051 regionCode = regionCode.toUpperCase(); 3052 /** @type {i18n.phonenumbers.PhoneMetadata} */ 3053 var metadata = this.regionToMetadataMap[regionCode]; 3054 if (metadata == null) { 3055 /** @type {goog.proto2.PbLiteSerializer} */ 3056 var serializer = new goog.proto2.PbLiteSerializer(); 3057 /** @type {Array} */ 3058 var metadataSerialized = 3059 i18n.phonenumbers.metadata.countryToMetadata[regionCode]; 3060 if (metadataSerialized == null) { 3061 return null; 3062 } 3063 metadata = /** @type {i18n.phonenumbers.PhoneMetadata} */ ( 3064 serializer.deserialize(i18n.phonenumbers.PhoneMetadata.getDescriptor(), 3065 metadataSerialized)); 3066 this.regionToMetadataMap[regionCode] = metadata; 3067 } 3068 return metadata; 3069}; 3070 3071 3072/** 3073 * @param {number} countryCallingCode 3074 * @return {?i18n.phonenumbers.PhoneMetadata} 3075 */ 3076i18n.phonenumbers.PhoneNumberUtil.prototype. 3077 getMetadataForNonGeographicalRegion = function(countryCallingCode) { 3078 3079 return this.getMetadataForRegion('' + countryCallingCode); 3080}; 3081 3082 3083/** 3084 * @param {string} nationalNumber 3085 * @param {i18n.phonenumbers.PhoneNumberDesc} numberDesc 3086 * @return {boolean} 3087 * @private 3088 */ 3089i18n.phonenumbers.PhoneNumberUtil.prototype.isNumberMatchingDesc_ = 3090 function(nationalNumber, numberDesc) { 3091 // Check if any possible number lengths are present; if so, we use them to 3092 // avoid checking the validation pattern if they don't match. If they are 3093 // absent, this means they match the general description, which we have 3094 // already checked before a specific number type. 3095 var actualLength = nationalNumber.length; 3096 if (numberDesc.possibleLengthCount() > 0 && 3097 numberDesc.possibleLengthArray().indexOf(actualLength) == -1) { 3098 return false; 3099 } 3100 return i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 3101 numberDesc.getNationalNumberPatternOrDefault(), nationalNumber); 3102}; 3103 3104 3105/** 3106 * Tests whether a phone number matches a valid pattern. Note this doesn't 3107 * verify the number is actually in use, which is impossible to tell by just 3108 * looking at a number itself. 3109 * It only verifies whether the parsed, canonicalised number is valid: not 3110 * whether a particular series of digits entered by the user is diallable from 3111 * the region provided when parsing. For example, the number +41 (0) 78 927 2696 3112 * can be parsed into a number with country code "41" and national significant 3113 * number "789272696". This is valid, while the original string is not 3114 * diallable. 3115 * 3116 * @param {!i18n.phonenumbers.PhoneNumber} number the phone number that we want 3117 * to validate. 3118 * @return {boolean} a boolean that indicates whether the number is of a valid 3119 * pattern. 3120 */ 3121i18n.phonenumbers.PhoneNumberUtil.prototype.isValidNumber = function(number) { 3122 /** @type {?string} */ 3123 var regionCode = this.getRegionCodeForNumber(number); 3124 return this.isValidNumberForRegion(number, regionCode); 3125}; 3126 3127 3128/** 3129 * Tests whether a phone number is valid for a certain region. Note this doesn't 3130 * verify the number is actually in use, which is impossible to tell by just 3131 * looking at a number itself. If the country calling code is not the same as 3132 * the country calling code for the region, this immediately exits with false. 3133 * After this, the specific number pattern rules for the region are examined. 3134 * This is useful for determining for example whether a particular number is 3135 * valid for Canada, rather than just a valid NANPA number. 3136 * Warning: In most cases, you want to use {@link #isValidNumber} instead. For 3137 * example, this method will mark numbers from British Crown dependencies such 3138 * as the Isle of Man as invalid for the region "GB" (United Kingdom), since it 3139 * has its own region code, "IM", which may be undesirable. 3140 * 3141 * @param {!i18n.phonenumbers.PhoneNumber} number the phone number that we want 3142 * to validate. 3143 * @param {?string} regionCode the region that we want to validate the phone 3144 * number for. 3145 * @return {boolean} a boolean that indicates whether the number is of a valid 3146 * pattern. 3147 */ 3148i18n.phonenumbers.PhoneNumberUtil.prototype.isValidNumberForRegion = 3149 function(number, regionCode) { 3150 3151 /** @type {number} */ 3152 var countryCode = number.getCountryCodeOrDefault(); 3153 /** @type {i18n.phonenumbers.PhoneMetadata} */ 3154 var metadata = 3155 this.getMetadataForRegionOrCallingCode_(countryCode, regionCode); 3156 if (metadata == null || 3157 (i18n.phonenumbers.PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY != 3158 regionCode && 3159 countryCode != this.getCountryCodeForValidRegion_(regionCode))) { 3160 // Either the region code was invalid, or the country calling code for this 3161 // number does not match that of the region code. 3162 return false; 3163 } 3164 /** @type {string} */ 3165 var nationalSignificantNumber = this.getNationalSignificantNumber(number); 3166 3167 return this.getNumberTypeHelper_(nationalSignificantNumber, metadata) != 3168 i18n.phonenumbers.PhoneNumberType.UNKNOWN; 3169}; 3170 3171 3172/** 3173 * Returns the region where a phone number is from. This could be used for 3174 * geocoding at the region level. Only guarantees correct results for valid, 3175 * full numbers (not short-codes, or invalid numbers). 3176 * 3177 * @param {?i18n.phonenumbers.PhoneNumber} number the phone number whose origin 3178 * we want to know. 3179 * @return {?string} the region where the phone number is from, or null 3180 * if no region matches this calling code. 3181 */ 3182i18n.phonenumbers.PhoneNumberUtil.prototype.getRegionCodeForNumber = 3183 function(number) { 3184 3185 if (number == null) { 3186 return null; 3187 } 3188 /** @type {number} */ 3189 var countryCode = number.getCountryCodeOrDefault(); 3190 /** @type {Array.<string>} */ 3191 var regions = 3192 i18n.phonenumbers.metadata.countryCodeToRegionCodeMap[countryCode]; 3193 if (regions == null) { 3194 return null; 3195 } 3196 if (regions.length == 1) { 3197 return regions[0]; 3198 } else { 3199 return this.getRegionCodeForNumberFromRegionList_(number, regions); 3200 } 3201}; 3202 3203 3204/** 3205 * @param {!i18n.phonenumbers.PhoneNumber} number 3206 * @param {Array.<string>} regionCodes 3207 * @return {?string} 3208 * @private 3209 */ 3210i18n.phonenumbers.PhoneNumberUtil.prototype. 3211 getRegionCodeForNumberFromRegionList_ = function(number, regionCodes) { 3212 3213 /** @type {string} */ 3214 var nationalNumber = this.getNationalSignificantNumber(number); 3215 /** @type {string} */ 3216 var regionCode; 3217 /** @type {number} */ 3218 var regionCodesLength = regionCodes.length; 3219 for (var i = 0; i < regionCodesLength; i++) { 3220 regionCode = regionCodes[i]; 3221 // If leadingDigits is present, use this. Otherwise, do full validation. 3222 // Metadata cannot be null because the region codes come from the country 3223 // calling code map. 3224 /** @type {i18n.phonenumbers.PhoneMetadata} */ 3225 var metadata = this.getMetadataForRegion(regionCode); 3226 if (metadata.hasLeadingDigits()) { 3227 if (nationalNumber.search(metadata.getLeadingDigits()) == 0) { 3228 return regionCode; 3229 } 3230 } else if (this.getNumberTypeHelper_(nationalNumber, metadata) != 3231 i18n.phonenumbers.PhoneNumberType.UNKNOWN) { 3232 return regionCode; 3233 } 3234 } 3235 return null; 3236}; 3237 3238 3239/** 3240 * Returns the region code that matches the specific country calling code. In 3241 * the case of no region code being found, ZZ will be returned. In the case of 3242 * multiple regions, the one designated in the metadata as the 'main' region for 3243 * this calling code will be returned. 3244 * 3245 * @param {number} countryCallingCode the country calling code. 3246 * @return {string} 3247 */ 3248i18n.phonenumbers.PhoneNumberUtil.prototype.getRegionCodeForCountryCode = 3249 function(countryCallingCode) { 3250 3251 /** @type {Array.<string>} */ 3252 var regionCodes = 3253 i18n.phonenumbers.metadata.countryCodeToRegionCodeMap[countryCallingCode]; 3254 return regionCodes == null ? 3255 i18n.phonenumbers.PhoneNumberUtil.UNKNOWN_REGION_ : regionCodes[0]; 3256}; 3257 3258 3259/** 3260 * Returns a list with the region codes that match the specific country calling 3261 * code. For non-geographical country calling codes, the region code 001 is 3262 * returned. Also, in the case of no region code being found, an empty list is 3263 * returned. 3264 * 3265 * @param {number} countryCallingCode the country calling code. 3266 * @return {!Array.<string>} 3267 */ 3268i18n.phonenumbers.PhoneNumberUtil.prototype.getRegionCodesForCountryCode = 3269 function(countryCallingCode) { 3270 3271 /** @type {Array.<string>} */ 3272 var regionCodes = 3273 i18n.phonenumbers.metadata.countryCodeToRegionCodeMap[countryCallingCode]; 3274 return regionCodes == null ? [] : regionCodes; 3275}; 3276 3277 3278/** 3279 * Returns the country calling code for a specific region. For example, this 3280 * would be 1 for the United States, and 64 for New Zealand. 3281 * 3282 * @param {?string} regionCode the region that we want to get the country 3283 * calling code for. 3284 * @return {number} the country calling code for the region denoted by 3285 * regionCode. 3286 */ 3287i18n.phonenumbers.PhoneNumberUtil.prototype.getCountryCodeForRegion = 3288 function(regionCode) { 3289 3290 if (!this.isValidRegionCode_(regionCode)) { 3291 return 0; 3292 } 3293 return this.getCountryCodeForValidRegion_(regionCode); 3294}; 3295 3296 3297/** 3298 * Returns the country calling code for a specific region. For example, this 3299 * would be 1 for the United States, and 64 for New Zealand. Assumes the region 3300 * is already valid. 3301 * 3302 * @param {?string} regionCode the region that we want to get the country 3303 * calling code for. 3304 * @return {number} the country calling code for the region denoted by 3305 * regionCode. 3306 * @throws {Error} if the region is invalid 3307 * @private 3308 */ 3309i18n.phonenumbers.PhoneNumberUtil.prototype.getCountryCodeForValidRegion_ = 3310 function(regionCode) { 3311 3312 /** @type {i18n.phonenumbers.PhoneMetadata} */ 3313 var metadata = this.getMetadataForRegion(regionCode); 3314 if (metadata == null) { 3315 throw new Error('Invalid region code: ' + regionCode); 3316 } 3317 return metadata.getCountryCodeOrDefault(); 3318}; 3319 3320 3321/** 3322 * Returns the national dialling prefix for a specific region. For example, this 3323 * would be 1 for the United States, and 0 for New Zealand. Set stripNonDigits 3324 * to true to strip symbols like '~' (which indicates a wait for a dialling 3325 * tone) from the prefix returned. If no national prefix is present, we return 3326 * null. 3327 * 3328 * <p>Warning: Do not use this method for do-your-own formatting - for some 3329 * regions, the national dialling prefix is used only for certain types of 3330 * numbers. Use the library's formatting functions to prefix the national prefix 3331 * when required. 3332 * 3333 * @param {?string} regionCode the region that we want to get the dialling 3334 * prefix for. 3335 * @param {boolean} stripNonDigits true to strip non-digits from the national 3336 * dialling prefix. 3337 * @return {?string} the dialling prefix for the region denoted by 3338 * regionCode. 3339 */ 3340i18n.phonenumbers.PhoneNumberUtil.prototype.getNddPrefixForRegion = function( 3341 regionCode, stripNonDigits) { 3342 /** @type {i18n.phonenumbers.PhoneMetadata} */ 3343 var metadata = this.getMetadataForRegion(regionCode); 3344 if (metadata == null) { 3345 return null; 3346 } 3347 /** @type {string} */ 3348 var nationalPrefix = metadata.getNationalPrefixOrDefault(); 3349 // If no national prefix was found, we return null. 3350 if (nationalPrefix.length == 0) { 3351 return null; 3352 } 3353 if (stripNonDigits) { 3354 // Note: if any other non-numeric symbols are ever used in national 3355 // prefixes, these would have to be removed here as well. 3356 nationalPrefix = nationalPrefix.replace('~', ''); 3357 } 3358 return nationalPrefix; 3359}; 3360 3361 3362/** 3363 * Checks if this is a region under the North American Numbering Plan 3364 * Administration (NANPA). 3365 * 3366 * @param {?string} regionCode the CLDR two-letter region code. 3367 * @return {boolean} true if regionCode is one of the regions under NANPA. 3368 */ 3369i18n.phonenumbers.PhoneNumberUtil.prototype.isNANPACountry = function( 3370 regionCode) { 3371 return regionCode != null && 3372 i18n.phonenumbers.metadata 3373 .countryCodeToRegionCodeMap[i18n.phonenumbers.PhoneNumberUtil 3374 .NANPA_COUNTRY_CODE_] 3375 .includes(regionCode.toUpperCase()); 3376}; 3377 3378 3379/** 3380 * Checks if the number is a valid vanity (alpha) number such as 800 MICROSOFT. 3381 * A valid vanity number will start with at least 3 digits and will have three 3382 * or more alpha characters. This does not do region-specific checks - to work 3383 * out if this number is actually valid for a region, it should be parsed and 3384 * methods such as {@link #isPossibleNumberWithReason} and 3385 * {@link #isValidNumber} should be used. 3386 * 3387 * @param {string} number the number that needs to be checked. 3388 * @return {boolean} true if the number is a valid vanity number. 3389 */ 3390i18n.phonenumbers.PhoneNumberUtil.prototype.isAlphaNumber = function(number) { 3391 if (!i18n.phonenumbers.PhoneNumberUtil.isViablePhoneNumber(number)) { 3392 // Number is too short, or doesn't match the basic phone number pattern. 3393 return false; 3394 } 3395 /** @type {!goog.string.StringBuffer} */ 3396 var strippedNumber = new goog.string.StringBuffer(number); 3397 this.maybeStripExtension(strippedNumber); 3398 return i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 3399 i18n.phonenumbers.PhoneNumberUtil.VALID_ALPHA_PHONE_PATTERN_, 3400 strippedNumber.toString()); 3401}; 3402 3403 3404/** 3405 * Convenience wrapper around {@link #isPossibleNumberWithReason}. Instead of 3406 * returning the reason for failure, this method returns true if the number is 3407 * either a possible fully-qualified number (containing the area code and 3408 * country code), or if the number could be a possible local number (with a 3409 * country code, but missing an area code). Local numbers are considered 3410 * possible if they could be possibly dialled in this format: if the area code 3411 * is needed for a call to connect, the number is not considered possible 3412 * without it. 3413 * 3414 * @param {i18n.phonenumbers.PhoneNumber} number the number that needs to be 3415 * checked 3416 * @return {boolean} true if the number is possible 3417 */ 3418i18n.phonenumbers.PhoneNumberUtil.prototype.isPossibleNumber = 3419 function(number) { 3420 /** @type {!i18n.phonenumbers.PhoneNumberUtil.ValidationResult} */ 3421 var result = this.isPossibleNumberWithReason(number); 3422 return result == 3423 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE || 3424 result == 3425 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE_LOCAL_ONLY; 3426}; 3427 3428 3429/** 3430 * Convenience wrapper around {@link #isPossibleNumberForTypeWithReason}. 3431 * Instead of returning the reason for failure, this method returns true if the 3432 * number is either a possible fully-qualified number (containing the area code 3433 * and country code), or if the number could be a possible local number (with a 3434 * country code, but missing an area code). Local numbers are considered 3435 * possible if they could be possibly dialled in this format: if the area code 3436 * is needed for a call to connect, the number is not considered possible 3437 * without it. 3438 * 3439 * @param {i18n.phonenumbers.PhoneNumber} number the number that needs to be 3440 * checked 3441 * @param {i18n.phonenumbers.PhoneNumberType} type the type we are interested in 3442 * @return {boolean} true if the number is possible for this particular type 3443 */ 3444i18n.phonenumbers.PhoneNumberUtil.prototype.isPossibleNumberForType = 3445 function(number, type) { 3446 /** @type {!i18n.phonenumbers.PhoneNumberUtil.ValidationResult} */ 3447 var result = this.isPossibleNumberForTypeWithReason(number, type); 3448 return result == 3449 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE || 3450 result == 3451 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE_LOCAL_ONLY; 3452}; 3453 3454 3455/** 3456 * Helper method to check a number against possible lengths for this region, 3457 * based on the metadata being passed in, and determine whether it matches, or 3458 * is too short or too long. 3459 * 3460 * @param {string} number 3461 * @param {i18n.phonenumbers.PhoneMetadata} metadata 3462 * @return {i18n.phonenumbers.PhoneNumberUtil.ValidationResult} 3463 * @private 3464 */ 3465i18n.phonenumbers.PhoneNumberUtil.prototype.testNumberLength_ = 3466 function(number, metadata) { 3467 return this.testNumberLengthForType_( 3468 number, metadata, i18n.phonenumbers.PhoneNumberType.UNKNOWN); 3469}; 3470 3471 3472/** 3473 * Helper method to check a number against a particular pattern and determine 3474 * whether it matches, or is too short or too long. 3475 * 3476 * @param {string} number 3477 * @param {i18n.phonenumbers.PhoneMetadata} metadata 3478 * @param {i18n.phonenumbers.PhoneNumberType} type 3479 * @return {i18n.phonenumbers.PhoneNumberUtil.ValidationResult} 3480 * @private 3481 */ 3482i18n.phonenumbers.PhoneNumberUtil.prototype.testNumberLengthForType_ = 3483 function(number, metadata, type) { 3484 var descForType = 3485 i18n.phonenumbers.PhoneNumberUtil.getNumberDescByType_(metadata, type); 3486 // There should always be "possibleLengths" set for every element. This is 3487 // declared in the XML schema which is verified by 3488 // PhoneNumberMetadataSchemaTest. 3489 // For size efficiency, where a sub-description (e.g. fixed-line) has the 3490 // same possibleLengths as the parent, this is missing, so we fall back to 3491 // the general desc (where no numbers of the type exist at all, there is one 3492 // possible length (-1) which is guaranteed not to match the length of any 3493 // real phone number). 3494 var possibleLengths = descForType.possibleLengthCount() == 0 ? 3495 metadata.getGeneralDesc().possibleLengthArray() : 3496 descForType.possibleLengthArray(); 3497 var localLengths = descForType.possibleLengthLocalOnlyArray(); 3498 3499 if (type == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE) { 3500 if (!i18n.phonenumbers.PhoneNumberUtil.descHasPossibleNumberData_( 3501 i18n.phonenumbers.PhoneNumberUtil.getNumberDescByType_( 3502 metadata, i18n.phonenumbers.PhoneNumberType.FIXED_LINE))) { 3503 // The rare case has been encountered where no fixedLine data is 3504 // available (true for some non-geographical entities), so we just check 3505 // mobile. 3506 return this.testNumberLengthForType_( 3507 number, metadata, i18n.phonenumbers.PhoneNumberType.MOBILE); 3508 } else { 3509 var mobileDesc = i18n.phonenumbers.PhoneNumberUtil.getNumberDescByType_( 3510 metadata, i18n.phonenumbers.PhoneNumberType.MOBILE); 3511 if (i18n.phonenumbers.PhoneNumberUtil.descHasPossibleNumberData_( 3512 mobileDesc)) { 3513 // Merge the mobile data in if there was any. "Concat" creates a new 3514 // array, it doesn't edit possibleLengths in place, so we don't need a 3515 // copy. 3516 // Note that when adding the possible lengths from mobile, we have 3517 // to again check they aren't empty since if they are this indicates 3518 // they are the same as the general desc and should be obtained from 3519 // there. 3520 possibleLengths = possibleLengths.concat( 3521 mobileDesc.possibleLengthCount() == 0 ? 3522 metadata.getGeneralDesc().possibleLengthArray() : 3523 mobileDesc.possibleLengthArray()); 3524 // The current list is sorted; we need to merge in the new list and 3525 // re-sort (duplicates are okay). Sorting isn't so expensive because the 3526 // lists are very small. 3527 possibleLengths.sort(); 3528 3529 if (localLengths.length == 0) { 3530 localLengths = mobileDesc.possibleLengthLocalOnlyArray(); 3531 } else { 3532 localLengths = localLengths.concat( 3533 mobileDesc.possibleLengthLocalOnlyArray()); 3534 localLengths.sort(); 3535 } 3536 } 3537 } 3538 } 3539 // If the type is not supported at all (indicated by the possible lengths 3540 // containing -1 at this point) we return invalid length. 3541 if (possibleLengths[0] == -1) { 3542 return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.INVALID_LENGTH; 3543 } 3544 3545 var actualLength = number.length; 3546 // This is safe because there is never an overlap beween the possible lengths 3547 // and the local-only lengths; this is checked at build time. 3548 if (localLengths.indexOf(actualLength) > -1) { 3549 return i18n.phonenumbers.PhoneNumberUtil.ValidationResult 3550 .IS_POSSIBLE_LOCAL_ONLY; 3551 } 3552 var minimumLength = possibleLengths[0]; 3553 if (minimumLength == actualLength) { 3554 return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE; 3555 } else if (minimumLength > actualLength) { 3556 return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT; 3557 } else if (possibleLengths[possibleLengths.length - 1] < actualLength) { 3558 return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_LONG; 3559 } 3560 // We skip the first element since we've already checked it. 3561 return (possibleLengths.indexOf(actualLength, 1) > -1) ? 3562 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE : 3563 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.INVALID_LENGTH; 3564}; 3565 3566 3567/** 3568 * Check whether a phone number is a possible number. It provides a more lenient 3569 * check than {@link #isValidNumber} in the following sense: 3570 * <ol> 3571 * <li>It only checks the length of phone numbers. In particular, it doesn't 3572 * check starting digits of the number. 3573 * <li>It doesn't attempt to figure out the type of the number, but uses general 3574 * rules which applies to all types of phone numbers in a region. Therefore, it 3575 * is much faster than isValidNumber. 3576 * <li>For some numbers (particularly fixed-line), many regions have the concept 3577 * of area code, which together with subscriber number constitute the national 3578 * significant number. It is sometimes okay to dial only the subscriber number 3579 * when dialing in the same area. This function will return 3580 * IS_POSSIBLE_LOCAL_ONLY if the subscriber-number-only version is passed in. On 3581 * the other hand, because isValidNumber validates using information on both 3582 * starting digits (for fixed line numbers, that would most likely be area 3583 * codes) and length (obviously includes the length of area codes for fixed line 3584 * numbers), it will return false for the subscriber-number-only version. 3585 * </ol> 3586 * 3587 * @param {i18n.phonenumbers.PhoneNumber} number the number that needs to be 3588 * checked 3589 * @return {i18n.phonenumbers.PhoneNumberUtil.ValidationResult} a 3590 * ValidationResult object which indicates whether the number is possible 3591 */ 3592i18n.phonenumbers.PhoneNumberUtil.prototype.isPossibleNumberWithReason = 3593 function(number) { 3594 return this.isPossibleNumberForTypeWithReason( 3595 number, i18n.phonenumbers.PhoneNumberType.UNKNOWN); 3596}; 3597 3598 3599/** 3600 * Check whether a phone number is a possible number. It provides a more lenient 3601 * check than {@link #isValidNumber} in the following sense: 3602 * <ol> 3603 * <li>It only checks the length of phone numbers. In particular, it doesn't 3604 * check starting digits of the number. 3605 * <li>For some numbers (particularly fixed-line), many regions have the concept 3606 * of area code, which together with subscriber number constitute the national 3607 * significant number. It is sometimes okay to dial only the subscriber number 3608 * when dialing in the same area. This function will return 3609 * IS_POSSIBLE_LOCAL_ONLY if the subscriber-number-only version is passed in. On 3610 * the other hand, because isValidNumber validates using information on both 3611 * starting digits (for fixed line numbers, that would most likely be area 3612 * codes) and length (obviously includes the length of area codes for fixed line 3613 * numbers), it will return false for the subscriber-number-only version. 3614 * </ol> 3615 * 3616 * @param {i18n.phonenumbers.PhoneNumber} number the number that needs to be 3617 * checked 3618 * @param {i18n.phonenumbers.PhoneNumberType} type the type we are interested in 3619 * @return {i18n.phonenumbers.PhoneNumberUtil.ValidationResult} a 3620 * ValidationResult object which indicates whether the number is possible 3621 */ 3622i18n.phonenumbers.PhoneNumberUtil.prototype.isPossibleNumberForTypeWithReason = 3623 function(number, type) { 3624 3625 /** @type {string} */ 3626 var nationalNumber = this.getNationalSignificantNumber(number); 3627 /** @type {number} */ 3628 var countryCode = number.getCountryCodeOrDefault(); 3629 // Note: For regions that share a country calling code, like NANPA numbers, 3630 // we just use the rules from the default region (US in this case) since the 3631 // getRegionCodeForNumber will not work if the number is possible but not 3632 // valid. There is in fact one country calling code (290) where the possible 3633 // number pattern differs between various regions (Saint Helena and Tristan 3634 // da Cunha), but this is handled by putting all possible lengths for any 3635 // country with this country calling code in the metadata for the default 3636 // region in this case. 3637 if (!this.hasValidCountryCallingCode_(countryCode)) { 3638 return i18n.phonenumbers.PhoneNumberUtil.ValidationResult 3639 .INVALID_COUNTRY_CODE; 3640 } 3641 /** @type {string} */ 3642 var regionCode = this.getRegionCodeForCountryCode(countryCode); 3643 // Metadata cannot be null because the country calling code is valid. 3644 /** @type {i18n.phonenumbers.PhoneMetadata} */ 3645 var metadata = 3646 this.getMetadataForRegionOrCallingCode_(countryCode, regionCode); 3647 return this.testNumberLengthForType_(nationalNumber, metadata, type); 3648}; 3649 3650 3651/** 3652 * Check whether a phone number is a possible number given a number in the form 3653 * of a string, and the region where the number could be dialed from. It 3654 * provides a more lenient check than {@link #isValidNumber}. See 3655 * {@link #isPossibleNumber} for details. 3656 * 3657 * <p>This method first parses the number, then invokes 3658 * {@link #isPossibleNumber} with the resultant PhoneNumber object. 3659 * 3660 * @param {string} number the number that needs to be checked, in the form of a 3661 * string. 3662 * @param {string} regionDialingFrom the region that we are expecting the number 3663 * to be dialed from. 3664 * Note this is different from the region where the number belongs. 3665 * For example, the number +1 650 253 0000 is a number that belongs to US. 3666 * When written in this form, it can be dialed from any region. When it is 3667 * written as 00 1 650 253 0000, it can be dialed from any region which uses 3668 * an international dialling prefix of 00. When it is written as 3669 * 650 253 0000, it can only be dialed from within the US, and when written 3670 * as 253 0000, it can only be dialed from within a smaller area in the US 3671 * (Mountain View, CA, to be more specific). 3672 * @return {boolean} true if the number is possible. 3673 */ 3674i18n.phonenumbers.PhoneNumberUtil.prototype.isPossibleNumberString = 3675 function(number, regionDialingFrom) { 3676 3677 try { 3678 return this.isPossibleNumber(this.parse(number, regionDialingFrom)); 3679 } catch (e) { 3680 return false; 3681 } 3682}; 3683 3684 3685/** 3686 * Attempts to extract a valid number from a phone number that is too long to be 3687 * valid, and resets the PhoneNumber object passed in to that valid version. If 3688 * no valid number could be extracted, the PhoneNumber object passed in will not 3689 * be modified. 3690 * @param {!i18n.phonenumbers.PhoneNumber} number a PhoneNumber object which 3691 * contains a number that is too long to be valid. 3692 * @return {boolean} true if a valid phone number can be successfully extracted. 3693 */ 3694i18n.phonenumbers.PhoneNumberUtil.prototype.truncateTooLongNumber = 3695 function(number) { 3696 3697 if (this.isValidNumber(number)) { 3698 return true; 3699 } 3700 /** @type {i18n.phonenumbers.PhoneNumber} */ 3701 var numberCopy = number.clone(); 3702 /** @type {number} */ 3703 var nationalNumber = number.getNationalNumberOrDefault(); 3704 do { 3705 nationalNumber = Math.floor(nationalNumber / 10); 3706 numberCopy.setNationalNumber(nationalNumber); 3707 if (nationalNumber == 0 || 3708 this.isPossibleNumberWithReason(numberCopy) == 3709 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT) { 3710 return false; 3711 } 3712 } while (!this.isValidNumber(numberCopy)); 3713 number.setNationalNumber(nationalNumber); 3714 return true; 3715}; 3716 3717 3718/** 3719 * Extracts country calling code from fullNumber, returns it and places the 3720 * remaining number in nationalNumber. It assumes that the leading plus sign or 3721 * IDD has already been removed. Returns 0 if fullNumber doesn't start with a 3722 * valid country calling code, and leaves nationalNumber unmodified. 3723 * 3724 * @param {!goog.string.StringBuffer} fullNumber 3725 * @param {!goog.string.StringBuffer} nationalNumber 3726 * @return {number} 3727 */ 3728i18n.phonenumbers.PhoneNumberUtil.prototype.extractCountryCode = 3729 function(fullNumber, nationalNumber) { 3730 3731 /** @type {string} */ 3732 var fullNumberStr = fullNumber.toString(); 3733 if ((fullNumberStr.length == 0) || (fullNumberStr.charAt(0) == '0')) { 3734 // Country codes do not begin with a '0'. 3735 return 0; 3736 } 3737 /** @type {number} */ 3738 var potentialCountryCode; 3739 /** @type {number} */ 3740 var numberLength = fullNumberStr.length; 3741 for (var i = 1; 3742 i <= i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_COUNTRY_CODE_ && 3743 i <= numberLength; ++i) { 3744 potentialCountryCode = parseInt(fullNumberStr.substring(0, i), 10); 3745 if (potentialCountryCode in 3746 i18n.phonenumbers.metadata.countryCodeToRegionCodeMap) { 3747 nationalNumber.append(fullNumberStr.substring(i)); 3748 return potentialCountryCode; 3749 } 3750 } 3751 return 0; 3752}; 3753 3754 3755/** 3756 * Tries to extract a country calling code from a number. This method will 3757 * return zero if no country calling code is considered to be present. Country 3758 * calling codes are extracted in the following ways: 3759 * <ul> 3760 * <li>by stripping the international dialing prefix of the region the person is 3761 * dialing from, if this is present in the number, and looking at the next 3762 * digits 3763 * <li>by stripping the '+' sign if present and then looking at the next digits 3764 * <li>by comparing the start of the number and the country calling code of the 3765 * default region. If the number is not considered possible for the numbering 3766 * plan of the default region initially, but starts with the country calling 3767 * code of this region, validation will be reattempted after stripping this 3768 * country calling code. If this number is considered a possible number, then 3769 * the first digits will be considered the country calling code and removed as 3770 * such. 3771 * </ul> 3772 * 3773 * It will throw a i18n.phonenumbers.Error if the number starts with a '+' but 3774 * the country calling code supplied after this does not match that of any known 3775 * region. 3776 * 3777 * @param {string} number non-normalized telephone number that we wish to 3778 * extract a country calling code from - may begin with '+'. 3779 * @param {i18n.phonenumbers.PhoneMetadata} defaultRegionMetadata metadata 3780 * about the region this number may be from. 3781 * @param {!goog.string.StringBuffer} nationalNumber a string buffer to store 3782 * the national significant number in, in the case that a country calling 3783 * code was extracted. The number is appended to any existing contents. If 3784 * no country calling code was extracted, this will be left unchanged. 3785 * @param {boolean} keepRawInput true if the country_code_source and 3786 * preferred_carrier_code fields of phoneNumber should be populated. 3787 * @param {i18n.phonenumbers.PhoneNumber} phoneNumber the PhoneNumber object 3788 * where the country_code and country_code_source need to be populated. 3789 * Note the country_code is always populated, whereas country_code_source is 3790 * only populated when keepCountryCodeSource is true. 3791 * @return {number} the country calling code extracted or 0 if none could be 3792 * extracted. 3793 * @throws {Error} 3794 */ 3795i18n.phonenumbers.PhoneNumberUtil.prototype.maybeExtractCountryCode = 3796 function(number, defaultRegionMetadata, nationalNumber, 3797 keepRawInput, phoneNumber) { 3798 3799 if (number.length == 0) { 3800 return 0; 3801 } 3802 /** @type {!goog.string.StringBuffer} */ 3803 var fullNumber = new goog.string.StringBuffer(number); 3804 // Set the default prefix to be something that will never match. 3805 /** @type {?string} */ 3806 var possibleCountryIddPrefix; 3807 if (defaultRegionMetadata != null) { 3808 possibleCountryIddPrefix = defaultRegionMetadata.getInternationalPrefix(); 3809 } 3810 if (possibleCountryIddPrefix == null) { 3811 possibleCountryIddPrefix = 'NonMatch'; 3812 } 3813 3814 /** @type {i18n.phonenumbers.PhoneNumber.CountryCodeSource} */ 3815 var countryCodeSource = this.maybeStripInternationalPrefixAndNormalize( 3816 fullNumber, possibleCountryIddPrefix); 3817 if (keepRawInput) { 3818 phoneNumber.setCountryCodeSource(countryCodeSource); 3819 } 3820 if (countryCodeSource != 3821 i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_DEFAULT_COUNTRY) { 3822 if (fullNumber.getLength() <= 3823 i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) { 3824 throw new Error(i18n.phonenumbers.Error.TOO_SHORT_AFTER_IDD); 3825 } 3826 /** @type {number} */ 3827 var potentialCountryCode = this.extractCountryCode(fullNumber, 3828 nationalNumber); 3829 if (potentialCountryCode != 0) { 3830 phoneNumber.setCountryCode(potentialCountryCode); 3831 return potentialCountryCode; 3832 } 3833 3834 // If this fails, they must be using a strange country calling code that we 3835 // don't recognize, or that doesn't exist. 3836 throw new Error(i18n.phonenumbers.Error.INVALID_COUNTRY_CODE); 3837 } else if (defaultRegionMetadata != null) { 3838 // Check to see if the number starts with the country calling code for the 3839 // default region. If so, we remove the country calling code, and do some 3840 // checks on the validity of the number before and after. 3841 /** @type {number} */ 3842 var defaultCountryCode = defaultRegionMetadata.getCountryCodeOrDefault(); 3843 /** @type {string} */ 3844 var defaultCountryCodeString = '' + defaultCountryCode; 3845 /** @type {string} */ 3846 var normalizedNumber = fullNumber.toString(); 3847 if (goog.string.startsWith(normalizedNumber, defaultCountryCodeString)) { 3848 /** @type {!goog.string.StringBuffer} */ 3849 var potentialNationalNumber = new goog.string.StringBuffer( 3850 normalizedNumber.substring(defaultCountryCodeString.length)); 3851 /** @type {i18n.phonenumbers.PhoneNumberDesc} */ 3852 var generalDesc = defaultRegionMetadata.getGeneralDesc(); 3853 /** @type {!RegExp} */ 3854 var validNumberPattern = 3855 new RegExp(generalDesc.getNationalNumberPatternOrDefault()); 3856 // Passing null since we don't need the carrier code. 3857 this.maybeStripNationalPrefixAndCarrierCode( 3858 potentialNationalNumber, defaultRegionMetadata, null); 3859 /** @type {string} */ 3860 var potentialNationalNumberStr = potentialNationalNumber.toString(); 3861 // If the number was not valid before but is valid now, or if it was too 3862 // long before, we consider the number with the country calling code 3863 // stripped to be a better result and keep that instead. 3864 if ((!i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 3865 validNumberPattern, fullNumber.toString()) && 3866 i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 3867 validNumberPattern, potentialNationalNumberStr)) || 3868 this.testNumberLength_( 3869 fullNumber.toString(), defaultRegionMetadata) == 3870 i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_LONG) { 3871 nationalNumber.append(potentialNationalNumberStr); 3872 if (keepRawInput) { 3873 phoneNumber.setCountryCodeSource( 3874 i18n.phonenumbers.PhoneNumber.CountryCodeSource 3875 .FROM_NUMBER_WITHOUT_PLUS_SIGN); 3876 } 3877 phoneNumber.setCountryCode(defaultCountryCode); 3878 return defaultCountryCode; 3879 } 3880 } 3881 } 3882 // No country calling code present. 3883 phoneNumber.setCountryCode(0); 3884 return 0; 3885}; 3886 3887 3888/** 3889 * Strips the IDD from the start of the number if present. Helper function used 3890 * by maybeStripInternationalPrefixAndNormalize. 3891 * 3892 * @param {!RegExp} iddPattern the regular expression for the international 3893 * prefix. 3894 * @param {!goog.string.StringBuffer} number the phone number that we wish to 3895 * strip any international dialing prefix from. 3896 * @return {boolean} true if an international prefix was present. 3897 * @private 3898 */ 3899i18n.phonenumbers.PhoneNumberUtil.prototype.parsePrefixAsIdd_ = 3900 function(iddPattern, number) { 3901 3902 /** @type {string} */ 3903 var numberStr = number.toString(); 3904 if (numberStr.search(iddPattern) == 0) { 3905 /** @type {number} */ 3906 var matchEnd = numberStr.match(iddPattern)[0].length; 3907 /** @type {Array.<string>} */ 3908 var matchedGroups = numberStr.substring(matchEnd).match( 3909 i18n.phonenumbers.PhoneNumberUtil.CAPTURING_DIGIT_PATTERN); 3910 if (matchedGroups && matchedGroups[1] != null && 3911 matchedGroups[1].length > 0) { 3912 /** @type {string} */ 3913 var normalizedGroup = 3914 i18n.phonenumbers.PhoneNumberUtil.normalizeDigitsOnly( 3915 matchedGroups[1]); 3916 if (normalizedGroup == '0') { 3917 return false; 3918 } 3919 } 3920 number.clear(); 3921 number.append(numberStr.substring(matchEnd)); 3922 return true; 3923 } 3924 return false; 3925}; 3926 3927 3928/** 3929 * Strips any international prefix (such as +, 00, 011) present in the number 3930 * provided, normalizes the resulting number, and indicates if an international 3931 * prefix was present. 3932 * 3933 * @param {!goog.string.StringBuffer} number the non-normalized telephone number 3934 * that we wish to strip any international dialing prefix from. 3935 * @param {string} possibleIddPrefix the international direct dialing prefix 3936 * from the region we think this number may be dialed in. 3937 * @return {i18n.phonenumbers.PhoneNumber.CountryCodeSource} the corresponding 3938 * CountryCodeSource if an international dialing prefix could be removed 3939 * from the number, otherwise CountryCodeSource.FROM_DEFAULT_COUNTRY if 3940 * the number did not seem to be in international format. 3941 */ 3942i18n.phonenumbers.PhoneNumberUtil.prototype. 3943 maybeStripInternationalPrefixAndNormalize = function(number, 3944 possibleIddPrefix) { 3945 /** @type {string} */ 3946 var numberStr = number.toString(); 3947 if (numberStr.length == 0) { 3948 return i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_DEFAULT_COUNTRY; 3949 } 3950 // Check to see if the number begins with one or more plus signs. 3951 if (i18n.phonenumbers.PhoneNumberUtil.LEADING_PLUS_CHARS_PATTERN 3952 .test(numberStr)) { 3953 numberStr = numberStr.replace( 3954 i18n.phonenumbers.PhoneNumberUtil.LEADING_PLUS_CHARS_PATTERN, ''); 3955 // Can now normalize the rest of the number since we've consumed the '+' 3956 // sign at the start. 3957 number.clear(); 3958 number.append(i18n.phonenumbers.PhoneNumberUtil.normalize(numberStr)); 3959 return i18n.phonenumbers.PhoneNumber.CountryCodeSource 3960 .FROM_NUMBER_WITH_PLUS_SIGN; 3961 } 3962 // Attempt to parse the first digits as an international prefix. 3963 /** @type {!RegExp} */ 3964 var iddPattern = new RegExp(possibleIddPrefix); 3965 i18n.phonenumbers.PhoneNumberUtil.normalizeSB_(number); 3966 return this.parsePrefixAsIdd_(iddPattern, number) ? 3967 i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_NUMBER_WITH_IDD : 3968 i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_DEFAULT_COUNTRY; 3969}; 3970 3971 3972/** 3973 * Strips any national prefix (such as 0, 1) present in the number provided. 3974 * 3975 * @param {!goog.string.StringBuffer} number the normalized telephone number 3976 * that we wish to strip any national dialing prefix from. 3977 * @param {i18n.phonenumbers.PhoneMetadata} metadata the metadata for the 3978 * region that we think this number is from. 3979 * @param {goog.string.StringBuffer} carrierCode a place to insert the carrier 3980 * code if one is extracted. 3981 * @return {boolean} true if a national prefix or carrier code (or both) could 3982 * be extracted. 3983 */ 3984i18n.phonenumbers.PhoneNumberUtil.prototype. 3985 maybeStripNationalPrefixAndCarrierCode = function(number, metadata, 3986 carrierCode) { 3987 /** @type {string} */ 3988 var numberStr = number.toString(); 3989 /** @type {number} */ 3990 var numberLength = numberStr.length; 3991 /** @type {?string} */ 3992 var possibleNationalPrefix = metadata.getNationalPrefixForParsing(); 3993 if (numberLength == 0 || possibleNationalPrefix == null || 3994 possibleNationalPrefix.length == 0) { 3995 // Early return for numbers of zero length. 3996 return false; 3997 } 3998 // Attempt to parse the first digits as a national prefix. 3999 /** @type {!RegExp} */ 4000 var prefixPattern = new RegExp('^(?:' + possibleNationalPrefix + ')'); 4001 /** @type {Array.<string>} */ 4002 var prefixMatcher = prefixPattern.exec(numberStr); 4003 if (prefixMatcher) { 4004 /** @type {!RegExp} */ 4005 var nationalNumberRule = new RegExp( 4006 metadata.getGeneralDesc().getNationalNumberPatternOrDefault()); 4007 // Check if the original number is viable. 4008 /** @type {boolean} */ 4009 var isViableOriginalNumber = 4010 i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 4011 nationalNumberRule, numberStr); 4012 // prefixMatcher[numOfGroups] == null implies nothing was captured by the 4013 // capturing groups in possibleNationalPrefix; therefore, no transformation 4014 // is necessary, and we just remove the national prefix. 4015 /** @type {number} */ 4016 var numOfGroups = prefixMatcher.length - 1; 4017 /** @type {?string} */ 4018 var transformRule = metadata.getNationalPrefixTransformRule(); 4019 /** @type {boolean} */ 4020 var noTransform = transformRule == null || transformRule.length == 0 || 4021 prefixMatcher[numOfGroups] == null || 4022 prefixMatcher[numOfGroups].length == 0; 4023 if (noTransform) { 4024 // If the original number was viable, and the resultant number is not, 4025 // we return. 4026 if (isViableOriginalNumber && 4027 !i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 4028 nationalNumberRule, 4029 numberStr.substring(prefixMatcher[0].length))) { 4030 return false; 4031 } 4032 if (carrierCode != null && 4033 numOfGroups > 0 && prefixMatcher[numOfGroups] != null) { 4034 carrierCode.append(prefixMatcher[1]); 4035 } 4036 number.set(numberStr.substring(prefixMatcher[0].length)); 4037 return true; 4038 } else { 4039 // Check that the resultant number is still viable. If not, return. Check 4040 // this by copying the string buffer and making the transformation on the 4041 // copy first. 4042 /** @type {string} */ 4043 var transformedNumber; 4044 transformedNumber = numberStr.replace(prefixPattern, transformRule); 4045 if (isViableOriginalNumber && 4046 !i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( 4047 nationalNumberRule, transformedNumber)) { 4048 return false; 4049 } 4050 if (carrierCode != null && numOfGroups > 0) { 4051 carrierCode.append(prefixMatcher[1]); 4052 } 4053 number.set(transformedNumber); 4054 return true; 4055 } 4056 } 4057 return false; 4058}; 4059 4060 4061/** 4062 * Strips any extension (as in, the part of the number dialled after the call is 4063 * connected, usually indicated with extn, ext, x or similar) from the end of 4064 * the number, and returns it. 4065 * 4066 * @param {!goog.string.StringBuffer} number the non-normalized telephone number 4067 * that we wish to strip the extension from. 4068 * @return {string} the phone extension. 4069 */ 4070i18n.phonenumbers.PhoneNumberUtil.prototype.maybeStripExtension = 4071 function(number) { 4072 4073 /** @type {string} */ 4074 var numberStr = number.toString(); 4075 /** @type {number} */ 4076 var mStart = 4077 numberStr.search(i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERN_); 4078 // If we find a potential extension, and the number preceding this is a viable 4079 // number, we assume it is an extension. 4080 if (mStart >= 0 && i18n.phonenumbers.PhoneNumberUtil.isViablePhoneNumber( 4081 numberStr.substring(0, mStart))) { 4082 // The numbers are captured into groups in the regular expression. 4083 /** @type {Array.<string>} */ 4084 var matchedGroups = 4085 numberStr.match(i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERN_); 4086 /** @type {number} */ 4087 var matchedGroupsLength = matchedGroups.length; 4088 for (var i = 1; i < matchedGroupsLength; ++i) { 4089 if (matchedGroups[i] != null && matchedGroups[i].length > 0) { 4090 // We go through the capturing groups until we find one that captured 4091 // some digits. If none did, then we will return the empty string. 4092 number.clear(); 4093 number.append(numberStr.substring(0, mStart)); 4094 return matchedGroups[i]; 4095 } 4096 } 4097 } 4098 return ''; 4099}; 4100 4101 4102/** 4103 * Checks to see that the region code used is valid, or if it is not valid, that 4104 * the number to parse starts with a + symbol so that we can attempt to infer 4105 * the region from the number. 4106 * @param {string} numberToParse number that we are attempting to parse. 4107 * @param {?string} defaultRegion region that we are expecting the number to be 4108 * from. 4109 * @return {boolean} false if it cannot use the region provided and the region 4110 * cannot be inferred. 4111 * @private 4112 */ 4113i18n.phonenumbers.PhoneNumberUtil.prototype.checkRegionForParsing_ = function( 4114 numberToParse, defaultRegion) { 4115 // If the number is null or empty, we can't infer the region. 4116 return this.isValidRegionCode_(defaultRegion) || 4117 (numberToParse != null && numberToParse.length > 0 && 4118 i18n.phonenumbers.PhoneNumberUtil.LEADING_PLUS_CHARS_PATTERN.test( 4119 numberToParse)); 4120}; 4121 4122 4123/** 4124 * Parses a string and returns it as a phone number in proto buffer format. The 4125 * method is quite lenient and looks for a number in the input text (raw input) 4126 * and does not check whether the string is definitely only a phone number. To 4127 * do this, it ignores punctuation and white-space, as well as any text before 4128 * the number (e.g. a leading "Tel: ") and trims the non-number bits. It will 4129 * accept a number in any format (E164, national, international etc), assuming 4130 * it can be interpreted with the defaultRegion supplied. It also attempts to 4131 * convert any alpha characters into digits if it thinks this is a vanity number 4132 * of the type "1800 MICROSOFT". 4133 * 4134 * Note this method canonicalizes the phone number such that different 4135 * representations can be easily compared, no matter what form it was originally 4136 * entered in (e.g. national, international). If you want to record context 4137 * about the number being parsed, such as the raw input that was entered, how 4138 * the country code was derived etc. then call parseAndKeepRawInput() instead. 4139 * 4140 * This method will throw a {@link i18n.phonenumbers.Error} if the number is not 4141 * considered to be a possible number. Note that validation of whether the 4142 * number is actually a valid number for a particular region is not performed. 4143 * This can be done separately with {@link #isValidNumber}. 4144 * 4145 * @param {?string} numberToParse number that we are attempting to parse. This 4146 * can contain formatting such as +, ( and -, as well as a phone number 4147 * extension. It can also be provided in RFC3966 format. 4148 * @param {?string} defaultRegion region that we are expecting the number to be 4149 * from. This is only used if the number being parsed is not written in 4150 * international format. The country_code for the number in this case would 4151 * be stored as that of the default region supplied. If the number is 4152 * guaranteed to start with a '+' followed by the country calling code, then 4153 * 'ZZ' or null can be supplied. 4154 * @return {!i18n.phonenumbers.PhoneNumber} a phone number proto buffer filled 4155 * with the parsed number. 4156 * @throws {Error} if the string is not considered to be a 4157 * viable phone number (e.g. too few or too many digits) or if no default 4158 * region was supplied and the number is not in international format (does 4159 * not start with +). 4160 */ 4161i18n.phonenumbers.PhoneNumberUtil.prototype.parse = function(numberToParse, 4162 defaultRegion) { 4163 return this.parseHelper_(numberToParse, defaultRegion, false, true); 4164}; 4165 4166 4167/** 4168 * Parses a string and returns it in proto buffer format. This method differs 4169 * from {@link #parse} in that it always populates the raw_input field of the 4170 * protocol buffer with numberToParse as well as the country_code_source field. 4171 * 4172 * @param {string} numberToParse number that we are attempting to parse. This 4173 * can contain formatting such as +, ( and -, as well as a phone number 4174 * extension. 4175 * @param {?string} defaultRegion region that we are expecting the number to be 4176 * from. This is only used if the number being parsed is not written in 4177 * international format. The country calling code for the number in this 4178 * case would be stored as that of the default region supplied. 4179 * @return {!i18n.phonenumbers.PhoneNumber} a phone number proto buffer filled 4180 * with the parsed number. 4181 * @throws {Error} if the string is not considered to be a 4182 * viable phone number or if no default region was supplied. 4183 */ 4184i18n.phonenumbers.PhoneNumberUtil.prototype.parseAndKeepRawInput = 4185 function(numberToParse, defaultRegion) { 4186 4187 if (!this.isValidRegionCode_(defaultRegion)) { 4188 if (numberToParse.length > 0 && numberToParse.charAt(0) != 4189 i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN) { 4190 throw new Error(i18n.phonenumbers.Error.INVALID_COUNTRY_CODE); 4191 } 4192 } 4193 return this.parseHelper_(numberToParse, defaultRegion, true, true); 4194}; 4195 4196 4197/** 4198 * A helper function to set the values related to leading zeros in a 4199 * PhoneNumber. 4200 * 4201 * @param {string} nationalNumber the number we are parsing. 4202 * @param {i18n.phonenumbers.PhoneNumber} phoneNumber a phone number proto 4203 * buffer to fill in. 4204 * @private 4205 */ 4206i18n.phonenumbers.PhoneNumberUtil.setItalianLeadingZerosForPhoneNumber_ = 4207 function(nationalNumber, phoneNumber) { 4208 if (nationalNumber.length > 1 && nationalNumber.charAt(0) == '0') { 4209 phoneNumber.setItalianLeadingZero(true); 4210 var numberOfLeadingZeros = 1; 4211 // Note that if the national number is all "0"s, the last "0" is not counted 4212 // as a leading zero. 4213 while (numberOfLeadingZeros < nationalNumber.length - 1 && 4214 nationalNumber.charAt(numberOfLeadingZeros) == '0') { 4215 numberOfLeadingZeros++; 4216 } 4217 if (numberOfLeadingZeros != 1) { 4218 phoneNumber.setNumberOfLeadingZeros(numberOfLeadingZeros); 4219 } 4220 } 4221}; 4222 4223 4224/** 4225 * Parses a string and returns it in proto buffer format. This method is the 4226 * same as the public {@link #parse} method, with the exception that it allows 4227 * the default region to be null, for use by {@link #isNumberMatch}. 4228 * 4229 * Note if any new field is added to this method that should always be filled 4230 * in, even when keepRawInput is false, it should also be handled in the 4231 * copyCoreFieldsOnly method. 4232 * 4233 * @param {?string} numberToParse number that we are attempting to parse. This 4234 * can contain formatting such as +, ( and -, as well as a phone number 4235 * extension. 4236 * @param {?string} defaultRegion region that we are expecting the number to be 4237 * from. This is only used if the number being parsed is not written in 4238 * international format. The country calling code for the number in this 4239 * case would be stored as that of the default region supplied. 4240 * @param {boolean} keepRawInput whether to populate the raw_input field of the 4241 * phoneNumber with numberToParse. 4242 * @param {boolean} checkRegion should be set to false if it is permitted for 4243 * the default coregion to be null or unknown ('ZZ'). 4244 * @return {!i18n.phonenumbers.PhoneNumber} a phone number proto buffer filled 4245 * with the parsed number. 4246 * @throws {Error} 4247 * @private 4248 */ 4249i18n.phonenumbers.PhoneNumberUtil.prototype.parseHelper_ = 4250 function(numberToParse, defaultRegion, keepRawInput, checkRegion) { 4251 4252 if (numberToParse == null) { 4253 throw new Error(i18n.phonenumbers.Error.NOT_A_NUMBER); 4254 } else if (numberToParse.length > 4255 i18n.phonenumbers.PhoneNumberUtil.MAX_INPUT_STRING_LENGTH_) { 4256 throw new Error(i18n.phonenumbers.Error.TOO_LONG); 4257 } 4258 4259 /** @type {!goog.string.StringBuffer} */ 4260 var nationalNumber = new goog.string.StringBuffer(); 4261 this.buildNationalNumberForParsing_(numberToParse, nationalNumber); 4262 4263 if (!i18n.phonenumbers.PhoneNumberUtil.isViablePhoneNumber( 4264 nationalNumber.toString())) { 4265 throw new Error(i18n.phonenumbers.Error.NOT_A_NUMBER); 4266 } 4267 4268 // Check the region supplied is valid, or that the extracted number starts 4269 // with some sort of + sign so the number's region can be determined. 4270 if (checkRegion && 4271 !this.checkRegionForParsing_(nationalNumber.toString(), defaultRegion)) { 4272 throw new Error(i18n.phonenumbers.Error.INVALID_COUNTRY_CODE); 4273 } 4274 4275 /** @type {i18n.phonenumbers.PhoneNumber} */ 4276 var phoneNumber = new i18n.phonenumbers.PhoneNumber(); 4277 if (keepRawInput) { 4278 phoneNumber.setRawInput(numberToParse); 4279 } 4280 // Attempt to parse extension first, since it doesn't require region-specific 4281 // data and we want to have the non-normalised number here. 4282 /** @type {string} */ 4283 var extension = this.maybeStripExtension(nationalNumber); 4284 if (extension.length > 0) { 4285 phoneNumber.setExtension(extension); 4286 } 4287 4288 /** @type {i18n.phonenumbers.PhoneMetadata} */ 4289 var regionMetadata = this.getMetadataForRegion(defaultRegion); 4290 // Check to see if the number is given in international format so we know 4291 // whether this number is from the default region or not. 4292 /** @type {!goog.string.StringBuffer} */ 4293 var normalizedNationalNumber = new goog.string.StringBuffer(); 4294 /** @type {number} */ 4295 var countryCode = 0; 4296 /** @type {string} */ 4297 var nationalNumberStr = nationalNumber.toString(); 4298 try { 4299 countryCode = this.maybeExtractCountryCode(nationalNumberStr, 4300 regionMetadata, normalizedNationalNumber, keepRawInput, phoneNumber); 4301 } catch (e) { 4302 if (e.message == i18n.phonenumbers.Error.INVALID_COUNTRY_CODE && 4303 i18n.phonenumbers.PhoneNumberUtil.LEADING_PLUS_CHARS_PATTERN 4304 .test(nationalNumberStr)) { 4305 // Strip the plus-char, and try again. 4306 nationalNumberStr = nationalNumberStr.replace( 4307 i18n.phonenumbers.PhoneNumberUtil.LEADING_PLUS_CHARS_PATTERN, ''); 4308 countryCode = this.maybeExtractCountryCode(nationalNumberStr, 4309 regionMetadata, normalizedNationalNumber, keepRawInput, phoneNumber); 4310 if (countryCode == 0) { 4311 throw e; 4312 } 4313 } else { 4314 throw e; 4315 } 4316 } 4317 if (countryCode != 0) { 4318 /** @type {string} */ 4319 var phoneNumberRegion = this.getRegionCodeForCountryCode(countryCode); 4320 if (phoneNumberRegion != defaultRegion) { 4321 // Metadata cannot be null because the country calling code is valid. 4322 regionMetadata = this.getMetadataForRegionOrCallingCode_( 4323 countryCode, phoneNumberRegion); 4324 } 4325 } else { 4326 // If no extracted country calling code, use the region supplied instead. 4327 // The national number is just the normalized version of the number we were 4328 // given to parse. 4329 i18n.phonenumbers.PhoneNumberUtil.normalizeSB_(nationalNumber); 4330 normalizedNationalNumber.append(nationalNumber.toString()); 4331 if (defaultRegion != null) { 4332 countryCode = regionMetadata.getCountryCodeOrDefault(); 4333 phoneNumber.setCountryCode(countryCode); 4334 } else if (keepRawInput) { 4335 phoneNumber.clearCountryCodeSource(); 4336 } 4337 } 4338 if (normalizedNationalNumber.getLength() < 4339 i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) { 4340 throw new Error(i18n.phonenumbers.Error.TOO_SHORT_NSN); 4341 } 4342 4343 if (regionMetadata != null) { 4344 /** @type {!goog.string.StringBuffer} */ 4345 var carrierCode = new goog.string.StringBuffer(); 4346 /** @type {!goog.string.StringBuffer} */ 4347 var potentialNationalNumber = 4348 new goog.string.StringBuffer(normalizedNationalNumber.toString()); 4349 this.maybeStripNationalPrefixAndCarrierCode( 4350 potentialNationalNumber, regionMetadata, carrierCode); 4351 // We require that the NSN remaining after stripping the national prefix and 4352 // carrier code be long enough to be a possible length for the region. 4353 // Otherwise, we don't do the stripping, since the original number could be 4354 // a valid short number. 4355 var validationResult = this.testNumberLength_( 4356 potentialNationalNumber.toString(), regionMetadata); 4357 var validationResults = i18n.phonenumbers.PhoneNumberUtil.ValidationResult; 4358 if (validationResult != validationResults.TOO_SHORT && 4359 validationResult != validationResults.IS_POSSIBLE_LOCAL_ONLY && 4360 validationResult != validationResults.INVALID_LENGTH) { 4361 normalizedNationalNumber = potentialNationalNumber; 4362 if (keepRawInput && carrierCode.toString().length > 0) { 4363 phoneNumber.setPreferredDomesticCarrierCode(carrierCode.toString()); 4364 } 4365 } 4366 } 4367 /** @type {string} */ 4368 var normalizedNationalNumberStr = normalizedNationalNumber.toString(); 4369 /** @type {number} */ 4370 var lengthOfNationalNumber = normalizedNationalNumberStr.length; 4371 if (lengthOfNationalNumber < 4372 i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) { 4373 throw new Error(i18n.phonenumbers.Error.TOO_SHORT_NSN); 4374 } 4375 if (lengthOfNationalNumber > 4376 i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_) { 4377 throw new Error(i18n.phonenumbers.Error.TOO_LONG); 4378 } 4379 i18n.phonenumbers.PhoneNumberUtil.setItalianLeadingZerosForPhoneNumber_( 4380 normalizedNationalNumberStr, phoneNumber); 4381 phoneNumber.setNationalNumber(parseInt(normalizedNationalNumberStr, 10)); 4382 return phoneNumber; 4383}; 4384 4385 4386/** 4387 * Extracts the value of the phone-context parameter of numberToExtractFrom, 4388 * following the syntax defined in RFC3966. 4389 * @param {?string} numberToExtractFrom 4390 * @return {string|null} the extracted string (possibly empty), or null if no 4391 * phone-context parameter is found. 4392 * @private 4393 */ 4394i18n.phonenumbers.PhoneNumberUtil.prototype.extractPhoneContext_ = 4395 function (numberToExtractFrom) { 4396 /** @type {number} */ 4397 var indexOfPhoneContext = numberToExtractFrom.indexOf(i18n 4398 .phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_); 4399 // If no phone-context parameter is present 4400 if (indexOfPhoneContext === -1) { 4401 return null; 4402 } 4403 4404 /** @type {number} */ 4405 var phoneContextStart = indexOfPhoneContext + i18n 4406 .phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_.length; 4407 // If phone-context parameter is empty 4408 if (phoneContextStart >= numberToExtractFrom.length) { 4409 return ""; 4410 } 4411 4412 /** @type {number} */ 4413 var phoneContextEnd = numberToExtractFrom.indexOf(';', phoneContextStart); 4414 // If phone-context is not the last parameter 4415 if (phoneContextEnd !== -1) { 4416 return numberToExtractFrom.substring(phoneContextStart, 4417 phoneContextEnd); 4418 } else { 4419 return numberToExtractFrom.substring(phoneContextStart); 4420 } 4421 } 4422 4423 4424/** 4425 * Returns whether the value of phoneContext follows the syntax defined in 4426 * RFC3966. 4427 * 4428 * @param {string|null} phoneContext 4429 * @return {boolean} 4430 * @private 4431 */ 4432i18n.phonenumbers.PhoneNumberUtil.prototype.isPhoneContextValid_ = 4433 function (phoneContext) { 4434 if (phoneContext == null) { 4435 return true; 4436 } 4437 4438 if (phoneContext.length === 0) { 4439 return false; 4440 } 4441 4442 var globalNumberDigitsMatcher = 4443 i18n.phonenumbers.PhoneNumberUtil.RFC3966_GLOBAL_NUMBER_DIGITS_PATTERN_.exec( 4444 phoneContext); 4445 var domainnameMatcher = 4446 i18n.phonenumbers.PhoneNumberUtil.RFC3966_DOMAINNAME_PATTERN_.exec( 4447 phoneContext); 4448 // Does phone-context value match pattern of global-number-digits or 4449 // domainname 4450 return globalNumberDigitsMatcher !== null || domainnameMatcher !== null; 4451 } 4452 4453 4454/** 4455 * Converts numberToParse to a form that we can parse and write it to 4456 * nationalNumber if it is written in RFC3966; otherwise extract a possible 4457 * number out of it and write to nationalNumber. 4458 * 4459 * @param {?string} numberToParse number that we are attempting to parse. This 4460 * can contain formatting such as +, ( and -, as well as a phone number 4461 * extension. 4462 * @param {!goog.string.StringBuffer} nationalNumber a string buffer for storing 4463 * the national significant number. 4464 * @throws {Error} 4465 * @private 4466 */ 4467i18n.phonenumbers.PhoneNumberUtil.prototype.buildNationalNumberForParsing_ = 4468 function (numberToParse, nationalNumber) { 4469 var phoneContext = 4470 i18n.phonenumbers.PhoneNumberUtil.prototype.extractPhoneContext_( 4471 numberToParse); 4472 4473 if (!i18n.phonenumbers.PhoneNumberUtil.prototype.isPhoneContextValid_( 4474 phoneContext)) { 4475 throw new Error(i18n.phonenumbers.Error.NOT_A_NUMBER); 4476 } 4477 if (phoneContext != null) { 4478 // If the phone context contains a phone number prefix, we need to capture 4479 // it, whereas domains will be ignored. 4480 if (phoneContext.charAt(0) === 4481 i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN) { 4482 nationalNumber.append(phoneContext); 4483 } 4484 4485 // Now append everything between the "tel:" prefix and the phone-context. 4486 // This should include the national number, an optional extension or 4487 // isdn-subaddress component. Note we also handle the case when "tel:" is 4488 // missing, as we have seen in some of the phone number inputs. 4489 // In that case, we append everything from the beginning. 4490 var indexOfRfc3966Prefix = numberToParse.indexOf( 4491 i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_); 4492 var indexOfNationalNumber = (indexOfRfc3966Prefix >= 0) ? 4493 indexOfRfc3966Prefix + 4494 i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_.length : 0; 4495 var indexOfPhoneContext = numberToParse.indexOf( 4496 i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_); 4497 nationalNumber.append(numberToParse.substring(indexOfNationalNumber, 4498 indexOfPhoneContext)); 4499 } else { 4500 // Extract a possible number from the string passed in (this strips leading 4501 // characters that could not be the start of a phone number.) 4502 nationalNumber.append( 4503 i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber( 4504 numberToParse ?? "")); 4505 } 4506 4507 // Delete the isdn-subaddress and everything after it if it is present. 4508 // Note extension won't appear at the same time with isdn-subaddress 4509 // according to paragraph 5.3 of the RFC3966 spec, 4510 /** @type {string} */ 4511 var nationalNumberStr = nationalNumber.toString(); 4512 var indexOfIsdn = nationalNumberStr.indexOf( 4513 i18n.phonenumbers.PhoneNumberUtil.RFC3966_ISDN_SUBADDRESS_); 4514 if (indexOfIsdn > 0) { 4515 nationalNumber.clear(); 4516 nationalNumber.append(nationalNumberStr.substring(0, indexOfIsdn)); 4517 } 4518 // If both phone context and isdn-subaddress are absent but other 4519 // parameters are present, the parameters are left in nationalNumber. This 4520 // is because we are concerned about deleting content from a potential 4521 // number string when there is no strong evidence that the number is 4522 // actually written in RFC3966. 4523 }; 4524 4525 4526/** 4527 * Returns a new phone number containing only the fields needed to uniquely 4528 * identify a phone number, rather than any fields that capture the context in 4529 * which the phone number was created. 4530 * These fields correspond to those set in parse() rather than 4531 * parseAndKeepRawInput(). 4532 * 4533 * @param {i18n.phonenumbers.PhoneNumber} numberIn number that we want to copy 4534 * fields from. 4535 * @return {!i18n.phonenumbers.PhoneNumber} number with core fields only. 4536 * @private 4537 */ 4538i18n.phonenumbers.PhoneNumberUtil.copyCoreFieldsOnly_ = function(numberIn) { 4539 /** @type {i18n.phonenumbers.PhoneNumber} */ 4540 var phoneNumber = new i18n.phonenumbers.PhoneNumber(); 4541 phoneNumber.setCountryCode(numberIn.getCountryCodeOrDefault()); 4542 phoneNumber.setNationalNumber(numberIn.getNationalNumberOrDefault()); 4543 if (numberIn.getExtensionOrDefault().length > 0) { 4544 phoneNumber.setExtension(numberIn.getExtensionOrDefault()); 4545 } 4546 if (numberIn.getItalianLeadingZero()) { 4547 phoneNumber.setItalianLeadingZero(true); 4548 // This field is only relevant if there are leading zeros at all. 4549 phoneNumber.setNumberOfLeadingZeros( 4550 numberIn.getNumberOfLeadingZerosOrDefault()); 4551 } 4552 return phoneNumber; 4553}; 4554 4555 4556/** 4557 * Takes two phone numbers and compares them for equality. 4558 * 4559 * <p>Returns EXACT_MATCH if the country_code, NSN, presence of a leading zero 4560 * for Italian numbers and any extension present are the same. Returns NSN_MATCH 4561 * if either or both has no region specified, and the NSNs and extensions are 4562 * the same. Returns SHORT_NSN_MATCH if either or both has no region specified, 4563 * or the region specified is the same, and one NSN could be a shorter version 4564 * of the other number. This includes the case where one has an extension 4565 * specified, and the other does not. Returns NO_MATCH otherwise. For example, 4566 * the numbers +1 345 657 1234 and 657 1234 are a SHORT_NSN_MATCH. The numbers 4567 * +1 345 657 1234 and 345 657 are a NO_MATCH. 4568 * 4569 * @param {i18n.phonenumbers.PhoneNumber|string} firstNumberIn first number to 4570 * compare. If it is a string it can contain formatting, and can have 4571 * country calling code specified with + at the start. 4572 * @param {i18n.phonenumbers.PhoneNumber|string} secondNumberIn second number to 4573 * compare. If it is a string it can contain formatting, and can have 4574 * country calling code specified with + at the start. 4575 * @return {i18n.phonenumbers.PhoneNumberUtil.MatchType} NOT_A_NUMBER, NO_MATCH, 4576 * SHORT_NSN_MATCH, NSN_MATCH or EXACT_MATCH depending on the level of 4577 * equality of the two numbers, described in the method definition. 4578 */ 4579i18n.phonenumbers.PhoneNumberUtil.prototype.isNumberMatch = 4580 function(firstNumberIn, secondNumberIn) { 4581 4582 // If the input arguements are strings parse them to a proto buffer format. 4583 // Else make copies of the phone numbers so that the numbers passed in are not 4584 // edited. 4585 /** @type {i18n.phonenumbers.PhoneNumber} */ 4586 var firstNumber; 4587 /** @type {i18n.phonenumbers.PhoneNumber} */ 4588 var secondNumber; 4589 if (typeof firstNumberIn == 'string') { 4590 // First see if the first number has an implicit country calling code, by 4591 // attempting to parse it. 4592 try { 4593 firstNumber = this.parse( 4594 firstNumberIn, i18n.phonenumbers.PhoneNumberUtil.UNKNOWN_REGION_); 4595 } catch (e) { 4596 if (e.message != i18n.phonenumbers.Error.INVALID_COUNTRY_CODE) { 4597 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NOT_A_NUMBER; 4598 } 4599 // The first number has no country calling code. EXACT_MATCH is no longer 4600 // possible. We parse it as if the region was the same as that for the 4601 // second number, and if EXACT_MATCH is returned, we replace this with 4602 // NSN_MATCH. 4603 if (typeof secondNumberIn != 'string') { 4604 /** @type {string} */ 4605 var secondNumberRegion = this.getRegionCodeForCountryCode( 4606 secondNumberIn.getCountryCodeOrDefault()); 4607 if (secondNumberRegion != 4608 i18n.phonenumbers.PhoneNumberUtil.UNKNOWN_REGION_) { 4609 try { 4610 firstNumber = this.parse(firstNumberIn, secondNumberRegion); 4611 } catch (e2) { 4612 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NOT_A_NUMBER; 4613 } 4614 /** @type {i18n.phonenumbers.PhoneNumberUtil.MatchType} */ 4615 var match = this.isNumberMatch(firstNumber, secondNumberIn); 4616 if (match == 4617 i18n.phonenumbers.PhoneNumberUtil.MatchType.EXACT_MATCH) { 4618 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NSN_MATCH; 4619 } 4620 return match; 4621 } 4622 } 4623 // If the second number is a string or doesn't have a valid country 4624 // calling code, we parse the first number without country calling code. 4625 try { 4626 firstNumber = this.parseHelper_(firstNumberIn, null, false, false); 4627 } catch (e2) { 4628 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NOT_A_NUMBER; 4629 } 4630 } 4631 } else { 4632 firstNumber = firstNumberIn.clone(); 4633 } 4634 if (typeof secondNumberIn == 'string') { 4635 try { 4636 secondNumber = this.parse( 4637 secondNumberIn, i18n.phonenumbers.PhoneNumberUtil.UNKNOWN_REGION_); 4638 return this.isNumberMatch(firstNumberIn, secondNumber); 4639 } catch (e) { 4640 if (e.message != i18n.phonenumbers.Error.INVALID_COUNTRY_CODE) { 4641 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NOT_A_NUMBER; 4642 } 4643 return this.isNumberMatch(secondNumberIn, firstNumber); 4644 } 4645 } else { 4646 secondNumber = secondNumberIn.clone(); 4647 } 4648 var firstNumberToCompare = 4649 i18n.phonenumbers.PhoneNumberUtil.copyCoreFieldsOnly_(firstNumber); 4650 var secondNumberToCompare = 4651 i18n.phonenumbers.PhoneNumberUtil.copyCoreFieldsOnly_(secondNumber); 4652 4653 // Early exit if both had extensions and these are different. 4654 if (firstNumberToCompare.hasExtension() && 4655 secondNumberToCompare.hasExtension() && 4656 firstNumberToCompare.getExtension() != 4657 secondNumberToCompare.getExtension()) { 4658 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NO_MATCH; 4659 } 4660 /** @type {number} */ 4661 var firstNumberCountryCode = firstNumberToCompare.getCountryCodeOrDefault(); 4662 /** @type {number} */ 4663 var secondNumberCountryCode = secondNumberToCompare.getCountryCodeOrDefault(); 4664 // Both had country_code specified. 4665 if (firstNumberCountryCode != 0 && secondNumberCountryCode != 0) { 4666 if (firstNumberToCompare.equals(secondNumberToCompare)) { 4667 return i18n.phonenumbers.PhoneNumberUtil.MatchType.EXACT_MATCH; 4668 } else if (firstNumberCountryCode == secondNumberCountryCode && 4669 this.isNationalNumberSuffixOfTheOther_( 4670 firstNumberToCompare, secondNumberToCompare)) { 4671 // A SHORT_NSN_MATCH occurs if there is a difference because of the 4672 // presence or absence of an 'Italian leading zero', the presence or 4673 // absence of an extension, or one NSN being a shorter variant of the 4674 // other. 4675 return i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH; 4676 } 4677 // This is not a match. 4678 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NO_MATCH; 4679 } 4680 // Checks cases where one or both country_code fields were not specified. To 4681 // make equality checks easier, we first set the country_code fields to be 4682 // equal. 4683 firstNumberToCompare.setCountryCode(0); 4684 secondNumberToCompare.setCountryCode(0); 4685 // If all else was the same, then this is an NSN_MATCH. 4686 if (firstNumberToCompare.equals(secondNumberToCompare)) { 4687 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NSN_MATCH; 4688 } 4689 if (this.isNationalNumberSuffixOfTheOther_(firstNumberToCompare, 4690 secondNumberToCompare)) { 4691 return i18n.phonenumbers.PhoneNumberUtil.MatchType.SHORT_NSN_MATCH; 4692 } 4693 return i18n.phonenumbers.PhoneNumberUtil.MatchType.NO_MATCH; 4694}; 4695 4696 4697/** 4698 * Returns true when one national number is the suffix of the other or both are 4699 * the same. 4700 * 4701 * @param {i18n.phonenumbers.PhoneNumber} firstNumber the first PhoneNumber 4702 * object. 4703 * @param {i18n.phonenumbers.PhoneNumber} secondNumber the second PhoneNumber 4704 * object. 4705 * @return {boolean} true if one PhoneNumber is the suffix of the other one. 4706 * @private 4707 */ 4708i18n.phonenumbers.PhoneNumberUtil.prototype.isNationalNumberSuffixOfTheOther_ = 4709 function(firstNumber, secondNumber) { 4710 4711 /** @type {string} */ 4712 var firstNumberNationalNumber = '' + firstNumber.getNationalNumber(); 4713 /** @type {string} */ 4714 var secondNumberNationalNumber = '' + secondNumber.getNationalNumber(); 4715 // Note that endsWith returns true if the numbers are equal. 4716 return goog.string.endsWith(firstNumberNationalNumber, 4717 secondNumberNationalNumber) || 4718 goog.string.endsWith(secondNumberNationalNumber, 4719 firstNumberNationalNumber); 4720}; 4721 4722 4723/** 4724 * Returns true if the number can be dialled from outside the region, or 4725 * unknown. If the number can only be dialled from within the region, returns 4726 * false. Does not check the number is a valid number. Note that, at the 4727 * moment, this method does not handle short numbers (which are currently 4728 * all presumed to not be diallable from outside their country). 4729 * 4730 * @param {i18n.phonenumbers.PhoneNumber} number the phone-number for which we 4731 * want to know whether it is diallable from outside the region. 4732 * @return {boolean} true if the number can only be dialled from within the 4733 * country. 4734 */ 4735i18n.phonenumbers.PhoneNumberUtil.prototype.canBeInternationallyDialled = 4736 function(number) { 4737 /** @type {i18n.phonenumbers.PhoneMetadata} */ 4738 var metadata = this.getMetadataForRegion(this.getRegionCodeForNumber(number)); 4739 if (metadata == null) { 4740 // Note numbers belonging to non-geographical entities (e.g. +800 numbers) 4741 // are always internationally diallable, and will be caught here. 4742 return true; 4743 } 4744 /** @type {string} */ 4745 var nationalSignificantNumber = this.getNationalSignificantNumber(number); 4746 return !this.isNumberMatchingDesc_(nationalSignificantNumber, 4747 metadata.getNoInternationalDialling()); 4748}; 4749 4750 4751/** 4752 * Check whether the entire input sequence can be matched against the regular 4753 * expression. 4754 * 4755 * @param {!RegExp|string} regex the regular expression to match against. 4756 * @param {string} str the string to test. 4757 * @return {boolean} true if str can be matched entirely against regex. 4758 * @package 4759 */ 4760i18n.phonenumbers.PhoneNumberUtil.matchesEntirely = function(regex, str) { 4761 /** @type {Array.<string>} */ 4762 var matchedGroups = (typeof regex == 'string') ? 4763 str.match('^(?:' + regex + ')$') : str.match(regex); 4764 if (matchedGroups && matchedGroups[0].length == str.length) { 4765 return true; 4766 } 4767 return false; 4768}; 4769 4770 4771/** 4772 * Check whether the input sequence can be prefix-matched against the regular 4773 * expression. 4774 * 4775 * @param {!RegExp|string} regex the regular expression to match against. 4776 * @param {string} str the string to test 4777 * @return {boolean} true if a prefix of the string can be matched with this 4778 * regex. 4779 * @package 4780 */ 4781i18n.phonenumbers.PhoneNumberUtil.matchesPrefix = function(regex, str) { 4782 /** @type {Array.<string>} */ 4783 var matchedGroups = (typeof regex == 'string') ? 4784 str.match('^(?:' + regex + ')') : str.match(regex); 4785 if (matchedGroups && goog.string.startsWith(str, matchedGroups[0])) { 4786 return true; 4787 } 4788 return false; 4789}; 4790