/**
* @license
- * Copyright (C) 2010 The Libphonenumber Authors
+ * Copyright (C) 2010 The Libphonenumber Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
goog.provide('i18n.phonenumbers.PhoneNumberFormat');
goog.provide('i18n.phonenumbers.PhoneNumberType');
goog.provide('i18n.phonenumbers.PhoneNumberUtil');
+goog.provide('i18n.phonenumbers.PhoneNumberUtil.MatchType');
+goog.provide('i18n.phonenumbers.PhoneNumberUtil.ValidationResult');
goog.require('goog.array');
goog.require('goog.proto2.PbLiteSerializer');
* @type {number}
* @private
*/
-i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 3;
+i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ = 2;
/**
* @type {number}
* @private
*/
-i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_ = 16;
+i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_ = 17;
/**
/**
+ * Map of country calling codes that use a mobile token before the area code.
+ * One example of when this is relevant is when determining the length of the
+ * national destination code, which should be the length of the area code plus
+ * the length of the mobile token.
+ *
+ * @const
+ * @type {!Object.<number, string>}
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.MOBILE_TOKEN_MAPPINGS_ = {
+ 52: '1',
+ 54: '9'
+};
+
+
+/**
* The PLUS_SIGN signifies the international prefix.
*
* @const
/**
- * We include the "+" here since RFC3966 format specifies that the context must
- * be specified in international format.
- *
* @const
* @type {string}
* @private
*/
-i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_ = ';phone-context=+';
+i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_ = ';phone-context=';
+
+
+/**
+ * @const
+ * @type {string}
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.RFC3966_ISDN_SUBADDRESS_ = ';isub=';
/**
/**
* A map that contains characters that are essential when dialling. That means
* any of the characters in this map must not be removed from a number when
- * dialing, otherwise the call will not reach the intended destination.
+ * dialling, otherwise the call will not reach the intended destination.
*
* @const
* @type {!Object.<string, string>}
'7': '7',
'8': '8',
'9': '9',
- '+': '+',
+ '+': i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN,
'*': '*'
};
* @type {string}
*/
i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION =
- '-x\u2010-\u2015\u2212\u30FC\uFF0D-\uFF0F \u00A0\u200B\u2060\u3000()' +
- '\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E';
+ '-x\u2010-\u2015\u2212\u30FC\uFF0D-\uFF0F \u00A0\u00AD\u200B\u2060\u3000' +
+ '()\uFF08\uFF09\uFF3B\uFF3D.\\[\\]/~\u2053\u223C\uFF5E';
/**
* used as a placeholder for carrier codes, for example in Brazilian phone
* numbers. We also allow multiple '+' characters at the start.
* Corresponds to the following:
+ * [digits]{minLengthNsn}|
* plus_sign*
* (([punctuation]|[star])*[digits]){3,}([punctuation]|[star]|[digits]|[alpha])*
+ *
+ * The first reg-ex is to allow short numbers (two digits long) to be parsed if
+ * they are entered as "15" etc, but only if there is no punctuation in them.
+ * The second expression restricts the number of digits to three or more, but
+ * then allows them to be in international form, and to have alpha-characters
+ * and punctuation. We split up the two reg-exes here and combine them when
+ * creating the reg-ex VALID_PHONE_NUMBER_PATTERN_ itself so we can prefix it
+ * with ^ and append $ to each branch.
+ *
* Note VALID_PUNCTUATION starts with a -, so must be the first in the range.
*
* @const
* @type {string}
* @private
*/
+i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_PHONE_NUMBER_PATTERN_ =
+ '[' + i18n.phonenumbers.PhoneNumberUtil.VALID_DIGITS_ + ']{' +
+ i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ + '}';
+
+
+/**
+ * See MIN_LENGTH_PHONE_NUMBER_PATTERN_ for a full description of this reg-exp.
+ *
+ * @const
+ * @type {string}
+ * @private
+ */
i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_ =
'[' + i18n.phonenumbers.PhoneNumberUtil.PLUS_CHARS_ + ']*(?:[' +
i18n.phonenumbers.PhoneNumberUtil.VALID_PUNCTUATION +
* @private
*/
i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_PATTERN_ =
- new RegExp('^' + i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_ +
- '(?:' +
- i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERNS_FOR_PARSING_ +
- ')?' + '$', 'i');
+ new RegExp(
+ '^' +
+ i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_PHONE_NUMBER_PATTERN_ +
+ '$|' +
+ '^' + i18n.phonenumbers.PhoneNumberUtil.VALID_PHONE_NUMBER_ +
+ '(?:' + i18n.phonenumbers.PhoneNumberUtil.EXTN_PATTERNS_FOR_PARSING_ +
+ ')?' + '$', 'i');
/**
/**
+ * A pattern that is used to determine if the national prefix formatting rule
+ * has the first group only, i.e., does not start with the national prefix.
+ * Note that the pattern explicitly allows for unbalanced parentheses.
+ * @const
+ * @type {!RegExp}
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_ONLY_PREFIX_PATTERN_ =
+ /^\(?\$1\)?$/;
+
+
+/**
* @const
* @type {string}
*/
/**
* Checks to see if the string of characters could possibly be a phone number at
- * all. At the moment, checks to see that the string begins with at least 3
+ * all. At the moment, checks to see that the string begins with at least 2
* digits, ignoring any punctuation commonly found in phone numbers. This method
* does not require the number to be normalized in advance - but does assume
* that leading non-number symbols have been removed, such as by the method
*/
i18n.phonenumbers.PhoneNumberUtil.prototype.getLengthOfGeographicalAreaCode =
function(number) {
-
- if (number == null) {
- return 0;
- }
- /** @type {?string} */
- var regionCode = this.getRegionCodeForNumber(number);
- if (!this.isValidRegionCode_(regionCode)) {
+ /** @type {i18n.phonenumbers.PhoneMetadata} */
+ var metadata = this.getMetadataForRegion(this.getRegionCodeForNumber(number));
+ if (metadata == null) {
return 0;
}
- /** @type {i18n.phonenumbers.PhoneMetadata} */
- var metadata = this.getMetadataForRegion(regionCode);
- // If a country doesn't use a national prefix, and this number doesn't have an
- // Italian leading zero, we assume it is a closed dialling plan with no area
- // codes.
+ // If a country doesn't use a national prefix, and this number doesn't have
+ // an Italian leading zero, we assume it is a closed dialling plan with no
+ // area codes.
if (!metadata.hasNationalPrefix() && !number.hasItalianLeadingZero()) {
return 0;
}
- /** @type {i18n.phonenumbers.PhoneNumberType} */
- var type = this.getNumberTypeHelper_(
- this.getNationalSignificantNumber(number), metadata);
- // Most numbers other than the two types below have to be dialled in full.
- if (type != i18n.phonenumbers.PhoneNumberType.FIXED_LINE &&
- type != i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE) {
+ if (!this.isNumberGeographical(number)) {
return 0;
}
return 0;
}
- if (this.getRegionCodeForCountryCode(number.getCountryCodeOrDefault()) ==
- 'AR' &&
- this.getNumberType(number) == i18n.phonenumbers.PhoneNumberType.MOBILE) {
- // Argentinian mobile numbers, when formatted in the international format,
- // are in the form of +54 9 NDC XXXX.... As a result, we take the length of
- // the third group (NDC) and add 1 for the digit 9, which also forms part of
- // the national significant number.
- //
- // TODO: Investigate the possibility of better modeling the metadata to make
- // it easier to obtain the NDC.
- return numberGroups[2].length + 1;
+ if (this.getNumberType(number) == i18n.phonenumbers.PhoneNumberType.MOBILE) {
+ // For example Argentinian mobile numbers, when formatted in the
+ // international format, are in the form of +54 9 NDC XXXX.... As a result,
+ // we take the length of the third group (NDC) and add the length of the
+ // mobile token, which also forms part of the national significant number.
+ // This assumes that the mobile token is always formatted separately from
+ // the rest of the phone number.
+ /** @type {string} */
+ var mobileToken = i18n.phonenumbers.PhoneNumberUtil.getCountryMobileToken(
+ number.getCountryCodeOrDefault());
+ if (mobileToken != '') {
+ return numberGroups[2].length + mobileToken.length;
+ }
}
return numberGroups[1].length;
};
/**
+ * Returns the mobile token for the provided country calling code if it has
+ * one, otherwise returns an empty string. A mobile token is a number inserted
+ * before the area code when dialing a mobile number from that country from
+ * abroad.
+ *
+ * @param {number} countryCallingCode the country calling code for which we
+ * want the mobile token.
+ * @return {string} the mobile token for the given country calling code.
+ */
+i18n.phonenumbers.PhoneNumberUtil.getCountryMobileToken =
+ function(countryCallingCode) {
+ return i18n.phonenumbers.PhoneNumberUtil.MOBILE_TOKEN_MAPPINGS_[
+ countryCallingCode] || '';
+};
+
+
+/**
+ * Convenience method to get a list of what regions the library has metadata
+ * for.
+ * @return {!Array.<string>} region codes supported by the library.
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.getSupportedRegions = function() {
+ return goog.array.filter(
+ Object.keys(i18n.phonenumbers.metadata.countryToMetadata),
+ function(regionCode) {
+ return isNaN(regionCode);
+ });
+};
+
+
+/**
+ * Convenience method to get a list of what global network calling codes the
+ * library has metadata for.
+ * @return {!Array.<number>} global network calling codes supported by the
+ * library.
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.
+ getSupportedGlobalNetworkCallingCodes = function() {
+ var callingCodesAsStrings = goog.array.filter(
+ Object.keys(i18n.phonenumbers.metadata.countryToMetadata),
+ function(regionCode) {
+ return !isNaN(regionCode);
+ });
+ return goog.array.map(callingCodesAsStrings,
+ function(callingCode) {
+ return parseInt(callingCode, 10);
+ });
+};
+
+
+/**
* Normalizes a string of characters representing a phone number by replacing
* all characters found in the accompanying map with the values therein, and
* stripping all other characters if removeNonMatches is true.
/**
+ * Helper function to check if the national prefix formatting rule has the first
+ * group only, i.e., does not start with the national prefix.
+ *
+ * @param {string} nationalPrefixFormattingRule The formatting rule for the
+ * national prefix.
+ * @return {boolean} true if the national prefix formatting rule has the first
+ * group only.
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.formattingRuleHasFirstGroupOnly =
+ function(nationalPrefixFormattingRule) {
+ return nationalPrefixFormattingRule.length == 0 ||
+ i18n.phonenumbers.PhoneNumberUtil.FIRST_GROUP_ONLY_PREFIX_PATTERN_.
+ test(nationalPrefixFormattingRule);
+};
+
+
+/**
+ * Tests whether a phone number has a geographical association. It checks if
+ * the number is associated to a certain region in the country where it belongs
+ * to. Note that this doesn't verify if the number is actually in use.
+ *
+ * @param {i18n.phonenumbers.PhoneNumber} phoneNumber The phone number to test.
+ * @return {boolean} true if the phone number has a geographical association.
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.isNumberGeographical =
+ function(phoneNumber) {
+ /** @type {i18n.phonenumbers.PhoneNumberType} */
+ var numberType = this.getNumberType(phoneNumber);
+ // TODO: Include mobile phone numbers from countries like Indonesia, which
+ // has some mobile numbers that are geographical.
+ return numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE ||
+ numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE;
+};
+
+
+/**
* Helper function to check region code is not unknown or null.
*
* @param {?string} regionCode the ISO 3166-1 two-letter region code.
function(number, numberFormat) {
if (number.getNationalNumber() == 0 && number.hasRawInput()) {
+ // Unparseable numbers that kept their raw input just use that.
+ // This is the only case where a number can be formatted as E164 without a
+ // leading '+' symbol (but the original number wasn't parseable anyway).
+ // TODO: Consider removing the 'if' above so that unparseable strings
+ // without raw input format to the empty string instead of "+00"
/** @type {string} */
var rawInput = number.getRawInputOrDefault();
if (rawInput.length > 0) {
/** @type {string} */
var nationalSignificantNumber = this.getNationalSignificantNumber(number);
if (numberFormat == i18n.phonenumbers.PhoneNumberFormat.E164) {
- // Early exit for E164 case since no formatting of the national number needs
- // to be applied. Extensions are not formatted.
+ // Early exit for E164 case (even if the country calling code is invalid)
+ // since no formatting of the national number needs to be applied.
+ // Extensions are not formatted.
return this.prefixNumberWithCountryCallingCode_(
countryCallingCode, i18n.phonenumbers.PhoneNumberFormat.E164,
nationalSignificantNumber, '');
}
+ if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
+ return nationalSignificantNumber;
+ }
// Note getRegionCodeForCountryCode() is used because formatting information
// for regions which share a country calling code is contained by only one
// region for performance reasons. For example, for NANPA regions it will be
// contained in the metadata for US.
/** @type {string} */
var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
- if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
- return nationalSignificantNumber;
- }
+ // Metadata cannot be null because the country calling code is valid (which
+ // means that the region code cannot be ZZ and must be one of our supported
+ // region codes).
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadata =
this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode);
var countryCallingCode = number.getCountryCodeOrDefault();
/** @type {string} */
var nationalSignificantNumber = this.getNationalSignificantNumber(number);
+ if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
+ return nationalSignificantNumber;
+ }
// Note getRegionCodeForCountryCode() is used because formatting information
// for regions which share a country calling code is contained by only one
// region for performance reasons. For example, for NANPA regions it will be
// contained in the metadata for US.
/** @type {string} */
var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
- if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
- return nationalSignificantNumber;
- }
+ // Metadata cannot be null because the country calling code is valid
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadata =
this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode);
// Before we do a replacement of the national prefix pattern $NP with the
// national prefix, we need to copy the rule so that subsequent replacements
// for different numbers have the appropriate national prefix.
- /** type {i18n.phonenumbers.NumberFormat} */
+ /** @type {i18n.phonenumbers.NumberFormat} */
var numFormatCopy = formattingPattern.clone();
/** @type {string} */
var nationalPrefixFormattingRule =
formattingPattern.getNationalPrefixFormattingRuleOrDefault();
- /** @type {string} */
if (nationalPrefixFormattingRule.length > 0) {
+ /** @type {string} */
var nationalPrefix = metadata.getNationalPrefixOrDefault();
if (nationalPrefix.length > 0) {
// Replace $NP with national prefix and $FG with the first group ($1).
var countryCallingCode = number.getCountryCodeOrDefault();
/** @type {string} */
var nationalSignificantNumber = this.getNationalSignificantNumber(number);
+ if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
+ return nationalSignificantNumber;
+ }
+
// Note getRegionCodeForCountryCode() is used because formatting information
// for regions which share a country calling code is contained by only one
// region for performance reasons. For example, for NANPA regions it will be
// contained in the metadata for US.
/** @type {string} */
var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
- if (!this.hasValidCountryCallingCode_(countryCallingCode)) {
- return nationalSignificantNumber;
- }
-
+ // Metadata cannot be null because the country calling code is valid.
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadata =
this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode);
}
/** @type {string} */
- var formattedNumber;
+ var formattedNumber = '';
// Clear the extension, as that part cannot normally be dialed together with
// the main number.
/** @type {i18n.phonenumbers.PhoneNumber} */
var numberNoExt = number.clone();
numberNoExt.clearExtension();
- /** @type {i18n.phonenumbers.PhoneNumberType} */
- var numberType = this.getNumberType(numberNoExt);
/** @type {string} */
var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
- if (regionCode == 'CO' && regionCallingFrom == 'CO') {
- if (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) {
+ /** @type {i18n.phonenumbers.PhoneNumberType} */
+ var numberType = this.getNumberType(numberNoExt);
+ /** @type {boolean} */
+ var isValidNumber = (numberType != i18n.phonenumbers.PhoneNumberType.UNKNOWN);
+ if (regionCallingFrom == regionCode) {
+ /** @type {boolean} */
+ var isFixedLineOrMobile =
+ (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) ||
+ (numberType == i18n.phonenumbers.PhoneNumberType.MOBILE) ||
+ (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE);
+ // Carrier codes may be needed in some countries. We handle this here.
+ if (regionCode == 'CO' &&
+ numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) {
formattedNumber = this.formatNationalNumberWithCarrierCode(
numberNoExt,
i18n.phonenumbers.PhoneNumberUtil
.COLOMBIA_MOBILE_TO_FIXED_LINE_PREFIX_);
+ } else if (regionCode == 'BR' && isFixedLineOrMobile) {
+ formattedNumber = numberNoExt.hasPreferredDomesticCarrierCode() ?
+ this.formatNationalNumberWithPreferredCarrierCode(numberNoExt, '') :
+ // Brazilian fixed line and mobile numbers need to be dialed with a
+ // carrier code when called within Brazil. Without that, most of the
+ // carriers won't connect the call. Because of that, we return an
+ // empty string here.
+ '';
+ } else if (isValidNumber && regionCode == 'HU') {
+ // The national format for HU numbers doesn't contain the national prefix,
+ // because that is how numbers are normally written down. However, the
+ // national prefix is obligatory when dialing from a mobile phone. As a
+ // result, we add it back here if it is a valid regular length phone
+ // number.
+ formattedNumber =
+ this.getNddPrefixForRegion(regionCode, true /* strip non-digits */) +
+ ' ' + this.format(numberNoExt,
+ i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
+ } else if (countryCallingCode ==
+ i18n.phonenumbers.PhoneNumberUtil.NANPA_COUNTRY_CODE_) {
+ // For NANPA countries, we output international format for numbers that
+ // can be dialed internationally, since that always works, except for
+ // numbers which might potentially be short numbers, which are always
+ // dialled in national format.
+ /** @type {i18n.phonenumbers.PhoneMetadata} */
+ var regionMetadata = this.getMetadataForRegion(regionCallingFrom);
+ if (this.canBeInternationallyDialled(numberNoExt) &&
+ !this.isShorterThanPossibleNormalNumber_(
+ regionMetadata, this.getNationalSignificantNumber(numberNoExt))) {
+ formattedNumber = this.format(
+ numberNoExt, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL);
+ } else {
+ formattedNumber = this.format(
+ numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
+ }
} else {
- // E164 doesn't work at all when dialing within Colombia.
- formattedNumber = this.format(
- numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
+ // For non-geographical countries, Mexican and Chilean fixed line and
+ // mobile numbers, we output international format for numbers that can be
+ // dialed internationally, as that always works.
+ if ((regionCode ==
+ i18n.phonenumbers.PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY ||
+ // MX fixed line and mobile numbers should always be formatted in
+ // international format, even when dialed within MX. For national
+ // format to work, a carrier code needs to be used, and the correct
+ // carrier code depends on if the caller and callee are from the
+ // same local area. It is trickier to get that to work correctly than
+ // using international format, which is tested to work fine on all
+ // carriers.
+ // CL fixed line numbers need the national prefix when dialing in the
+ // national format, but don't have it when used for display. The
+ // reverse is true for mobile numbers. As a result, we output them in
+ // the international format to make it work.
+ ((regionCode == 'MX' || regionCode == 'CL') &&
+ isFixedLineOrMobile)) &&
+ this.canBeInternationallyDialled(numberNoExt)) {
+ formattedNumber = this.format(
+ numberNoExt, i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL);
+ } else {
+ formattedNumber = this.format(
+ numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
+ }
}
- } else if (regionCode == 'PE' && regionCallingFrom == 'PE') {
- // In Peru, numbers cannot be dialled using E164 format from a mobile phone
- // for Movistar. Instead they must be dialled in national format.
- formattedNumber = this.format(
- numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
- } else if (regionCode == 'BR' && regionCallingFrom == 'BR' &&
- ((numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE) ||
- (numberType == i18n.phonenumbers.PhoneNumberType.MOBILE) ||
- (numberType == i18n.phonenumbers.PhoneNumberType.FIXED_LINE_OR_MOBILE))) {
- formattedNumber = numberNoExt.hasPreferredDomesticCarrierCode() ?
- this.formatNationalNumberWithPreferredCarrierCode(numberNoExt, '') :
- // Brazilian fixed line and mobile numbers need to be dialed with a
- // carrier code when called within Brazil. Without that, most of the
- // carriers won't connect the call. Because of that, we return an empty
- // string here.
- '';
- } else if (this.canBeInternationallyDialled(numberNoExt)) {
+ } else if (isValidNumber && this.canBeInternationallyDialled(numberNoExt)) {
+ // We assume that short numbers are not diallable from outside their region,
+ // so if a number is not a valid regular length phone number, we treat it as
+ // if it cannot be internationally dialled.
return withFormatting ?
this.format(numberNoExt,
i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL) :
this.format(numberNoExt, i18n.phonenumbers.PhoneNumberFormat.E164);
- } else {
- formattedNumber = (regionCallingFrom == regionCode) ?
- this.format(numberNoExt, i18n.phonenumbers.PhoneNumberFormat.NATIONAL) :
- '';
}
return withFormatting ?
formattedNumber :
}
} else if (countryCallingCode ==
this.getCountryCodeForValidRegion_(regionCallingFrom)) {
- // For regions that share a country calling code, the country calling code
- // need not be dialled. This also applies when dialling within a region, so
- // this if clause covers both these cases. Technically this is the case for
+ // If regions share a country calling code, the country calling code need
+ // not be dialled. This also applies when dialling within a region, so this
+ // if clause covers both these cases. Technically this is the case for
// dialling from La Reunion to other overseas departments of France (French
// Guiana, Martinique, Guadeloupe), but not vice versa - so we don't cover
// this edge case for now and for those cases return the version including
return this.format(number,
i18n.phonenumbers.PhoneNumberFormat.NATIONAL);
}
+ // Metadata cannot be null because we checked 'isValidRegionCode()' above.
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadataForRegionCallingFrom =
this.getMetadataForRegion(regionCallingFrom);
/** @type {string} */
var regionCode = this.getRegionCodeForCountryCode(countryCallingCode);
+ // Metadata cannot be null because the country calling code is valid.
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadataForRegion =
this.getMetadataForRegionOrCallingCode_(countryCallingCode, regionCode);
formattedNumber = nationalFormat;
break;
}
+ // Metadata cannot be null here because getNddPrefixForRegion() (above)
+ // returns null if there is no metadata for the region.
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadata = this.getMetadataForRegion(regionCode);
/** @type {string} */
/** @type {i18n.phonenumbers.NumberFormat} */
var formatRule = this.chooseFormattingPatternForNumber_(
metadata.numberFormatArray(), nationalNumber);
+ // The format rule could still be null here if the national number was 0
+ // and there was no raw input (this should not be possible for numbers
+ // generated by the phonenumber library as they would also not have a
+ // country calling code and we would have exited earlier).
+ if (formatRule == null) {
+ formattedNumber = nationalFormat;
+ break;
+ }
// When the format we apply to this number doesn't contain national
// prefix, we can just return the national format.
// TODO: Refactor the code below with the code in
// If no digit is inserted/removed/modified as a result of our formatting, we
// return the formatted phone number; otherwise we return the raw input the
// user entered.
- return (formattedNumber != null &&
- i18n.phonenumbers.PhoneNumberUtil
- .normalizeDigitsOnly(formattedNumber) ==
- i18n.phonenumbers.PhoneNumberUtil.normalizeDigitsOnly(rawInput)) ?
- formattedNumber :
- rawInput;
+ if (formattedNumber != null && rawInput.length > 0) {
+ /** @type {string} */
+ var normalizedFormattedNumber =
+ i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(formattedNumber,
+ i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_,
+ true /* remove non matches */);
+ /** @type {string} */
+ var normalizedRawInput =
+ i18n.phonenumbers.PhoneNumberUtil.normalizeHelper_(rawInput,
+ i18n.phonenumbers.PhoneNumberUtil.DIALLABLE_CHAR_MAPPINGS_,
+ true /* remove non matches */);
+ if (normalizedFormattedNumber != normalizedRawInput) {
+ formattedNumber = rawInput;
+ }
+ }
+ return formattedNumber;
};
if (this.isNANPACountry(regionCallingFrom)) {
return countryCode + ' ' + rawInput;
}
- } else if (this.isValidRegionCode_(regionCallingFrom) &&
+ } else if (metadataForRegionCallingFrom != null &&
countryCode == this.getCountryCodeForValidRegion_(regionCallingFrom)) {
/** @type {i18n.phonenumbers.NumberFormat} */
var formattingPattern = this.chooseFormattingPatternForNumber_(
}
/** @type {string} */
var regionCode = this.getRegionCodeForCountryCode(countryCode);
+ // Metadata cannot be null because the country calling code is valid.
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadataForRegion =
this.getMetadataForRegionOrCallingCode_(countryCode, regionCode);
i18n.phonenumbers.PhoneNumberUtil.prototype.getNationalSignificantNumber =
function(number) {
- // If a leading zero has been set, we prefix this now. Note this is not a
+ // If leading zero(s) have been set, we prefix this now. Note this is not a
// national prefix.
/** @type {string} */
var nationalNumber = '' + number.getNationalNumber();
if (number.hasItalianLeadingZero() && number.getItalianLeadingZero()) {
- return '0' + nationalNumber;
+ return Array(number.getNumberOfLeadingZerosOrDefault() + 1).join('0') +
+ nationalNumber;
}
return nationalNumber;
};
/**
- * Note that carrierCode is optional - if NULL or an empty string, no carrier
+ * Note that carrierCode is optional - if null or an empty string, no carrier
* code replacement will take place.
*
* @param {string} nationalNumber a string of characters representing a phone
/** @type {?string} */
var regionCode = this.getRegionCodeForNumber(number);
- if (!this.isValidRegionCode_(regionCode) &&
- i18n.phonenumbers.PhoneNumberUtil.REGION_CODE_FOR_NON_GEO_ENTITY !=
- regionCode) {
+ /** @type {i18n.phonenumbers.PhoneMetadata} */
+ var metadata = this.getMetadataForRegionOrCallingCode_(
+ number.getCountryCodeOrDefault(), regionCode);
+ if (metadata == null) {
return i18n.phonenumbers.PhoneNumberType.UNKNOWN;
}
/** @type {string} */
var nationalSignificantNumber = this.getNationalSignificantNumber(number);
- /** @type {i18n.phonenumbers.PhoneMetadata} */
- var metadata = this.getMetadataForRegionOrCallingCode_(
- number.getCountryCodeOrDefault(), regionCode);
return this.getNumberTypeHelper_(nationalSignificantNumber, metadata);
};
i18n.phonenumbers.PhoneNumberUtil.prototype.getNumberTypeHelper_ =
function(nationalNumber, metadata) {
- /** @type {i18n.phonenumbers.PhoneNumberDesc} */
- var generalNumberDesc = metadata.getGeneralDesc();
- if (!generalNumberDesc.hasNationalNumberPattern() ||
- !this.isNumberMatchingDesc_(nationalNumber, generalNumberDesc)) {
+ if (!this.isNumberMatchingDesc_(nationalNumber, metadata.getGeneralDesc())) {
return i18n.phonenumbers.PhoneNumberType.UNKNOWN;
}
/**
+ * Returns the metadata for the given region code or {@code null} if the region
+ * code is invalid or unknown.
+ *
* @param {?string} regionCode
* @return {i18n.phonenumbers.PhoneMetadata}
*/
* After this, the specific number pattern rules for the region are examined.
* This is useful for determining for example whether a particular number is
* valid for Canada, rather than just a valid NANPA number.
+ * Warning: In most cases, you want to use {@link #isValidNumber} instead. For
+ * example, this method will mark numbers from British Crown dependencies such
+ * as the Isle of Man as invalid for the region "GB" (United Kingdom), since it
+ * has its own region code, "IM", which may be undesirable.
*
* @param {i18n.phonenumbers.PhoneNumber} number the phone number that we want
* to validate.
// number does not match that of the region code.
return false;
}
- /** @type {i18n.phonenumbers.PhoneNumberDesc} */
- var generalNumDesc = metadata.getGeneralDesc();
/** @type {string} */
var nationalSignificantNumber = this.getNationalSignificantNumber(number);
- // For regions where we don't have metadata for PhoneNumberDesc, we treat any
- // number passed in as a valid number if its national significant number is
- // between the minimum and maximum lengths defined by ITU for a national
- // significant number.
- if (!generalNumDesc.hasNationalNumberPattern()) {
- /** @type {number} */
- var numberLength = nationalSignificantNumber.length;
- return numberLength >
- i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_ &&
- numberLength <= i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_;
- }
return this.getNumberTypeHelper_(nationalSignificantNumber, metadata) !=
i18n.phonenumbers.PhoneNumberType.UNKNOWN;
};
for (var i = 0; i < regionCodesLength; i++) {
regionCode = regionCodes[i];
// If leadingDigits is present, use this. Otherwise, do full validation.
+ // Metadata cannot be null because the region codes come from the country
+ // calling code map.
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadata = this.getMetadataForRegion(regionCode);
if (metadata.hasLeadingDigits()) {
/**
+ * Returns a list with the region codes that match the specific country calling
+ * code. For non-geographical country calling codes, the region code 001 is
+ * returned. Also, in the case of no region code being found, an empty list is
+ * returned.
+ *
+ * @param {number} countryCallingCode the country calling code.
+ * @return {Array.<string>}
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.getRegionCodesForCountryCode =
+ function(countryCallingCode) {
+
+ /** @type {Array.<string>} */
+ var regionCodes =
+ i18n.phonenumbers.metadata.countryCodeToRegionCodeMap[countryCallingCode];
+ return regionCodes == null ? [] : regionCodes;
+};
+
+
+/**
* Returns the country calling code for a specific region. For example, this
* would be 1 for the United States, and 64 for New Zealand.
*
* calling code for.
* @return {number} the country calling code for the region denoted by
* regionCode.
+ * @throws {string} if the region is invalid
* @private
*/
i18n.phonenumbers.PhoneNumberUtil.prototype.getCountryCodeForValidRegion_ =
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadata = this.getMetadataForRegion(regionCode);
+ if (metadata == null) {
+ throw 'Invalid region code: ' + regionCode;
+ }
return metadata.getCountryCodeOrDefault();
};
*/
i18n.phonenumbers.PhoneNumberUtil.prototype.getNddPrefixForRegion = function(
regionCode, stripNonDigits) {
- if (!this.isValidRegionCode_(regionCode)) {
- return null;
- }
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadata = this.getMetadataForRegion(regionCode);
+ if (metadata == null) {
+ return null;
+ }
/** @type {string} */
var nationalPrefix = metadata.getNationalPrefixOrDefault();
// If no national prefix was found, we return null.
i18n.phonenumbers.PhoneNumberUtil.prototype.isLeadingZeroPossible =
function(countryCallingCode) {
/** @type {i18n.phonenumbers.PhoneMetadata} */
- var mainMetadataForCallingCode = this.getMetadataForRegion(
+ var mainMetadataForCallingCode = this.getMetadataForRegionOrCallingCode_(
+ countryCallingCode,
this.getRegionCodeForCountryCode(countryCallingCode));
return mainMetadataForCallingCode != null &&
mainMetadataForCallingCode.getLeadingZeroPossibleOrDefault();
/**
+ * Helper method to check whether a number is too short to be a regular length
+ * phone number in a region.
+ *
+ * @param {i18n.phonenumbers.PhoneMetadata} regionMetadata
+ * @param {string} number
+ * @return {boolean}
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.isShorterThanPossibleNormalNumber_ =
+ function(regionMetadata, number) {
+ /** @type {string} */
+ var possibleNumberPattern =
+ regionMetadata.getGeneralDesc().getPossibleNumberPatternOrDefault();
+ return this.testNumberLengthAgainstPattern_(possibleNumberPattern, number) ==
+ i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT;
+};
+
+
+/**
* Check whether a phone number is a possible number. It provides a more lenient
* check than {@link #isValidNumber} in the following sense:
* <ol>
}
/** @type {string} */
var regionCode = this.getRegionCodeForCountryCode(countryCode);
+ // Metadata cannot be null because the country calling code is valid.
/** @type {i18n.phonenumbers.PhoneMetadata} */
var metadata =
this.getMetadataForRegionOrCallingCode_(countryCode, regionCode);
- /** @type {i18n.phonenumbers.PhoneNumberDesc} */
- var generalNumDesc = metadata.getGeneralDesc();
- // Handling case of numbers with no metadata.
- if (!generalNumDesc.hasNationalNumberPattern()) {
- /** @type {number} */
- var numberLength = nationalNumber.length;
- if (numberLength < i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) {
- return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT;
- } else if (numberLength >
- i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_) {
- return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_LONG;
- } else {
- return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE;
- }
- }
/** @type {string} */
var possibleNumberPattern =
- generalNumDesc.getPossibleNumberPatternOrDefault();
+ metadata.getGeneralDesc().getPossibleNumberPatternOrDefault();
return this.testNumberLengthAgainstPattern_(possibleNumberPattern,
nationalNumber);
};
}
if (countryCodeSource !=
i18n.phonenumbers.PhoneNumber.CountryCodeSource.FROM_DEFAULT_COUNTRY) {
- if (fullNumber.getLength() <
+ if (fullNumber.getLength() <=
i18n.phonenumbers.PhoneNumberUtil.MIN_LENGTH_FOR_NSN_) {
throw i18n.phonenumbers.Error.TOO_SHORT_AFTER_IDD;
}
i18n.phonenumbers.PhoneNumberUtil.prototype.
maybeStripNationalPrefixAndCarrierCode = function(number, metadata,
carrierCode) {
-
/** @type {string} */
var numberStr = number.toString();
/** @type {number} */
/** @type {!RegExp} */
var nationalNumberRule = new RegExp(
metadata.getGeneralDesc().getNationalNumberPatternOrDefault());
+ // Check if the original number is viable.
+ /** @type {boolean} */
+ var isViableOriginalNumber =
+ i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
+ nationalNumberRule, numberStr);
// prefixMatcher[numOfGroups] == null implies nothing was captured by the
// capturing groups in possibleNationalPrefix; therefore, no transformation
// is necessary, and we just remove the national prefix.
var numOfGroups = prefixMatcher.length - 1;
/** @type {?string} */
var transformRule = metadata.getNationalPrefixTransformRule();
- /** @type {string} */
- var transformedNumber;
/** @type {boolean} */
var noTransform = transformRule == null || transformRule.length == 0 ||
prefixMatcher[numOfGroups] == null ||
prefixMatcher[numOfGroups].length == 0;
if (noTransform) {
- transformedNumber = numberStr.substring(prefixMatcher[0].length);
+ // If the original number was viable, and the resultant number is not,
+ // we return.
+ if (isViableOriginalNumber &&
+ !i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
+ nationalNumberRule,
+ numberStr.substring(prefixMatcher[0].length))) {
+ return false;
+ }
+ if (carrierCode != null &&
+ numOfGroups > 0 && prefixMatcher[numOfGroups] != null) {
+ carrierCode.append(prefixMatcher[1]);
+ }
+ number.set(numberStr.substring(prefixMatcher[0].length));
+ return true;
} else {
+ // Check that the resultant number is still viable. If not, return. Check
+ // this by copying the string buffer and making the transformation on the
+ // copy first.
+ /** @type {string} */
+ var transformedNumber;
transformedNumber = numberStr.replace(prefixPattern, transformRule);
- }
- // If the original number was viable, and the resultant number is not,
- // we return.
- if (i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
- nationalNumberRule, numberStr) &&
- !i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
- nationalNumberRule, transformedNumber)) {
- return false;
- }
- if ((noTransform && numOfGroups > 0 && prefixMatcher[1] != null) ||
- (!noTransform && numOfGroups > 1)) {
- if (carrierCode != null) {
+ if (isViableOriginalNumber &&
+ !i18n.phonenumbers.PhoneNumberUtil.matchesEntirely_(
+ nationalNumberRule, transformedNumber)) {
+ return false;
+ }
+ if (carrierCode != null && numOfGroups > 0) {
carrierCode.append(prefixMatcher[1]);
}
+ number.set(transformedNumber);
+ return true;
}
- number.clear();
- number.append(transformedNumber);
- return true;
}
return false;
};
*
* @param {?string} numberToParse number that we are attempting to parse. This
* can contain formatting such as +, ( and -, as well as a phone number
- * extension.
+ * extension. It can also be provided in RFC3966 format.
* @param {?string} defaultRegion region that we are expecting the number to be
* from. This is only used if the number being parsed is not written in
* international format. The country_code for the number in this case would
/**
+ * A helper function to set the values related to leading zeros in a
+ * PhoneNumber.
+ *
+ * @param {string} nationalNumber the number we are parsing.
+ * @param {i18n.phonenumbers.PhoneNumber} phoneNumber a phone number proto
+ * buffer to fill in.
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.setItalianLeadingZerosForPhoneNumber_ =
+ function(nationalNumber, phoneNumber) {
+ if (nationalNumber.length > 1 && nationalNumber.charAt(0) == '0') {
+ phoneNumber.setItalianLeadingZero(true);
+ var numberOfLeadingZeros = 1;
+ // Note that if the national number is all "0"s, the last "0" is not counted
+ // as a leading zero.
+ while (numberOfLeadingZeros < nationalNumber.length - 1 &&
+ nationalNumber.charAt(numberOfLeadingZeros) == '0') {
+ numberOfLeadingZeros++;
+ }
+ if (numberOfLeadingZeros != 1) {
+ phoneNumber.setNumberOfLeadingZeros(numberOfLeadingZeros);
+ }
+ }
+};
+
+
+/**
* Parses a string and returns it in proto buffer format. This method is the
* same as the public {@link #parse} method, with the exception that it allows
* the default region to be null, for use by {@link #isNumberMatch}.
throw i18n.phonenumbers.Error.NOT_A_NUMBER;
} else if (numberToParse.length >
i18n.phonenumbers.PhoneNumberUtil.MAX_INPUT_STRING_LENGTH_) {
- throw 'The string supplied was too long to parse';
+ throw i18n.phonenumbers.Error.TOO_LONG;
}
- /** @type {number} */
- var indexOfPhoneContext = numberToParse.indexOf(
- i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_);
/** @type {!goog.string.StringBuffer} */
var nationalNumber = new goog.string.StringBuffer();
- if (indexOfPhoneContext > 0) {
- // Prefix the number with the phone context. The offset here is because the
- // context we are expecting to match should start with a "+" sign, and we
- // want to include this at the start of the number.
- nationalNumber.append(numberToParse.substring(
- indexOfPhoneContext +
- i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_.length - 1));
- // Now append everything between the "tel:" prefix and the phone-context.
- nationalNumber.append(numberToParse.substring(
- numberToParse.indexOf(
- i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_) +
- i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_.length,
- indexOfPhoneContext));
- // Note that phone-contexts that are URLs will not be parsed -
- // isViablePhoneNumber will throw an exception below.
- } else {
- // Extract a possible number from the string passed in (this strips leading
- // characters that could not be the start of a phone number.)
- nationalNumber.append(
- i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber(numberToParse));
- }
+ this.buildNationalNumberForParsing_(numberToParse, nationalNumber);
if (!i18n.phonenumbers.PhoneNumberUtil.isViablePhoneNumber(
nationalNumber.toString())) {
/** @type {string} */
var phoneNumberRegion = this.getRegionCodeForCountryCode(countryCode);
if (phoneNumberRegion != defaultRegion) {
+ // Metadata cannot be null because the country calling code is valid.
regionMetadata = this.getMetadataForRegionOrCallingCode_(
countryCode, phoneNumberRegion);
}
}
if (regionMetadata != null) {
- /** @type {goog.string.StringBuffer} */
+ /** @type {!goog.string.StringBuffer} */
var carrierCode = new goog.string.StringBuffer();
+ /** @type {!goog.string.StringBuffer} */
+ var potentialNationalNumber =
+ new goog.string.StringBuffer(normalizedNationalNumber.toString());
this.maybeStripNationalPrefixAndCarrierCode(
- normalizedNationalNumber, regionMetadata, carrierCode);
- if (keepRawInput) {
- phoneNumber.setPreferredDomesticCarrierCode(carrierCode.toString());
+ potentialNationalNumber, regionMetadata, carrierCode);
+ if (!this.isShorterThanPossibleNormalNumber_(
+ regionMetadata, potentialNationalNumber.toString())) {
+ normalizedNationalNumber = potentialNationalNumber;
+ if (keepRawInput) {
+ phoneNumber.setPreferredDomesticCarrierCode(carrierCode.toString());
+ }
}
}
/** @type {string} */
i18n.phonenumbers.PhoneNumberUtil.MAX_LENGTH_FOR_NSN_) {
throw i18n.phonenumbers.Error.TOO_LONG;
}
- if (normalizedNationalNumberStr.charAt(0) == '0') {
- phoneNumber.setItalianLeadingZero(true);
- }
+ this.setItalianLeadingZerosForPhoneNumber_(
+ normalizedNationalNumberStr, phoneNumber);
phoneNumber.setNationalNumber(parseInt(normalizedNationalNumberStr, 10));
return phoneNumber;
};
/**
+ * Converts numberToParse to a form that we can parse and write it to
+ * nationalNumber if it is written in RFC3966; otherwise extract a possible
+ * number out of it and write to nationalNumber.
+ *
+ * @param {?string} numberToParse number that we are attempting to parse. This
+ * can contain formatting such as +, ( and -, as well as a phone number
+ * extension.
+ * @param {!goog.string.StringBuffer} nationalNumber a string buffer for storing
+ * the national significant number.
+ * @private
+ */
+i18n.phonenumbers.PhoneNumberUtil.prototype.buildNationalNumberForParsing_ =
+ function(numberToParse, nationalNumber) {
+
+ /** @type {number} */
+ var indexOfPhoneContext = numberToParse.indexOf(
+ i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_);
+ if (indexOfPhoneContext > 0) {
+ var phoneContextStart = indexOfPhoneContext +
+ i18n.phonenumbers.PhoneNumberUtil.RFC3966_PHONE_CONTEXT_.length;
+ // If the phone context contains a phone number prefix, we need to capture
+ // it, whereas domains will be ignored.
+ if (numberToParse.charAt(phoneContextStart) ==
+ i18n.phonenumbers.PhoneNumberUtil.PLUS_SIGN) {
+ // Additional parameters might follow the phone context. If so, we will
+ // remove them here because the parameters after phone context are not
+ // important for parsing the phone number.
+ var phoneContextEnd = numberToParse.indexOf(';', phoneContextStart);
+ if (phoneContextEnd > 0) {
+ nationalNumber.append(numberToParse.substring(phoneContextStart,
+ phoneContextEnd));
+ } else {
+ nationalNumber.append(numberToParse.substring(phoneContextStart));
+ }
+ }
+
+ // Now append everything between the "tel:" prefix and the phone-context.
+ // This should include the national number, an optional extension or
+ // isdn-subaddress component. Note we also handle the case when "tel:" is
+ // missing, as we have seen in some of the phone number inputs.
+ // In that case, we append everything from the beginning.
+ var indexOfRfc3966Prefix = numberToParse.indexOf(
+ i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_);
+ var indexOfNationalNumber = (indexOfRfc3966Prefix >= 0) ?
+ indexOfRfc3966Prefix +
+ i18n.phonenumbers.PhoneNumberUtil.RFC3966_PREFIX_.length : 0;
+ nationalNumber.append(numberToParse.substring(indexOfNationalNumber,
+ indexOfPhoneContext));
+ } else {
+ // Extract a possible number from the string passed in (this strips leading
+ // characters that could not be the start of a phone number.)
+ nationalNumber.append(
+ i18n.phonenumbers.PhoneNumberUtil.extractPossibleNumber(numberToParse));
+ }
+
+ // Delete the isdn-subaddress and everything after it if it is present.
+ // Note extension won't appear at the same time with isdn-subaddress
+ // according to paragraph 5.3 of the RFC3966 spec,
+ /** @type {string} */
+ var nationalNumberStr = nationalNumber.toString();
+ var indexOfIsdn = nationalNumberStr.indexOf(
+ i18n.phonenumbers.PhoneNumberUtil.RFC3966_ISDN_SUBADDRESS_);
+ if (indexOfIsdn > 0) {
+ nationalNumber.clear();
+ nationalNumber.append(nationalNumberStr.substring(0, indexOfIsdn));
+ }
+ // If both phone context and isdn-subaddress are absent but other
+ // parameters are present, the parameters are left in nationalNumber. This
+ // is because we are concerned about deleting content from a potential
+ // number string when there is no strong evidence that the number is
+ // actually written in RFC3966.
+};
+
+
+/**
* Takes two phone numbers and compares them for equality.
*
* <p>Returns EXACT_MATCH if the country_code, NSN, presence of a leading zero
*/
i18n.phonenumbers.PhoneNumberUtil.prototype.canBeInternationallyDialled =
function(number) {
- /** @type {?string} */
- var regionCode = this.getRegionCodeForNumber(number);
- if (!this.isValidRegionCode_(regionCode)) {
+ /** @type {i18n.phonenumbers.PhoneMetadata} */
+ var metadata = this.getMetadataForRegion(this.getRegionCodeForNumber(number));
+ if (metadata == null) {
// Note numbers belonging to non-geographical entities (e.g. +800 numbers)
// are always internationally diallable, and will be caught here.
return true;
}
- /** @type {i18n.phonenumbers.PhoneMetadata} */
- var metadata = this.getMetadataForRegion(regionCode);
/** @type {string} */
var nationalSignificantNumber = this.getNationalSignificantNumber(number);
return !this.isNumberMatchingDesc_(nationalSignificantNumber,