libphonenumber-js
Version:
A simpler (and smaller) rewrite of Google Android's popular libphonenumber library
241 lines (208 loc) • 8.58 kB
JavaScript
import { stripIDDPrefix } from './IDD';
// `DASHES` will be right after the opening square bracket of the "character class"
var DASHES = '-\u2010-\u2015\u2212\u30FC\uFF0D';
var SLASHES = '\uFF0F/';
var DOTS = '\uFF0E.';
export var WHITESPACE = ' \xA0\xAD\u200B\u2060\u3000';
var BRACKETS = '()\uFF08\uFF09\uFF3B\uFF3D\\[\\]';
// export const OPENING_BRACKETS = '(\uFF08\uFF3B\\\['
var TILDES = '~\u2053\u223C\uFF5E';
// Digits accepted in phone numbers
// (ascii, fullwidth, arabic-indic, and eastern arabic digits).
export var VALID_DIGITS = '0-9\uFF10-\uFF19\u0660-\u0669\u06F0-\u06F9';
// Regular expression of acceptable punctuation found in phone numbers. This
// excludes punctuation found as a leading character only. This consists of dash
// characters, white space characters, full stops, slashes, square brackets,
// parentheses and tildes. Full-width variants are also present.
export var VALID_PUNCTUATION = '' + DASHES + SLASHES + DOTS + WHITESPACE + BRACKETS + TILDES;
export var PLUS_CHARS = '+\uFF0B';
var LEADING_PLUS_CHARS_PATTERN = new RegExp('^[' + PLUS_CHARS + ']+');
// The ITU says the maximum length should be 15,
// but one can find longer numbers in Germany.
export var MAX_LENGTH_FOR_NSN = 17;
// The maximum length of the country calling code.
export var MAX_LENGTH_COUNTRY_CODE = 3;
// These mappings map a character (key) to a specific digit that should
// replace it for normalization purposes. Non-European digits that
// may be used in phone numbers are mapped to a European equivalent.
//
// E.g. in Iraq they don't write `+442323234` but rather `+٤٤٢٣٢٣٢٣٤`.
//
export var DIGITS = {
'0': '0',
'1': '1',
'2': '2',
'3': '3',
'4': '4',
'5': '5',
'6': '6',
'7': '7',
'8': '8',
'9': '9',
'\uFF10': '0', // Fullwidth digit 0
'\uFF11': '1', // Fullwidth digit 1
'\uFF12': '2', // Fullwidth digit 2
'\uFF13': '3', // Fullwidth digit 3
'\uFF14': '4', // Fullwidth digit 4
'\uFF15': '5', // Fullwidth digit 5
'\uFF16': '6', // Fullwidth digit 6
'\uFF17': '7', // Fullwidth digit 7
'\uFF18': '8', // Fullwidth digit 8
'\uFF19': '9', // Fullwidth digit 9
'\u0660': '0', // Arabic-indic digit 0
'\u0661': '1', // Arabic-indic digit 1
'\u0662': '2', // Arabic-indic digit 2
'\u0663': '3', // Arabic-indic digit 3
'\u0664': '4', // Arabic-indic digit 4
'\u0665': '5', // Arabic-indic digit 5
'\u0666': '6', // Arabic-indic digit 6
'\u0667': '7', // Arabic-indic digit 7
'\u0668': '8', // Arabic-indic digit 8
'\u0669': '9', // Arabic-indic digit 9
'\u06F0': '0', // Eastern-Arabic digit 0
'\u06F1': '1', // Eastern-Arabic digit 1
'\u06F2': '2', // Eastern-Arabic digit 2
'\u06F3': '3', // Eastern-Arabic digit 3
'\u06F4': '4', // Eastern-Arabic digit 4
'\u06F5': '5', // Eastern-Arabic digit 5
'\u06F6': '6', // Eastern-Arabic digit 6
'\u06F7': '7', // Eastern-Arabic digit 7
'\u06F8': '8', // Eastern-Arabic digit 8
'\u06F9': '9' // Eastern-Arabic digit 9
};
export function parseDigit(character) {
return DIGITS[character];
}
/**
* Drops all punctuation leaving only digits and the leading `+` sign (if any).
* Also converts wide-ascii and arabic-indic numerals to conventional numerals.
*
* E.g. in Iraq they don't write `+442323234` but rather `+٤٤٢٣٢٣٢٣٤`.
*
* @param {string} number
* @return {string}
*/
export function parse_phone_number_digits(number) {
return (LEADING_PLUS_CHARS_PATTERN.test(number) ? '+' : '') + drop_and_substitute_characters(number, DIGITS);
}
// Parses a formatted phone number
// and returns `{ country_calling_code, number }`
// where `number` is the national (significant) phone number.
//
// (aka `maybeExtractCountryPhoneCode`)
//
export function parse_national_number_and_country_calling_code(number, country, metadata) {
number = parse_phone_number_digits(number);
if (!number) {
return {};
}
// If this is not an international phone number,
// then don't extract country phone code.
if (number[0] !== '+') {
// Convert an "out-of-country" dialing phone number
// to a proper international phone number.
var numberWithoutIDD = stripIDDPrefix(number, country, metadata.metadata);
// If an IDD prefix was stripped then
// convert the number to international one.
if (numberWithoutIDD && numberWithoutIDD !== number) {
number = '+' + numberWithoutIDD;
} else {
return { number: number };
}
}
// Fast abortion: country codes do not begin with a '0'
if (number[1] === '0') {
return {};
}
// The thing with country phone codes
// is that they are orthogonal to each other
// i.e. there's no such country phone code A
// for which country phone code B exists
// where B starts with A.
// Therefore, while scanning digits,
// if a valid country code is found,
// that means that it is the country code.
//
var i = 2;
while (i - 1 <= MAX_LENGTH_COUNTRY_CODE && i <= number.length) {
var countryCallingCode = number.slice(1, i);
if (metadata.countryCallingCodes()[countryCallingCode]) {
return {
countryCallingCode: countryCallingCode,
number: number.slice(i)
};
}
i++;
}
return {};
}
// Checks whether the entire input sequence can be matched
// against the regular expression.
export function matches_entirely() {
var text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var regular_expression = arguments[1];
if (typeof regular_expression === 'string') {
regular_expression = '^(?:' + regular_expression + ')$';
}
var matched_groups = text.match(regular_expression);
return matched_groups !== null && matched_groups[0].length === text.length;
}
// For any character not being part of `replacements`
// it is removed from the phone number.
function drop_and_substitute_characters(text, replacements) {
var replaced = '';
// Using `.split('')` to iterate through a string here
// to avoid requiring `Symbol.iterator` polyfill.
// `.split('')` is generally not safe for Unicode,
// but in this particular case for `digits` it is safe.
// for (const character of text)
for (var _iterator = text.split(''), _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) {
var _ref;
if (_isArray) {
if (_i >= _iterator.length) break;
_ref = _iterator[_i++];
} else {
_i = _iterator.next();
if (_i.done) break;
_ref = _i.value;
}
var character = _ref;
var replacement = replacements[character.toUpperCase()];
if (replacement) {
replaced += replacement;
}
}
return replaced;
}
// The RFC 3966 format for extensions.
var RFC3966_EXTN_PREFIX = ';ext=';
// Pattern to capture digits used in an extension.
// Places a maximum length of '7' for an extension.
var CAPTURING_EXTN_DIGITS = '([' + VALID_DIGITS + ']{1,7})';
/**
* Regexp of all possible ways to write extensions, for use when parsing. This
* will be run as a case-insensitive regexp match. Wide character versions are
* also provided after each ASCII version. There are three regular expressions
* here. The first covers RFC 3966 format, where the extension is added using
* ';ext='. The second more generic one starts with optional white space and
* ends with an optional full stop (.), followed by zero or more spaces/tabs
* /commas and then the numbers themselves. The other one covers the special
* case of American numbers where the extension is written with a hash at the
* end, such as '- 503#'. Note that the only capturing groups should be around
* the digits that you want to capture as part of the extension, or else parsing
* will fail! We allow two options for representing the accented o - the
* character itself, and one in the unicode decomposed form with the combining
* acute accent.
*/
export function create_extension_pattern(purpose) {
// One-character symbols that can be used to indicate an extension.
var single_extension_characters = 'x\uFF58#\uFF03~\uFF5E';
switch (purpose) {
// For parsing, we are slightly more lenient in our interpretation than for matching. Here we
// allow "comma" and "semicolon" as possible extension indicators. When matching, these are
case 'parsing':
single_extension_characters = ',;' + single_extension_characters;
}
return RFC3966_EXTN_PREFIX + CAPTURING_EXTN_DIGITS + '|' + '[ \xA0\\t,]*' + '(?:e?xt(?:ensi(?:o\u0301?|\xF3))?n?|\uFF45?\uFF58\uFF54\uFF4E?|' + '[' + single_extension_characters + ']|int|anexo|\uFF49\uFF4E\uFF54)' + '[:\\.\uFF0E]?[ \xA0\\t,-]*' + CAPTURING_EXTN_DIGITS + '#?|' + '[- ]+([' + VALID_DIGITS + ']{1,5})#';
}
//# sourceMappingURL=common.js.map