libphonenumber-js
Advanced tools
Comparing version 0.2.19 to 0.2.20
@@ -19,591 +19,521 @@ 'use strict'; | ||
exports.close_dangling_braces = close_dangling_braces; | ||
exports.count_occurences = count_occurences; | ||
exports.repeat = repeat; | ||
exports.default = function (metadata) { | ||
var as_you_type = function () { | ||
function as_you_type(country_code) { | ||
(0, _classCallCheck3.default)(this, as_you_type); | ||
var _metadata = require('../metadata.min'); | ||
if (country_code && metadata.countries[country_code]) { | ||
this.default_country = country_code; | ||
} | ||
var _metadata2 = _interopRequireDefault(_metadata); | ||
this.reset(); | ||
} | ||
var _metadata3 = require('./metadata'); | ||
(0, _createClass3.default)(as_you_type, [{ | ||
key: 'input', | ||
value: function input(text) { | ||
// Parse input | ||
var _parse = require('./parse'); | ||
var extracted_number = (0, _parse.extract_formatted_phone_number)(text); | ||
var _format = require('./format'); | ||
// Special case for a lone '+' sign | ||
// since it's not considered a possible phone number. | ||
if (!extracted_number) { | ||
if (text && text.indexOf('+') >= 0) { | ||
extracted_number = '+'; | ||
} | ||
} | ||
var _common = require('./common'); | ||
// Validate possible first part of a phone number | ||
if (!(0, _common.matches_entirely)(extracted_number, VALID_INCOMPLETE_PHONE_NUMBER_PATTERN)) { | ||
return this.current_output; | ||
} | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
return this.process_input((0, _parse.parse_phone_number)(extracted_number)); | ||
} | ||
}, { | ||
key: 'process_input', | ||
value: function process_input(input) { | ||
// If an out of position '+' sign detected | ||
// (or a second '+' sign), | ||
// then just drop it from the input. | ||
if (input[0] === '+') { | ||
if (!this.parsed_input) { | ||
this.parsed_input += '+'; | ||
// Used in phone number format template creation. | ||
// Could be any digit, I guess. | ||
var DUMMY_DIGIT = '9'; // This is an enhanced port of Google Android `libphonenumber`'s | ||
// `asyoutypeformatter.js` of 17th November, 2016. | ||
// | ||
// https://github.com/googlei18n/libphonenumber/blob/8d21a365061de2ba0675c878a710a7b24f74d2ae/javascript/i18n/phonenumbers/asyoutypeformatter.js | ||
// If a default country was set | ||
// then reset it because an explicitly international | ||
// phone number is being entered | ||
this.reset_countriness(); | ||
} | ||
var DUMMY_DIGIT_MATCHER = new RegExp(DUMMY_DIGIT, 'g'); | ||
// I don't know why is it exactly `15` | ||
var LONGEST_NATIONAL_PHONE_NUMBER_LENGTH = 15; | ||
// Create a phone number consisting only of the digit 9 that matches the | ||
// `number_pattern` by applying the pattern to the "longest phone number" string. | ||
var LONGEST_DUMMY_PHONE_NUMBER = repeat(DUMMY_DIGIT, LONGEST_NATIONAL_PHONE_NUMBER_LENGTH); | ||
input = input.slice(1); | ||
} | ||
// The digits that have not been entered yet will be represented by a \u2008, | ||
// the punctuation space. | ||
var DIGIT_PLACEHOLDER = 'x'; // '\u2008' (punctuation space) | ||
var DIGIT_PLACEHOLDER_MATCHER = new RegExp(DIGIT_PLACEHOLDER); | ||
var DIGIT_PLACEHOLDER_MATCHER_GLOBAL = new RegExp(DIGIT_PLACEHOLDER, 'g'); | ||
// Raw phone number | ||
this.parsed_input += input; | ||
// A pattern that is used to match character classes in regular expressions. | ||
// An example of a character class is "[1-4]". | ||
var CHARACTER_CLASS_PATTERN = /\[([^\[\]])*\]/g; | ||
// Reset phone number validation state | ||
this.valid = false; | ||
// Any digit in a regular expression that actually denotes a digit. For | ||
// example, in the regular expression "80[0-2]\d{6,10}", the first 2 digits | ||
// (8 and 0) are standalone digits, but the rest are not. | ||
// Two look-aheads are needed because the number following \\d could be a | ||
// two-digit number, since the phone number can be as long as 15 digits. | ||
var STANDALONE_DIGIT_PATTERN = /\d(?=[^,}][^,}])/g; | ||
// Add digits to the national number | ||
this.national_number += input; | ||
// A pattern that is used to determine if a `format` is eligible | ||
// to be used by the "as you type formatter". | ||
// It is eligible when the `format` contains groups of the dollar sign | ||
// followed by a single digit, separated by valid phone number punctuation. | ||
// This prevents invalid punctuation (such as the star sign in Israeli star numbers) | ||
// getting into the output of the "as you type formatter". | ||
var ELIGIBLE_FORMAT_PATTERN = new RegExp('^' + '[' + _parse.VALID_PUNCTUATION + ']*' + '(\\$\\d[' + _parse.VALID_PUNCTUATION + ']*)+' + '$'); | ||
// Try to format the parsed input | ||
// This is the minimum length of the leading digits of a phone number | ||
// to guarantee the first "leading digits pattern" for a phone number format | ||
// to be preemptive. | ||
var MIN_LEADING_DIGITS_LENGTH = 3; | ||
if (this.is_international()) { | ||
if (!this.country_phone_code) { | ||
// If one looks at country phone codes | ||
// then he can notice that no one country phone code | ||
// is ever a (leftmost) substring of another country phone code. | ||
// So if a valid country code is extracted so far | ||
// then it means that this is the country code. | ||
var VALID_INCOMPLETE_PHONE_NUMBER = '[' + _parse.PLUS_CHARS + ']{0,1}' + '[' + _parse.VALID_PUNCTUATION + _parse.VALID_DIGITS + ']*'; | ||
// If no country phone code could be extracted so far, | ||
// then just return the raw phone number, | ||
// because it has no way of knowing | ||
// how to format the phone number so far. | ||
if (!this.extract_country_phone_code()) { | ||
// Return raw phone number | ||
return this.parsed_input; | ||
} | ||
var VALID_INCOMPLETE_PHONE_NUMBER_PATTERN = new RegExp('^' + VALID_INCOMPLETE_PHONE_NUMBER + '$', 'i'); | ||
// Initialize country-specific data | ||
this.initialize_phone_number_formats_for_this_country_phone_code(); | ||
this.reset_format(); | ||
this.determine_the_country(); | ||
} | ||
// `this.country` could be `undefined`, | ||
// for instance, when there is ambiguity | ||
// in a form of several different countries | ||
// each corresponding to the same country phone code | ||
// (e.g. NANPA: USA, Canada, etc), | ||
// and there's not enough digits entered | ||
// to reliably determine the country | ||
// the phone number belongs to. | ||
// Therefore, in cases of such ambiguity, | ||
// each time something is input, | ||
// try to determine the country | ||
// (if it's not determined yet). | ||
else if (!this.country) { | ||
this.determine_the_country(); | ||
} | ||
} else { | ||
// Some national prefixes are substrings of other national prefixes | ||
// (for the same country), therefore try to extract national prefix each time | ||
// because a longer national prefix might be available at some point in time. | ||
var as_you_type = function () { | ||
function as_you_type(country_code) { | ||
(0, _classCallCheck3.default)(this, as_you_type); | ||
var previous_national_prefix = this.national_prefix; | ||
this.national_number = this.national_prefix + this.national_number; | ||
if (country_code && _metadata2.default.countries[country_code]) { | ||
this.default_country = country_code; | ||
} | ||
// Possibly extract a national prefix | ||
this.extract_national_prefix(); | ||
this.reset(); | ||
} | ||
if (this.national_prefix !== previous_national_prefix) { | ||
// National number has changed | ||
// (due to another national prefix been extracted) | ||
// therefore national number has changed | ||
// therefore reset all previous formatting data. | ||
// (and leading digits matching state) | ||
this.matching_formats = this.available_formats; | ||
this.reset_format(); | ||
} | ||
} | ||
(0, _createClass3.default)(as_you_type, [{ | ||
key: 'input', | ||
value: function input(text) { | ||
// Parse input | ||
// Format the phone number (given the next digits) | ||
var formatted_national_phone_number = this.format_national_phone_number(input); | ||
var extracted_number = (0, _parse.extract_formatted_phone_number)(text); | ||
// Special case for a lone '+' sign | ||
// since it's not considered a possible phone number. | ||
if (!extracted_number) { | ||
if (text && text.indexOf('+') >= 0) { | ||
extracted_number = '+'; | ||
// If the phone number could be formatted, | ||
// then return it, possibly prepending with country phone code | ||
// (for international phone numbers only) | ||
if (formatted_national_phone_number) { | ||
return this.full_phone_number(formatted_national_phone_number); | ||
} | ||
} | ||
// Validate possible first part of a phone number | ||
if (!(0, _common.matches_entirely)(extracted_number, VALID_INCOMPLETE_PHONE_NUMBER_PATTERN)) { | ||
return this.current_output; | ||
// If the phone number couldn't be formatted, | ||
// then just fall back to the raw phone number. | ||
return this.parsed_input; | ||
} | ||
return this.process_input((0, _parse.parse_phone_number)(extracted_number)); | ||
} | ||
}, { | ||
key: 'process_input', | ||
value: function process_input(input) { | ||
// If an out of position '+' sign detected | ||
// (or a second '+' sign), | ||
// then just drop it from the input. | ||
if (input[0] === '+') { | ||
if (!this.parsed_input) { | ||
this.parsed_input += '+'; | ||
// If a default country was set | ||
// then reset it because an explicitly international | ||
// phone number is being entered | ||
this.reset_countriness(); | ||
}, { | ||
key: 'format_national_phone_number', | ||
value: function format_national_phone_number(next_digits) { | ||
// Format the next phone number digits | ||
// using the previously chosen phone number format. | ||
// | ||
// This is done here because if `attempt_to_format_complete_phone_number` | ||
// was placed before this call then the `template` | ||
// wouldn't reflect the situation correctly (and would therefore be inconsistent) | ||
// | ||
var national_number_formatted_with_previous_format = void 0; | ||
if (this.chosen_format) { | ||
national_number_formatted_with_previous_format = this.format_next_national_number_digits(next_digits); | ||
} | ||
input = input.slice(1); | ||
} | ||
// See if the input digits can be formatted properly already. If not, | ||
// use the results from format_next_national_number_digits(), which does formatting | ||
// based on the formatting pattern chosen. | ||
// Raw phone number | ||
this.parsed_input += input; | ||
var formatted_number = this.attempt_to_format_complete_phone_number(); | ||
// Reset phone number validation state | ||
this.valid = false; | ||
if (formatted_number) { | ||
this.valid = true; | ||
return formatted_number; | ||
} | ||
// Add digits to the national number | ||
this.national_number += input; | ||
// Check if the previously chosen phone number format still holds | ||
this.match_formats_by_leading_digits(); | ||
// Try to format the parsed input | ||
// If the previously chosen phone number format | ||
// didn't match the next (current) digit being input | ||
// (leading digits pattern didn't match). | ||
if (this.choose_another_format()) { | ||
// And a more appropriate phone number format | ||
// has been chosen for these `leading digits`, | ||
// then format the national phone number (so far) | ||
// using the newly selected phone number pattern. | ||
if (this.is_international()) { | ||
if (!this.country_phone_code) { | ||
// If one looks at country phone codes | ||
// then he can notice that no one country phone code | ||
// is ever a (leftmost) substring of another country phone code. | ||
// So if a valid country code is extracted so far | ||
// then it means that this is the country code. | ||
// Will return `undefined` if it couldn't format | ||
// the supplied national number | ||
// using the selected phone number pattern. | ||
// If no country phone code could be extracted so far, | ||
// then just return the raw phone number, | ||
// because it has no way of knowing | ||
// how to format the phone number so far. | ||
if (!this.extract_country_phone_code()) { | ||
// Return raw phone number | ||
return this.parsed_input; | ||
} | ||
// Initialize country-specific data | ||
this.initialize_phone_number_formats_for_this_country_phone_code(); | ||
this.reset_format(); | ||
this.determine_the_country(); | ||
return this.reformat_national_number(); | ||
} | ||
// `this.country` could be `undefined`, | ||
// for instance, when there is ambiguity | ||
// in a form of several different countries | ||
// each corresponding to the same country phone code | ||
// (e.g. NANPA: USA, Canada, etc), | ||
// and there's not enough digits entered | ||
// to reliably determine the country | ||
// the phone number belongs to. | ||
// Therefore, in cases of such ambiguity, | ||
// each time something is input, | ||
// try to determine the country | ||
// (if it's not determined yet). | ||
else if (!this.country) { | ||
this.determine_the_country(); | ||
} | ||
} else { | ||
// Some national prefixes are substrings of other national prefixes | ||
// (for the same country), therefore try to extract national prefix each time | ||
// because a longer national prefix might be available at some point in time. | ||
var previous_national_prefix = this.national_prefix; | ||
this.national_number = this.national_prefix + this.national_number; | ||
// If could format the next (current) digit | ||
// using the previously chosen phone number format | ||
// then return the formatted number so far. | ||
// Possibly extract a national prefix | ||
this.extract_national_prefix(); | ||
// If no new phone number format could be chosen, | ||
// and couldn't format the supplied national number | ||
// using the selected phone number pattern, | ||
// then it will return `undefined`. | ||
if (this.national_prefix !== previous_national_prefix) { | ||
// National number has changed | ||
// (due to another national prefix been extracted) | ||
// therefore national number has changed | ||
// therefore reset all previous formatting data. | ||
// (and leading digits matching state) | ||
this.matching_formats = this.available_formats; | ||
this.reset_format(); | ||
} | ||
return national_number_formatted_with_previous_format; | ||
} | ||
}, { | ||
key: 'reset', | ||
value: function reset() { | ||
// Input stripped of non-phone-number characters. | ||
// Can only contain a possible leading '+' sign and digits. | ||
this.parsed_input = ''; | ||
// Format the phone number (given the next digits) | ||
var formatted_national_phone_number = this.format_national_phone_number(input); | ||
this.current_output = ''; | ||
// If the phone number could be formatted, | ||
// then return it, possibly prepending with country phone code | ||
// (for international phone numbers only) | ||
if (formatted_national_phone_number) { | ||
return this.full_phone_number(formatted_national_phone_number); | ||
} | ||
// This contains the national prefix that has been extracted. It contains only | ||
// digits without formatting. | ||
this.national_prefix = ''; | ||
// If the phone number couldn't be formatted, | ||
// then just fall back to the raw phone number. | ||
return this.parsed_input; | ||
} | ||
}, { | ||
key: 'format_national_phone_number', | ||
value: function format_national_phone_number(next_digits) { | ||
// Format the next phone number digits | ||
// using the previously chosen phone number format. | ||
// | ||
// This is done here because if `attempt_to_format_complete_phone_number` | ||
// was placed before this call then the `template` | ||
// wouldn't reflect the situation correctly (and would therefore be inconsistent) | ||
// | ||
var national_number_formatted_with_previous_format = void 0; | ||
if (this.chosen_format) { | ||
national_number_formatted_with_previous_format = this.format_next_national_number_digits(next_digits); | ||
} | ||
this.national_number = ''; | ||
// See if the input digits can be formatted properly already. If not, | ||
// use the results from format_next_national_number_digits(), which does formatting | ||
// based on the formatting pattern chosen. | ||
this.reset_countriness(); | ||
var formatted_number = this.attempt_to_format_complete_phone_number(); | ||
this.reset_format(); | ||
if (formatted_number) { | ||
this.valid = true; | ||
return formatted_number; | ||
this.valid = false; | ||
return this; | ||
} | ||
}, { | ||
key: 'reset_country', | ||
value: function reset_country() { | ||
if (this.default_country && !this.is_international()) { | ||
this.country = this.default_country; | ||
} else { | ||
this.country = undefined; | ||
} | ||
} | ||
}, { | ||
key: 'reset_countriness', | ||
value: function reset_countriness() { | ||
this.reset_country(); | ||
// Check if the previously chosen phone number format still holds | ||
this.match_formats_by_leading_digits(); | ||
if (this.default_country && !this.is_international()) { | ||
this.country_metadata = metadata.countries[this.default_country]; | ||
this.country_phone_code = this.country_metadata.phone_code; | ||
// If the previously chosen phone number format | ||
// didn't match the next (current) digit being input | ||
// (leading digits pattern didn't match). | ||
if (this.choose_another_format()) { | ||
// And a more appropriate phone number format | ||
// has been chosen for these `leading digits`, | ||
// then format the national phone number (so far) | ||
// using the newly selected phone number pattern. | ||
this.initialize_phone_number_formats_for_this_country_phone_code(); | ||
} else { | ||
this.country_metadata = undefined; | ||
this.country_phone_code = undefined; | ||
// Will return `undefined` if it couldn't format | ||
// the supplied national number | ||
// using the selected phone number pattern. | ||
return this.reformat_national_number(); | ||
this.available_formats = []; | ||
this.matching_formats = this.available_formats; | ||
} | ||
} | ||
}, { | ||
key: 'reset_format', | ||
value: function reset_format() { | ||
this.chosen_format = undefined; | ||
this.template = undefined; | ||
this.partially_populated_template = undefined; | ||
this.last_match_position = -1; | ||
} | ||
// If could format the next (current) digit | ||
// using the previously chosen phone number format | ||
// then return the formatted number so far. | ||
// Format each digit of national phone number (so far) | ||
// using the newly selected phone number pattern. | ||
// If no new phone number format could be chosen, | ||
// and couldn't format the supplied national number | ||
// using the selected phone number pattern, | ||
// then it will return `undefined`. | ||
return national_number_formatted_with_previous_format; | ||
} | ||
}, { | ||
key: 'reset', | ||
value: function reset() { | ||
// Input stripped of non-phone-number characters. | ||
// Can only contain a possible leading '+' sign and digits. | ||
this.parsed_input = ''; | ||
this.current_output = ''; | ||
// This contains the national prefix that has been extracted. It contains only | ||
// digits without formatting. | ||
this.national_prefix = ''; | ||
this.national_number = ''; | ||
this.reset_countriness(); | ||
this.reset_format(); | ||
this.valid = false; | ||
return this; | ||
} | ||
}, { | ||
key: 'reset_country', | ||
value: function reset_country() { | ||
if (this.default_country && !this.is_international()) { | ||
this.country = this.default_country; | ||
} else { | ||
this.country = undefined; | ||
}, { | ||
key: 'reformat_national_number', | ||
value: function reformat_national_number() { | ||
// Format each digit of national phone number (so far) | ||
// using the selected phone number pattern. | ||
return this.format_next_national_number_digits(this.national_number); | ||
} | ||
} | ||
}, { | ||
key: 'reset_countriness', | ||
value: function reset_countriness() { | ||
this.reset_country(); | ||
}, { | ||
key: 'initialize_phone_number_formats_for_this_country_phone_code', | ||
value: function initialize_phone_number_formats_for_this_country_phone_code() { | ||
// Get all "eligible" phone number formats for this country | ||
this.available_formats = (0, _metadata.get_formats)(this.country_metadata).filter(function (format) { | ||
return ELIGIBLE_FORMAT_PATTERN.test((0, _metadata.get_format_international_format)(format)); | ||
}) | ||
// Try the formats with "leading digits" defined first | ||
.sort(function (a, b) { | ||
// Leading digits are defined for most formats | ||
/* istanbul ignore next */ | ||
if ((0, _metadata.get_format_leading_digits_patterns)(a).length === 0 && (0, _metadata.get_format_leading_digits_patterns)(b).length > 0) { | ||
return -1; | ||
} | ||
if (this.default_country && !this.is_international()) { | ||
this.country_metadata = _metadata2.default.countries[this.default_country]; | ||
this.country_phone_code = this.country_metadata.phone_code; | ||
// Leading digits are defined for most formats | ||
/* istanbul ignore next */ | ||
if ((0, _metadata.get_format_leading_digits_patterns)(a).length > 0 && (0, _metadata.get_format_leading_digits_patterns)(b).length === 0) { | ||
return 1; | ||
} | ||
this.initialize_phone_number_formats_for_this_country_phone_code(); | ||
} else { | ||
this.country_metadata = undefined; | ||
this.country_phone_code = undefined; | ||
return 0; | ||
}); | ||
this.available_formats = []; | ||
this.matching_formats = this.available_formats; | ||
} | ||
} | ||
}, { | ||
key: 'reset_format', | ||
value: function reset_format() { | ||
this.chosen_format = undefined; | ||
this.template = undefined; | ||
this.partially_populated_template = undefined; | ||
this.last_match_position = -1; | ||
} | ||
}, { | ||
key: 'match_formats_by_leading_digits', | ||
value: function match_formats_by_leading_digits() { | ||
var leading_digits = this.national_number; | ||
// Format each digit of national phone number (so far) | ||
// using the newly selected phone number pattern. | ||
// "leading digits" patterns start with a maximum 3 digits, | ||
// and then with each additional digit | ||
// a more precise "leading digits" pattern is specified. | ||
// They could make "leading digits" patterns start | ||
// with a maximum of a single digit, but they didn't, | ||
// so it's possible that some phone number formats | ||
// will be falsely rejected until there are at least | ||
// 3 digits in the national (significant) number being input. | ||
}, { | ||
key: 'reformat_national_number', | ||
value: function reformat_national_number() { | ||
// Format each digit of national phone number (so far) | ||
// using the selected phone number pattern. | ||
return this.format_next_national_number_digits(this.national_number); | ||
} | ||
}, { | ||
key: 'initialize_phone_number_formats_for_this_country_phone_code', | ||
value: function initialize_phone_number_formats_for_this_country_phone_code() { | ||
// Get all "eligible" phone number formats for this country | ||
this.available_formats = (0, _metadata3.get_formats)(this.country_metadata).filter(function (format) { | ||
return ELIGIBLE_FORMAT_PATTERN.test((0, _metadata3.get_format_international_format)(format)); | ||
}) | ||
// Try the formats with "leading digits" defined first | ||
.sort(function (a, b) { | ||
// Leading digits are defined for most formats | ||
/* istanbul ignore next */ | ||
if ((0, _metadata3.get_format_leading_digits_patterns)(a).length === 0 && (0, _metadata3.get_format_leading_digits_patterns)(b).length > 0) { | ||
return -1; | ||
} | ||
var index_of_leading_digits_pattern = leading_digits.length - MIN_LEADING_DIGITS_LENGTH; | ||
// Leading digits are defined for most formats | ||
/* istanbul ignore next */ | ||
if ((0, _metadata3.get_format_leading_digits_patterns)(a).length > 0 && (0, _metadata3.get_format_leading_digits_patterns)(b).length === 0) { | ||
return 1; | ||
if (index_of_leading_digits_pattern < 0) { | ||
index_of_leading_digits_pattern = 0; | ||
} | ||
return 0; | ||
}); | ||
this.matching_formats = this.get_relevant_phone_number_formats().filter(function (format) { | ||
var leading_digits_pattern_count = (0, _metadata.get_format_leading_digits_patterns)(format).length; | ||
this.matching_formats = this.available_formats; | ||
} | ||
}, { | ||
key: 'match_formats_by_leading_digits', | ||
value: function match_formats_by_leading_digits() { | ||
var leading_digits = this.national_number; | ||
// Keep everything that isn't restricted by leading digits. | ||
if (leading_digits_pattern_count === 0) { | ||
return true; | ||
} | ||
// "leading digits" patterns start with a maximum 3 digits, | ||
// and then with each additional digit | ||
// a more precise "leading digits" pattern is specified. | ||
// They could make "leading digits" patterns start | ||
// with a maximum of a single digit, but they didn't, | ||
// so it's possible that some phone number formats | ||
// will be falsely rejected until there are at least | ||
// 3 digits in the national (significant) number being input. | ||
var index_of_leading_digits_pattern = leading_digits.length - MIN_LEADING_DIGITS_LENGTH; | ||
if (index_of_leading_digits_pattern < 0) { | ||
index_of_leading_digits_pattern = 0; | ||
var leading_digits_pattern_index = Math.min(index_of_leading_digits_pattern, leading_digits_pattern_count - 1); | ||
var leading_digits_pattern = (0, _metadata.get_format_leading_digits_patterns)(format)[leading_digits_pattern_index]; | ||
return new RegExp('^' + leading_digits_pattern).test(leading_digits); | ||
}); | ||
} | ||
}, { | ||
key: 'get_relevant_phone_number_formats', | ||
value: function get_relevant_phone_number_formats() { | ||
var leading_digits = this.national_number; | ||
this.matching_formats = this.get_relevant_phone_number_formats().filter(function (format) { | ||
var leading_digits_pattern_count = (0, _metadata3.get_format_leading_digits_patterns)(format).length; | ||
// "leading digits" patterns start with a maximum 3 digits, | ||
// and then with each additional digit | ||
// a more precise "leading digits" pattern is specified. | ||
// They could make "leading digits" patterns start | ||
// with a maximum of a single digit, but they didn't, | ||
// so it's possible that some phone number formats | ||
// will be falsely rejected until there are at least | ||
// 3 digits in the national (significant) number being input. | ||
// Keep everything that isn't restricted by leading digits. | ||
if (leading_digits_pattern_count === 0) { | ||
return true; | ||
if (leading_digits.length <= MIN_LEADING_DIGITS_LENGTH) { | ||
return this.available_formats; | ||
} | ||
var leading_digits_pattern_index = Math.min(index_of_leading_digits_pattern, leading_digits_pattern_count - 1); | ||
var leading_digits_pattern = (0, _metadata3.get_format_leading_digits_patterns)(format)[leading_digits_pattern_index]; | ||
return new RegExp('^' + leading_digits_pattern).test(leading_digits); | ||
}); | ||
} | ||
}, { | ||
key: 'get_relevant_phone_number_formats', | ||
value: function get_relevant_phone_number_formats() { | ||
var leading_digits = this.national_number; | ||
// "leading digits" patterns start with a maximum 3 digits, | ||
// and then with each additional digit | ||
// a more precise "leading digits" pattern is specified. | ||
// They could make "leading digits" patterns start | ||
// with a maximum of a single digit, but they didn't, | ||
// so it's possible that some phone number formats | ||
// will be falsely rejected until there are at least | ||
// 3 digits in the national (significant) number being input. | ||
if (leading_digits.length <= MIN_LEADING_DIGITS_LENGTH) { | ||
return this.available_formats; | ||
return this.matching_formats; | ||
} | ||
return this.matching_formats; | ||
} | ||
// Check to see if there is an exact pattern match for these digits. If so, we | ||
// should use this instead of any other formatting template whose | ||
// leadingDigitsPattern also matches the input. | ||
// Check to see if there is an exact pattern match for these digits. If so, we | ||
// should use this instead of any other formatting template whose | ||
// leadingDigitsPattern also matches the input. | ||
}, { | ||
key: 'attempt_to_format_complete_phone_number', | ||
value: function attempt_to_format_complete_phone_number() { | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
}, { | ||
key: 'attempt_to_format_complete_phone_number', | ||
value: function attempt_to_format_complete_phone_number() { | ||
var _iteratorNormalCompletion = true; | ||
var _didIteratorError = false; | ||
var _iteratorError = undefined; | ||
try { | ||
for (var _iterator = (0, _getIterator3.default)(this.get_relevant_phone_number_formats()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var format = _step.value; | ||
try { | ||
for (var _iterator = (0, _getIterator3.default)(this.get_relevant_phone_number_formats()), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
var format = _step.value; | ||
var matcher = new RegExp('^(?:' + (0, _metadata.get_format_pattern)(format) + ')$'); | ||
var matcher = new RegExp('^(?:' + (0, _metadata3.get_format_pattern)(format) + ')$'); | ||
var matches = false; | ||
var discard_national_prefix = false; | ||
if (!matcher.test(this.national_number)) { | ||
// If the national prefix is optional | ||
// then also try to format the phone number | ||
// without the national prefix being extracted. | ||
if (this.national_prefix && (0, _metadata.get_format_national_prefix_is_optional_when_formatting)(format, this.country_metadata)) { | ||
if (!matcher.test(this.national_prefix + this.national_number)) { | ||
continue; | ||
} | ||
// If the national prefix is optional | ||
// then also try to format the phone number | ||
// without the national prefix being extracted. | ||
if (this.national_prefix && (0, _metadata3.get_format_national_prefix_is_optional_when_formatting)(format, this.country_metadata)) { | ||
matches = matcher.test(this.national_prefix + this.national_number); | ||
this.national_number = this.national_prefix + this.national_number; | ||
this.national_prefix = ''; | ||
} | ||
if (matches) { | ||
discard_national_prefix = true; | ||
continue; | ||
} | ||
} | ||
matches = matches || matcher.test(this.national_number); | ||
if (!this.validate_format(format)) { | ||
continue; | ||
} | ||
if (!matches) { | ||
continue; | ||
} | ||
// To leave the formatter in a consistent state | ||
this.reset_format(); | ||
this.chosen_format = format; | ||
if (discard_national_prefix) { | ||
this.national_number = this.national_prefix + this.national_number; | ||
this.national_prefix = ''; | ||
} | ||
var formatted_number = (0, _format.format_national_number_using_format)(this.national_number, format, this.is_international(), this.national_prefix.length > 0, this.country_metadata); | ||
if (!this.validate_format(format)) { | ||
continue; | ||
} | ||
// To leave the formatter in a consistent state | ||
this.reset_format(); | ||
this.chosen_format = format; | ||
var number_pattern = this.validate_format_for_template(format); | ||
var formatted_number = (0, _format.format_national_number_using_format)(this.national_number, format, this.is_international(), this.national_prefix.length > 0, this.country_metadata); | ||
if (number_pattern) { | ||
// Set `this.template` and `this.partially_populated_template` | ||
this.create_formatting_template(format, number_pattern); | ||
// Populate `this.partially_populated_template` | ||
this.reformat_national_number(); | ||
} else { | ||
var full_number = this.full_phone_number(formatted_number); | ||
// | ||
// `else` case doesn't ever happen | ||
// with the current metadata, | ||
// but just in case. | ||
// | ||
/* istanbul ignore else */ | ||
if (this.create_formatting_template(format)) { | ||
// Populate `this.partially_populated_template` | ||
this.reformat_national_number(); | ||
} else { | ||
var full_number = this.full_phone_number(formatted_number); | ||
this.template = full_number.replace(/[\d\+]/g, DIGIT_PLACEHOLDER); | ||
this.partially_populated_template = full_number; | ||
} | ||
this.template = full_number.replace(/[\d\+]/g, DIGIT_PLACEHOLDER); | ||
this.partially_populated_template = full_number; | ||
} | ||
return formatted_number; | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
return formatted_number; | ||
} | ||
} catch (err) { | ||
_didIteratorError = true; | ||
_iteratorError = err; | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
try { | ||
if (!_iteratorNormalCompletion && _iterator.return) { | ||
_iterator.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError) { | ||
throw _iteratorError; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
// Combines the national number with the appropriate prefix | ||
// Combines the national number with the appropriate prefix | ||
}, { | ||
key: 'full_phone_number', | ||
value: function full_phone_number(formatted_national_number) { | ||
if (this.is_international()) { | ||
return '+' + this.country_phone_code + ' ' + formatted_national_number; | ||
}, { | ||
key: 'full_phone_number', | ||
value: function full_phone_number(formatted_national_number) { | ||
if (this.is_international()) { | ||
return '+' + this.country_phone_code + ' ' + formatted_national_number; | ||
} | ||
return formatted_national_number; | ||
} | ||
return formatted_national_number; | ||
} | ||
// Extracts the country calling code from the beginning | ||
// of the entered `national_number` (so far), | ||
// and places the remaining input into the `national_number`. | ||
// Extracts the country calling code from the beginning | ||
// of the entered `national_number` (so far), | ||
// and places the remaining input into the `national_number`. | ||
}, { | ||
key: 'extract_country_phone_code', | ||
value: function extract_country_phone_code() { | ||
if (!this.national_number) { | ||
return; | ||
} | ||
}, { | ||
key: 'extract_country_phone_code', | ||
value: function extract_country_phone_code() { | ||
if (!this.national_number) { | ||
return; | ||
} | ||
var _parse_phone_number_a = (0, _parse.parse_phone_number_and_country_phone_code)(this.parsed_input, metadata); | ||
var _parse_phone_number_a = (0, _parse.parse_phone_number_and_country_phone_code)(this.parsed_input); | ||
var country_phone_code = _parse_phone_number_a.country_phone_code; | ||
var number = _parse_phone_number_a.number; | ||
var country_phone_code = _parse_phone_number_a.country_phone_code; | ||
var number = _parse_phone_number_a.number; | ||
if (!country_phone_code) { | ||
return; | ||
} | ||
if (!country_phone_code) { | ||
return; | ||
this.country_phone_code = country_phone_code; | ||
this.national_number = number; | ||
return this.country_metadata = (0, _metadata.get_metadata_by_country_phone_code)(country_phone_code, metadata); | ||
} | ||
}, { | ||
key: 'extract_national_prefix', | ||
value: function extract_national_prefix() { | ||
this.national_prefix = ''; | ||
this.country_phone_code = country_phone_code; | ||
this.national_number = number; | ||
if (!this.country_metadata) { | ||
return; | ||
} | ||
return this.country_metadata = (0, _metadata3.get_metadata_by_country_phone_code)(country_phone_code, _metadata2.default); | ||
} | ||
}, { | ||
key: 'extract_national_prefix', | ||
value: function extract_national_prefix() { | ||
this.national_prefix = ''; | ||
var national_prefix_for_parsing = (0, _metadata.get_national_prefix_for_parsing)(this.country_metadata); | ||
if (!this.country_metadata) { | ||
return; | ||
} | ||
if (!national_prefix_for_parsing) { | ||
return; | ||
} | ||
var national_prefix_for_parsing = (0, _metadata3.get_national_prefix_for_parsing)(this.country_metadata); | ||
var matches = this.national_number.match(new RegExp('^(?:' + national_prefix_for_parsing + ')')); | ||
if (!national_prefix_for_parsing) { | ||
return; | ||
} | ||
// Since some national prefix patterns are entirely optional, check that a | ||
// national prefix could actually be extracted. | ||
if (!matches || !matches[0]) { | ||
return; | ||
} | ||
var matches = this.national_number.match(new RegExp('^(?:' + national_prefix_for_parsing + ')')); | ||
var national_number_starts_at = matches[0].length; | ||
// Since some national prefix patterns are entirely optional, check that a | ||
// national prefix could actually be extracted. | ||
if (!matches || !matches[0]) { | ||
return; | ||
this.national_prefix = this.national_number.slice(0, national_number_starts_at); | ||
this.national_number = this.national_number.slice(national_number_starts_at); | ||
return this.national_prefix; | ||
} | ||
}, { | ||
key: 'choose_another_format', | ||
value: function choose_another_format() { | ||
// When there are multiple available formats, the formatter uses the first | ||
// format where a formatting template could be created. | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
var national_number_starts_at = matches[0].length; | ||
try { | ||
for (var _iterator2 = (0, _getIterator3.default)(this.get_relevant_phone_number_formats()), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var format = _step2.value; | ||
this.national_prefix = this.national_number.slice(0, national_number_starts_at); | ||
this.national_number = this.national_number.slice(national_number_starts_at); | ||
// If this format is currently being used | ||
// and is still possible, then stick to it. | ||
if (this.chosen_format === format) { | ||
return; | ||
} | ||
return this.national_prefix; | ||
} | ||
}, { | ||
key: 'choose_another_format', | ||
value: function choose_another_format() { | ||
// When there are multiple available formats, the formatter uses the first | ||
// format where a formatting template could be created. | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
// If this `format` is suitable for "as you type", | ||
// then extract the template from this format | ||
// and use it to format the phone number being input. | ||
try { | ||
for (var _iterator2 = (0, _getIterator3.default)(this.get_relevant_phone_number_formats()), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var format = _step2.value; | ||
if (!this.validate_format(format)) { | ||
continue; | ||
} | ||
// If this format is currently being used | ||
// and is still possible, then stick to it. | ||
if (this.chosen_format === format) { | ||
return; | ||
} | ||
if (!this.create_formatting_template(format)) { | ||
continue; | ||
} | ||
// If this `format` is suitable for "as you type", | ||
// then extract the template from this format | ||
// and use it to format the phone number being input. | ||
if (!this.validate_format(format)) { | ||
continue; | ||
} | ||
var number_pattern = this.validate_format_for_template(format); | ||
if (number_pattern) { | ||
this.create_formatting_template(format, number_pattern); | ||
this.chosen_format = format; | ||
@@ -617,207 +547,259 @@ | ||
} | ||
} | ||
// No format matches the phone number, | ||
// therefore set `country` to `undefined` | ||
// (or to the default country). | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
// No format matches the phone number, | ||
// therefore set `country` to `undefined` | ||
// (or to the default country). | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
} | ||
this.reset_country(); | ||
this.reset_country(); | ||
// No format matches the national phone number entered | ||
this.reset_format(); | ||
} | ||
}, { | ||
key: 'validate_format', | ||
value: function validate_format(format) { | ||
// If national prefix is mandatory for this phone number format | ||
// and the user didn't input the national prefix, | ||
// then this phone number format isn't suitable. | ||
if (!this.is_international() && !this.national_prefix && (0, _metadata3.get_format_national_prefix_is_mandatory_when_formatting)(format, this.country_metadata)) { | ||
return; | ||
// No format matches the national phone number entered | ||
this.reset_format(); | ||
} | ||
}, { | ||
key: 'validate_format', | ||
value: function validate_format(format) { | ||
// If national prefix is mandatory for this phone number format | ||
// and the user didn't input the national prefix, | ||
// then this phone number format isn't suitable. | ||
if (!this.is_international() && !this.national_prefix && (0, _metadata.get_format_national_prefix_is_mandatory_when_formatting)(format, this.country_metadata)) { | ||
return; | ||
} | ||
return true; | ||
} | ||
}, { | ||
key: 'validate_format_for_template', | ||
value: function validate_format_for_template(format) { | ||
// The formatter doesn't format numbers when numberPattern contains '|', e.g. | ||
// (20|3)\d{4}. In those cases we quickly return. | ||
// (Though there's no such format in current metadata) | ||
/* istanbul ignore if */ | ||
if ((0, _metadata3.get_format_pattern)(format).indexOf('|') >= 0) { | ||
return; | ||
return true; | ||
} | ||
}, { | ||
key: 'create_formatting_template', | ||
value: function create_formatting_template(format) { | ||
// The formatter doesn't format numbers when numberPattern contains '|', e.g. | ||
// (20|3)\d{4}. In those cases we quickly return. | ||
// (Though there's no such format in current metadata) | ||
/* istanbul ignore if */ | ||
if ((0, _metadata.get_format_pattern)(format).indexOf('|') >= 0) { | ||
return; | ||
} | ||
var national_prefix_formatting_rule = (0, _metadata3.get_format_national_prefix_formatting_rule)(format, this.country_metadata); | ||
var national_prefix_formatting_rule = (0, _metadata.get_format_national_prefix_formatting_rule)(format, this.country_metadata); | ||
// A very smart trick by the guys at Google | ||
var number_pattern = (0, _metadata3.get_format_pattern)(format) | ||
// Replace anything in the form of [..] with \d | ||
.replace(CHARACTER_CLASS_PATTERN, '\\d') | ||
// Replace any standalone digit (not the one in `{}`) with \d | ||
.replace(STANDALONE_DIGIT_PATTERN, '\\d'); | ||
// A very smart trick by the guys at Google | ||
var number_pattern = (0, _metadata.get_format_pattern)(format) | ||
// Replace anything in the form of [..] with \d | ||
.replace(CHARACTER_CLASS_PATTERN, '\\d') | ||
// Replace any standalone digit (not the one in `{}`) with \d | ||
.replace(STANDALONE_DIGIT_PATTERN, '\\d'); | ||
// This match will always succeed, | ||
// because the "longest dummy phone number" | ||
// has enough length to accomodate any possible | ||
// national phone number format pattern. | ||
var dummy_phone_number_matching_format_pattern = LONGEST_DUMMY_PHONE_NUMBER.match(number_pattern)[0]; | ||
// This match will always succeed, | ||
// because the "longest dummy phone number" | ||
// has enough length to accomodate any possible | ||
// national phone number format pattern. | ||
var dummy_phone_number_matching_format_pattern = LONGEST_DUMMY_PHONE_NUMBER.match(number_pattern)[0]; | ||
// If the national number entered is too long | ||
// for any phone number format, then abort. | ||
if (this.national_number.length > dummy_phone_number_matching_format_pattern.length) { | ||
return; | ||
} | ||
// If the national number entered is too long | ||
// for any phone number format, then abort. | ||
if (this.national_number.length > dummy_phone_number_matching_format_pattern.length) { | ||
return; | ||
} | ||
return number_pattern; | ||
} | ||
}, { | ||
key: 'create_formatting_template', | ||
value: function create_formatting_template(format, number_pattern) { | ||
var number_format = this.get_format_format(format); | ||
// Now prepare phone number format | ||
var number_format = this.get_format_format(format); | ||
// If the user did input the national prefix | ||
// then maybe make it a part of the phone number template | ||
if (this.national_prefix) { | ||
var national_prefix_formatting_rule = (0, _metadata3.get_format_national_prefix_formatting_rule)(format, this.country_metadata); | ||
// If the user did input the national prefix | ||
// then maybe make it a part of the phone number template | ||
if (this.national_prefix) { | ||
var _national_prefix_formatting_rule = (0, _metadata.get_format_national_prefix_formatting_rule)(format, this.country_metadata); | ||
// If national prefix formatting rule is set | ||
// for this phone number format | ||
if (national_prefix_formatting_rule) { | ||
// Make the national prefix a part of the phone number template | ||
number_format = number_format.replace(_format.FIRST_GROUP_PATTERN, national_prefix_formatting_rule); | ||
// If national prefix formatting rule is set | ||
// for this phone number format | ||
if (_national_prefix_formatting_rule) { | ||
// Make the national prefix a part of the phone number template | ||
number_format = number_format.replace(_format.FIRST_GROUP_PATTERN, _national_prefix_formatting_rule); | ||
} | ||
} | ||
} | ||
// Get a formatting template which can be used to efficiently format | ||
// a partial number where digits are added one by one. | ||
// Get a formatting template which can be used to efficiently format | ||
// a partial number where digits are added one by one. | ||
// This match will always succeed, | ||
// because the "longest dummy phone number" | ||
// has enough length to accomodate any possible | ||
// national phone number format pattern. | ||
var dummy_phone_number_matching_format_pattern = LONGEST_DUMMY_PHONE_NUMBER.match(number_pattern)[0]; | ||
// Create formatting template for this phone number format | ||
var template = dummy_phone_number_matching_format_pattern | ||
// Format the dummy phone number according to the format | ||
.replace(new RegExp(number_pattern, 'g'), number_format) | ||
// Replace each dummy digit with a DIGIT_PLACEHOLDER | ||
.replace(DUMMY_DIGIT_MATCHER, DIGIT_PLACEHOLDER); | ||
// Create formatting template for this phone number format | ||
var template = dummy_phone_number_matching_format_pattern | ||
// Format the dummy phone number according to the format | ||
.replace(new RegExp(number_pattern, 'g'), number_format) | ||
// Replace each dummy digit with a DIGIT_PLACEHOLDER | ||
.replace(DUMMY_DIGIT_MATCHER, DIGIT_PLACEHOLDER); | ||
// This one is for national number only | ||
this.partially_populated_template = template; | ||
// This one is for national number only | ||
this.partially_populated_template = template; | ||
// For convenience, the public `.template` property | ||
// is gonna contain the whole international number | ||
// if the phone number being input is international. | ||
if (this.is_international()) { | ||
template = DIGIT_PLACEHOLDER + repeat(DIGIT_PLACEHOLDER, this.country_phone_code.length) + ' ' + template; | ||
} | ||
// For local numbers, replace national prefix | ||
// with a digit placeholder. | ||
else { | ||
template = template.replace(/\d/g, DIGIT_PLACEHOLDER); | ||
} | ||
// For convenience, the public `.template` property | ||
// is gonna contain the whole international number | ||
// if the phone number being input is international. | ||
if (this.is_international()) { | ||
template = DIGIT_PLACEHOLDER + repeat(DIGIT_PLACEHOLDER, this.country_phone_code.length) + ' ' + template; | ||
// This one is for the full phone number | ||
return this.template = template; | ||
} | ||
// For local numbers, replace national prefix | ||
// with a digit placeholder. | ||
else { | ||
template = template.replace(/\d/g, DIGIT_PLACEHOLDER); | ||
} | ||
}, { | ||
key: 'format_next_national_number_digits', | ||
value: function format_next_national_number_digits(digits) { | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
// This one is for the full phone number | ||
this.template = template; | ||
} | ||
}, { | ||
key: 'format_next_national_number_digits', | ||
value: function format_next_national_number_digits(digits) { | ||
var _iteratorNormalCompletion3 = true; | ||
var _didIteratorError3 = false; | ||
var _iteratorError3 = undefined; | ||
try { | ||
for (var _iterator3 = (0, _getIterator3.default)(digits), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var digit = _step3.value; | ||
try { | ||
for (var _iterator3 = (0, _getIterator3.default)(digits), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
var digit = _step3.value; | ||
// If there is room for more digits in current `template`, | ||
// then set the next digit in the `template`, | ||
// and return the formatted digits so far. | ||
// If there is room for more digits in current `template`, | ||
// then set the next digit in the `template`, | ||
// and return the formatted digits so far. | ||
// If more digits are entered than the current format could handle | ||
if (this.partially_populated_template.slice(this.last_match_position + 1).search(DIGIT_PLACEHOLDER_MATCHER) === -1) { | ||
// Reset the current format, | ||
// so that the new format will be chosen | ||
// in a subsequent `this.choose_another_format()` call | ||
// later in code. | ||
this.chosen_format = undefined; | ||
this.template = undefined; | ||
this.partially_populated_template = undefined; | ||
return; | ||
} | ||
// If more digits are entered than the current format could handle | ||
if (this.partially_populated_template.slice(this.last_match_position + 1).search(DIGIT_PLACEHOLDER_MATCHER) === -1) { | ||
// Reset the current format, | ||
// so that the new format will be chosen | ||
// in a subsequent `this.choose_another_format()` call | ||
// later in code. | ||
this.chosen_format = undefined; | ||
this.template = undefined; | ||
this.partially_populated_template = undefined; | ||
return; | ||
this.last_match_position = this.partially_populated_template.search(DIGIT_PLACEHOLDER_MATCHER); | ||
this.partially_populated_template = this.partially_populated_template.replace(DIGIT_PLACEHOLDER_MATCHER, digit); | ||
} | ||
this.last_match_position = this.partially_populated_template.search(DIGIT_PLACEHOLDER_MATCHER); | ||
this.partially_populated_template = this.partially_populated_template.replace(DIGIT_PLACEHOLDER_MATCHER, digit); | ||
} | ||
// Return the formatted phone number so far | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return) { | ||
_iterator3.return(); | ||
} | ||
// Return the formatted phone number so far | ||
} catch (err) { | ||
_didIteratorError3 = true; | ||
_iteratorError3 = err; | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
try { | ||
if (!_iteratorNormalCompletion3 && _iterator3.return) { | ||
_iterator3.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError3) { | ||
throw _iteratorError3; | ||
} | ||
} | ||
} | ||
return close_dangling_braces(this.partially_populated_template, this.last_match_position + 1).replace(DIGIT_PLACEHOLDER_MATCHER_GLOBAL, ' '); | ||
} | ||
}, { | ||
key: 'is_international', | ||
value: function is_international() { | ||
return this.parsed_input && this.parsed_input[0] === '+'; | ||
} | ||
}, { | ||
key: 'get_format_format', | ||
value: function get_format_format(format) { | ||
if (this.is_international()) { | ||
return (0, _format.local_to_international_style)((0, _metadata.get_format_international_format)(format)); | ||
} | ||
return close_dangling_braces(this.partially_populated_template, this.last_match_position + 1).replace(DIGIT_PLACEHOLDER_MATCHER_GLOBAL, ' '); | ||
} | ||
}, { | ||
key: 'is_international', | ||
value: function is_international() { | ||
return this.parsed_input && this.parsed_input[0] === '+'; | ||
} | ||
}, { | ||
key: 'get_format_format', | ||
value: function get_format_format(format) { | ||
if (this.is_international()) { | ||
return (0, _format.local_to_international_style)((0, _metadata3.get_format_international_format)(format)); | ||
return (0, _metadata.get_format_format)(format); | ||
} | ||
return (0, _metadata3.get_format_format)(format); | ||
} | ||
// Determines the country of the phone number | ||
// entered so far based on the country phone code | ||
// and the national phone number. | ||
// Determines the country of the phone number | ||
// entered so far based on the country phone code | ||
// and the national phone number. | ||
}, { | ||
key: 'determine_the_country', | ||
value: function determine_the_country() { | ||
this.country = (0, _parse.find_country_code)(this.country_phone_code, this.national_number, metadata); | ||
} | ||
}]); | ||
return as_you_type; | ||
}(); | ||
}, { | ||
key: 'determine_the_country', | ||
value: function determine_the_country() { | ||
this.country = (0, _parse.find_country_code)(this.country_phone_code, this.national_number); | ||
} | ||
}]); | ||
as_you_type.DIGIT_PLACEHOLDER = DIGIT_PLACEHOLDER; | ||
return as_you_type; | ||
}(); | ||
}; | ||
exports.default = as_you_type; | ||
exports.close_dangling_braces = close_dangling_braces; | ||
exports.count_occurences = count_occurences; | ||
exports.repeat = repeat; | ||
var _metadata = require('./metadata'); | ||
as_you_type.DIGIT_PLACEHOLDER = DIGIT_PLACEHOLDER; | ||
var _parse = require('./parse'); | ||
var _format = require('./format'); | ||
var _common = require('./common'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// Used in phone number format template creation. | ||
// Could be any digit, I guess. | ||
// This is an enhanced port of Google Android `libphonenumber`'s | ||
// `asyoutypeformatter.js` of 17th November, 2016. | ||
// | ||
// https://github.com/googlei18n/libphonenumber/blob/8d21a365061de2ba0675c878a710a7b24f74d2ae/javascript/i18n/phonenumbers/asyoutypeformatter.js | ||
var DUMMY_DIGIT = '9'; | ||
var DUMMY_DIGIT_MATCHER = new RegExp(DUMMY_DIGIT, 'g'); | ||
// I don't know why is it exactly `15` | ||
var LONGEST_NATIONAL_PHONE_NUMBER_LENGTH = 15; | ||
// Create a phone number consisting only of the digit 9 that matches the | ||
// `number_pattern` by applying the pattern to the "longest phone number" string. | ||
var LONGEST_DUMMY_PHONE_NUMBER = repeat(DUMMY_DIGIT, LONGEST_NATIONAL_PHONE_NUMBER_LENGTH); | ||
// The digits that have not been entered yet will be represented by a \u2008, | ||
// the punctuation space. | ||
var DIGIT_PLACEHOLDER = 'x'; // '\u2008' (punctuation space) | ||
var DIGIT_PLACEHOLDER_MATCHER = new RegExp(DIGIT_PLACEHOLDER); | ||
var DIGIT_PLACEHOLDER_MATCHER_GLOBAL = new RegExp(DIGIT_PLACEHOLDER, 'g'); | ||
// A pattern that is used to match character classes in regular expressions. | ||
// An example of a character class is "[1-4]". | ||
var CHARACTER_CLASS_PATTERN = /\[([^\[\]])*\]/g; | ||
// Any digit in a regular expression that actually denotes a digit. For | ||
// example, in the regular expression "80[0-2]\d{6,10}", the first 2 digits | ||
// (8 and 0) are standalone digits, but the rest are not. | ||
// Two look-aheads are needed because the number following \\d could be a | ||
// two-digit number, since the phone number can be as long as 15 digits. | ||
var STANDALONE_DIGIT_PATTERN = /\d(?=[^,}][^,}])/g; | ||
// A pattern that is used to determine if a `format` is eligible | ||
// to be used by the "as you type formatter". | ||
// It is eligible when the `format` contains groups of the dollar sign | ||
// followed by a single digit, separated by valid phone number punctuation. | ||
// This prevents invalid punctuation (such as the star sign in Israeli star numbers) | ||
// getting into the output of the "as you type formatter". | ||
var ELIGIBLE_FORMAT_PATTERN = new RegExp('^' + '[' + _parse.VALID_PUNCTUATION + ']*' + '(\\$\\d[' + _parse.VALID_PUNCTUATION + ']*)+' + '$'); | ||
// This is the minimum length of the leading digits of a phone number | ||
// to guarantee the first "leading digits pattern" for a phone number format | ||
// to be preemptive. | ||
var MIN_LEADING_DIGITS_LENGTH = 3; | ||
var VALID_INCOMPLETE_PHONE_NUMBER = '[' + _parse.PLUS_CHARS + ']{0,1}' + '[' + _parse.VALID_PUNCTUATION + _parse.VALID_DIGITS + ']*'; | ||
var VALID_INCOMPLETE_PHONE_NUMBER_PATTERN = new RegExp('^' + VALID_INCOMPLETE_PHONE_NUMBER + '$', 'i'); | ||
function close_dangling_braces(template, cut_before) { | ||
@@ -824,0 +806,0 @@ var retained_template = template.slice(0, cut_before); |
@@ -19,17 +19,8 @@ 'use strict'; | ||
var _metadata = require('../metadata.min'); | ||
var _metadata2 = _interopRequireDefault(_metadata); | ||
var _parse = require('./parse'); | ||
var _metadata3 = require('./metadata'); | ||
var _metadata = require('./metadata'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
// This is a port of Google Android `libphonenumber`'s | ||
// `phonenumberutil.js` of 17th November, 2016. | ||
// | ||
// https://github.com/googlei18n/libphonenumber/commits/master/javascript/i18n/phonenumbers/phonenumberutil.js | ||
function format() { | ||
@@ -54,6 +45,6 @@ var input = arguments.length <= 0 || arguments[0] === undefined ? '' : arguments[0]; | ||
if (input.country) { | ||
country_metadata = _metadata2.default.countries[input.country]; | ||
country_metadata = this.metadata.countries[input.country]; | ||
} | ||
var _parse_phone_number_a = (0, _parse.parse_phone_number_and_country_phone_code)(input.phone); | ||
var _parse_phone_number_a = (0, _parse.parse_phone_number_and_country_phone_code)(input.phone, this.metadata); | ||
@@ -66,7 +57,7 @@ var country_phone_code = _parse_phone_number_a.country_phone_code; | ||
// Check country restriction | ||
if (input.country && country_phone_code !== (0, _metadata3.get_phone_code)(country_metadata)) { | ||
if (input.country && country_phone_code !== (0, _metadata.get_phone_code)(country_metadata)) { | ||
return input.phone; | ||
} | ||
country_metadata = (0, _metadata3.get_metadata_by_country_phone_code)(country_phone_code, _metadata2.default); | ||
country_metadata = (0, _metadata.get_metadata_by_country_phone_code)(country_phone_code, this.metadata); | ||
} | ||
@@ -81,9 +72,9 @@ | ||
if (!number) { | ||
return '+' + (0, _metadata3.get_phone_code)(country_metadata); | ||
return '+' + (0, _metadata.get_phone_code)(country_metadata); | ||
} | ||
var national_number = format_national_number(number, 'International', false, country_metadata); | ||
return '+' + (0, _metadata3.get_phone_code)(country_metadata) + ' ' + national_number; | ||
return '+' + (0, _metadata.get_phone_code)(country_metadata) + ' ' + national_number; | ||
case 'International_plaintext': | ||
return '+' + (0, _metadata3.get_phone_code)(country_metadata) + input.phone; | ||
return '+' + (0, _metadata.get_phone_code)(country_metadata) + input.phone; | ||
@@ -102,8 +93,13 @@ case 'National': | ||
// group actually used in the pattern will be matched. | ||
// This is a port of Google Android `libphonenumber`'s | ||
// `phonenumberutil.js` of 17th November, 2016. | ||
// | ||
// https://github.com/googlei18n/libphonenumber/commits/master/javascript/i18n/phonenumbers/phonenumberutil.js | ||
var FIRST_GROUP_PATTERN = exports.FIRST_GROUP_PATTERN = /(\$\d)/; | ||
function format_national_number_using_format(number, format, international, enforce_national_prefix, country_metadata) { | ||
var format_pattern_matcher = new RegExp((0, _metadata3.get_format_pattern)(format)); | ||
var format_pattern_matcher = new RegExp((0, _metadata.get_format_pattern)(format)); | ||
var national_prefix_formatting_rule = (0, _metadata3.get_format_national_prefix_formatting_rule)(format, country_metadata); | ||
var national_prefix_formatting_rule = (0, _metadata.get_format_national_prefix_formatting_rule)(format, country_metadata); | ||
@@ -114,14 +110,9 @@ // National prefix is omitted if there's no national prefix formatting rule | ||
// (and it is not enforced explicitly) | ||
var national_prefix_may_be_omitted = !national_prefix_formatting_rule || national_prefix_formatting_rule && (0, _metadata3.get_format_national_prefix_is_optional_when_formatting)(format, country_metadata) && !enforce_national_prefix; | ||
var national_prefix_may_be_omitted = !national_prefix_formatting_rule || national_prefix_formatting_rule && (0, _metadata.get_format_national_prefix_is_optional_when_formatting)(format, country_metadata) && !enforce_national_prefix; | ||
if (!international && !national_prefix_may_be_omitted) { | ||
// If national prefix formatting rule is set | ||
// (e.g. it is not set for US) | ||
if (national_prefix_formatting_rule) { | ||
return number.replace(format_pattern_matcher, (0, _metadata3.get_format_format)(format).replace(FIRST_GROUP_PATTERN, national_prefix_formatting_rule)); | ||
} | ||
return number.replace(format_pattern_matcher, (0, _metadata.get_format_format)(format).replace(FIRST_GROUP_PATTERN, national_prefix_formatting_rule)); | ||
} | ||
var formatted_number = number.replace(format_pattern_matcher, international ? (0, _metadata3.get_format_international_format)(format) : (0, _metadata3.get_format_format)(format)); | ||
var formatted_number = number.replace(format_pattern_matcher, international ? (0, _metadata.get_format_international_format)(format) : (0, _metadata.get_format_format)(format)); | ||
@@ -136,3 +127,3 @@ if (international) { | ||
function format_national_number(number, format_as, enforce_national_prefix, country_metadata) { | ||
var format = choose_format_for_number((0, _metadata3.get_formats)(country_metadata), number); | ||
var format = choose_format_for_number((0, _metadata.get_formats)(country_metadata), number); | ||
@@ -155,5 +146,5 @@ if (!format) { | ||
if ((0, _metadata3.get_format_leading_digits_patterns)(_format).length > 0) { | ||
if ((0, _metadata.get_format_leading_digits_patterns)(_format).length > 0) { | ||
// The last leading_digits_pattern is used here, as it is the most detailed | ||
var last_leading_digits_pattern = (0, _metadata3.get_format_leading_digits_patterns)(_format)[(0, _metadata3.get_format_leading_digits_patterns)(_format).length - 1]; | ||
var last_leading_digits_pattern = (0, _metadata.get_format_leading_digits_patterns)(_format)[(0, _metadata.get_format_leading_digits_patterns)(_format).length - 1]; | ||
@@ -165,3 +156,3 @@ if (national_number.search(last_leading_digits_pattern) !== 0) { | ||
if ((0, _common.matches_entirely)(national_number, new RegExp((0, _metadata3.get_format_pattern)(_format)))) { | ||
if ((0, _common.matches_entirely)(national_number, new RegExp((0, _metadata.get_format_pattern)(_format)))) { | ||
return _format; | ||
@@ -168,0 +159,0 @@ } |
@@ -30,14 +30,6 @@ 'use strict'; | ||
var _metadata = require('../metadata.min'); | ||
var _metadata = require('./metadata'); | ||
var _metadata2 = _interopRequireDefault(_metadata); | ||
var _metadata3 = require('./metadata'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
var PLUS_CHARS = exports.PLUS_CHARS = '++'; | ||
// Digits accepted in phone numbers | ||
// (ascii, fullwidth, arabic-indic, and eastern arabic digits). | ||
// This is a port of Google Android `libphonenumber`'s | ||
@@ -48,2 +40,6 @@ // `phonenumberutil.js` of 17th November, 2016. | ||
var PLUS_CHARS = exports.PLUS_CHARS = '++'; | ||
// Digits accepted in phone numbers | ||
// (ascii, fullwidth, arabic-indic, and eastern arabic digits). | ||
var VALID_DIGITS = exports.VALID_DIGITS = '0-90-9٠-٩۰-۹'; | ||
@@ -211,3 +207,3 @@ | ||
var _parse_phone_number_a = parse_phone_number_and_country_phone_code(formatted_phone_number); | ||
var _parse_phone_number_a = parse_phone_number_and_country_phone_code(formatted_phone_number, this.metadata); | ||
@@ -228,7 +224,7 @@ var country_phone_code = _parse_phone_number_a.country_phone_code; | ||
// Check country restriction | ||
if (options.country.restrict && country_phone_code !== (0, _metadata3.get_phone_code)(_metadata2.default.countries[options.country.restrict])) { | ||
if (options.country.restrict && country_phone_code !== (0, _metadata.get_phone_code)(this.metadata.countries[options.country.restrict])) { | ||
return {}; | ||
} | ||
country_metadata = (0, _metadata3.get_metadata_by_country_phone_code)(country_phone_code, _metadata2.default); | ||
country_metadata = (0, _metadata.get_metadata_by_country_phone_code)(country_phone_code, this.metadata); | ||
@@ -243,3 +239,3 @@ // `country` will be set later, | ||
country = options.country.default || options.country.restrict; | ||
country_metadata = _metadata2.default.countries[country]; | ||
country_metadata = this.metadata.countries[country]; | ||
@@ -262,3 +258,3 @@ number = normalize(text); | ||
if (!country) { | ||
country = find_country_code(country_phone_code, national_number); | ||
country = find_country_code(country_phone_code, national_number, this.metadata); | ||
@@ -285,3 +281,3 @@ // Just in case there's a bug in Google's metadata | ||
var national_number_rule = new RegExp((0, _metadata3.get_national_number_pattern)(country_metadata)); | ||
var national_number_rule = new RegExp((0, _metadata.get_national_number_pattern)(country_metadata)); | ||
@@ -395,3 +391,3 @@ if (!(0, _common.matches_entirely)(national_number, national_number_rule)) { | ||
// | ||
function parse_phone_number_and_country_phone_code(number) { | ||
function parse_phone_number_and_country_phone_code(number, metadata) { | ||
number = parse_phone_number(number); | ||
@@ -430,3 +426,3 @@ | ||
if (_metadata2.default.country_phone_code_to_countries[country_phone_code]) { | ||
if (metadata.country_phone_code_to_countries[country_phone_code]) { | ||
return { country_phone_code: country_phone_code, number: number.slice(i) }; | ||
@@ -443,3 +439,3 @@ } | ||
function strip_national_prefix(number, country_metadata) { | ||
var national_prefix_for_parsing = (0, _metadata3.get_national_prefix_for_parsing)(country_metadata); | ||
var national_prefix_for_parsing = (0, _metadata.get_national_prefix_for_parsing)(country_metadata); | ||
@@ -458,3 +454,3 @@ if (!number || !national_prefix_for_parsing) { | ||
var national_prefix_transform_rule = (0, _metadata3.get_national_prefix_transform_rule)(country_metadata); | ||
var national_prefix_transform_rule = (0, _metadata.get_national_prefix_transform_rule)(country_metadata); | ||
@@ -474,3 +470,3 @@ var national_significant_number = void 0; | ||
var national_number_rule = new RegExp((0, _metadata3.get_national_number_pattern)(country_metadata)); | ||
var national_number_rule = new RegExp((0, _metadata.get_national_number_pattern)(country_metadata)); | ||
@@ -485,5 +481,5 @@ // If the original number was viable, and the resultant number is not, then return. | ||
function find_country_code(country_phone_code, national_phone_number) { | ||
function find_country_code(country_phone_code, national_phone_number, metadata) { | ||
// Is always non-empty, because `country_phone_code` is always valid | ||
var possible_countries = _metadata2.default.country_phone_code_to_countries[country_phone_code]; | ||
var possible_countries = metadata.country_phone_code_to_countries[country_phone_code]; | ||
@@ -504,7 +500,7 @@ // If there's just one country corresponding to the country code, | ||
var country = _metadata2.default.countries[country_code]; | ||
var country = metadata.countries[country_code]; | ||
// Leading digits check would be the simplest one | ||
if ((0, _metadata3.get_leading_digits)(country)) { | ||
if (national_phone_number && national_phone_number.search((0, _metadata3.get_leading_digits)(country)) === 0) { | ||
if ((0, _metadata.get_leading_digits)(country)) { | ||
if (national_phone_number && national_phone_number.search((0, _metadata.get_leading_digits)(country)) === 0) { | ||
return country_code; | ||
@@ -538,7 +534,7 @@ } | ||
// Is this national number even valid for this country | ||
if (!is_of_type(national_number, (0, _metadata3.get_national_number_pattern)(country_metadata))) { | ||
if (!is_of_type(national_number, (0, _metadata.get_national_number_pattern)(country_metadata))) { | ||
return; | ||
} | ||
if (is_of_type(national_number, (0, _metadata3.get_type_mobile)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_mobile)(country_metadata))) { | ||
// Because duplicate regular expressions are removed | ||
@@ -550,3 +546,3 @@ // to reduce metadata size, if there's no "fixed line" pattern | ||
/* istanbul ignore if */ | ||
if (!(0, _metadata3.get_type_fixed_line)(country_metadata)) { | ||
if (!(0, _metadata.get_type_fixed_line)(country_metadata)) { | ||
return 'FIXED_LINE_OR_MOBILE'; | ||
@@ -559,7 +555,7 @@ } | ||
// Is it fixed line number | ||
if (is_of_type(national_number, (0, _metadata3.get_type_fixed_line)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_fixed_line)(country_metadata))) { | ||
// Because duplicate regular expressions are removed | ||
// to reduce metadata size, if there's no "mobile" pattern | ||
// then it means it was removed due to being a duplicate of some other pattern. | ||
if (!(0, _metadata3.get_type_mobile)(country_metadata)) { | ||
if (!(0, _metadata.get_type_mobile)(country_metadata)) { | ||
return 'FIXED_LINE_OR_MOBILE'; | ||
@@ -571,11 +567,11 @@ } | ||
if (is_of_type(national_number, (0, _metadata3.get_type_toll_free)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_toll_free)(country_metadata))) { | ||
return 'TOLL_FREE'; | ||
} | ||
if (is_of_type(national_number, (0, _metadata3.get_type_premium_rate)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_premium_rate)(country_metadata))) { | ||
return 'PREMIUM_RATE'; | ||
} | ||
if (is_of_type(national_number, (0, _metadata3.get_type_personal_number)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_personal_number)(country_metadata))) { | ||
return 'PERSONAL_NUMBER'; | ||
@@ -585,3 +581,3 @@ } | ||
/* istanbul ignore if */ | ||
if (is_of_type(national_number, (0, _metadata3.get_type_voice_mail)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_voice_mail)(country_metadata))) { | ||
return 'VOICEMAIL'; | ||
@@ -591,3 +587,3 @@ } | ||
/* istanbul ignore if */ | ||
if (is_of_type(national_number, (0, _metadata3.get_type_uan)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_uan)(country_metadata))) { | ||
return 'UAN'; | ||
@@ -597,3 +593,3 @@ } | ||
/* istanbul ignore if */ | ||
if (is_of_type(national_number, (0, _metadata3.get_type_pager)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_pager)(country_metadata))) { | ||
return 'PAGER'; | ||
@@ -603,3 +599,3 @@ } | ||
/* istanbul ignore if */ | ||
if (is_of_type(national_number, (0, _metadata3.get_type_voip)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_voip)(country_metadata))) { | ||
return 'VOIP'; | ||
@@ -609,3 +605,3 @@ } | ||
/* istanbul ignore if */ | ||
if (is_of_type(national_number, (0, _metadata3.get_type_shared_cost)(country_metadata))) { | ||
if (is_of_type(national_number, (0, _metadata.get_type_shared_cost)(country_metadata))) { | ||
return 'SHARED_COST'; | ||
@@ -612,0 +608,0 @@ } |
@@ -20,5 +20,5 @@ 'use strict'; | ||
function is_valid(number, country_code) { | ||
return (0, _keys2.default)((0, _parse2.default)(number, country_code)).length > 0; | ||
return (0, _keys2.default)(_parse2.default.call(this, number, country_code)).length > 0; | ||
} | ||
module.exports = exports['default']; | ||
//# sourceMappingURL=validate.js.map |
@@ -0,1 +1,6 @@ | ||
0.2.20 / 28.12.2016 | ||
=================== | ||
* Added the ability to use custom-countries generated metadata as a parameter for the functions exported from this library | ||
0.2.19 / 25.12.2016 | ||
@@ -2,0 +7,0 @@ =================== |
'use strict' | ||
var metadata = require('./metadata.min') | ||
var custom = require('./custom') | ||
exports = module.exports = {} | ||
exports.parse = require('./build/parse').default | ||
exports.format = require('./build/format').default | ||
var context = { metadata: metadata } | ||
exports.is_valid_number = require('./build/validate') | ||
exports.isValidNumber = require('./build/validate') | ||
exports.parse = custom.parse.bind(context) | ||
exports.format = custom.format.bind(context) | ||
exports.as_you_type = require('./build/as you type').default | ||
exports.asYouType = require('./build/as you type').default | ||
exports.is_valid_number = custom.is_valid_number.bind(context) | ||
exports.isValidNumber = exports.is_valid_number | ||
exports.metadata = require('./metadata.min') | ||
exports.as_you_type = custom.as_you_type(metadata) | ||
exports.asYouType = exports.as_you_type | ||
exports.get_phone_code = require('./build/metadata').get_phone_code | ||
exports.getPhoneCode = require('./build/metadata').get_phone_code | ||
// exports['default'] = ... |
@@ -1,38 +0,19 @@ | ||
export | ||
{ | ||
default as parse | ||
} | ||
from './source/parse' | ||
import metadata from './metadata.min' | ||
export | ||
import | ||
{ | ||
default as format | ||
parse as _parse, | ||
format as _format, | ||
is_valid_number as _is_valid_number, | ||
as_you_type as _as_you_type | ||
} | ||
from './source/format' | ||
from './custom.es6' | ||
export | ||
{ | ||
default as is_valid_number, | ||
default as isValidNumber | ||
} | ||
from './source/validate' | ||
export const parse = _parse.bind({ metadata }) | ||
export const format = _format.bind({ metadata }) | ||
export | ||
{ | ||
default as as_you_type, | ||
default as asYouType | ||
} | ||
from './source/as you type' | ||
export const is_valid_number = _is_valid_number.bind({ metadata }) | ||
export const isValidNumber = is_valid_number | ||
export | ||
{ | ||
default as metadata | ||
} | ||
from './metadata.min' | ||
export | ||
{ | ||
get_phone_code, | ||
get_phone_code as getPhoneCode | ||
} | ||
from './source/metadata' | ||
export const as_you_type = _as_you_type(metadata) | ||
export const asYouType = as_you_type |
{ | ||
"name": "libphonenumber-js", | ||
"version": "0.2.19", | ||
"version": "0.2.20", | ||
"description": "A simpler (and smaller) rewrite of Google Android's popular libphonenumber library", | ||
@@ -5,0 +5,0 @@ "main": "index.common.js", |
@@ -196,5 +196,18 @@ # libphonenumber-js | ||
npm run metadata:generate NL,BE,FR,DE,LU,AT | ||
# The resulting `metadata.min.json` will only contain those countries | ||
``` | ||
Then use the generated `metadata.min.json` with the `libphonenumber-js/custom` functions | ||
```js | ||
import { parse, format, isValidNumber, asYouType } from 'libphonenumber-js/custom' | ||
import metadata from './metadata.min.json' | ||
const parseCustomCountries = parse.bind({ metadata }) | ||
const formatCustomCountries = format.bind({ metadata }) | ||
const isValidNumberCustomCountries = isValidNumber.bind({ metadata }) | ||
const asYouTypeCustomCountries = asYouType(metadata) | ||
``` | ||
For utilizing "tree-shaking" in ES6-capable bundlers (e.g. Webpack 2) `libphonenumber-js/custom.es6` may be used. | ||
## Contributing | ||
@@ -201,0 +214,0 @@ |
@@ -6,4 +6,2 @@ // This is an enhanced port of Google Android `libphonenumber`'s | ||
import metadata from '../metadata.min' | ||
import | ||
@@ -108,544 +106,540 @@ { | ||
export default class as_you_type | ||
export default function(metadata) | ||
{ | ||
constructor(country_code) | ||
class as_you_type | ||
{ | ||
if (country_code && metadata.countries[country_code]) | ||
constructor(country_code) | ||
{ | ||
this.default_country = country_code | ||
if (country_code && metadata.countries[country_code]) | ||
{ | ||
this.default_country = country_code | ||
} | ||
this.reset() | ||
} | ||
this.reset() | ||
} | ||
input(text) | ||
{ | ||
// Parse input | ||
input(text) | ||
{ | ||
// Parse input | ||
let extracted_number = extract_formatted_phone_number(text) | ||
let extracted_number = extract_formatted_phone_number(text) | ||
// Special case for a lone '+' sign | ||
// since it's not considered a possible phone number. | ||
if (!extracted_number) | ||
{ | ||
if (text && text.indexOf('+') >= 0) | ||
{ | ||
extracted_number = '+' | ||
} | ||
} | ||
// Special case for a lone '+' sign | ||
// since it's not considered a possible phone number. | ||
if (!extracted_number) | ||
{ | ||
if (text && text.indexOf('+') >= 0) | ||
// Validate possible first part of a phone number | ||
if (!matches_entirely(extracted_number, VALID_INCOMPLETE_PHONE_NUMBER_PATTERN)) | ||
{ | ||
extracted_number = '+' | ||
return this.current_output | ||
} | ||
} | ||
// Validate possible first part of a phone number | ||
if (!matches_entirely(extracted_number, VALID_INCOMPLETE_PHONE_NUMBER_PATTERN)) | ||
{ | ||
return this.current_output | ||
return this.process_input(parse_phone_number(extracted_number)) | ||
} | ||
return this.process_input(parse_phone_number(extracted_number)) | ||
} | ||
process_input(input) | ||
{ | ||
// If an out of position '+' sign detected | ||
// (or a second '+' sign), | ||
// then just drop it from the input. | ||
if (input[0] === '+') | ||
process_input(input) | ||
{ | ||
if (!this.parsed_input) | ||
// If an out of position '+' sign detected | ||
// (or a second '+' sign), | ||
// then just drop it from the input. | ||
if (input[0] === '+') | ||
{ | ||
this.parsed_input += '+' | ||
if (!this.parsed_input) | ||
{ | ||
this.parsed_input += '+' | ||
// If a default country was set | ||
// then reset it because an explicitly international | ||
// phone number is being entered | ||
this.reset_countriness() | ||
// If a default country was set | ||
// then reset it because an explicitly international | ||
// phone number is being entered | ||
this.reset_countriness() | ||
} | ||
input = input.slice(1) | ||
} | ||
input = input.slice(1) | ||
} | ||
// Raw phone number | ||
this.parsed_input += input | ||
// Raw phone number | ||
this.parsed_input += input | ||
// Reset phone number validation state | ||
this.valid = false | ||
// Reset phone number validation state | ||
this.valid = false | ||
// Add digits to the national number | ||
this.national_number += input | ||
// Add digits to the national number | ||
this.national_number += input | ||
// Try to format the parsed input | ||
// Try to format the parsed input | ||
if (this.is_international()) | ||
{ | ||
if (!this.country_phone_code) | ||
{ | ||
// If one looks at country phone codes | ||
// then he can notice that no one country phone code | ||
// is ever a (leftmost) substring of another country phone code. | ||
// So if a valid country code is extracted so far | ||
// then it means that this is the country code. | ||
if (this.is_international()) | ||
{ | ||
if (!this.country_phone_code) | ||
// If no country phone code could be extracted so far, | ||
// then just return the raw phone number, | ||
// because it has no way of knowing | ||
// how to format the phone number so far. | ||
if (!this.extract_country_phone_code()) | ||
{ | ||
// Return raw phone number | ||
return this.parsed_input | ||
} | ||
// Initialize country-specific data | ||
this.initialize_phone_number_formats_for_this_country_phone_code() | ||
this.reset_format() | ||
this.determine_the_country() | ||
} | ||
// `this.country` could be `undefined`, | ||
// for instance, when there is ambiguity | ||
// in a form of several different countries | ||
// each corresponding to the same country phone code | ||
// (e.g. NANPA: USA, Canada, etc), | ||
// and there's not enough digits entered | ||
// to reliably determine the country | ||
// the phone number belongs to. | ||
// Therefore, in cases of such ambiguity, | ||
// each time something is input, | ||
// try to determine the country | ||
// (if it's not determined yet). | ||
else if (!this.country) | ||
{ | ||
this.determine_the_country() | ||
} | ||
} | ||
else | ||
{ | ||
// If one looks at country phone codes | ||
// then he can notice that no one country phone code | ||
// is ever a (leftmost) substring of another country phone code. | ||
// So if a valid country code is extracted so far | ||
// then it means that this is the country code. | ||
// Some national prefixes are substrings of other national prefixes | ||
// (for the same country), therefore try to extract national prefix each time | ||
// because a longer national prefix might be available at some point in time. | ||
// If no country phone code could be extracted so far, | ||
// then just return the raw phone number, | ||
// because it has no way of knowing | ||
// how to format the phone number so far. | ||
if (!this.extract_country_phone_code()) | ||
const previous_national_prefix = this.national_prefix | ||
this.national_number = this.national_prefix + this.national_number | ||
// Possibly extract a national prefix | ||
this.extract_national_prefix() | ||
if (this.national_prefix !== previous_national_prefix) | ||
{ | ||
// Return raw phone number | ||
return this.parsed_input | ||
// National number has changed | ||
// (due to another national prefix been extracted) | ||
// therefore national number has changed | ||
// therefore reset all previous formatting data. | ||
// (and leading digits matching state) | ||
this.matching_formats = this.available_formats | ||
this.reset_format() | ||
} | ||
} | ||
// Initialize country-specific data | ||
this.initialize_phone_number_formats_for_this_country_phone_code() | ||
this.reset_format() | ||
this.determine_the_country() | ||
} | ||
// `this.country` could be `undefined`, | ||
// for instance, when there is ambiguity | ||
// in a form of several different countries | ||
// each corresponding to the same country phone code | ||
// (e.g. NANPA: USA, Canada, etc), | ||
// and there's not enough digits entered | ||
// to reliably determine the country | ||
// the phone number belongs to. | ||
// Therefore, in cases of such ambiguity, | ||
// each time something is input, | ||
// try to determine the country | ||
// (if it's not determined yet). | ||
else if (!this.country) | ||
// Format the phone number (given the next digits) | ||
const formatted_national_phone_number = this.format_national_phone_number(input) | ||
// If the phone number could be formatted, | ||
// then return it, possibly prepending with country phone code | ||
// (for international phone numbers only) | ||
if (formatted_national_phone_number) | ||
{ | ||
this.determine_the_country() | ||
return this.full_phone_number(formatted_national_phone_number) | ||
} | ||
// If the phone number couldn't be formatted, | ||
// then just fall back to the raw phone number. | ||
return this.parsed_input | ||
} | ||
else | ||
format_national_phone_number(next_digits) | ||
{ | ||
// Some national prefixes are substrings of other national prefixes | ||
// (for the same country), therefore try to extract national prefix each time | ||
// because a longer national prefix might be available at some point in time. | ||
// Format the next phone number digits | ||
// using the previously chosen phone number format. | ||
// | ||
// This is done here because if `attempt_to_format_complete_phone_number` | ||
// was placed before this call then the `template` | ||
// wouldn't reflect the situation correctly (and would therefore be inconsistent) | ||
// | ||
let national_number_formatted_with_previous_format | ||
if (this.chosen_format) | ||
{ | ||
national_number_formatted_with_previous_format = this.format_next_national_number_digits(next_digits) | ||
} | ||
const previous_national_prefix = this.national_prefix | ||
this.national_number = this.national_prefix + this.national_number | ||
// See if the input digits can be formatted properly already. If not, | ||
// use the results from format_next_national_number_digits(), which does formatting | ||
// based on the formatting pattern chosen. | ||
// Possibly extract a national prefix | ||
this.extract_national_prefix() | ||
const formatted_number = this.attempt_to_format_complete_phone_number() | ||
if (this.national_prefix !== previous_national_prefix) | ||
if (formatted_number) | ||
{ | ||
// National number has changed | ||
// (due to another national prefix been extracted) | ||
// therefore national number has changed | ||
// therefore reset all previous formatting data. | ||
// (and leading digits matching state) | ||
this.matching_formats = this.available_formats | ||
this.reset_format() | ||
this.valid = true | ||
return formatted_number | ||
} | ||
} | ||
// Format the phone number (given the next digits) | ||
const formatted_national_phone_number = this.format_national_phone_number(input) | ||
// Check if the previously chosen phone number format still holds | ||
this.match_formats_by_leading_digits() | ||
// If the phone number could be formatted, | ||
// then return it, possibly prepending with country phone code | ||
// (for international phone numbers only) | ||
if (formatted_national_phone_number) | ||
{ | ||
return this.full_phone_number(formatted_national_phone_number) | ||
} | ||
// If the previously chosen phone number format | ||
// didn't match the next (current) digit being input | ||
// (leading digits pattern didn't match). | ||
if (this.choose_another_format()) | ||
{ | ||
// And a more appropriate phone number format | ||
// has been chosen for these `leading digits`, | ||
// then format the national phone number (so far) | ||
// using the newly selected phone number pattern. | ||
// If the phone number couldn't be formatted, | ||
// then just fall back to the raw phone number. | ||
return this.parsed_input | ||
} | ||
// Will return `undefined` if it couldn't format | ||
// the supplied national number | ||
// using the selected phone number pattern. | ||
format_national_phone_number(next_digits) | ||
{ | ||
// Format the next phone number digits | ||
// using the previously chosen phone number format. | ||
// | ||
// This is done here because if `attempt_to_format_complete_phone_number` | ||
// was placed before this call then the `template` | ||
// wouldn't reflect the situation correctly (and would therefore be inconsistent) | ||
// | ||
let national_number_formatted_with_previous_format | ||
if (this.chosen_format) | ||
{ | ||
national_number_formatted_with_previous_format = this.format_next_national_number_digits(next_digits) | ||
} | ||
return this.reformat_national_number() | ||
} | ||
// See if the input digits can be formatted properly already. If not, | ||
// use the results from format_next_national_number_digits(), which does formatting | ||
// based on the formatting pattern chosen. | ||
// If could format the next (current) digit | ||
// using the previously chosen phone number format | ||
// then return the formatted number so far. | ||
const formatted_number = this.attempt_to_format_complete_phone_number() | ||
// If no new phone number format could be chosen, | ||
// and couldn't format the supplied national number | ||
// using the selected phone number pattern, | ||
// then it will return `undefined`. | ||
if (formatted_number) | ||
{ | ||
this.valid = true | ||
return formatted_number | ||
return national_number_formatted_with_previous_format | ||
} | ||
// Check if the previously chosen phone number format still holds | ||
this.match_formats_by_leading_digits() | ||
// If the previously chosen phone number format | ||
// didn't match the next (current) digit being input | ||
// (leading digits pattern didn't match). | ||
if (this.choose_another_format()) | ||
reset() | ||
{ | ||
// And a more appropriate phone number format | ||
// has been chosen for these `leading digits`, | ||
// then format the national phone number (so far) | ||
// using the newly selected phone number pattern. | ||
// Input stripped of non-phone-number characters. | ||
// Can only contain a possible leading '+' sign and digits. | ||
this.parsed_input = '' | ||
// Will return `undefined` if it couldn't format | ||
// the supplied national number | ||
// using the selected phone number pattern. | ||
this.current_output = '' | ||
return this.reformat_national_number() | ||
} | ||
// This contains the national prefix that has been extracted. It contains only | ||
// digits without formatting. | ||
this.national_prefix = '' | ||
// If could format the next (current) digit | ||
// using the previously chosen phone number format | ||
// then return the formatted number so far. | ||
this.national_number = '' | ||
// If no new phone number format could be chosen, | ||
// and couldn't format the supplied national number | ||
// using the selected phone number pattern, | ||
// then it will return `undefined`. | ||
this.reset_countriness() | ||
return national_number_formatted_with_previous_format | ||
} | ||
this.reset_format() | ||
reset() | ||
{ | ||
// Input stripped of non-phone-number characters. | ||
// Can only contain a possible leading '+' sign and digits. | ||
this.parsed_input = '' | ||
this.valid = false | ||
this.current_output = '' | ||
return this | ||
} | ||
// This contains the national prefix that has been extracted. It contains only | ||
// digits without formatting. | ||
this.national_prefix = '' | ||
reset_country() | ||
{ | ||
if (this.default_country && !this.is_international()) | ||
{ | ||
this.country = this.default_country | ||
} | ||
else | ||
{ | ||
this.country = undefined | ||
} | ||
} | ||
this.national_number = '' | ||
reset_countriness() | ||
{ | ||
this.reset_country() | ||
this.reset_countriness() | ||
if (this.default_country && !this.is_international()) | ||
{ | ||
this.country_metadata = metadata.countries[this.default_country] | ||
this.country_phone_code = this.country_metadata.phone_code | ||
this.reset_format() | ||
this.initialize_phone_number_formats_for_this_country_phone_code() | ||
} | ||
else | ||
{ | ||
this.country_metadata = undefined | ||
this.country_phone_code = undefined | ||
this.valid = false | ||
this.available_formats = [] | ||
this.matching_formats = this.available_formats | ||
} | ||
} | ||
return this | ||
} | ||
reset_country() | ||
{ | ||
if (this.default_country && !this.is_international()) | ||
reset_format() | ||
{ | ||
this.country = this.default_country | ||
this.chosen_format = undefined | ||
this.template = undefined | ||
this.partially_populated_template = undefined | ||
this.last_match_position = -1 | ||
} | ||
else | ||
// Format each digit of national phone number (so far) | ||
// using the newly selected phone number pattern. | ||
reformat_national_number() | ||
{ | ||
this.country = undefined | ||
// Format each digit of national phone number (so far) | ||
// using the selected phone number pattern. | ||
return this.format_next_national_number_digits(this.national_number) | ||
} | ||
} | ||
reset_countriness() | ||
{ | ||
this.reset_country() | ||
if (this.default_country && !this.is_international()) | ||
initialize_phone_number_formats_for_this_country_phone_code() | ||
{ | ||
this.country_metadata = metadata.countries[this.default_country] | ||
this.country_phone_code = this.country_metadata.phone_code | ||
// Get all "eligible" phone number formats for this country | ||
this.available_formats = get_formats(this.country_metadata).filter((format) => | ||
{ | ||
return ELIGIBLE_FORMAT_PATTERN.test(get_format_international_format(format)) | ||
}) | ||
// Try the formats with "leading digits" defined first | ||
.sort((a, b) => | ||
{ | ||
// Leading digits are defined for most formats | ||
/* istanbul ignore next */ | ||
if (get_format_leading_digits_patterns(a).length === 0 | ||
&& get_format_leading_digits_patterns(b).length > 0) | ||
{ | ||
return -1 | ||
} | ||
this.initialize_phone_number_formats_for_this_country_phone_code() | ||
} | ||
else | ||
{ | ||
this.country_metadata = undefined | ||
this.country_phone_code = undefined | ||
// Leading digits are defined for most formats | ||
/* istanbul ignore next */ | ||
if (get_format_leading_digits_patterns(a).length > 0 | ||
&& get_format_leading_digits_patterns(b).length === 0) | ||
{ | ||
return 1 | ||
} | ||
this.available_formats = [] | ||
return 0 | ||
}) | ||
this.matching_formats = this.available_formats | ||
} | ||
} | ||
reset_format() | ||
{ | ||
this.chosen_format = undefined | ||
this.template = undefined | ||
this.partially_populated_template = undefined | ||
this.last_match_position = -1 | ||
} | ||
match_formats_by_leading_digits() | ||
{ | ||
const leading_digits = this.national_number | ||
// Format each digit of national phone number (so far) | ||
// using the newly selected phone number pattern. | ||
reformat_national_number() | ||
{ | ||
// Format each digit of national phone number (so far) | ||
// using the selected phone number pattern. | ||
return this.format_next_national_number_digits(this.national_number) | ||
} | ||
// "leading digits" patterns start with a maximum 3 digits, | ||
// and then with each additional digit | ||
// a more precise "leading digits" pattern is specified. | ||
// They could make "leading digits" patterns start | ||
// with a maximum of a single digit, but they didn't, | ||
// so it's possible that some phone number formats | ||
// will be falsely rejected until there are at least | ||
// 3 digits in the national (significant) number being input. | ||
initialize_phone_number_formats_for_this_country_phone_code() | ||
{ | ||
// Get all "eligible" phone number formats for this country | ||
this.available_formats = get_formats(this.country_metadata).filter((format) => | ||
{ | ||
return ELIGIBLE_FORMAT_PATTERN.test(get_format_international_format(format)) | ||
}) | ||
// Try the formats with "leading digits" defined first | ||
.sort((a, b) => | ||
{ | ||
// Leading digits are defined for most formats | ||
/* istanbul ignore next */ | ||
if (get_format_leading_digits_patterns(a).length === 0 | ||
&& get_format_leading_digits_patterns(b).length > 0) | ||
let index_of_leading_digits_pattern = leading_digits.length - MIN_LEADING_DIGITS_LENGTH | ||
if (index_of_leading_digits_pattern < 0) | ||
{ | ||
return -1 | ||
index_of_leading_digits_pattern = 0 | ||
} | ||
// Leading digits are defined for most formats | ||
/* istanbul ignore next */ | ||
if (get_format_leading_digits_patterns(a).length > 0 | ||
&& get_format_leading_digits_patterns(b).length === 0) | ||
this.matching_formats = this.get_relevant_phone_number_formats().filter((format) => | ||
{ | ||
return 1 | ||
} | ||
const leading_digits_pattern_count = get_format_leading_digits_patterns(format).length | ||
return 0 | ||
}) | ||
// Keep everything that isn't restricted by leading digits. | ||
if (leading_digits_pattern_count === 0) | ||
{ | ||
return true | ||
} | ||
this.matching_formats = this.available_formats | ||
} | ||
const leading_digits_pattern_index = Math.min(index_of_leading_digits_pattern, leading_digits_pattern_count - 1) | ||
const leading_digits_pattern = get_format_leading_digits_patterns(format)[leading_digits_pattern_index] | ||
return new RegExp('^' + leading_digits_pattern).test(leading_digits) | ||
}) | ||
} | ||
match_formats_by_leading_digits() | ||
{ | ||
const leading_digits = this.national_number | ||
get_relevant_phone_number_formats() | ||
{ | ||
const leading_digits = this.national_number | ||
// "leading digits" patterns start with a maximum 3 digits, | ||
// and then with each additional digit | ||
// a more precise "leading digits" pattern is specified. | ||
// They could make "leading digits" patterns start | ||
// with a maximum of a single digit, but they didn't, | ||
// so it's possible that some phone number formats | ||
// will be falsely rejected until there are at least | ||
// 3 digits in the national (significant) number being input. | ||
// "leading digits" patterns start with a maximum 3 digits, | ||
// and then with each additional digit | ||
// a more precise "leading digits" pattern is specified. | ||
// They could make "leading digits" patterns start | ||
// with a maximum of a single digit, but they didn't, | ||
// so it's possible that some phone number formats | ||
// will be falsely rejected until there are at least | ||
// 3 digits in the national (significant) number being input. | ||
let index_of_leading_digits_pattern = leading_digits.length - MIN_LEADING_DIGITS_LENGTH | ||
if (leading_digits.length <= MIN_LEADING_DIGITS_LENGTH) | ||
{ | ||
return this.available_formats | ||
} | ||
if (index_of_leading_digits_pattern < 0) | ||
{ | ||
index_of_leading_digits_pattern = 0 | ||
return this.matching_formats | ||
} | ||
this.matching_formats = this.get_relevant_phone_number_formats().filter((format) => | ||
// Check to see if there is an exact pattern match for these digits. If so, we | ||
// should use this instead of any other formatting template whose | ||
// leadingDigitsPattern also matches the input. | ||
attempt_to_format_complete_phone_number() | ||
{ | ||
const leading_digits_pattern_count = get_format_leading_digits_patterns(format).length | ||
// Keep everything that isn't restricted by leading digits. | ||
if (leading_digits_pattern_count === 0) | ||
for (let format of this.get_relevant_phone_number_formats()) | ||
{ | ||
return true | ||
} | ||
const matcher = new RegExp('^(?:' + get_format_pattern(format) + ')$') | ||
const leading_digits_pattern_index = Math.min(index_of_leading_digits_pattern, leading_digits_pattern_count - 1) | ||
const leading_digits_pattern = get_format_leading_digits_patterns(format)[leading_digits_pattern_index] | ||
return new RegExp('^' + leading_digits_pattern).test(leading_digits) | ||
}) | ||
} | ||
if (!matcher.test(this.national_number)) | ||
{ | ||
// If the national prefix is optional | ||
// then also try to format the phone number | ||
// without the national prefix being extracted. | ||
if (this.national_prefix | ||
&& get_format_national_prefix_is_optional_when_formatting(format, this.country_metadata)) | ||
{ | ||
if (!matcher.test(this.national_prefix + this.national_number)) | ||
{ | ||
continue | ||
} | ||
get_relevant_phone_number_formats() | ||
{ | ||
const leading_digits = this.national_number | ||
this.national_number = this.national_prefix + this.national_number | ||
this.national_prefix = '' | ||
} | ||
// "leading digits" patterns start with a maximum 3 digits, | ||
// and then with each additional digit | ||
// a more precise "leading digits" pattern is specified. | ||
// They could make "leading digits" patterns start | ||
// with a maximum of a single digit, but they didn't, | ||
// so it's possible that some phone number formats | ||
// will be falsely rejected until there are at least | ||
// 3 digits in the national (significant) number being input. | ||
continue | ||
} | ||
if (leading_digits.length <= MIN_LEADING_DIGITS_LENGTH) | ||
{ | ||
return this.available_formats | ||
} | ||
if (!this.validate_format(format)) | ||
{ | ||
continue | ||
} | ||
return this.matching_formats | ||
} | ||
// To leave the formatter in a consistent state | ||
this.reset_format() | ||
this.chosen_format = format | ||
// Check to see if there is an exact pattern match for these digits. If so, we | ||
// should use this instead of any other formatting template whose | ||
// leadingDigitsPattern also matches the input. | ||
attempt_to_format_complete_phone_number() | ||
{ | ||
for (let format of this.get_relevant_phone_number_formats()) | ||
{ | ||
const matcher = new RegExp('^(?:' + get_format_pattern(format) + ')$') | ||
let matches = false | ||
let discard_national_prefix = false | ||
const formatted_number = format_national_number_using_format | ||
( | ||
this.national_number, | ||
format, | ||
this.is_international(), | ||
this.national_prefix.length > 0, | ||
this.country_metadata | ||
) | ||
// If the national prefix is optional | ||
// then also try to format the phone number | ||
// without the national prefix being extracted. | ||
if (this.national_prefix | ||
&& get_format_national_prefix_is_optional_when_formatting(format, this.country_metadata)) | ||
{ | ||
matches = matcher.test(this.national_prefix + this.national_number) | ||
if (matches) | ||
// Set `this.template` and `this.partially_populated_template` | ||
// | ||
// `else` case doesn't ever happen | ||
// with the current metadata, | ||
// but just in case. | ||
// | ||
/* istanbul ignore else */ | ||
if (this.create_formatting_template(format)) | ||
{ | ||
discard_national_prefix = true | ||
// Populate `this.partially_populated_template` | ||
this.reformat_national_number() | ||
} | ||
} | ||
else | ||
{ | ||
const full_number = this.full_phone_number(formatted_number) | ||
matches = matches || matcher.test(this.national_number) | ||
this.template = full_number.replace(/[\d\+]/g, DIGIT_PLACEHOLDER) | ||
this.partially_populated_template = full_number | ||
} | ||
if (!matches) | ||
{ | ||
continue | ||
return formatted_number | ||
} | ||
} | ||
if (discard_national_prefix) | ||
// Combines the national number with the appropriate prefix | ||
full_phone_number(formatted_national_number) | ||
{ | ||
if (this.is_international()) | ||
{ | ||
this.national_number = this.national_prefix + this.national_number | ||
this.national_prefix = '' | ||
return `+${this.country_phone_code} ${formatted_national_number}` | ||
} | ||
if (!this.validate_format(format)) | ||
return formatted_national_number | ||
} | ||
// Extracts the country calling code from the beginning | ||
// of the entered `national_number` (so far), | ||
// and places the remaining input into the `national_number`. | ||
extract_country_phone_code() | ||
{ | ||
if (!this.national_number) | ||
{ | ||
continue | ||
return | ||
} | ||
// To leave the formatter in a consistent state | ||
this.reset_format() | ||
this.chosen_format = format | ||
const { country_phone_code, number } = parse_phone_number_and_country_phone_code(this.parsed_input, metadata) | ||
const number_pattern = this.validate_format_for_template(format) | ||
const formatted_number = format_national_number_using_format | ||
( | ||
this.national_number, | ||
format, | ||
this.is_international(), | ||
this.national_prefix.length > 0, | ||
this.country_metadata | ||
) | ||
if (number_pattern) | ||
if (!country_phone_code) | ||
{ | ||
// Set `this.template` and `this.partially_populated_template` | ||
this.create_formatting_template(format, number_pattern) | ||
// Populate `this.partially_populated_template` | ||
this.reformat_national_number() | ||
return | ||
} | ||
else | ||
{ | ||
const full_number = this.full_phone_number(formatted_number) | ||
this.template = full_number.replace(/[\d\+]/g, DIGIT_PLACEHOLDER) | ||
this.partially_populated_template = full_number | ||
} | ||
this.country_phone_code = country_phone_code | ||
this.national_number = number | ||
return formatted_number | ||
return this.country_metadata = get_metadata_by_country_phone_code(country_phone_code, metadata) | ||
} | ||
} | ||
// Combines the national number with the appropriate prefix | ||
full_phone_number(formatted_national_number) | ||
{ | ||
if (this.is_international()) | ||
extract_national_prefix() | ||
{ | ||
return `+${this.country_phone_code} ${formatted_national_number}` | ||
} | ||
this.national_prefix = '' | ||
return formatted_national_number | ||
} | ||
if (!this.country_metadata) | ||
{ | ||
return | ||
} | ||
// Extracts the country calling code from the beginning | ||
// of the entered `national_number` (so far), | ||
// and places the remaining input into the `national_number`. | ||
extract_country_phone_code() | ||
{ | ||
if (!this.national_number) | ||
{ | ||
return | ||
} | ||
const national_prefix_for_parsing = get_national_prefix_for_parsing(this.country_metadata) | ||
const { country_phone_code, number } = parse_phone_number_and_country_phone_code(this.parsed_input) | ||
if (!national_prefix_for_parsing) | ||
{ | ||
return | ||
} | ||
if (!country_phone_code) | ||
{ | ||
return | ||
} | ||
const matches = this.national_number.match(new RegExp('^(?:' + national_prefix_for_parsing + ')')) | ||
this.country_phone_code = country_phone_code | ||
this.national_number = number | ||
// Since some national prefix patterns are entirely optional, check that a | ||
// national prefix could actually be extracted. | ||
if (!matches || !matches[0]) | ||
{ | ||
return | ||
} | ||
return this.country_metadata = get_metadata_by_country_phone_code(country_phone_code, metadata) | ||
} | ||
const national_number_starts_at = matches[0].length | ||
extract_national_prefix() | ||
{ | ||
this.national_prefix = '' | ||
this.national_prefix = this.national_number.slice(0, national_number_starts_at) | ||
this.national_number = this.national_number.slice(national_number_starts_at) | ||
if (!this.country_metadata) | ||
{ | ||
return | ||
return this.national_prefix | ||
} | ||
const national_prefix_for_parsing = get_national_prefix_for_parsing(this.country_metadata) | ||
if (!national_prefix_for_parsing) | ||
choose_another_format() | ||
{ | ||
return | ||
} | ||
const matches = this.national_number.match(new RegExp('^(?:' + national_prefix_for_parsing + ')')) | ||
// Since some national prefix patterns are entirely optional, check that a | ||
// national prefix could actually be extracted. | ||
if (!matches || !matches[0]) | ||
{ | ||
return | ||
} | ||
const national_number_starts_at = matches[0].length | ||
this.national_prefix = this.national_number.slice(0, national_number_starts_at) | ||
this.national_number = this.national_number.slice(national_number_starts_at) | ||
return this.national_prefix | ||
} | ||
choose_another_format() | ||
{ | ||
// When there are multiple available formats, the formatter uses the first | ||
// format where a formatting template could be created. | ||
for (let format of this.get_relevant_phone_number_formats()) | ||
{ | ||
// If this format is currently being used | ||
// and is still possible, then stick to it. | ||
if (this.chosen_format === format) | ||
// When there are multiple available formats, the formatter uses the first | ||
// format where a formatting template could be created. | ||
for (let format of this.get_relevant_phone_number_formats()) | ||
{ | ||
return | ||
} | ||
// If this format is currently being used | ||
// and is still possible, then stick to it. | ||
if (this.chosen_format === format) | ||
{ | ||
return | ||
} | ||
// If this `format` is suitable for "as you type", | ||
// then extract the template from this format | ||
// and use it to format the phone number being input. | ||
// If this `format` is suitable for "as you type", | ||
// then extract the template from this format | ||
// and use it to format the phone number being input. | ||
if (!this.validate_format(format)) | ||
{ | ||
continue | ||
} | ||
if (!this.validate_format(format)) | ||
{ | ||
continue | ||
} | ||
const number_pattern = this.validate_format_for_template(format) | ||
if (!this.create_formatting_template(format)) | ||
{ | ||
continue | ||
} | ||
if (number_pattern) | ||
{ | ||
this.create_formatting_template(format, number_pattern) | ||
this.chosen_format = format | ||
@@ -659,174 +653,166 @@ | ||
} | ||
} | ||
// No format matches the phone number, | ||
// therefore set `country` to `undefined` | ||
// (or to the default country). | ||
this.reset_country() | ||
// No format matches the phone number, | ||
// therefore set `country` to `undefined` | ||
// (or to the default country). | ||
this.reset_country() | ||
// No format matches the national phone number entered | ||
this.reset_format() | ||
} | ||
// No format matches the national phone number entered | ||
this.reset_format() | ||
} | ||
validate_format(format) | ||
{ | ||
// If national prefix is mandatory for this phone number format | ||
// and the user didn't input the national prefix, | ||
// then this phone number format isn't suitable. | ||
if (!this.is_international() && !this.national_prefix && get_format_national_prefix_is_mandatory_when_formatting(format, this.country_metadata)) | ||
validate_format(format) | ||
{ | ||
return | ||
// If national prefix is mandatory for this phone number format | ||
// and the user didn't input the national prefix, | ||
// then this phone number format isn't suitable. | ||
if (!this.is_international() && !this.national_prefix && get_format_national_prefix_is_mandatory_when_formatting(format, this.country_metadata)) | ||
{ | ||
return | ||
} | ||
return true | ||
} | ||
return true | ||
} | ||
validate_format_for_template(format) | ||
{ | ||
// The formatter doesn't format numbers when numberPattern contains '|', e.g. | ||
// (20|3)\d{4}. In those cases we quickly return. | ||
// (Though there's no such format in current metadata) | ||
/* istanbul ignore if */ | ||
if (get_format_pattern(format).indexOf('|') >= 0) | ||
create_formatting_template(format) | ||
{ | ||
return | ||
} | ||
// The formatter doesn't format numbers when numberPattern contains '|', e.g. | ||
// (20|3)\d{4}. In those cases we quickly return. | ||
// (Though there's no such format in current metadata) | ||
/* istanbul ignore if */ | ||
if (get_format_pattern(format).indexOf('|') >= 0) | ||
{ | ||
return | ||
} | ||
const national_prefix_formatting_rule = get_format_national_prefix_formatting_rule(format, this.country_metadata) | ||
const national_prefix_formatting_rule = get_format_national_prefix_formatting_rule(format, this.country_metadata) | ||
// A very smart trick by the guys at Google | ||
const number_pattern = get_format_pattern(format) | ||
// Replace anything in the form of [..] with \d | ||
.replace(CHARACTER_CLASS_PATTERN, '\\d') | ||
// Replace any standalone digit (not the one in `{}`) with \d | ||
.replace(STANDALONE_DIGIT_PATTERN, '\\d') | ||
// A very smart trick by the guys at Google | ||
const number_pattern = get_format_pattern(format) | ||
// Replace anything in the form of [..] with \d | ||
.replace(CHARACTER_CLASS_PATTERN, '\\d') | ||
// Replace any standalone digit (not the one in `{}`) with \d | ||
.replace(STANDALONE_DIGIT_PATTERN, '\\d') | ||
// This match will always succeed, | ||
// because the "longest dummy phone number" | ||
// has enough length to accomodate any possible | ||
// national phone number format pattern. | ||
const dummy_phone_number_matching_format_pattern = LONGEST_DUMMY_PHONE_NUMBER.match(number_pattern)[0] | ||
// This match will always succeed, | ||
// because the "longest dummy phone number" | ||
// has enough length to accomodate any possible | ||
// national phone number format pattern. | ||
const dummy_phone_number_matching_format_pattern = LONGEST_DUMMY_PHONE_NUMBER.match(number_pattern)[0] | ||
// If the national number entered is too long | ||
// for any phone number format, then abort. | ||
if (this.national_number.length > dummy_phone_number_matching_format_pattern.length) | ||
{ | ||
return | ||
} | ||
// If the national number entered is too long | ||
// for any phone number format, then abort. | ||
if (this.national_number.length > dummy_phone_number_matching_format_pattern.length) | ||
{ | ||
return | ||
} | ||
return number_pattern | ||
} | ||
// Now prepare phone number format | ||
let number_format = this.get_format_format(format) | ||
create_formatting_template(format, number_pattern) | ||
{ | ||
let number_format = this.get_format_format(format) | ||
// If the user did input the national prefix | ||
// then maybe make it a part of the phone number template | ||
if (this.national_prefix) | ||
{ | ||
const national_prefix_formatting_rule = get_format_national_prefix_formatting_rule(format, this.country_metadata) | ||
// If the user did input the national prefix | ||
// then maybe make it a part of the phone number template | ||
if (this.national_prefix) | ||
{ | ||
const national_prefix_formatting_rule = get_format_national_prefix_formatting_rule(format, this.country_metadata) | ||
// If national prefix formatting rule is set | ||
// for this phone number format | ||
if (national_prefix_formatting_rule) | ||
{ | ||
// Make the national prefix a part of the phone number template | ||
number_format = number_format.replace(FIRST_GROUP_PATTERN, national_prefix_formatting_rule) | ||
} | ||
} | ||
// If national prefix formatting rule is set | ||
// for this phone number format | ||
if (national_prefix_formatting_rule) | ||
// Get a formatting template which can be used to efficiently format | ||
// a partial number where digits are added one by one. | ||
// Create formatting template for this phone number format | ||
let template = dummy_phone_number_matching_format_pattern | ||
// Format the dummy phone number according to the format | ||
.replace(new RegExp(number_pattern, 'g'), number_format) | ||
// Replace each dummy digit with a DIGIT_PLACEHOLDER | ||
.replace(DUMMY_DIGIT_MATCHER, DIGIT_PLACEHOLDER) | ||
// This one is for national number only | ||
this.partially_populated_template = template | ||
// For convenience, the public `.template` property | ||
// is gonna contain the whole international number | ||
// if the phone number being input is international. | ||
if (this.is_international()) | ||
{ | ||
// Make the national prefix a part of the phone number template | ||
number_format = number_format.replace(FIRST_GROUP_PATTERN, national_prefix_formatting_rule) | ||
template = DIGIT_PLACEHOLDER + repeat(DIGIT_PLACEHOLDER, this.country_phone_code.length) + ' ' + template | ||
} | ||
// For local numbers, replace national prefix | ||
// with a digit placeholder. | ||
else | ||
{ | ||
template = template.replace(/\d/g, DIGIT_PLACEHOLDER) | ||
} | ||
// This one is for the full phone number | ||
return this.template = template | ||
} | ||
// Get a formatting template which can be used to efficiently format | ||
// a partial number where digits are added one by one. | ||
format_next_national_number_digits(digits) | ||
{ | ||
for (let digit of digits) | ||
{ | ||
// If there is room for more digits in current `template`, | ||
// then set the next digit in the `template`, | ||
// and return the formatted digits so far. | ||
// This match will always succeed, | ||
// because the "longest dummy phone number" | ||
// has enough length to accomodate any possible | ||
// national phone number format pattern. | ||
const dummy_phone_number_matching_format_pattern = LONGEST_DUMMY_PHONE_NUMBER.match(number_pattern)[0] | ||
// If more digits are entered than the current format could handle | ||
if (this.partially_populated_template.slice(this.last_match_position + 1).search(DIGIT_PLACEHOLDER_MATCHER) === -1) | ||
{ | ||
// Reset the current format, | ||
// so that the new format will be chosen | ||
// in a subsequent `this.choose_another_format()` call | ||
// later in code. | ||
this.chosen_format = undefined | ||
this.template = undefined | ||
this.partially_populated_template = undefined | ||
return | ||
} | ||
// Create formatting template for this phone number format | ||
let template = dummy_phone_number_matching_format_pattern | ||
// Format the dummy phone number according to the format | ||
.replace(new RegExp(number_pattern, 'g'), number_format) | ||
// Replace each dummy digit with a DIGIT_PLACEHOLDER | ||
.replace(DUMMY_DIGIT_MATCHER, DIGIT_PLACEHOLDER) | ||
this.last_match_position = this.partially_populated_template.search(DIGIT_PLACEHOLDER_MATCHER) | ||
this.partially_populated_template = this.partially_populated_template.replace(DIGIT_PLACEHOLDER_MATCHER, digit) | ||
} | ||
// This one is for national number only | ||
this.partially_populated_template = template | ||
// Return the formatted phone number so far | ||
return close_dangling_braces(this.partially_populated_template, this.last_match_position + 1) | ||
.replace(DIGIT_PLACEHOLDER_MATCHER_GLOBAL, ' ') | ||
} | ||
// For convenience, the public `.template` property | ||
// is gonna contain the whole international number | ||
// if the phone number being input is international. | ||
if (this.is_international()) | ||
is_international() | ||
{ | ||
template = DIGIT_PLACEHOLDER + repeat(DIGIT_PLACEHOLDER, this.country_phone_code.length) + ' ' + template | ||
return this.parsed_input && this.parsed_input[0] === '+' | ||
} | ||
// For local numbers, replace national prefix | ||
// with a digit placeholder. | ||
else | ||
{ | ||
template = template.replace(/\d/g, DIGIT_PLACEHOLDER) | ||
} | ||
// This one is for the full phone number | ||
this.template = template | ||
} | ||
format_next_national_number_digits(digits) | ||
{ | ||
for (let digit of digits) | ||
get_format_format(format) | ||
{ | ||
// If there is room for more digits in current `template`, | ||
// then set the next digit in the `template`, | ||
// and return the formatted digits so far. | ||
// If more digits are entered than the current format could handle | ||
if (this.partially_populated_template.slice(this.last_match_position + 1).search(DIGIT_PLACEHOLDER_MATCHER) === -1) | ||
if (this.is_international()) | ||
{ | ||
// Reset the current format, | ||
// so that the new format will be chosen | ||
// in a subsequent `this.choose_another_format()` call | ||
// later in code. | ||
this.chosen_format = undefined | ||
this.template = undefined | ||
this.partially_populated_template = undefined | ||
return | ||
return local_to_international_style(get_format_international_format(format)) | ||
} | ||
this.last_match_position = this.partially_populated_template.search(DIGIT_PLACEHOLDER_MATCHER) | ||
this.partially_populated_template = this.partially_populated_template.replace(DIGIT_PLACEHOLDER_MATCHER, digit) | ||
return get_format_format(format) | ||
} | ||
// Return the formatted phone number so far | ||
return close_dangling_braces(this.partially_populated_template, this.last_match_position + 1) | ||
.replace(DIGIT_PLACEHOLDER_MATCHER_GLOBAL, ' ') | ||
} | ||
is_international() | ||
{ | ||
return this.parsed_input && this.parsed_input[0] === '+' | ||
} | ||
get_format_format(format) | ||
{ | ||
if (this.is_international()) | ||
// Determines the country of the phone number | ||
// entered so far based on the country phone code | ||
// and the national phone number. | ||
determine_the_country() | ||
{ | ||
return local_to_international_style(get_format_international_format(format)) | ||
this.country = find_country_code(this.country_phone_code, this.national_number, metadata) | ||
} | ||
return get_format_format(format) | ||
} | ||
// Determines the country of the phone number | ||
// entered so far based on the country phone code | ||
// and the national phone number. | ||
determine_the_country() | ||
{ | ||
this.country = find_country_code(this.country_phone_code, this.national_number) | ||
} | ||
as_you_type.DIGIT_PLACEHOLDER = DIGIT_PLACEHOLDER | ||
return as_you_type | ||
} | ||
as_you_type.DIGIT_PLACEHOLDER = DIGIT_PLACEHOLDER | ||
export function close_dangling_braces(template, cut_before) | ||
@@ -833,0 +819,0 @@ { |
@@ -7,3 +7,2 @@ // This is a port of Google Android `libphonenumber`'s | ||
import { matches_entirely } from './common' | ||
import metadata from '../metadata.min' | ||
@@ -51,6 +50,6 @@ import | ||
{ | ||
country_metadata = metadata.countries[input.country] | ||
country_metadata = this.metadata.countries[input.country] | ||
} | ||
const { country_phone_code, number } = parse_phone_number_and_country_phone_code(input.phone) | ||
const { country_phone_code, number } = parse_phone_number_and_country_phone_code(input.phone, this.metadata) | ||
@@ -65,3 +64,3 @@ if (country_phone_code) | ||
country_metadata = get_metadata_by_country_phone_code(country_phone_code, metadata) | ||
country_metadata = get_metadata_by_country_phone_code(country_phone_code, this.metadata) | ||
} | ||
@@ -117,17 +116,11 @@ | ||
{ | ||
// If national prefix formatting rule is set | ||
// (e.g. it is not set for US) | ||
if (national_prefix_formatting_rule) | ||
{ | ||
return number.replace | ||
return number.replace | ||
( | ||
format_pattern_matcher, | ||
get_format_format(format).replace | ||
( | ||
format_pattern_matcher, | ||
get_format_format(format).replace | ||
( | ||
FIRST_GROUP_PATTERN, | ||
national_prefix_formatting_rule | ||
) | ||
FIRST_GROUP_PATTERN, | ||
national_prefix_formatting_rule | ||
) | ||
} | ||
) | ||
} | ||
@@ -134,0 +127,0 @@ |
@@ -7,3 +7,2 @@ // This is a port of Google Android `libphonenumber`'s | ||
import { matches_entirely } from './common' | ||
import metadata from '../metadata.min' | ||
@@ -224,3 +223,3 @@ import | ||
let { country_phone_code, number } = parse_phone_number_and_country_phone_code(formatted_phone_number) | ||
let { country_phone_code, number } = parse_phone_number_and_country_phone_code(formatted_phone_number, this.metadata) | ||
@@ -240,3 +239,3 @@ // Maybe invalid country phone code encountered | ||
if (options.country.restrict && | ||
country_phone_code !== get_phone_code(metadata.countries[options.country.restrict])) | ||
country_phone_code !== get_phone_code(this.metadata.countries[options.country.restrict])) | ||
{ | ||
@@ -246,3 +245,3 @@ return {} | ||
country_metadata = get_metadata_by_country_phone_code(country_phone_code, metadata) | ||
country_metadata = get_metadata_by_country_phone_code(country_phone_code, this.metadata) | ||
@@ -259,3 +258,3 @@ // `country` will be set later, | ||
country = options.country.default || options.country.restrict | ||
country_metadata = metadata.countries[country] | ||
country_metadata = this.metadata.countries[country] | ||
@@ -280,3 +279,3 @@ number = normalize(text) | ||
{ | ||
country = find_country_code(country_phone_code, national_number) | ||
country = find_country_code(country_phone_code, national_number, this.metadata) | ||
@@ -406,3 +405,3 @@ // Just in case there's a bug in Google's metadata | ||
// | ||
export function parse_phone_number_and_country_phone_code(number) | ||
export function parse_phone_number_and_country_phone_code(number, metadata) | ||
{ | ||
@@ -506,3 +505,3 @@ number = parse_phone_number(number) | ||
export function find_country_code(country_phone_code, national_phone_number) | ||
export function find_country_code(country_phone_code, national_phone_number, metadata) | ||
{ | ||
@@ -509,0 +508,0 @@ // Is always non-empty, because `country_phone_code` is always valid |
@@ -5,3 +5,3 @@ import parse from './parse' | ||
{ | ||
return Object.keys(parse(number, country_code)).length > 0 | ||
return Object.keys(parse.call(this, number, country_code)).length > 0 | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1109839
39
257
4634