| /** |
| * @license |
| * Copyright (C) 2018 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. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /** |
| * @fileoverview Utility for international phone numbers. |
| * Functionality includes formatting, parsing and validation. |
| * (based on the java implementation). |
| * |
| * NOTE: A lot of methods in this class require Region Code strings. These must |
| * be provided using CLDR two-letter region-code format. These should be in |
| * upper-case. The list of the codes can be found here: |
| * http://www.unicode.org/cldr/charts/30/supplemental/territory_information.html |
| * |
| * @author James Wright |
| */ |
| |
| goog.provide('i18n.phonenumbers.ShortNumberInfo'); |
| |
| goog.require('goog.proto2.PbLiteSerializer'); |
| goog.require('i18n.phonenumbers.PhoneMetadata'); |
| goog.require('i18n.phonenumbers.PhoneNumber'); |
| goog.require('i18n.phonenumbers.PhoneNumberDesc'); |
| goog.require('i18n.phonenumbers.PhoneNumberUtil'); |
| goog.require('i18n.phonenumbers.metadata'); |
| goog.require('i18n.phonenumbers.shortnumbermetadata'); |
| |
| |
| |
| /** |
| * @constructor |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo = function() { |
| /** |
| * A mapping from region code to the short-number metadata for that region. |
| * @type {Object.<string, i18n.phonenumbers.PhoneMetadata>} |
| */ |
| this.regionToMetadataMap = {}; |
| }; |
| goog.addSingletonGetter(i18n.phonenumbers.ShortNumberInfo); |
| |
| |
| /** |
| * In these countries, if extra digits are added to an emergency number, it no |
| * longer connects to the emergency service. |
| * @const |
| * @type {!Array<string>} |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo. |
| REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT_ = [ |
| 'BR', |
| 'CL', |
| 'NI' |
| ]; |
| |
| |
| /** |
| * @enum {number} Cost categories of short numbers. |
| */ |
| i18n.phonenumbers.ShortNumberInfo.ShortNumberCost = { |
| TOLL_FREE: 0, |
| STANDARD_RATE: 1, |
| PREMIUM_RATE: 2, |
| UNKNOWN_COST: 3 |
| }; |
| |
| |
| /** |
| * 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 |
| * @return {!Array<string>} The region codes that match the given country code. |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.getRegionCodesForCountryCode_ = |
| function(countryCallingCode) { |
| var regionCodes = i18n.phonenumbers.metadata |
| .countryCodeToRegionCodeMap[countryCallingCode]; |
| return regionCodes ? regionCodes : []; |
| }; |
| |
| |
| /** |
| * Helper method to check that the country calling code of the number matches |
| * the region it's being dialed from. |
| * @param {i18n.phonenumbers.PhoneNumber} number |
| * @param {?string} regionDialingFrom |
| * @return {boolean} |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.regionDialingFromMatchesNumber_ = |
| function(number, regionDialingFrom) { |
| var regionCodes = this.getRegionCodesForCountryCode_( |
| number.getCountryCodeOrDefault()); |
| return regionDialingFrom != null && regionCodes.includes(regionDialingFrom); |
| }; |
| |
| |
| /** |
| * Check whether a short number is a possible number when dialed from the given |
| * region. This provides a more lenient check than |
| * {@link #isValidShortNumberForRegion}. |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the short number to check |
| * @param {string} regionDialingFrom the region from which the number is dialed |
| * @return {boolean} whether the number is a possible short number |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.isPossibleShortNumberForRegion = |
| function(number, regionDialingFrom) { |
| if (!this.regionDialingFromMatchesNumber_(number, regionDialingFrom)) { |
| return false; |
| } |
| var phoneMetadata = this.getMetadataForRegion_(regionDialingFrom); |
| if (!phoneMetadata) { |
| return false; |
| } |
| var numberLength = this.getNationalSignificantNumber_(number).length; |
| return phoneMetadata.getGeneralDesc().possibleLengthArray().includes( |
| numberLength); |
| }; |
| |
| |
| /** |
| * Check whether a short number is a possible number. If a country calling code |
| * is shared by multiple regions, this returns true if it's possible in any of |
| * them. This provides a more lenient check than {@link #isValidShortNumber}. |
| * See {@link #isPossibleShortNumberForRegion(PhoneNumber, String)} for details. |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the short number to check |
| * @return {boolean} whether the number is a possible short number |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.isPossibleShortNumber = |
| function(number) { |
| var regionCodes = this.getRegionCodesForCountryCode_( |
| number.getCountryCodeOrDefault()); |
| var shortNumberLength = this.getNationalSignificantNumber_(number).length; |
| for (var i = 0; i < regionCodes.length; i++) { |
| var region = regionCodes[i]; |
| var phoneMetadata = this.getMetadataForRegion_(region); |
| if (!phoneMetadata) { |
| continue; |
| } |
| var possibleLengths = phoneMetadata.getGeneralDesc().possibleLengthArray(); |
| if (possibleLengths.includes(shortNumberLength)) { |
| return true; |
| } |
| } |
| return false; |
| }; |
| |
| |
| /** |
| * Tests whether a short number matches a valid pattern in a region. Note that |
| * this doesn't verify the number is actually in use, which is impossible to |
| * tell by just looking at the number itself. |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the short number for which we |
| * want to test the validity |
| * @param {?string} regionDialingFrom the region from which the number is dialed |
| * @return {boolean} whether the short number matches a valid pattern |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.isValidShortNumberForRegion = |
| function(number, regionDialingFrom) { |
| if (!this.regionDialingFromMatchesNumber_(number, regionDialingFrom)) { |
| return false; |
| } |
| var phoneMetadata = this.getMetadataForRegion_(regionDialingFrom); |
| if (!phoneMetadata) { |
| return false; |
| } |
| var shortNumber = this.getNationalSignificantNumber_(number); |
| var generalDesc = phoneMetadata.getGeneralDesc(); |
| if (!this.matchesPossibleNumberAndNationalNumber_(shortNumber, generalDesc)) { |
| return false; |
| } |
| var shortNumberDesc = phoneMetadata.getShortCode(); |
| return this.matchesPossibleNumberAndNationalNumber_(shortNumber, |
| shortNumberDesc); |
| }; |
| |
| |
| /** |
| * Tests whether a short number matches a valid pattern. If a country calling |
| * code is shared by multiple regions, this returns true if it's valid in any of |
| * them. Note that this doesn't verify the number is actually in use, which is |
| * impossible to tell by just looking at the number itself. See |
| * {@link #isValidShortNumberForRegion(PhoneNumber, String)} for details. |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the short number for which we |
| * want to test the validity |
| * @return {boolean} whether the short number matches a valid pattern |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.isValidShortNumber = |
| function(number) { |
| var regionCodes = this.getRegionCodesForCountryCode_( |
| number.getCountryCodeOrDefault()); |
| var regionCode = this.getRegionCodeForShortNumberFromRegionList_(number, |
| regionCodes); |
| if (regionCodes.length > 1 && regionCode != null) { |
| // If a matching region had been found for the phone number from among two |
| // or more regions, then we have already implicitly verified its validity |
| // for that region. |
| return true; |
| } |
| return this.isValidShortNumberForRegion(number, regionCode); |
| }; |
| |
| |
| /** |
| * Gets the expected cost category of a short number when dialed from a region |
| * (however, nothing is implied about its validity). If it is important that the |
| * number is valid, then its validity must first be checked using |
| * {@link #isValidShortNumberForRegion}. Note that emergency numbers are always |
| * considered toll-free. Example usage: |
| * <pre>{@code |
| * // The region for which the number was parsed and the region we subsequently |
| * // check against need not be the same. Here we parse the number in the US and |
| * // check it for Canada. |
| * PhoneNumber number = phoneUtil.parse("110", "US"); |
| * ... |
| * String regionCode = "CA"; |
| * ShortNumberInfo shortInfo = ShortNumberInfo.getInstance(); |
| * if (shortInfo.isValidShortNumberForRegion(shortNumber, regionCode)) { |
| * ShortNumberCost cost = shortInfo.getExpectedCostForRegion(number, |
| * regionCode); |
| * // Do something with the cost information here. |
| * }}</pre> |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the short number for which we |
| * want to know the expected cost category |
| * @param {string} regionDialingFrom the region from which the number is dialed |
| * @return {i18n.phonenumbers.ShortNumberInfo.ShortNumberCost} the expected cost |
| * category for that region of the short number. Returns UNKNOWN_COST if the |
| * number does not match a cost category. Note that an invalid number may |
| * match any cost category. |
| * @package |
| */ |
| // @VisibleForTesting |
| i18n.phonenumbers.ShortNumberInfo.prototype.getExpectedCostForRegion = |
| function(number, regionDialingFrom) { |
| var ShortNumberCost = i18n.phonenumbers.ShortNumberInfo.ShortNumberCost; |
| if (!this.regionDialingFromMatchesNumber_(number, regionDialingFrom)) { |
| return ShortNumberCost.UNKNOWN_COST; |
| } |
| var phoneMetadata = this.getMetadataForRegion_(regionDialingFrom); |
| if (!phoneMetadata) { |
| return ShortNumberCost.UNKNOWN_COST; |
| } |
| var shortNumber = this.getNationalSignificantNumber_(number); |
| |
| if (!phoneMetadata.getGeneralDesc().possibleLengthArray().includes( |
| shortNumber.length)) { |
| return ShortNumberCost.UNKNOWN_COST; |
| } |
| if (this.matchesPossibleNumberAndNationalNumber_( |
| shortNumber, phoneMetadata.getPremiumRate())) { |
| return ShortNumberCost.PREMIUM_RATE; |
| } |
| if (this.matchesPossibleNumberAndNationalNumber_( |
| shortNumber, phoneMetadata.getStandardRate())) { |
| return ShortNumberCost.STANDARD_RATE; |
| } |
| if (this.matchesPossibleNumberAndNationalNumber_( |
| shortNumber, phoneMetadata.getTollFree())) { |
| return ShortNumberCost.TOLL_FREE; |
| } |
| if (this.isEmergencyNumber(shortNumber, regionDialingFrom)) { |
| // Emergency numbers are implicitly toll-free |
| return ShortNumberCost.TOLL_FREE; |
| } |
| return ShortNumberCost.UNKNOWN_COST; |
| }; |
| |
| |
| /** |
| * Gets the expected cost category of a short number (however, nothing is |
| * implied about its validity). If the country calling code is unique to a |
| * region, this method behaves exactly the same as |
| * {@link #getExpectedCostForRegion(PhoneNumber, String)}. However, if the |
| * country calling code is shared by multiple regions, then it returns the |
| * highest cost in the sequence PREMIUM_RATE, UNKNOWN_COST, STANDARD_RATE, |
| * TOLL_FREE. The reason for the position of UNKNOWN_COST in this order is that |
| * if a number is UNKNOWN_COST in one region but STANDARD_RATE or TOLL_FREE in |
| * another, its expected cost cannot be estimated as one of the latter since it |
| * might be a PREMIUM_RATE number. |
| * <p> |
| * For example, if a number is STANDARD_RATE in the US, but TOLL_FREE in Canada, |
| * the expected cost returned by this method will be STANDARD_RATE, since the |
| * NANPA countries share the same country calling code. |
| * <p> |
| * Note: If the region from which the number is dialed is known, it is highly |
| * preferable to call {@link #getExpectedCostForRegion(PhoneNumber, String)} |
| * instead. |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the short number for which we |
| * want to know the expected cost category |
| * @return {i18n.phonenumbers.ShortNumberInfo.ShortNumberCost} the highest |
| * expected cost category of the short number in the region(s) with the |
| * given country calling code |
| * @package |
| */ |
| // @VisibleForTesting |
| i18n.phonenumbers.ShortNumberInfo.prototype.getExpectedCost = function(number) { |
| var ShortNumberCost = i18n.phonenumbers.ShortNumberInfo.ShortNumberCost; |
| var regionCodes = this.getRegionCodesForCountryCode_( |
| number.getCountryCodeOrDefault()); |
| if (regionCodes.length === 0) { |
| return ShortNumberCost.UNKNOWN_COST; |
| } |
| if (regionCodes.length === 1) { |
| return this.getExpectedCostForRegion(number, regionCodes[0]); |
| } |
| var cost = ShortNumberCost.TOLL_FREE; |
| for (var i = 0; i < regionCodes.length; i++) { |
| var regionCode = regionCodes[i]; |
| var costForRegion = this.getExpectedCostForRegion(number, regionCode); |
| switch (costForRegion) { |
| case ShortNumberCost.PREMIUM_RATE: |
| return ShortNumberCost.PREMIUM_RATE; |
| case ShortNumberCost.UNKNOWN_COST: |
| cost = ShortNumberCost.UNKNOWN_COST; |
| break; |
| case ShortNumberCost.STANDARD_RATE: |
| if (cost !== ShortNumberCost.UNKNOWN_COST) { |
| cost = ShortNumberCost.STANDARD_RATE; |
| } |
| break; |
| case ShortNumberCost.TOLL_FREE: |
| // Do nothing. |
| break; |
| default: |
| throw new Error('Unrecognized cost for region: ' + costForRegion); |
| } |
| } |
| return cost; |
| }; |
| |
| |
| /** |
| * Helper method to get the region code for a given phone number, from a list |
| * of possible region codes. If the list contains more than one region, the |
| * first region for which the number is valid is returned. |
| * @param {!i18n.phonenumbers.PhoneNumber} number |
| * @param {Array<string>} regionCodes |
| * @return {?string} |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.getRegionCodeForShortNumberFromRegionList_ = |
| function(number, regionCodes) { |
| if (regionCodes.length === 0) { |
| return null; |
| } else if (regionCodes.length === 1) { |
| return regionCodes[0]; |
| } |
| var nationalNumber = this.getNationalSignificantNumber_(number); |
| for (var i = 0; i < regionCodes.length; i++) { |
| var regionCode = regionCodes[i]; |
| var phoneMetadata = this.getMetadataForRegion_(regionCode); |
| if (phoneMetadata && this.matchesPossibleNumberAndNationalNumber_( |
| nationalNumber, phoneMetadata.getShortCode())) { |
| return regionCode; |
| } |
| } |
| return null; |
| }; |
| |
| |
| /** |
| * Convenience method to get a list of what regions the library has metadata for |
| * @return {!Array<string>} the list of region codes |
| * @package |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.getSupportedRegions = function() { |
| return Object.keys(i18n.phonenumbers.shortnumbermetadata.countryToMetadata) |
| .filter(function(regionCode) { |
| return isNaN(regionCode); |
| }); |
| }; |
| |
| |
| /** |
| * Gets a valid short number for the specified region. |
| * |
| * @param {?string} regionCode the region for which an example short number is |
| * needed |
| * @return {string} a valid short number for the specified region. Returns an |
| * empty string when the metadata does not contain such information. |
| * @package |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.getExampleShortNumber = |
| function(regionCode) { |
| var phoneMetadata = this.getMetadataForRegion_(regionCode); |
| if (!phoneMetadata) { |
| return ''; |
| } |
| var desc = phoneMetadata.getShortCode(); |
| if (desc.hasExampleNumber()) { |
| return desc.getExampleNumber() || ''; |
| } |
| return ''; |
| }; |
| |
| |
| /** |
| * Gets a valid short number for the specified cost category. |
| * |
| * @param {string} regionCode the region for which an example short number is |
| * needed |
| * @param {i18n.phonenumbers.ShortNumberInfo.ShortNumberCost} cost the cost |
| * category of number that is needed |
| * @return {string} a valid short number for the specified region and cost |
| * category. Returns an empty string when the metadata does not contain such |
| * information, or the cost is UNKNOWN_COST. |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.getExampleShortNumberForCost = |
| function(regionCode, cost) { |
| var phoneMetadata = this.getMetadataForRegion_(regionCode); |
| if (!phoneMetadata) { |
| return ''; |
| } |
| var ShortNumberCost = i18n.phonenumbers.ShortNumberInfo.ShortNumberCost; |
| var desc = null; |
| switch (cost) { |
| case ShortNumberCost.TOLL_FREE: |
| desc = phoneMetadata.getTollFree(); |
| break; |
| case ShortNumberCost.STANDARD_RATE: |
| desc = phoneMetadata.getStandardRate(); |
| break; |
| case ShortNumberCost.PREMIUM_RATE: |
| desc = phoneMetadata.getPremiumRate(); |
| break; |
| default: |
| // UNKNOWN_COST numbers are computed by the process of elimination from |
| // the other cost categories. |
| } |
| if (desc && desc.hasExampleNumber()) { |
| return desc.getExampleNumber() || ''; |
| } |
| return ''; |
| }; |
| |
| |
| /** |
| * Returns true if the given number, exactly as dialed, might be used to |
| * connect to an emergency service in the given region. |
| * <p> |
| * This method accepts a string, rather than a PhoneNumber, because it needs |
| * to distinguish cases such as "+1 911" and "911", where the former may not |
| * connect to an emergency service in all cases but the latter would. This |
| * method takes into account cases where the number might contain formatting, |
| * or might have additional digits appended (when it is okay to do that in |
| * the specified region). |
| * |
| * @param {string} number the phone number to test |
| * @param {string} regionCode the region where the phone number is being |
| * dialed |
| * @return {boolean} whether the number might be used to connect to an |
| * emergency service in the given region |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.connectsToEmergencyNumber = |
| function(number, regionCode) { |
| return this.matchesEmergencyNumberHelper_(number, regionCode, |
| true /* allows prefix match */); |
| }; |
| |
| |
| /** |
| * Returns true if the given number exactly matches an emergency service |
| * number in the given region. |
| * <p> |
| * This method takes into account cases where the number might contain |
| * formatting, but doesn't allow additional digits to be appended. Note that |
| * {@code isEmergencyNumber(number, region)} implies |
| * {@code connectsToEmergencyNumber(number, region)}. |
| * |
| * @param {string} number the phone number to test |
| * @param {string} regionCode the region where the phone number is being |
| * dialed |
| * @return {boolean} whether the number exactly matches an emergency services |
| * number in the given region. |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.isEmergencyNumber = |
| function(number, regionCode) { |
| return this.matchesEmergencyNumberHelper_(number, regionCode, |
| false /* doesn't allow prefix match */); |
| }; |
| |
| |
| /** |
| * @param {?string} regionCode The region code to get metadata for |
| * @return {?i18n.phonenumbers.PhoneMetadata} The region code's metadata, or |
| * null if it is not available or the region code is invalid. |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.getMetadataForRegion_ = |
| function(regionCode) { |
| if (!regionCode) { |
| return null; |
| } |
| regionCode = regionCode.toUpperCase(); |
| var metadata = this.regionToMetadataMap[regionCode]; |
| if (metadata == null) { |
| /** @type {goog.proto2.PbLiteSerializer} */ |
| var serializer = new goog.proto2.PbLiteSerializer(); |
| var metadataSerialized = |
| i18n.phonenumbers.shortnumbermetadata.countryToMetadata[regionCode]; |
| if (metadataSerialized == null) { |
| return null; |
| } |
| metadata = /** @type {i18n.phonenumbers.PhoneMetadata} */ ( |
| serializer.deserialize(i18n.phonenumbers.PhoneMetadata.getDescriptor(), |
| metadataSerialized)); |
| this.regionToMetadataMap[regionCode] = metadata; |
| } |
| return metadata; |
| }; |
| |
| |
| /** |
| * @param {string} number the number to match against |
| * @param {string} regionCode the region code to check against |
| * @param {boolean} allowPrefixMatch whether to allow prefix matching |
| * @return {boolean} True iff the number matches an emergency number for that |
| * particular region. |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.matchesEmergencyNumberHelper_ = |
| function(number, regionCode, allowPrefixMatch) { |
| var possibleNumber = i18n.phonenumbers.PhoneNumberUtil |
| .extractPossibleNumber(number); |
| if (i18n.phonenumbers.PhoneNumberUtil.LEADING_PLUS_CHARS_PATTERN |
| .test(possibleNumber)) { |
| return false; |
| } |
| var metadata = this.getMetadataForRegion_(regionCode); |
| if (metadata == null || !metadata.hasEmergency()) { |
| return false; |
| } |
| |
| var normalizedNumber = i18n.phonenumbers.PhoneNumberUtil |
| .normalizeDigitsOnly(possibleNumber); |
| var allowPrefixMatchForRegion = allowPrefixMatch && |
| !i18n.phonenumbers.ShortNumberInfo |
| .REGIONS_WHERE_EMERGENCY_NUMBERS_MUST_BE_EXACT_.includes(regionCode); |
| var emergencyNumberPattern = metadata.getEmergency() |
| .getNationalNumberPatternOrDefault(); |
| var result = i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( |
| emergencyNumberPattern, normalizedNumber); |
| return result || |
| (allowPrefixMatchForRegion && |
| i18n.phonenumbers.PhoneNumberUtil |
| .matchesPrefix(emergencyNumberPattern, normalizedNumber)); |
| }; |
| |
| |
| /** |
| * Given a valid short number, determines whether it is carrier-specific |
| * (however, nothing is implied about its validity). Carrier-specific numbers |
| * may connect to a different end-point, or not connect at all, depending on |
| * the user's carrier. If it is important that the number is valid, then its |
| * validity must first be checked using {@link #isValidShortNumber} or |
| * {@link #isValidShortNumberForRegion}. |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the valid short number to |
| * check |
| * @return {boolean} whether the short number is carrier-specific, assuming the |
| * input was a valid short number |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.isCarrierSpecific = |
| function(number) { |
| var regionCodes = this.getRegionCodesForCountryCode_( |
| number.getCountryCodeOrDefault()); |
| var regionCode = this.getRegionCodeForShortNumberFromRegionList_(number, |
| regionCodes); |
| var nationalNumber = this.getNationalSignificantNumber_(number); |
| var phoneMetadata = this.getMetadataForRegion_(regionCode); |
| return !!phoneMetadata && this.matchesPossibleNumberAndNationalNumber_( |
| nationalNumber, phoneMetadata.getCarrierSpecific()); |
| }; |
| |
| |
| /** |
| * Given a valid short number, determines whether it is carrier-specific when |
| * dialed from the given region (however, nothing is implied about its |
| * validity). Carrier-specific numbers may connect to a different end-point, or |
| * not connect at all, depending on the user's carrier. If it is important that |
| * the number is valid, then its validity must first be checked using |
| * {@link #isValidShortNumber} or {@link #isValidShortNumberForRegion}. Returns |
| * false if the number doesn't match the region provided. |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the valid short number to |
| * check |
| * @param {string} regionDialingFrom the region from which the number is dialed |
| * @return {boolean} whether the short number is carrier-specific in the |
| * provided region, assuming the input was a valid short number |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.isCarrierSpecificForRegion = |
| function(number, regionDialingFrom) { |
| if (!this.regionDialingFromMatchesNumber_(number, regionDialingFrom)) { |
| return false; |
| } |
| var nationalNumber = this.getNationalSignificantNumber_(number); |
| var phoneMetadata = this.getMetadataForRegion_(regionDialingFrom); |
| return !!phoneMetadata && this.matchesPossibleNumberAndNationalNumber_( |
| nationalNumber, phoneMetadata.getCarrierSpecific()); |
| }; |
| |
| |
| /** |
| * Given a valid short number, determines whether it is an SMS service |
| * (however, nothing is implied about its validity). An SMS service is where the |
| * primary or only intended usage is to receive and/or send text messages |
| * (SMSs). This includes MMS as MMS numbers downgrade to SMS if the other party |
| * isn't MMS-capable. If it is important that the number is valid, then its |
| * validity must first be checked using {@link #isValidShortNumber} or {@link |
| * #isValidShortNumberForRegion}. Returns false if the number doesn't match the |
| * region provided. |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the valid short number to |
| * check |
| * @param {string} regionDialingFrom the region from which the number is dialed |
| * @return {boolean} whether the short number is an SMS service in the provided |
| * region, assuming the input was a valid short number |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.isSmsServiceForRegion = |
| function(number, regionDialingFrom) { |
| if (!this.regionDialingFromMatchesNumber_(number, regionDialingFrom)) { |
| return false; |
| } |
| var phoneMetadata = this.getMetadataForRegion_(regionDialingFrom); |
| var nationalNumber = this.getNationalSignificantNumber_(number); |
| return !!phoneMetadata && this.matchesPossibleNumberAndNationalNumber_( |
| nationalNumber, phoneMetadata.getSmsServices()); |
| }; |
| |
| |
| /** |
| * Gets the national significant number of a phone number. Note a national |
| * significant number doesn't contain a national prefix or any formatting. |
| * <p> |
| * This is a temporary duplicate of the {@code getNationalSignificantNumber} |
| * method from {@code PhoneNumberUtil}. Ultimately a canonical static version |
| * should exist in a separate utility class (to prevent {@code ShortNumberInfo} |
| * needing to depend on PhoneNumberUtil). |
| * |
| * @param {i18n.phonenumbers.PhoneNumber} number the phone number for which the |
| * national significant number is needed. |
| * @return {string} the national significant number of the PhoneNumber object |
| * passed in. |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype.getNationalSignificantNumber_ = |
| function(number) { |
| if (!number.hasNationalNumber()) { |
| return ''; |
| } |
| /** @type {string} */ |
| var nationalNumber = '' + number.getNationalNumber(); |
| // If leading zero(s) have been set, we prefix this now. Note that a single |
| // leading zero is not the same as a national prefix; leading zeros should be |
| // dialled no matter whether you are dialling from within or outside the |
| // country, national prefixes are added when formatting nationally if |
| // applicable. |
| if (number.hasItalianLeadingZero() && number.getItalianLeadingZero() && |
| number.getNumberOfLeadingZerosOrDefault() > 0) { |
| return Array(number.getNumberOfLeadingZerosOrDefault() + 1).join('0') + |
| nationalNumber; |
| } |
| return nationalNumber; |
| }; |
| |
| |
| /** |
| * Helper method to add in a performance optimization. |
| * TODO: Once we have benchmarked ShortNumberInfo, consider if it is worth |
| * keeping this performance optimization. |
| * @param {string} number |
| * @param {i18n.phonenumbers.PhoneNumberDesc} numberDesc |
| * @return {boolean} |
| * @private |
| */ |
| i18n.phonenumbers.ShortNumberInfo.prototype |
| .matchesPossibleNumberAndNationalNumber_ = function(number, numberDesc) { |
| if (numberDesc.possibleLengthArray().length > 0 && |
| !numberDesc.possibleLengthArray().includes(number.length)) { |
| return false; |
| } |
| return i18n.phonenumbers.PhoneNumberUtil.matchesEntirely( |
| numberDesc.getNationalNumberPatternOrDefault(), number.toString()); |
| }; |