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