├── .babelrc ├── .gitattributes ├── .gitignore ├── .gitlab-ci.yml ├── .npmignore ├── .nycrc ├── .travis.yml ├── AUTHORS ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── LICENSE.Apache ├── METADATA.md ├── PhoneNumberMetadata.xml ├── README.md ├── autoupdate.cmd ├── autoupdate.sh ├── core ├── index.cjs ├── index.cjs.js ├── index.d.ts ├── index.js └── package.json ├── custom.d.ts ├── custom.js ├── examples.mobile.json ├── examples.mobile.json.d.ts ├── index.cjs ├── index.cjs.js ├── index.d.ts ├── index.es6.exports ├── PhoneNumberSearch.js ├── findPhoneNumbers.js ├── format.js ├── getNumberType.js ├── isPossibleNumber.js ├── isValidNumber.js ├── isValidNumberForRegion.js ├── parse.js └── searchPhoneNumbers.js ├── index.es6.js ├── index.js ├── max ├── exports │ ├── AsYouType.js │ ├── Metadata.js │ ├── PhoneNumber.js │ ├── PhoneNumberMatcher.js │ ├── findNumbers.js │ ├── findPhoneNumbersInText.js │ ├── formatIncompletePhoneNumber.js │ ├── getCountries.js │ ├── getCountryCallingCode.js │ ├── getExampleNumber.js │ ├── getExtPrefix.js │ ├── isPossiblePhoneNumber.js │ ├── isSupportedCountry.js │ ├── isValidPhoneNumber.js │ ├── parsePhoneNumber.js │ ├── parsePhoneNumberWithError.js │ ├── searchNumbers.js │ ├── searchPhoneNumbersInText.js │ ├── validatePhoneNumberLength.js │ └── withMetadataArgument.js ├── index.cjs ├── index.cjs.js ├── index.js ├── metadata │ └── package.json └── package.json ├── metadata.full.json.d.ts ├── metadata.max.json.d.ts ├── metadata.min.json.d.ts ├── metadata.mobile.json.d.ts ├── min ├── exports │ ├── AsYouType.js │ ├── Metadata.js │ ├── PhoneNumber.js │ ├── PhoneNumberMatcher.js │ ├── findNumbers.js │ ├── findPhoneNumbersInText.js │ ├── formatIncompletePhoneNumber.js │ ├── getCountries.js │ ├── getCountryCallingCode.js │ ├── getExampleNumber.js │ ├── getExtPrefix.js │ ├── isPossiblePhoneNumber.js │ ├── isSupportedCountry.js │ ├── isValidPhoneNumber.js │ ├── parsePhoneNumber.js │ ├── parsePhoneNumberWithError.js │ ├── searchNumbers.js │ ├── searchPhoneNumbersInText.js │ ├── validatePhoneNumberLength.js │ └── withMetadataArgument.js ├── index.cjs ├── index.cjs.js ├── index.d.ts ├── index.js ├── metadata │ └── package.json └── package.json ├── mobile ├── examples │ └── package.json ├── exports │ ├── AsYouType.js │ ├── Metadata.js │ ├── PhoneNumber.js │ ├── PhoneNumberMatcher.js │ ├── findNumbers.js │ ├── findPhoneNumbersInText.js │ ├── formatIncompletePhoneNumber.js │ ├── getCountries.js │ ├── getCountryCallingCode.js │ ├── getExampleNumber.js │ ├── getExtPrefix.js │ ├── isPossiblePhoneNumber.js │ ├── isSupportedCountry.js │ ├── isValidPhoneNumber.js │ ├── parsePhoneNumber.js │ ├── parsePhoneNumberWithError.js │ ├── searchNumbers.js │ ├── searchPhoneNumbersInText.js │ ├── validatePhoneNumberLength.js │ └── withMetadataArgument.js ├── index.cjs ├── index.cjs.js ├── index.js ├── metadata │ └── package.json └── package.json ├── package-lock.json ├── package.json ├── project.sublime-project ├── rollup.config.mjs ├── runnable ├── create-commonjs-package-json.js ├── download.js ├── generate-country-codes.js ├── generate.js ├── json-to-js.js ├── metadata-branch.js ├── metadata-update-and-push-and-pull-request.js ├── metadata-update-and-push.js ├── metadata-update-and-release.js └── modules │ ├── commit.js │ ├── exec.js │ └── update-metadata.js ├── source ├── AsYouType.js ├── AsYouType.test.js ├── AsYouTypeFormatter.PatternMatcher.d.ts ├── AsYouTypeFormatter.PatternMatcher.js ├── AsYouTypeFormatter.PatternMatcher.test.js ├── AsYouTypeFormatter.PatternParser.d.ts ├── AsYouTypeFormatter.PatternParser.js ├── AsYouTypeFormatter.PatternParser.test.js ├── AsYouTypeFormatter.complete.js ├── AsYouTypeFormatter.js ├── AsYouTypeFormatter.util.js ├── AsYouTypeFormatter.util.test.js ├── AsYouTypeParser.js ├── AsYouTypeState.js ├── ParseError.js ├── PhoneNumber.js ├── PhoneNumber.test.js ├── PhoneNumberMatcher.js ├── PhoneNumberMatcher.test.js ├── constants.js ├── findNumbers │ ├── LRUCache.js │ ├── Leniency.js │ ├── Leniency.test.js │ ├── RegExpCache.js │ ├── isValidCandidate.js │ ├── isValidPreCandidate.js │ ├── matchPhoneNumberStringAgainstPhoneNumber.js │ ├── parsePreCandidate.js │ ├── utf-8.js │ ├── util.js │ └── util.test.js ├── findPhoneNumbersInText.js ├── findPhoneNumbersInText.test.js ├── format.js ├── format.test.js ├── formatIncompletePhoneNumber.js ├── formatIncompletePhoneNumber.test.js ├── formatPhoneNumberForMobileDialing.js ├── formatPhoneNumberForMobileDialing.test.js ├── getCountries.js ├── getCountries.test.js ├── getCountryCallingCode.js ├── getCountryCallingCode.test.js ├── getExampleNumber.js ├── getExampleNumber.test.js ├── helpers │ ├── RFC3966.js │ ├── RFC3966.test.js │ ├── applyInternationalSeparatorStyle.js │ ├── applyInternationalSeparatorStyle.test.js │ ├── checkNumberLength.js │ ├── checkNumberLength.test.js │ ├── extension │ │ ├── createExtensionPattern.js │ │ └── extractExtension.js │ ├── extractCountryCallingCode.js │ ├── extractCountryCallingCode.test.js │ ├── extractCountryCallingCodeFromInternationalNumberWithoutPlusSign.js │ ├── extractFormattedPhoneNumberFromPossibleRfc3966NumberUri.js │ ├── extractNationalNumber.js │ ├── extractNationalNumber.test.js │ ├── extractNationalNumberFromPossiblyIncompleteNumber.js │ ├── extractNationalNumberFromPossiblyIncompleteNumber.test.js │ ├── extractPhoneContext.js │ ├── extractPhoneContext.test.js │ ├── formatNationalNumberUsingFormat.js │ ├── getCountryByCallingCode.js │ ├── getCountryByNationalNumber.js │ ├── getIddPrefix.js │ ├── getNumberType.js │ ├── getNumberType.test.js │ ├── getPossibleCountriesForNumber.js │ ├── isObject.js │ ├── isViablePhoneNumber.js │ ├── matchesEntirely.js │ ├── matchesEntirely.test.js │ ├── mergeArrays.js │ ├── mergeArrays.test.js │ ├── parseDigits.js │ ├── parseDigits.test.js │ ├── stripIddPrefix.js │ └── stripIddPrefix.test.js ├── isPossible.js ├── isPossible.test.js ├── isPossiblePhoneNumber.js ├── isPossiblePhoneNumber.test.js ├── isValid.js ├── isValid.test.js ├── isValidPhoneNumber.js ├── isValidPhoneNumber.test.js ├── legacy │ ├── findNumbers.js │ ├── findNumbers.test.js │ ├── findPhoneNumbers.js │ ├── findPhoneNumbers.test.js │ ├── findPhoneNumbersInitialImplementation.js │ ├── format.js │ ├── format.test.js │ ├── getNumberType.js │ ├── getNumberType.test.js │ ├── isPossibleNumber.js │ ├── isPossibleNumber.test.js │ ├── isValidNumber.js │ ├── isValidNumber.test.js │ ├── isValidNumberForRegion.js │ ├── isValidNumberForRegion.test.js │ ├── isValidNumberForRegion_.js │ ├── parse.js │ ├── parse.test.js │ ├── searchNumbers.js │ └── searchNumbers.test.js ├── metadata.js ├── metadata.test.js ├── normalizeArguments.js ├── parse.js ├── parse.test.js ├── parseIncompletePhoneNumber.js ├── parseIncompletePhoneNumber.test.js ├── parsePhoneNumber.js ├── parsePhoneNumber.test.js ├── parsePhoneNumberWithError.js ├── parsePhoneNumberWithError.test.js ├── parsePhoneNumberWithError_.js ├── parsePhoneNumber_.js ├── searchPhoneNumbersInText.js ├── searchPhoneNumbersInText.test.js ├── tools │ ├── semver-compare.js │ └── semver-compare.test.js ├── validatePhoneNumberLength.js └── validatePhoneNumberLength.test.js ├── test ├── exports.core.test.js ├── exports.max.test.js ├── exports.min.test.js ├── exports.mobile.test.js ├── exports.test.js ├── metadata │ ├── 1.0.0 │ │ ├── AsYouType.test.js │ │ ├── format.test.js │ │ ├── isPossibleNumber.test.js │ │ ├── metadata.max.json │ │ ├── metadata.min.json │ │ ├── metadata.test.js │ │ ├── parse.test.js │ │ ├── types.test.js │ │ └── validate.test.js │ ├── 1.1.11 │ │ ├── metadata.max.json │ │ ├── metadata.min.json │ │ └── test │ │ │ ├── AsYouType.test.js │ │ │ ├── findPhoneNumbers.test.js │ │ │ ├── format.test.js │ │ │ ├── getCountryCallingCode.test.js │ │ │ ├── metadata.test.js │ │ │ ├── parse.test.js │ │ │ ├── parsePhoneNumberFromString.test.js │ │ │ ├── types.test.js │ │ │ └── validate.test.js │ ├── 1.7.34 │ │ ├── metadata.max.json │ │ ├── metadata.min.json │ │ └── test │ │ │ ├── AsYouType.test.js │ │ │ ├── findNumbers.test.js │ │ │ ├── findPhoneNumbersInText.test.js │ │ │ ├── format.test.js │ │ │ ├── getNumberType.test.js │ │ │ ├── isPossibleNumber.test.js │ │ │ ├── metadata.test.js │ │ │ ├── parse.test.js │ │ │ ├── parseIncompletePhoneNumber.test.js │ │ │ ├── parsePhoneNumber.test.js │ │ │ ├── parsePhoneNumberFromString.test.js │ │ │ └── validate.test.js │ └── 1.7.37 │ │ ├── metadata.min.json │ │ └── test │ │ ├── AsYouType.test.js │ │ ├── parse.test.js │ │ └── parsePhoneNumberFromString.test.js └── setup.js ├── types.d.ts └── website ├── index.html └── lib ├── input-format-react.min.js ├── input-format-react.min.js.map ├── input-format.min.js └── input-format.min.js.map /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/env" 4 | ], 5 | "plugins": [ 6 | ["@babel/transform-for-of", { "loose": true }], 7 | ["@babel/plugin-syntax-import-assertions"] 8 | ], 9 | "env": { 10 | "es6": { 11 | "presets": [ 12 | ["@babel/env", { "modules": false }] 13 | ] 14 | }, 15 | "nyc": { 16 | "plugins": [ 17 | "babel-plugin-istanbul" 18 | ] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # test coverage folder 2 | /coverage/ 3 | 4 | # npm modules 5 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 6 | /node_modules/ 7 | 8 | # npm errors 9 | npm-debug.log 10 | 11 | # for OS X users 12 | .DS_Store 13 | 14 | # cache files for sublime text 15 | *.tmlanguage.cache 16 | *.tmPreferences.cache 17 | *.stTheme.cache 18 | 19 | # workspace files are user-specific 20 | *.sublime-workspace 21 | 22 | # webpack build target folder 23 | /build/ 24 | /es6/ 25 | 26 | # browser builds 27 | /bundle/ 28 | 29 | # metadata 30 | /metadata.json 31 | /metadata.full.json 32 | /metadata.min.json 33 | /metadata.max.json 34 | /metadata.mobile.json 35 | /examples.mobile.json 36 | 37 | # metadata.js 38 | /metadata.full.json.js 39 | /metadata.min.json.js 40 | /metadata.max.json.js 41 | /metadata.mobile.json.js 42 | /examples.mobile.json.js 43 | 44 | # code coverage 45 | .nyc_output 46 | 47 | *.d.cts 48 | /max/index.d.ts 49 | /mobile/index.d.ts -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: node:16 2 | 3 | pages: 4 | script: 5 | - npm install 6 | - npm run metadata:generate 7 | - npm run build 8 | - mv ./bundle ./public 9 | - cp --recursive ./website/* ./public/ 10 | 11 | artifacts: 12 | paths: 13 | - public 14 | 15 | only: 16 | - master 17 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # git 2 | .gitignore 3 | .gitattributes 4 | 5 | # Babel 6 | babel.config.js 7 | 8 | # testing package 9 | /libphonenumber-js-*.tgz 10 | 11 | # Travis CI 12 | .travis.yml 13 | 14 | # test coverage folder 15 | /coverage/ 16 | 17 | # npm errors 18 | npm-debug.log 19 | 20 | # github pages 21 | /gh-pages/ 22 | 23 | # for OS X users 24 | .DS_Store 25 | 26 | # cache files for sublime text 27 | *.tmlanguage.cache 28 | *.tmPreferences.cache 29 | *.stTheme.cache 30 | 31 | # workspace files are user-specific 32 | *.sublime-workspace 33 | *.sublime-project 34 | 35 | # NUL 36 | /NUL 37 | 38 | # webpack is used in development 39 | /webpack.config.babel.js 40 | 41 | # tests aren't needed for npm 42 | /test/ 43 | 44 | # Since cdnjs is used, now including `bundle` in npm distribution 45 | # # browser builds 46 | # /bundle/ 47 | /bundle/index.html 48 | /bundle/legacy.html 49 | /bundle/input-format.min.js 50 | /bundle/input-format.min.js.map 51 | /bundle/input-format-react.min.js 52 | /bundle/input-format-react.min.js.map 53 | 54 | # "Intermediary" (non-compressed) metadata. 55 | # The npm package doesn't include it so that people don't attempt to use it directly. 56 | /metadata.json 57 | /PhoneNumberMetadata.xml 58 | 59 | /test.no.babel.runtime.js 60 | 61 | # GitLab pages 62 | /website/ 63 | 64 | # code coverage 65 | .nyc_output -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "require": [ 3 | "@babel/register" 4 | ], 5 | "reporter": [ 6 | "lcov", 7 | "text-summary" 8 | ], 9 | "include": [ 10 | "source/**/*.js" 11 | ], 12 | "exclude": [ 13 | "source/findNumbers/Leniency.js", 14 | "source/findNumbers/RegExpCache.js", 15 | "source/findNumbers/LRUCache.js", 16 | "source/PhoneNumberMatcher.js", 17 | "source/formatNumberForMobileDialing.js", 18 | "source/tools/*.js", 19 | "**/*.test.js" 20 | ], 21 | "check-coverage": true, 22 | "lines": "100", 23 | "sourceMap": false, 24 | "instrument": false, 25 | "cache": true, 26 | "all": true 27 | } -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "stable" 4 | sudo: false 5 | before_install: 6 | # Setup Node.js version-specific dependencies 7 | - "test $TRAVIS_NODE_VERSION != '0.8' || npm rm --save-dev istanbul" 8 | # before_script: 9 | # - npm update -g npm 10 | script: 11 | # Run test script, depending on istanbul install 12 | - "test -n $(npm -ps ls istanbul) || npm test" 13 | - "test -z $(npm -ps ls istanbul) || npm run-script test-travis" 14 | after_script: 15 | - "test -e ./coverage/lcov.info && npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" 16 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Google Inc. 2 | Vonage Holdings Corp. 3 | Ian Galpin 4 | Ben Gertzfield 5 | https://gitlab.com/catamphetamine -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2016 @catamphetamine 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /autoupdate.cmd: -------------------------------------------------------------------------------- 1 | :: Set the current folder to the script's folder. 2 | pushd %~dp0 3 | :: Update metadata and release the new version. 4 | npm run metadata:update:job 5 | :: Restore the previous current folder. 6 | popd 7 | -------------------------------------------------------------------------------- /autoupdate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | export PATH=/usr/local/bin:$PATH 4 | 5 | # Enter this script's folder 6 | cd "${0%/*}" 7 | 8 | now=$(date +'%d.%m.%Y') 9 | echo "================================================" | tee /dev/stderr 10 | echo "= Starting metadata update at $now" | tee /dev/stderr 11 | echo "================================================" | tee /dev/stderr 12 | 13 | npm run metadata:update:job 14 | 15 | # Older way through submitting a pull request to github 16 | # npm run metadata:update:pull-request 17 | -------------------------------------------------------------------------------- /core/index.cjs: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var parsePhoneNumberFromString = require('../build/parsePhoneNumber.js').default 4 | 5 | // ES5 `require()` "default" "interoperability" hack. 6 | // https://github.com/babel/babel/issues/2212#issuecomment-131827986 7 | // An alternative approach: 8 | // https://www.npmjs.com/package/babel-plugin-add-module-exports 9 | exports = module.exports = parsePhoneNumberFromString 10 | exports['default'] = parsePhoneNumberFromString 11 | 12 | exports.PhoneNumber = require('../build/PhoneNumber.js').default 13 | exports.ParseError = require('../build/ParseError.js').default 14 | var parsePhoneNumberWithError = require('../build/parsePhoneNumberWithError.js').default 15 | // `parsePhoneNumber()` named export has been renamed to `parsePhoneNumberWithError()`. 16 | exports.parsePhoneNumberWithError = parsePhoneNumberWithError 17 | exports.parsePhoneNumber = parsePhoneNumberWithError 18 | 19 | // `parsePhoneNumberFromString()` named export is now considered legacy: 20 | // it has been promoted to a default export due to being too verbose. 21 | exports.parsePhoneNumberFromString = parsePhoneNumberFromString 22 | 23 | exports.isValidPhoneNumber = require('../build/isValidPhoneNumber.js').default 24 | exports.isPossiblePhoneNumber = require('../build/isPossiblePhoneNumber.js').default 25 | exports.validatePhoneNumberLength = require('../build/validatePhoneNumberLength.js').default 26 | 27 | exports.findNumbers = require('../build/legacy/findNumbers.js').default 28 | exports.searchNumbers = require('../build/legacy/searchNumbers.js').default 29 | 30 | exports.findPhoneNumbersInText = require('../build/findPhoneNumbersInText.js').default 31 | exports.searchPhoneNumbersInText = require('../build/searchPhoneNumbersInText.js').default 32 | exports.PhoneNumberMatcher = require('../build/PhoneNumberMatcher.js').default 33 | 34 | exports.AsYouType = require('../build/AsYouType.js').default 35 | 36 | exports.Metadata = require('../build/metadata.js').default 37 | exports.isSupportedCountry = require('../build/metadata.js').isSupportedCountry 38 | exports.getCountries = require('../build/getCountries.js').default 39 | exports.getCountryCallingCode = require('../build/metadata.js').getCountryCallingCode 40 | exports.getExtPrefix = require('../build/metadata.js').getExtPrefix 41 | 42 | exports.getExampleNumber = require('../build/getExampleNumber.js').default 43 | 44 | exports.formatIncompletePhoneNumber = require('../build/formatIncompletePhoneNumber.js').default 45 | 46 | exports.parseIncompletePhoneNumber = require('../build/parseIncompletePhoneNumber.js').default 47 | exports.parsePhoneNumberCharacter = require('../build/parseIncompletePhoneNumber.js').parsePhoneNumberCharacter 48 | exports.parseDigits = require('../build/helpers/parseDigits.js').default 49 | exports.DIGIT_PLACEHOLDER = require('../build/AsYouTypeFormatter.js').DIGIT_PLACEHOLDER 50 | 51 | exports.parseRFC3966 = require('../build/helpers/RFC3966.js').parseRFC3966 52 | exports.formatRFC3966 = require('../build/helpers/RFC3966.js').formatRFC3966 53 | -------------------------------------------------------------------------------- /core/index.cjs.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // This file is deprecated. 4 | // It's the same as `index.cjs`, just with an added `.js` file extension. 5 | // It only exists for compatibility with the software that doesn't like `*.cjs` file extension. 6 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/61#note_950728292 7 | 8 | var parsePhoneNumberFromString = require('../build/parsePhoneNumber.js').default 9 | 10 | // ES5 `require()` "default" "interoperability" hack. 11 | // https://github.com/babel/babel/issues/2212#issuecomment-131827986 12 | // An alternative approach: 13 | // https://www.npmjs.com/package/babel-plugin-add-module-exports 14 | exports = module.exports = parsePhoneNumberFromString 15 | exports['default'] = parsePhoneNumberFromString 16 | 17 | exports.PhoneNumber = require('../build/PhoneNumber.js').default 18 | exports.ParseError = require('../build/ParseError.js').default 19 | var parsePhoneNumberWithError = require('../build/parsePhoneNumberWithError.js').default 20 | // `parsePhoneNumber()` named export has been renamed to `parsePhoneNumberWithError()`. 21 | exports.parsePhoneNumberWithError = parsePhoneNumberWithError 22 | exports.parsePhoneNumber = parsePhoneNumberWithError 23 | 24 | // `parsePhoneNumberFromString()` named export is now considered legacy: 25 | // it has been promoted to a default export due to being too verbose. 26 | exports.parsePhoneNumberFromString = parsePhoneNumberFromString 27 | 28 | exports.isValidPhoneNumber = require('../build/isValidPhoneNumber.js').default 29 | exports.isPossiblePhoneNumber = require('../build/isPossiblePhoneNumber.js').default 30 | exports.validatePhoneNumberLength = require('../build/validatePhoneNumberLength.js').default 31 | 32 | exports.findNumbers = require('../build/legacy/findNumbers.js').default 33 | exports.searchNumbers = require('../build/legacy/searchNumbers.js').default 34 | 35 | exports.findPhoneNumbersInText = require('../build/findPhoneNumbersInText.js').default 36 | exports.searchPhoneNumbersInText = require('../build/searchPhoneNumbersInText.js').default 37 | exports.PhoneNumberMatcher = require('../build/PhoneNumberMatcher.js').default 38 | 39 | exports.AsYouType = require('../build/AsYouType.js').default 40 | 41 | exports.Metadata = require('../build/metadata.js').default 42 | exports.isSupportedCountry = require('../build/metadata.js').isSupportedCountry 43 | exports.getCountries = require('../build/getCountries.js').default 44 | exports.getCountryCallingCode = require('../build/metadata.js').getCountryCallingCode 45 | exports.getExtPrefix = require('../build/metadata.js').getExtPrefix 46 | 47 | exports.getExampleNumber = require('../build/getExampleNumber.js').default 48 | 49 | exports.formatIncompletePhoneNumber = require('../build/formatIncompletePhoneNumber.js').default 50 | 51 | exports.parseIncompletePhoneNumber = require('../build/parseIncompletePhoneNumber.js').default 52 | exports.parsePhoneNumberCharacter = require('../build/parseIncompletePhoneNumber.js').parsePhoneNumberCharacter 53 | exports.parseDigits = require('../build/helpers/parseDigits.js').default 54 | exports.DIGIT_PLACEHOLDER = require('../build/AsYouTypeFormatter.js').DIGIT_PLACEHOLDER 55 | 56 | exports.parseRFC3966 = require('../build/helpers/RFC3966.js').parseRFC3966 57 | exports.formatRFC3966 = require('../build/helpers/RFC3966.js').formatRFC3966 58 | -------------------------------------------------------------------------------- /core/index.js: -------------------------------------------------------------------------------- 1 | export { default as PhoneNumber } from '../es6/PhoneNumber.js' 2 | export { default as ParseError } from '../es6/ParseError.js' 3 | // `parsePhoneNumber()` named export has been renamed to `parsePhoneNumberWithError()`. 4 | export { default as parsePhoneNumberWithError, default as parsePhoneNumber } from '../es6/parsePhoneNumberWithError.js' 5 | 6 | // `parsePhoneNumberFromString()` named export is now considered legacy: 7 | // it has been promoted to a default export due to being too verbose. 8 | export { default as default, default as parsePhoneNumberFromString } from '../es6/parsePhoneNumber.js' 9 | 10 | export { default as isValidPhoneNumber } from '../es6/isValidPhoneNumber.js' 11 | export { default as isPossiblePhoneNumber } from '../es6/isPossiblePhoneNumber.js' 12 | export { default as validatePhoneNumberLength } from '../es6/validatePhoneNumberLength.js' 13 | 14 | // Deprecated. 15 | export { default as findNumbers } from '../es6/legacy/findNumbers.js' 16 | export { default as searchNumbers } from '../es6/legacy/searchNumbers.js' 17 | 18 | export { default as findPhoneNumbersInText } from '../es6/findPhoneNumbersInText.js' 19 | export { default as searchPhoneNumbersInText } from '../es6/searchPhoneNumbersInText.js' 20 | export { default as PhoneNumberMatcher } from '../es6/PhoneNumberMatcher.js' 21 | 22 | export { default as AsYouType } from '../es6/AsYouType.js' 23 | export { DIGIT_PLACEHOLDER } from '../es6/AsYouTypeFormatter.js' 24 | 25 | export { default as getCountries } from '../es6/getCountries.js' 26 | export { default as Metadata, isSupportedCountry, getCountryCallingCode, getExtPrefix } from '../es6/metadata.js' 27 | 28 | export { default as getExampleNumber } from '../es6/getExampleNumber.js' 29 | 30 | export { default as formatIncompletePhoneNumber } from '../es6/formatIncompletePhoneNumber.js' 31 | export { default as parseIncompletePhoneNumber, parsePhoneNumberCharacter } from '../es6/parseIncompletePhoneNumber.js' 32 | export { default as parseDigits } from '../es6/helpers/parseDigits.js' 33 | 34 | export { parseRFC3966, formatRFC3966 } from '../es6/helpers/RFC3966.js' 35 | -------------------------------------------------------------------------------- /core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libphonenumber-js/core", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "import": "./index.js", 10 | "require": "./index.cjs" 11 | } 12 | }, 13 | "sideEffects": false 14 | } 15 | -------------------------------------------------------------------------------- /examples.mobile.json.d.ts: -------------------------------------------------------------------------------- 1 | import { Examples } from './types.d.js'; 2 | 3 | declare const examples: Examples; 4 | export default examples; -------------------------------------------------------------------------------- /index.es6.exports/PhoneNumberSearch.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../metadata.min.json.js' 4 | 5 | import { PhoneNumberSearch as _PhoneNumberSearch } from '../es6/legacy/findPhoneNumbersInitialImplementation.js' 6 | 7 | export function PhoneNumberSearch(text, options) { 8 | _PhoneNumberSearch.call(this, text, options, metadata) 9 | } 10 | 11 | // Deprecated. 12 | PhoneNumberSearch.prototype = Object.create(_PhoneNumberSearch.prototype, {}) 13 | PhoneNumberSearch.prototype.constructor = PhoneNumberSearch 14 | -------------------------------------------------------------------------------- /index.es6.exports/findPhoneNumbers.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from '../min/exports/withMetadataArgument.js' 2 | 3 | import _findPhoneNumbers from '../es6/legacy/findPhoneNumbers.js' 4 | 5 | export function findPhoneNumbers() { 6 | return withMetadataArgument(_findPhoneNumbers, arguments) 7 | } 8 | -------------------------------------------------------------------------------- /index.es6.exports/format.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from '../min/exports/withMetadataArgument.js' 2 | 3 | import _format from '../es6/legacy/format.js' 4 | 5 | export function format() { 6 | return withMetadataArgument(_format, arguments) 7 | } 8 | -------------------------------------------------------------------------------- /index.es6.exports/getNumberType.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from '../min/exports/withMetadataArgument.js' 2 | 3 | import _getNumberType from '../es6/legacy/getNumberType.js' 4 | 5 | export function getNumberType() { 6 | return withMetadataArgument(_getNumberType, arguments) 7 | } 8 | -------------------------------------------------------------------------------- /index.es6.exports/isPossibleNumber.js: -------------------------------------------------------------------------------- 1 | // Deprecated. 2 | 3 | import withMetadataArgument from '../min/exports/withMetadataArgument.js' 4 | 5 | import _isPossibleNumber from '../es6/legacy/isPossibleNumber.js' 6 | 7 | export function isPossibleNumber() { 8 | return withMetadataArgument(_isPossibleNumber, arguments) 9 | } 10 | -------------------------------------------------------------------------------- /index.es6.exports/isValidNumber.js: -------------------------------------------------------------------------------- 1 | // Deprecated. 2 | 3 | import withMetadataArgument from '../min/exports/withMetadataArgument.js' 4 | 5 | import _isValidNumber from '../es6/legacy/isValidNumber.js' 6 | 7 | export function isValidNumber() { 8 | return withMetadataArgument(_isValidNumber, arguments) 9 | } 10 | -------------------------------------------------------------------------------- /index.es6.exports/isValidNumberForRegion.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from '../min/exports/withMetadataArgument.js' 2 | 3 | import _isValidNumberForRegion from '../es6/legacy/isValidNumberForRegion.js' 4 | 5 | export function isValidNumberForRegion() { 6 | return withMetadataArgument(_isValidNumberForRegion, arguments) 7 | } 8 | -------------------------------------------------------------------------------- /index.es6.exports/parse.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from '../min/exports/withMetadataArgument.js' 2 | 3 | import _parse from '../es6/legacy/parse.js' 4 | 5 | export function parse() { 6 | return withMetadataArgument(_parse, arguments) 7 | } 8 | -------------------------------------------------------------------------------- /index.es6.exports/searchPhoneNumbers.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from '../min/exports/withMetadataArgument.js' 2 | 3 | import { searchPhoneNumbers as _searchPhoneNumbers } from '../es6/legacy/findPhoneNumbers.js' 4 | 5 | export function searchPhoneNumbers() { 6 | return withMetadataArgument(_searchPhoneNumbers, arguments) 7 | } 8 | -------------------------------------------------------------------------------- /index.es6.js: -------------------------------------------------------------------------------- 1 | // This file is deprecated. 2 | // It's only for backwards compatibility with some old code that might've imported 3 | // the library by a full path of the old `index.es6.js` file. That file was renamed 4 | // to `index.js`. 5 | 6 | export * from './index.js' -------------------------------------------------------------------------------- /max/exports/AsYouType.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.max.json.js' 4 | 5 | import { AsYouType as _AsYouType } from '../../core/index.js' 6 | 7 | export function AsYouType(country) { 8 | return _AsYouType.call(this, country, metadata) 9 | } 10 | 11 | AsYouType.prototype = Object.create(_AsYouType.prototype, {}) 12 | AsYouType.prototype.constructor = AsYouType -------------------------------------------------------------------------------- /max/exports/Metadata.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.max.json.js' 4 | 5 | import { Metadata as _Metadata } from '../../core/index.js' 6 | 7 | export function Metadata() { 8 | return _Metadata.call(this, metadata) 9 | } 10 | 11 | Metadata.prototype = Object.create(_Metadata.prototype, {}) 12 | Metadata.prototype.constructor = Metadata -------------------------------------------------------------------------------- /max/exports/PhoneNumber.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.max.json.js' 4 | 5 | import { PhoneNumber as _PhoneNumber } from '../../core/index.js' 6 | 7 | export function PhoneNumber(number) { 8 | return _PhoneNumber.call(this, number, metadata) 9 | } 10 | PhoneNumber.prototype = Object.create(_PhoneNumber.prototype, {}) 11 | PhoneNumber.prototype.constructor = PhoneNumber 12 | -------------------------------------------------------------------------------- /max/exports/PhoneNumberMatcher.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.max.json.js' 4 | 5 | import { PhoneNumberMatcher as _PhoneNumberMatcher } from '../../core/index.js' 6 | 7 | export function PhoneNumberMatcher(text, options) { 8 | return _PhoneNumberMatcher.call(this, text, options, metadata) 9 | } 10 | PhoneNumberMatcher.prototype = Object.create(_PhoneNumberMatcher.prototype, {}) 11 | PhoneNumberMatcher.prototype.constructor = PhoneNumberMatcher 12 | -------------------------------------------------------------------------------- /max/exports/findNumbers.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { findNumbers as _findNumbers } from '../../core/index.js' 3 | 4 | export function findNumbers() { 5 | return withMetadataArgument(_findNumbers, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/findPhoneNumbersInText.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { findPhoneNumbersInText as _findPhoneNumbersInText } from '../../core/index.js' 3 | 4 | export function findPhoneNumbersInText() { 5 | return withMetadataArgument(_findPhoneNumbersInText, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/formatIncompletePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { formatIncompletePhoneNumber as _formatIncompletePhoneNumber } from '../../core/index.js' 3 | 4 | export function formatIncompletePhoneNumber() { 5 | return withMetadataArgument(_formatIncompletePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/getCountries.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getCountries as _getCountries } from '../../core/index.js' 3 | 4 | export function getCountries() { 5 | return withMetadataArgument(_getCountries, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/getCountryCallingCode.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getCountryCallingCode as _getCountryCallingCode } from '../../core/index.js' 3 | 4 | export function getCountryCallingCode() { 5 | return withMetadataArgument(_getCountryCallingCode, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/getExampleNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getExampleNumber as _getExampleNumber } from '../../core/index.js' 3 | 4 | export function getExampleNumber() { 5 | return withMetadataArgument(_getExampleNumber, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/getExtPrefix.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getExtPrefix as _getExtPrefix } from '../../core/index.js' 3 | 4 | export function getExtPrefix() { 5 | return withMetadataArgument(_getExtPrefix, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/isPossiblePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isPossiblePhoneNumber as _isPossiblePhoneNumber } from '../../core/index.js' 3 | 4 | export function isPossiblePhoneNumber() { 5 | return withMetadataArgument(_isPossiblePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/isSupportedCountry.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isSupportedCountry as _isSupportedCountry } from '../../core/index.js' 3 | 4 | export function isSupportedCountry() { 5 | return withMetadataArgument(_isSupportedCountry, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/isValidPhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isValidPhoneNumber as _isValidPhoneNumber } from '../../core/index.js' 3 | 4 | export function isValidPhoneNumber() { 5 | return withMetadataArgument(_isValidPhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/parsePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { default as _parsePhoneNumber } from '../../core/index.js' 3 | 4 | export function parsePhoneNumber() { 5 | return withMetadataArgument(_parsePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/parsePhoneNumberWithError.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { parsePhoneNumberWithError as _parsePhoneNumberWithError } from '../../core/index.js' 3 | 4 | export function parsePhoneNumberWithError() { 5 | return withMetadataArgument(_parsePhoneNumberWithError, arguments) 6 | } 7 | -------------------------------------------------------------------------------- /max/exports/searchNumbers.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { searchNumbers as _searchNumbers } from '../../core/index.js' 3 | 4 | export function searchNumbers() { 5 | return withMetadataArgument(_searchNumbers, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/searchPhoneNumbersInText.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { searchPhoneNumbersInText as _searchPhoneNumbersInText } from '../../core/index.js' 3 | 4 | export function searchPhoneNumbersInText() { 5 | return withMetadataArgument(_searchPhoneNumbersInText, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/validatePhoneNumberLength.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { validatePhoneNumberLength as _validatePhoneNumberLength } from '../../core/index.js' 3 | 4 | export function validatePhoneNumberLength() { 5 | return withMetadataArgument(_validatePhoneNumberLength, arguments) 6 | } -------------------------------------------------------------------------------- /max/exports/withMetadataArgument.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.max.json.js' 4 | 5 | export default function withMetadataArgument(func, _arguments) { 6 | var args = Array.prototype.slice.call(_arguments) 7 | args.push(metadata) 8 | return func.apply(this, args) 9 | } -------------------------------------------------------------------------------- /max/index.js: -------------------------------------------------------------------------------- 1 | // `parsePhoneNumber()` named export has been renamed to `parsePhoneNumberWithError()`. 2 | export { parsePhoneNumberWithError, parsePhoneNumberWithError as parsePhoneNumber } from './exports/parsePhoneNumberWithError.js' 3 | // `parsePhoneNumberFromString()` named export is now considered legacy: 4 | // it has been promoted to a default export due to being too verbose. 5 | export { parsePhoneNumber as parsePhoneNumberFromString, parsePhoneNumber as default } from './exports/parsePhoneNumber.js' 6 | 7 | export { isValidPhoneNumber } from './exports/isValidPhoneNumber.js' 8 | export { isPossiblePhoneNumber } from './exports/isPossiblePhoneNumber.js' 9 | export { validatePhoneNumberLength } from './exports/validatePhoneNumberLength.js' 10 | 11 | // Deprecated. 12 | export { findNumbers } from './exports/findNumbers.js' 13 | export { searchNumbers } from './exports/searchNumbers.js' 14 | 15 | export { findPhoneNumbersInText } from './exports/findPhoneNumbersInText.js' 16 | export { searchPhoneNumbersInText } from './exports/searchPhoneNumbersInText.js' 17 | export { PhoneNumberMatcher } from './exports/PhoneNumberMatcher.js' 18 | 19 | export { AsYouType } from './exports/AsYouType.js' 20 | 21 | export { isSupportedCountry } from './exports/isSupportedCountry.js' 22 | export { getCountries } from './exports/getCountries.js' 23 | export { getCountryCallingCode } from './exports/getCountryCallingCode.js' 24 | export { getExtPrefix } from './exports/getExtPrefix.js' 25 | 26 | export { Metadata } from './exports/Metadata.js' 27 | export { getExampleNumber } from './exports/getExampleNumber.js' 28 | 29 | export { formatIncompletePhoneNumber } from './exports/formatIncompletePhoneNumber.js' 30 | export { PhoneNumber } from './exports/PhoneNumber.js' 31 | 32 | export { 33 | ParseError, 34 | parseIncompletePhoneNumber, 35 | parsePhoneNumberCharacter, 36 | parseDigits, 37 | parseRFC3966, 38 | formatRFC3966, 39 | DIGIT_PLACEHOLDER 40 | } from '../core/index.js' 41 | -------------------------------------------------------------------------------- /max/metadata/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libphonenumber-js/max/metadata", 4 | "main": "../../metadata.max.json", 5 | "module": "../../metadata.max.json.js", 6 | "types": "../../metadata.max.json.d.ts", 7 | "type": "module", 8 | "exports": { 9 | ".": { 10 | "types": "../../metadata.max.json.d.ts", 11 | "import": "../../metadata.max.json.js", 12 | "require": "../../metadata.max.json" 13 | } 14 | }, 15 | "sideEffects": false 16 | } 17 | -------------------------------------------------------------------------------- /max/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libphonenumber-js/max", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "import": "./index.js", 10 | "require": "./index.cjs" 11 | } 12 | }, 13 | "sideEffects": false 14 | } 15 | -------------------------------------------------------------------------------- /metadata.full.json.d.ts: -------------------------------------------------------------------------------- 1 | import { MetadataJson } from './types.d.js'; 2 | 3 | declare const metadata: MetadataJson; 4 | export default metadata; 5 | -------------------------------------------------------------------------------- /metadata.max.json.d.ts: -------------------------------------------------------------------------------- 1 | import { MetadataJson } from './types.d.js'; 2 | 3 | declare const metadata: MetadataJson; 4 | export default metadata; 5 | -------------------------------------------------------------------------------- /metadata.min.json.d.ts: -------------------------------------------------------------------------------- 1 | import { MetadataJson } from './types.d.js'; 2 | 3 | declare const metadata: MetadataJson; 4 | export default metadata; 5 | -------------------------------------------------------------------------------- /metadata.mobile.json.d.ts: -------------------------------------------------------------------------------- 1 | import { MetadataJson } from './types.d.js'; 2 | 3 | declare const metadata: MetadataJson; 4 | export default metadata; 5 | -------------------------------------------------------------------------------- /min/exports/AsYouType.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.min.json.js' 4 | 5 | import { AsYouType as _AsYouType } from '../../core/index.js' 6 | 7 | export function AsYouType(country) { 8 | return _AsYouType.call(this, country, metadata) 9 | } 10 | 11 | AsYouType.prototype = Object.create(_AsYouType.prototype, {}) 12 | AsYouType.prototype.constructor = AsYouType -------------------------------------------------------------------------------- /min/exports/Metadata.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.min.json.js' 4 | 5 | import { Metadata as _Metadata } from '../../core/index.js' 6 | 7 | export function Metadata() { 8 | return _Metadata.call(this, metadata) 9 | } 10 | 11 | Metadata.prototype = Object.create(_Metadata.prototype, {}) 12 | Metadata.prototype.constructor = Metadata -------------------------------------------------------------------------------- /min/exports/PhoneNumber.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.min.json.js' 4 | 5 | import { PhoneNumber as _PhoneNumber } from '../../core/index.js' 6 | 7 | export function PhoneNumber(number) { 8 | return _PhoneNumber.call(this, number, metadata) 9 | } 10 | PhoneNumber.prototype = Object.create(_PhoneNumber.prototype, {}) 11 | PhoneNumber.prototype.constructor = PhoneNumber 12 | -------------------------------------------------------------------------------- /min/exports/PhoneNumberMatcher.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.min.json.js' 4 | 5 | import { PhoneNumberMatcher as _PhoneNumberMatcher } from '../../core/index.js' 6 | 7 | export function PhoneNumberMatcher(text, options) { 8 | return _PhoneNumberMatcher.call(this, text, options, metadata) 9 | } 10 | PhoneNumberMatcher.prototype = Object.create(_PhoneNumberMatcher.prototype, {}) 11 | PhoneNumberMatcher.prototype.constructor = PhoneNumberMatcher 12 | -------------------------------------------------------------------------------- /min/exports/findNumbers.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { findNumbers as _findNumbers } from '../../core/index.js' 3 | 4 | export function findNumbers() { 5 | return withMetadataArgument(_findNumbers, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/findPhoneNumbersInText.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { findPhoneNumbersInText as _findPhoneNumbersInText } from '../../core/index.js' 3 | 4 | export function findPhoneNumbersInText() { 5 | return withMetadataArgument(_findPhoneNumbersInText, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/formatIncompletePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { formatIncompletePhoneNumber as _formatIncompletePhoneNumber } from '../../core/index.js' 3 | 4 | export function formatIncompletePhoneNumber() { 5 | return withMetadataArgument(_formatIncompletePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/getCountries.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getCountries as _getCountries } from '../../core/index.js' 3 | 4 | export function getCountries() { 5 | return withMetadataArgument(_getCountries, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/getCountryCallingCode.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getCountryCallingCode as _getCountryCallingCode } from '../../core/index.js' 3 | 4 | export function getCountryCallingCode() { 5 | return withMetadataArgument(_getCountryCallingCode, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/getExampleNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getExampleNumber as _getExampleNumber } from '../../core/index.js' 3 | 4 | export function getExampleNumber() { 5 | return withMetadataArgument(_getExampleNumber, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/getExtPrefix.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getExtPrefix as _getExtPrefix } from '../../core/index.js' 3 | 4 | export function getExtPrefix() { 5 | return withMetadataArgument(_getExtPrefix, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/isPossiblePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isPossiblePhoneNumber as _isPossiblePhoneNumber } from '../../core/index.js' 3 | 4 | export function isPossiblePhoneNumber() { 5 | return withMetadataArgument(_isPossiblePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/isSupportedCountry.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isSupportedCountry as _isSupportedCountry } from '../../core/index.js' 3 | 4 | export function isSupportedCountry() { 5 | return withMetadataArgument(_isSupportedCountry, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/isValidPhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isValidPhoneNumber as _isValidPhoneNumber } from '../../core/index.js' 3 | 4 | export function isValidPhoneNumber() { 5 | return withMetadataArgument(_isValidPhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/parsePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { default as _parsePhoneNumber } from '../../core/index.js' 3 | 4 | export function parsePhoneNumber() { 5 | return withMetadataArgument(_parsePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/parsePhoneNumberWithError.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { parsePhoneNumberWithError as _parsePhoneNumberWithError } from '../../core/index.js' 3 | 4 | export function parsePhoneNumberWithError() { 5 | return withMetadataArgument(_parsePhoneNumberWithError, arguments) 6 | } 7 | -------------------------------------------------------------------------------- /min/exports/searchNumbers.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { searchNumbers as _searchNumbers } from '../../core/index.js' 3 | 4 | export function searchNumbers() { 5 | return withMetadataArgument(_searchNumbers, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/searchPhoneNumbersInText.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { searchPhoneNumbersInText as _searchPhoneNumbersInText } from '../../core/index.js' 3 | 4 | export function searchPhoneNumbersInText() { 5 | return withMetadataArgument(_searchPhoneNumbersInText, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/validatePhoneNumberLength.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { validatePhoneNumberLength as _validatePhoneNumberLength } from '../../core/index.js' 3 | 4 | export function validatePhoneNumberLength() { 5 | return withMetadataArgument(_validatePhoneNumberLength, arguments) 6 | } -------------------------------------------------------------------------------- /min/exports/withMetadataArgument.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.min.json.js' 4 | 5 | export default function withMetadataArgument(func, _arguments) { 6 | var args = Array.prototype.slice.call(_arguments) 7 | args.push(metadata) 8 | return func.apply(this, args) 9 | } -------------------------------------------------------------------------------- /min/index.js: -------------------------------------------------------------------------------- 1 | // `parsePhoneNumber()` named export has been renamed to `parsePhoneNumberWithError()`. 2 | export { parsePhoneNumberWithError, parsePhoneNumberWithError as parsePhoneNumber } from './exports/parsePhoneNumberWithError.js' 3 | // `parsePhoneNumberFromString()` named export is now considered legacy: 4 | // it has been promoted to a default export due to being too verbose. 5 | export { parsePhoneNumber as parsePhoneNumberFromString, parsePhoneNumber as default } from './exports/parsePhoneNumber.js' 6 | 7 | export { isValidPhoneNumber } from './exports/isValidPhoneNumber.js' 8 | export { isPossiblePhoneNumber } from './exports/isPossiblePhoneNumber.js' 9 | export { validatePhoneNumberLength } from './exports/validatePhoneNumberLength.js' 10 | 11 | // Deprecated. 12 | export { findNumbers } from './exports/findNumbers.js' 13 | export { searchNumbers } from './exports/searchNumbers.js' 14 | 15 | export { findPhoneNumbersInText } from './exports/findPhoneNumbersInText.js' 16 | export { searchPhoneNumbersInText } from './exports/searchPhoneNumbersInText.js' 17 | export { PhoneNumberMatcher } from './exports/PhoneNumberMatcher.js' 18 | 19 | export { AsYouType } from './exports/AsYouType.js' 20 | 21 | export { isSupportedCountry } from './exports/isSupportedCountry.js' 22 | export { getCountries } from './exports/getCountries.js' 23 | export { getCountryCallingCode } from './exports/getCountryCallingCode.js' 24 | export { getExtPrefix } from './exports/getExtPrefix.js' 25 | 26 | export { Metadata } from './exports/Metadata.js' 27 | export { getExampleNumber } from './exports/getExampleNumber.js' 28 | 29 | export { formatIncompletePhoneNumber } from './exports/formatIncompletePhoneNumber.js' 30 | export { PhoneNumber } from './exports/PhoneNumber.js' 31 | 32 | export { 33 | ParseError, 34 | parseIncompletePhoneNumber, 35 | parsePhoneNumberCharacter, 36 | parseDigits, 37 | parseRFC3966, 38 | formatRFC3966, 39 | DIGIT_PLACEHOLDER 40 | } from '../core/index.js' 41 | -------------------------------------------------------------------------------- /min/metadata/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libphonenumber-js/min/metadata", 4 | "main": "../../metadata.min.json", 5 | "module": "../../metadata.min.json.js", 6 | "types": "../../metadata.min.json.d.ts", 7 | "type": "module", 8 | "exports": { 9 | ".": { 10 | "types": "../../metadata.min.json.d.ts", 11 | "import": "../../metadata.min.json.js", 12 | "require": "../../metadata.min.json" 13 | } 14 | }, 15 | "sideEffects": false 16 | } 17 | -------------------------------------------------------------------------------- /min/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libphonenumber-js/min", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "import": "./index.js", 10 | "require": "./index.cjs" 11 | } 12 | }, 13 | "sideEffects": false 14 | } 15 | -------------------------------------------------------------------------------- /mobile/examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libphonenumber-js/mobile/examples", 4 | "main": "../../examples.mobile.json", 5 | "module": "../../examples.mobile.json.js", 6 | "types": "../../examples.mobile.json.d.ts", 7 | "type": "module", 8 | "exports": { 9 | ".": { 10 | "types": "../../examples.mobile.json.d.ts", 11 | "import": "../../examples.mobile.json.js", 12 | "require": "../../examples.mobile.json" 13 | } 14 | }, 15 | "sideEffects": false 16 | } 17 | -------------------------------------------------------------------------------- /mobile/exports/AsYouType.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.mobile.json.js' 4 | 5 | import { AsYouType as _AsYouType } from '../../core/index.js' 6 | 7 | export function AsYouType(country) { 8 | return _AsYouType.call(this, country, metadata) 9 | } 10 | 11 | AsYouType.prototype = Object.create(_AsYouType.prototype, {}) 12 | AsYouType.prototype.constructor = AsYouType -------------------------------------------------------------------------------- /mobile/exports/Metadata.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.mobile.json.js' 4 | 5 | import { Metadata as _Metadata } from '../../core/index.js' 6 | 7 | export function Metadata() { 8 | return _Metadata.call(this, metadata) 9 | } 10 | 11 | Metadata.prototype = Object.create(_Metadata.prototype, {}) 12 | Metadata.prototype.constructor = Metadata -------------------------------------------------------------------------------- /mobile/exports/PhoneNumber.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.mobile.json.js' 4 | 5 | import { PhoneNumber as _PhoneNumber } from '../../core/index.js' 6 | 7 | export function PhoneNumber(number) { 8 | return _PhoneNumber.call(this, number, metadata) 9 | } 10 | PhoneNumber.prototype = Object.create(_PhoneNumber.prototype, {}) 11 | PhoneNumber.prototype.constructor = PhoneNumber 12 | -------------------------------------------------------------------------------- /mobile/exports/PhoneNumberMatcher.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.mobile.json.js' 4 | 5 | import { PhoneNumberMatcher as _PhoneNumberMatcher } from '../../core/index.js' 6 | 7 | export function PhoneNumberMatcher(text, options) { 8 | return _PhoneNumberMatcher.call(this, text, options, metadata) 9 | } 10 | PhoneNumberMatcher.prototype = Object.create(_PhoneNumberMatcher.prototype, {}) 11 | PhoneNumberMatcher.prototype.constructor = PhoneNumberMatcher 12 | -------------------------------------------------------------------------------- /mobile/exports/findNumbers.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { findNumbers as _findNumbers } from '../../core/index.js' 3 | 4 | export function findNumbers() { 5 | return withMetadataArgument(_findNumbers, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/findPhoneNumbersInText.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { findPhoneNumbersInText as _findPhoneNumbersInText } from '../../core/index.js' 3 | 4 | export function findPhoneNumbersInText() { 5 | return withMetadataArgument(_findPhoneNumbersInText, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/formatIncompletePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { formatIncompletePhoneNumber as _formatIncompletePhoneNumber } from '../../core/index.js' 3 | 4 | export function formatIncompletePhoneNumber() { 5 | return withMetadataArgument(_formatIncompletePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/getCountries.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getCountries as _getCountries } from '../../core/index.js' 3 | 4 | export function getCountries() { 5 | return withMetadataArgument(_getCountries, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/getCountryCallingCode.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getCountryCallingCode as _getCountryCallingCode } from '../../core/index.js' 3 | 4 | export function getCountryCallingCode() { 5 | return withMetadataArgument(_getCountryCallingCode, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/getExampleNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getExampleNumber as _getExampleNumber } from '../../core/index.js' 3 | 4 | export function getExampleNumber() { 5 | return withMetadataArgument(_getExampleNumber, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/getExtPrefix.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { getExtPrefix as _getExtPrefix } from '../../core/index.js' 3 | 4 | export function getExtPrefix() { 5 | return withMetadataArgument(_getExtPrefix, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/isPossiblePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isPossiblePhoneNumber as _isPossiblePhoneNumber } from '../../core/index.js' 3 | 4 | export function isPossiblePhoneNumber() { 5 | return withMetadataArgument(_isPossiblePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/isSupportedCountry.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isSupportedCountry as _isSupportedCountry } from '../../core/index.js' 3 | 4 | export function isSupportedCountry() { 5 | return withMetadataArgument(_isSupportedCountry, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/isValidPhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { isValidPhoneNumber as _isValidPhoneNumber } from '../../core/index.js' 3 | 4 | export function isValidPhoneNumber() { 5 | return withMetadataArgument(_isValidPhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/parsePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { default as _parsePhoneNumber } from '../../core/index.js' 3 | 4 | export function parsePhoneNumber() { 5 | return withMetadataArgument(_parsePhoneNumber, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/parsePhoneNumberWithError.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { parsePhoneNumberWithError as _parsePhoneNumberWithError } from '../../core/index.js' 3 | 4 | export function parsePhoneNumberWithError() { 5 | return withMetadataArgument(_parsePhoneNumberWithError, arguments) 6 | } 7 | -------------------------------------------------------------------------------- /mobile/exports/searchNumbers.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { searchNumbers as _searchNumbers } from '../../core/index.js' 3 | 4 | export function searchNumbers() { 5 | return withMetadataArgument(_searchNumbers, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/searchPhoneNumbersInText.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { searchPhoneNumbersInText as _searchPhoneNumbersInText } from '../../core/index.js' 3 | 4 | export function searchPhoneNumbersInText() { 5 | return withMetadataArgument(_searchPhoneNumbersInText, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/validatePhoneNumberLength.js: -------------------------------------------------------------------------------- 1 | import withMetadataArgument from './withMetadataArgument.js' 2 | import { validatePhoneNumberLength as _validatePhoneNumberLength } from '../../core/index.js' 3 | 4 | export function validatePhoneNumberLength() { 5 | return withMetadataArgument(_validatePhoneNumberLength, arguments) 6 | } -------------------------------------------------------------------------------- /mobile/exports/withMetadataArgument.js: -------------------------------------------------------------------------------- 1 | // Importing from a ".js" file is a workaround for Node.js "ES Modules" 2 | // importing system which is even uncapable of importing "*.json" files. 3 | import metadata from '../../metadata.mobile.json.js' 4 | 5 | export default function withMetadataArgument(func, _arguments) { 6 | var args = Array.prototype.slice.call(_arguments) 7 | args.push(metadata) 8 | return func.apply(this, args) 9 | } -------------------------------------------------------------------------------- /mobile/index.js: -------------------------------------------------------------------------------- 1 | // `parsePhoneNumber()` named export has been renamed to `parsePhoneNumberWithError()`. 2 | export { parsePhoneNumberWithError, parsePhoneNumberWithError as parsePhoneNumber } from './exports/parsePhoneNumberWithError.js' 3 | // `parsePhoneNumberFromString()` named export is now considered legacy: 4 | // it has been promoted to a default export due to being too verbose. 5 | export { parsePhoneNumber as parsePhoneNumberFromString, parsePhoneNumber as default } from './exports/parsePhoneNumber.js' 6 | 7 | export { isValidPhoneNumber } from './exports/isValidPhoneNumber.js' 8 | export { isPossiblePhoneNumber } from './exports/isPossiblePhoneNumber.js' 9 | export { validatePhoneNumberLength } from './exports/validatePhoneNumberLength.js' 10 | 11 | // Deprecated. 12 | export { findNumbers } from './exports/findNumbers.js' 13 | export { searchNumbers } from './exports/searchNumbers.js' 14 | 15 | export { findPhoneNumbersInText } from './exports/findPhoneNumbersInText.js' 16 | export { searchPhoneNumbersInText } from './exports/searchPhoneNumbersInText.js' 17 | export { PhoneNumberMatcher } from './exports/PhoneNumberMatcher.js' 18 | 19 | export { AsYouType } from './exports/AsYouType.js' 20 | 21 | export { isSupportedCountry } from './exports/isSupportedCountry.js' 22 | export { getCountries } from './exports/getCountries.js' 23 | export { getCountryCallingCode } from './exports/getCountryCallingCode.js' 24 | export { getExtPrefix } from './exports/getExtPrefix.js' 25 | 26 | export { Metadata } from './exports/Metadata.js' 27 | export { getExampleNumber } from './exports/getExampleNumber.js' 28 | 29 | export { formatIncompletePhoneNumber } from './exports/formatIncompletePhoneNumber.js' 30 | export { PhoneNumber } from './exports/PhoneNumber.js' 31 | 32 | export { 33 | ParseError, 34 | parseIncompletePhoneNumber, 35 | parsePhoneNumberCharacter, 36 | parseDigits, 37 | parseRFC3966, 38 | formatRFC3966, 39 | DIGIT_PLACEHOLDER 40 | } from '../core/index.js' 41 | -------------------------------------------------------------------------------- /mobile/metadata/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libphonenumber-js/mobile/metadata", 4 | "main": "../../metadata.mobile.json", 5 | "module": "../../metadata.mobile.json.js", 6 | "types": "../../metadata.mobile.json.d.ts", 7 | "type": "module", 8 | "exports": { 9 | ".": { 10 | "types": "../../metadata.mobile.json.d.ts", 11 | "import": "../../metadata.mobile.json.js", 12 | "require": "../../metadata.mobile.json" 13 | } 14 | }, 15 | "sideEffects": false 16 | } 17 | -------------------------------------------------------------------------------- /mobile/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "libphonenumber-js/mobile", 4 | "main": "index.cjs", 5 | "module": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": { 9 | "import": "./index.js", 10 | "require": "./index.cjs" 11 | } 12 | }, 13 | "sideEffects": false 14 | } 15 | -------------------------------------------------------------------------------- /project.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "follow_symlinks": true, 6 | "path": ".", 7 | "folder_exclude_patterns": ["libphonenumber-js/node_modules", "libphonenumber-js/bundle", "libphonenumber-js/coverage", "libphonenumber-js/build", "libphonenumber-js/lib", "libphonenumber-js/es6", "libphonenumber-js/.nyc_output", "libphonenumber-js/website/lib", "project.sublime-workspace"] 8 | } 9 | ], 10 | "settings": 11 | { 12 | "tab_size": 3, 13 | "translate_tabs_to_spaces": false, 14 | "trim_trailing_white_space_on_save": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /rollup.config.mjs: -------------------------------------------------------------------------------- 1 | import json from 'rollup-plugin-json' 2 | import { terser } from 'rollup-plugin-terser' 3 | 4 | export default [ 5 | { 6 | input: 'min/index.js', 7 | plugins: [ 8 | json(), 9 | terser() 10 | ], 11 | output: { 12 | format: 'umd', 13 | name: 'libphonenumber', 14 | file: 'bundle/libphonenumber-min.js', 15 | sourcemap: true 16 | } 17 | }, 18 | { 19 | input: 'mobile/index.js', 20 | plugins: [ 21 | json(), 22 | terser() 23 | ], 24 | output: { 25 | format: 'umd', 26 | name: 'libphonenumber', 27 | file: 'bundle/libphonenumber-mobile.js', 28 | sourcemap: true 29 | } 30 | }, 31 | { 32 | input: 'max/index.js', 33 | plugins: [ 34 | json(), 35 | terser() 36 | ], 37 | output: { 38 | format: 'umd', 39 | name: 'libphonenumber', 40 | file: 'bundle/libphonenumber-max.js', 41 | sourcemap: true 42 | } 43 | }, 44 | { 45 | input: 'index.es6', 46 | plugins: [ 47 | json(), 48 | terser() 49 | ], 50 | output: { 51 | format: 'umd', 52 | name: 'libphonenumber', 53 | file: 'bundle/libphonenumber-js.min.js', 54 | sourcemap: true 55 | } 56 | } 57 | ] -------------------------------------------------------------------------------- /runnable/create-commonjs-package-json.js: -------------------------------------------------------------------------------- 1 | // Creates a `package.json` file in the CommonJS `build` folder. 2 | // That marks that whole folder as CommonJS so that Node.js doesn't complain 3 | // about `require()`-ing those files. 4 | 5 | import fs from 'fs' 6 | 7 | fs.writeFileSync('./build/package.json', JSON.stringify({ 8 | name: 'libphonenumber-js/build', 9 | type: 'commonjs', 10 | private: true 11 | }, null, 2), 'utf8') -------------------------------------------------------------------------------- /runnable/download.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import { download } from 'libphonenumber-metadata-generator' 3 | 4 | const url = process.argv[2] 5 | const outputPath = process.argv[3] 6 | 7 | download(url).then((contents) => { 8 | fs.writeFileSync(outputPath, contents) 9 | }).catch((error) => { 10 | console.error(error) 11 | process.exit(1) 12 | }) -------------------------------------------------------------------------------- /runnable/generate-country-codes.js: -------------------------------------------------------------------------------- 1 | import metadata from '../metadata.min.json' assert { type: 'json' } 2 | import fs from 'fs' 3 | 4 | const countryCodes = Object.keys(metadata.countries) 5 | 6 | fs.writeFileSync( 7 | './types.d.ts', 8 | fs.readFileSync('./types.d.ts', 'utf-8').replace( 9 | /export type CountryCode = .*;/, 10 | `export type CountryCode = ${countryCodes.map(_ => `'${_}'`).join(' | ')};` 11 | ), 12 | 'utf-8' 13 | ) -------------------------------------------------------------------------------- /runnable/generate.js: -------------------------------------------------------------------------------- 1 | import minimist from 'minimist' 2 | import path from 'path' 3 | import fs from 'fs' 4 | 5 | import { version, generate, compress } from 'libphonenumber-metadata-generator' 6 | 7 | // https://ru.stackoverflow.com/questions/1281148/referenceerror-dirname-is-not-defined 8 | import { fileURLToPath } from 'url' 9 | import { dirname } from 'path' 10 | const __filename = fileURLToPath(import.meta.url) 11 | const __dirname = dirname(__filename) 12 | 13 | // const REGION_CODE_FOR_NON_GEO_ENTITY = '001' 14 | 15 | const input = fs.readFileSync(path.join(__dirname, process.argv[2]), 'utf8') 16 | const output_file = process.argv[3] 17 | 18 | const command_line_arguments = minimist(process.argv.slice(4)) 19 | 20 | // Included countries 21 | let included_countries 22 | if (command_line_arguments.countries) { 23 | included_countries = command_line_arguments.countries.split(',') 24 | console.log('Included countries:', included_countries) 25 | included_countries = new Set(included_countries) 26 | } 27 | 28 | // Include all regular expressions 29 | let extended = false 30 | if (command_line_arguments.extended) { 31 | console.log('Include extra validation regular expressions') 32 | extended = true 33 | } 34 | 35 | // Included phone number types 36 | let included_phone_number_types 37 | if (command_line_arguments.types) { 38 | included_phone_number_types = command_line_arguments.types.split(',') 39 | console.log('Included phone number types:', included_phone_number_types) 40 | included_phone_number_types = new Set(included_phone_number_types) 41 | } 42 | 43 | // Generate and compress metadata 44 | generate(input, version, included_countries, extended, included_phone_number_types).then((output) => { 45 | // Write uncompressed metadata into a file for easier debugging 46 | if (command_line_arguments.debug) { 47 | console.log('Output uncompressed JSON for debugging') 48 | fs.writeFileSync(path.join(__dirname, '../metadata.json'), JSON.stringify(output, undefined, 3)) 49 | } 50 | 51 | // Compress the generated metadata 52 | fs.writeFileSync(path.join(__dirname, output_file), JSON.stringify(compress(output))) 53 | 54 | // Output mobile phone number type examples 55 | if (command_line_arguments.examples === 'mobile') { 56 | var examples = Object.keys(output.countries).reduce(function(out, country_code) { 57 | // if (country_code === REGION_CODE_FOR_NON_GEO_ENTITY) { 58 | // return out 59 | // } 60 | var mobile = output.countries[country_code].examples.mobile 61 | var fixed_line = output.countries[country_code].examples.fixed_line 62 | if (mobile) { 63 | out[country_code] = mobile 64 | } 65 | // "TA" country doesn't have any mobile phone number example 66 | else if (fixed_line) { 67 | console.warn(`Country ${country_code} doesn't have a mobile phone number example. Substituting with a fixed line phone number example.`) 68 | out[country_code] = fixed_line 69 | } else { 70 | console.error(`Country ${country_code} doesn't have neither a mobile phone number example nor a fixed line phone number example.`) 71 | // `async` errors aren't being caught at the top level in Node.js 72 | process.exit(1) 73 | } 74 | return out 75 | }, {}) 76 | fs.writeFileSync( 77 | path.join(__dirname, '../examples.mobile.json'), 78 | JSON.stringify(examples) 79 | ) 80 | } 81 | }) -------------------------------------------------------------------------------- /runnable/json-to-js.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | 3 | const COMMENT = '// This file is a workaround for a bug in web browsers\' "native"' + '\n' + 4 | '// ES6 importing system which is uncapable of importing "*.json" files.' + '\n' + 5 | '// https://github.com/catamphetamine/libphonenumber-js/issues/239' 6 | 7 | const path = process.argv[2] 8 | jsonToJs(path) 9 | 10 | function jsonToJs(path) { 11 | let contents = fs.readFileSync(path, 'utf-8') 12 | contents = COMMENT + '\n' + 'export default ' + contents 13 | fs.writeFileSync(path + '.js', contents) 14 | } -------------------------------------------------------------------------------- /runnable/metadata-branch.js: -------------------------------------------------------------------------------- 1 | import exec from './modules/exec.js' 2 | 3 | let metadata_branch_exists = false 4 | 5 | try 6 | { 7 | exec('git rev-parse --verify update-metadata') 8 | metadata_branch_exists = true 9 | } 10 | catch (error) 11 | { 12 | if (error.message.indexOf('fatal: Needed a single revision') === -1) 13 | { 14 | throw error 15 | } 16 | } 17 | 18 | if (metadata_branch_exists) 19 | { 20 | console.log(exec('git checkout master')) 21 | console.log(exec('git branch -D update-metadata')) 22 | } 23 | 24 | console.log(exec('git pull')) 25 | console.log(exec('git branch update-metadata origin/master')) 26 | console.log(exec('git checkout update-metadata')) -------------------------------------------------------------------------------- /runnable/metadata-update-and-push-and-pull-request.js: -------------------------------------------------------------------------------- 1 | // In order for this script to work: 2 | // 3 | // * Install `hub` command line tool: `brew install hub` 4 | // * Create a "Personal Access Token" in GitHub account settings (just "repo_public" would be enough) 5 | // * Tell `hub` to use the token for creating GitHub pull requests: `echo "---\ngithub.com:\n- protocol: https\n user: GITHUB_USERNAME\n oauth_token: TOKEN" >> ~/.config/hub` 6 | 7 | import update_metadata from './modules/update-metadata.js' 8 | import commit from './modules/commit.js' 9 | import exec from './modules/exec.js' 10 | 11 | if (update_metadata()) 12 | { 13 | commit() 14 | 15 | console.log() 16 | console.log('========================================') 17 | console.log('= Pushing changes =') 18 | console.log('========================================') 19 | console.log() 20 | 21 | // Delete previous `update-metadata` remote branch 22 | // (if it already exists) 23 | if (exec('git ls-remote --heads origin update-metadata')) 24 | { 25 | console.log(exec('git push origin update-metadata --delete')) 26 | } 27 | 28 | // Push the local `update-metadata` branch to GitHub 29 | console.log(exec('git push origin update-metadata')) 30 | 31 | console.log() 32 | console.log('========================================') 33 | console.log('= Pushed. Creating Pull Request. =') 34 | console.log('========================================') 35 | console.log() 36 | 37 | console.log(exec('hub pull-request -m "Updated metadata" -b catamphetamine/libphonenumber-js:master -h update-metadata')) 38 | 39 | console.log() 40 | console.log('========================================') 41 | console.log('= Pull Request created =') 42 | console.log('========================================') 43 | console.log() 44 | 45 | console.log(exec('git checkout master')) 46 | console.log(exec('git branch -D update-metadata')) 47 | } -------------------------------------------------------------------------------- /runnable/metadata-update-and-push.js: -------------------------------------------------------------------------------- 1 | import update_metadata from './modules/update-metadata.js' 2 | import commit from './modules/commit.js' 3 | import exec from './modules/exec.js' 4 | 5 | if (update_metadata()) 6 | { 7 | commit() 8 | 9 | console.log() 10 | console.log('========================================') 11 | console.log('= Pushing changes =') 12 | console.log('========================================') 13 | console.log() 14 | 15 | // Delete previous `update-metadata` remote branch 16 | // (if it already exists) 17 | if (exec('git ls-remote --heads origin update-metadata')) 18 | { 19 | console.log(exec('git push origin update-metadata --delete')) 20 | } 21 | 22 | // Push the local `update-metadata` branch to GitHub 23 | console.log(exec('git push origin update-metadata')) 24 | 25 | console.log() 26 | console.log('==========================================') 27 | console.log('= Pushed. Create Pull Request on GitHub. =') 28 | console.log('==========================================') 29 | console.log() 30 | } -------------------------------------------------------------------------------- /runnable/metadata-update-and-release.js: -------------------------------------------------------------------------------- 1 | import update_metadata from './modules/update-metadata.js' 2 | import commit from './modules/commit.js' 3 | import exec from './modules/exec.js' 4 | 5 | if (update_metadata()) 6 | { 7 | commit() 8 | 9 | console.log() 10 | console.log('========================================') 11 | console.log('= Pushing changes =') 12 | console.log('========================================') 13 | console.log() 14 | 15 | console.log(exec('git push')) 16 | 17 | console.log() 18 | console.log('========================================') 19 | console.log('= Pushed. Releasing. =') 20 | console.log('========================================') 21 | console.log() 22 | 23 | console.log(exec('npm version patch')) 24 | console.log(exec('npm publish')) 25 | console.log(exec('git push')) 26 | 27 | console.log() 28 | console.log('========================================') 29 | console.log('= Released =') 30 | console.log('========================================') 31 | console.log() 32 | } -------------------------------------------------------------------------------- /runnable/modules/commit.js: -------------------------------------------------------------------------------- 1 | import exec from './exec.js' 2 | 3 | export default function() 4 | { 5 | console.log() 6 | console.log('========================================') 7 | console.log('= Committing changes =') 8 | console.log('========================================') 9 | console.log() 10 | 11 | console.log(exec('git add .')) 12 | 13 | console.log(exec('git commit -m "Updated metadata"')) 14 | } -------------------------------------------------------------------------------- /runnable/modules/exec.js: -------------------------------------------------------------------------------- 1 | import child_process from 'child_process' 2 | 3 | export default function exec(command) 4 | { 5 | return child_process.execSync(command).toString().trim() 6 | } -------------------------------------------------------------------------------- /runnable/modules/update-metadata.js: -------------------------------------------------------------------------------- 1 | import exec from './exec.js' 2 | 3 | export default function() 4 | { 5 | var metadata_changed = exec('git ls-files --modified PhoneNumberMetadata.xml') 6 | 7 | if (!metadata_changed) 8 | { 9 | console.log() 10 | console.log('========================================') 11 | console.log('= Metadata is up-to-date. Exiting. =') 12 | console.log('========================================') 13 | console.log() 14 | 15 | return 16 | } 17 | 18 | console.log() 19 | console.log('========================================') 20 | console.log('= Metadata has changed, updating files =') 21 | console.log('========================================') 22 | console.log() 23 | 24 | console.log(exec('npm run metadata:generate')) 25 | 26 | console.log() 27 | console.log('========================================') 28 | console.log('= Running tests =') 29 | console.log('========================================') 30 | console.log() 31 | 32 | // console.log('* Actually not running tests because if they fail then it won\'t be reported in any way, and if instead tests fail for the Pull Request on github then the repo owner will be notified by Travis CI about that.') 33 | console.log(exec('npm run build')) 34 | console.log(exec('npm test')) 35 | 36 | var modified_files = exec('git ls-files --modified').split(/\s/) 37 | 38 | var unexpected_modified_files = modified_files.filter(function(file) 39 | { 40 | return file !== 'PhoneNumberMetadata.xml' && 41 | !/^metadata\.[a-z]+\.json$/.test(file) && 42 | !/^examples\.[a-z]+\.json$/.test(file) 43 | }) 44 | 45 | // Turned off this "modified files" check 46 | // because on Windows random files constantly got "modified" 47 | // without actually being modified. 48 | // (perhaps something related to line endings) 49 | if (false && unexpected_modified_files.length > 0) 50 | { 51 | var error 52 | 53 | error += 'Only `PhoneNumberMetadata.xml`, `metadata.*.json` and `examples.*.json` files should be modified. Unexpected modified files:' 54 | error += '\n' 55 | error += '\n' 56 | error += unexpected_modified_files.join('\n') 57 | 58 | console.log() 59 | console.log('========================================') 60 | console.log('= Error =') 61 | console.log('========================================') 62 | console.log() 63 | console.log(error) 64 | 65 | throw new Error(error) 66 | } 67 | 68 | // Doesn't work 69 | // 70 | // // http://stackoverflow.com/questions/33610682/git-list-of-staged-files 71 | // var staged_files = exec('git diff --name-only --cached').split(/\s/) 72 | // 73 | // if (staged_files.length > 0) 74 | // { 75 | // console.log() 76 | // console.log('========================================') 77 | // console.log('= Error =') 78 | // console.log('========================================') 79 | // console.log() 80 | // console.log('There are some staged files already. Aborting metadata update process.') 81 | // console.log() 82 | // console.log(staged_files.join('\n')) 83 | // 84 | // process.exit(1) 85 | // } 86 | 87 | return true 88 | } -------------------------------------------------------------------------------- /source/AsYouTypeFormatter.PatternMatcher.d.ts: -------------------------------------------------------------------------------- 1 | import { 2 | character, 3 | MatchTree 4 | } from './AsYouTypeFormatter.PatternParser.d.js' 5 | 6 | interface MatchOptions { 7 | allowOverflow?: boolean; 8 | } 9 | 10 | // An "overflow" match is when the string matches the pattern 11 | // and there're still some characters left in it. 12 | // 13 | // For example, "12345" matches "12[0-5]|78" pattern with an overflow 14 | // because "123" matches the "12[0-5]" variant of the pattern 15 | // and there're still "45" characters left. 16 | // 17 | // This type of match is only returned when `allowOverflow` option is `true`. 18 | // By default, `allowOverflow` is `false` and `undefined` ("no match" result) 19 | // is returned in case of an "overflow" match. 20 | // 21 | interface MatchResultOverflow { 22 | overflow: true; 23 | } 24 | 25 | // When there's a ("full") match, returns a match result. 26 | // 27 | // A ("full") match is when the string matches the entire pattern. 28 | // 29 | // For example, "123" fully matches "12[0-5]|78" pattern. 30 | // 31 | interface MatchResultFullMatch { 32 | match: true; 33 | matchedChars: character[]; 34 | } 35 | 36 | // When there's a "partial" match, returns a "partial" match result. 37 | // 38 | // A "partial" match is when the string is not long enough 39 | // to match the whole matching tree. 40 | // 41 | // For example, "123" is a partial match for "12[0-5]4|78" pattern, 42 | // because "123" matched the "12[0-5]" part and the "4" part of the pattern 43 | // is left uninvolved. 44 | // 45 | interface MatchResultPartialMatch { 46 | partialMatch: true; 47 | } 48 | 49 | // When there's no match, returns `undefined`. 50 | // 51 | // For example, "123" doesn't match "456|789" pattern. 52 | // 53 | type MatchResultNoMatch = undefined; 54 | 55 | type MatchResult = 56 | MatchResultOverflow | 57 | MatchResultFullMatch | 58 | MatchResultPartialMatch | 59 | MatchResultNoMatch; 60 | 61 | export default class PatternMatcher { 62 | constructor(pattern: string); 63 | match(string: string, options?: MatchOptions): MatchResult; 64 | } 65 | 66 | function match(characters: character[], tree: MatchTree, last?: boolean); 67 | -------------------------------------------------------------------------------- /source/AsYouTypeFormatter.PatternParser.d.ts: -------------------------------------------------------------------------------- 1 | // A string of length `1`. 2 | // 3 | // Example: "3". 4 | // 5 | export type character = string; 6 | 7 | // Matches any character. 8 | // 9 | // Example: 10 | // 11 | // String pattern "123" compiles into 3 characters: 12 | // 13 | // ["1", "2", "3"] 14 | // 15 | type Character = character; 16 | 17 | // Matches one of characters. 18 | // 19 | // Example: 20 | // 21 | // String pattern "[5-9]" compiles into: 22 | // 23 | // { op: "[]", args: ["5", "6", "7", "8", "9"] } 24 | // 25 | interface OneOfCharacters { 26 | op: '[]'; 27 | args: character[]; 28 | } 29 | 30 | // Matches any of the subtrees. 31 | // 32 | // Example: 33 | // 34 | // String pattern "123|[5-9]0" compiles into: 35 | // 36 | // { 37 | // op: "|", 38 | // args: [ 39 | // // First subtree: 40 | // ["1", "2", "3"], 41 | // // Second subtree: 42 | // [ 43 | // { op: "[]", args: ["5", "6", "7", "8", "9"] }, 44 | // "0" 45 | // ] 46 | // ] 47 | // } 48 | // 49 | interface OrCondition { 50 | op: '|'; 51 | args: MatchTree[]; 52 | } 53 | 54 | export type MatchTree = Character | OneOfCharacters | MatchTree[] | OrCondition; 55 | 56 | export default class PatternParser { 57 | parse(pattern: string): MatchTree; 58 | } 59 | -------------------------------------------------------------------------------- /source/AsYouTypeFormatter.PatternParser.test.js: -------------------------------------------------------------------------------- 1 | import PatternParser from './AsYouTypeFormatter.PatternParser.js' 2 | 3 | describe('PatternParser', function() { 4 | it('should parse single-character patterns', function() { 5 | new PatternParser().parse('2').should.deep.equal('2') 6 | }) 7 | 8 | it('should parse string patterns', function() { 9 | new PatternParser().parse('123').should.deep.equal(['1', '2', '3']) 10 | }) 11 | 12 | it('should parse "one of" patterns', function() { 13 | new PatternParser().parse('[5-9]').should.deep.equal({ 14 | op: '[]', 15 | args: ['5', '6', '7', '8', '9'] 16 | }) 17 | }) 18 | 19 | it('should parse "or" patterns', function() { 20 | new PatternParser().parse('123|[5-9]').should.deep.equal({ 21 | op: '|', 22 | args: [ 23 | ['1', '2', '3'], 24 | { 25 | op: '[]', 26 | args: ['5', '6', '7', '8', '9'] 27 | } 28 | ] 29 | }) 30 | 31 | new PatternParser().parse('123|[5-9]0').should.deep.equal({ 32 | op: '|', 33 | args: [ 34 | ['1', '2', '3'], 35 | [ 36 | { 37 | op: '[]', 38 | args: ['5', '6', '7', '8', '9'] 39 | }, 40 | '0' 41 | ] 42 | ] 43 | }) 44 | }) 45 | 46 | it('should parse nested "or" patterns', function() { 47 | new PatternParser().parse('123|(?:2|34)[5-9]').should.deep.equal({ 48 | op: '|', 49 | args: [ 50 | ['1', '2', '3'], 51 | [ 52 | { 53 | op: '|', 54 | args: [ 55 | '2', 56 | ['3', '4'] 57 | ] 58 | }, 59 | { 60 | op: '[]', 61 | args: ['5', '6', '7', '8', '9'] 62 | } 63 | ] 64 | ] 65 | }) 66 | }) 67 | }) -------------------------------------------------------------------------------- /source/AsYouTypeFormatter.util.test.js: -------------------------------------------------------------------------------- 1 | import { closeNonPairedParens, stripNonPairedParens, repeat } from './AsYouTypeFormatter.util.js' 2 | 3 | describe('closeNonPairedParens', () => { 4 | it('should close non-paired braces', () => { 5 | closeNonPairedParens('(000) 123-45 (9 )', 15).should.equal('(000) 123-45 (9 )') 6 | }) 7 | }) 8 | 9 | describe('stripNonPairedParens', () => { 10 | it('should strip non-paired braces', () => { 11 | stripNonPairedParens('(000) 123-45 (9').should.equal('(000) 123-45 9') 12 | stripNonPairedParens('(000) 123-45 (9)').should.equal('(000) 123-45 (9)') 13 | }) 14 | }) 15 | 16 | describe('repeat', () => { 17 | it('should repeat string N times', () => { 18 | repeat('a', 0).should.equal('') 19 | repeat('a', 3).should.equal('aaa') 20 | repeat('a', 4).should.equal('aaaa') 21 | }) 22 | }) -------------------------------------------------------------------------------- /source/AsYouTypeState.js: -------------------------------------------------------------------------------- 1 | // This "state" object simply holds the state of the "AsYouType" parser: 2 | // 3 | // * `country?: string` 4 | // * `callingCode?: string` 5 | // * `digits: string` 6 | // * `international: boolean` 7 | // * `missingPlus: boolean` 8 | // * `IDDPrefix?: string` 9 | // * `carrierCode?: string` 10 | // * `nationalPrefix?: string` 11 | // * `nationalSignificantNumber?: string` 12 | // * `nationalSignificantNumberMatchesInput: boolean` 13 | // * `complexPrefixBeforeNationalSignificantNumber?: string` 14 | // 15 | // `state.country` and `state.callingCode` aren't required to be in sync. 16 | // For example, `state.country` could be `"AR"` and `state.callingCode` could be `undefined`. 17 | // So `state.country` and `state.callingCode` are totally independent. 18 | // 19 | export default class AsYouTypeState { 20 | constructor({ onCountryChange, onCallingCodeChange }) { 21 | this.onCountryChange = onCountryChange 22 | this.onCallingCodeChange = onCallingCodeChange 23 | } 24 | 25 | reset({ country, callingCode }) { 26 | this.international = false 27 | this.missingPlus = false 28 | this.IDDPrefix = undefined 29 | this.callingCode = undefined 30 | this.digits = '' 31 | this.resetNationalSignificantNumber() 32 | this.initCountryAndCallingCode(country, callingCode) 33 | } 34 | 35 | resetNationalSignificantNumber() { 36 | this.nationalSignificantNumber = this.getNationalDigits() 37 | this.nationalSignificantNumberMatchesInput = true 38 | this.nationalPrefix = undefined 39 | this.carrierCode = undefined 40 | this.complexPrefixBeforeNationalSignificantNumber = undefined 41 | } 42 | 43 | update(properties) { 44 | for (const key of Object.keys(properties)) { 45 | this[key] = properties[key] 46 | } 47 | } 48 | 49 | initCountryAndCallingCode(country, callingCode) { 50 | this.setCountry(country) 51 | this.setCallingCode(callingCode) 52 | } 53 | 54 | setCountry(country) { 55 | this.country = country 56 | this.onCountryChange(country) 57 | } 58 | 59 | setCallingCode(callingCode) { 60 | this.callingCode = callingCode 61 | this.onCallingCodeChange(callingCode, this.country) 62 | } 63 | 64 | startInternationalNumber(country, callingCode) { 65 | // Prepend the `+` to parsed input. 66 | this.international = true 67 | // If a default country was set then reset it 68 | // because an explicitly international phone 69 | // number is being entered. 70 | this.initCountryAndCallingCode(country, callingCode) 71 | } 72 | 73 | appendDigits(nextDigits) { 74 | this.digits += nextDigits 75 | } 76 | 77 | appendNationalSignificantNumberDigits(nextDigits) { 78 | this.nationalSignificantNumber += nextDigits 79 | } 80 | 81 | /** 82 | * Returns the part of `this.digits` that corresponds to the national number. 83 | * Basically, all digits that have been input by the user, except for the 84 | * international prefix and the country calling code part 85 | * (if the number is an international one). 86 | * @return {string} 87 | */ 88 | getNationalDigits() { 89 | if (this.international) { 90 | return this.digits.slice( 91 | (this.IDDPrefix ? this.IDDPrefix.length : 0) + 92 | (this.callingCode ? this.callingCode.length : 0) 93 | ) 94 | } 95 | return this.digits 96 | } 97 | 98 | getDigitsWithoutInternationalPrefix() { 99 | if (this.international) { 100 | if (this.IDDPrefix) { 101 | return this.digits.slice(this.IDDPrefix.length) 102 | } 103 | } 104 | return this.digits 105 | } 106 | } -------------------------------------------------------------------------------- /source/ParseError.js: -------------------------------------------------------------------------------- 1 | // https://stackoverflow.com/a/46971044/970769 2 | // "Breaking changes in Typescript 2.1" 3 | // "Extending built-ins like Error, Array, and Map may no longer work." 4 | // "As a recommendation, you can manually adjust the prototype immediately after any super(...) calls." 5 | // https://github.com/Microsoft/TypeScript-wiki/blob/main/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work 6 | export default class ParseError extends Error { 7 | constructor(code) { 8 | super(code) 9 | // Set the prototype explicitly. 10 | // Any subclass of FooError will have to manually set the prototype as well. 11 | Object.setPrototypeOf(this, ParseError.prototype) 12 | this.name = this.constructor.name 13 | } 14 | } -------------------------------------------------------------------------------- /source/PhoneNumberMatcher.test.js: -------------------------------------------------------------------------------- 1 | import PhoneNumberMatcher from './PhoneNumberMatcher.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | function test(text, defaultCountry, expectedNumbers) { 5 | if (typeof expectedNumbers === 'string') { 6 | expectedNumbers = [{ 7 | nationalNumber: expectedNumbers 8 | }] 9 | } 10 | const matcher = new PhoneNumberMatcher(text, { defaultCountry, v2: true }, metadata) 11 | while (matcher.hasNext()) { 12 | const number = matcher.next() 13 | const phoneNumber = expectedNumbers.shift() 14 | if (phoneNumber.startsAt !== undefined) { 15 | number.startsAt.should.equal(phoneNumber.startsAt) 16 | } 17 | if (phoneNumber.endsAt !== undefined) { 18 | number.endsAt.should.equal(phoneNumber.endsAt) 19 | } 20 | number.number.country.should.equal(phoneNumber.country || defaultCountry) 21 | number.number.nationalNumber.should.equal(phoneNumber.nationalNumber) 22 | } 23 | expectedNumbers.length.should.equal(0) 24 | } 25 | 26 | describe('PhoneNumberMatcher', () => { 27 | it('should find phone numbers', () => { 28 | test( 29 | 'The number is +7 (800) 555-35-35 and not (213) 373-4253 as written in the document.', 30 | 'US', 31 | [{ 32 | country: 'RU', 33 | nationalNumber: '8005553535', 34 | startsAt: 14, 35 | endsAt: 32 36 | }, { 37 | country: 'US', 38 | nationalNumber: '2133734253', 39 | startsAt: 41, 40 | endsAt: 55 41 | }] 42 | ) 43 | }) 44 | 45 | it('should find phone numbers from Mexico', () => { 46 | // Test parsing fixed-line numbers of Mexico. 47 | test('+52 (449)978-0001', 'MX', '4499780001') 48 | test('01 (449)978-0001', 'MX', '4499780001') 49 | test('(449)978-0001', 'MX', '4499780001') 50 | 51 | // "Dialling tokens 01, 02, 044, 045 and 1 are removed as they are 52 | // no longer valid since August 2019." 53 | // // Test parsing mobile numbers of Mexico. 54 | // test('+52 1 33 1234-5678', 'MX', '3312345678') 55 | // test('044 (33) 1234-5678', 'MX', '3312345678') 56 | // test('045 33 1234-5678', 'MX', '3312345678') 57 | }) 58 | 59 | it('should find phone numbers from Argentina', () => { 60 | // Test parsing mobile numbers of Argentina. 61 | test('+54 9 343 555 1212', 'AR', '93435551212') 62 | test('0343 15-555-1212', 'AR', '93435551212') 63 | 64 | test('+54 9 3715 65 4320', 'AR', '93715654320') 65 | test('03715 15 65 4320', 'AR', '93715654320') 66 | 67 | // Test parsing fixed-line numbers of Argentina. 68 | test('+54 11 3797 0000', 'AR', '1137970000') 69 | test('011 3797 0000', 'AR', '1137970000') 70 | 71 | test('+54 3715 65 4321', 'AR', '3715654321') 72 | test('03715 65 4321', 'AR', '3715654321') 73 | 74 | test('+54 23 1234 0000', 'AR', '2312340000') 75 | test('023 1234 0000', 'AR', '2312340000') 76 | }) 77 | 78 | it('should only support the supported leniency values', function() { 79 | expect(() => new PhoneNumberMatcher('+54 23 1234 0000', { leniency: 'STRICT_GROUPING', v2: true }, metadata)).to.throw('Supported values: "POSSIBLE", "VALID".') 80 | }) 81 | }) -------------------------------------------------------------------------------- /source/constants.js: -------------------------------------------------------------------------------- 1 | // The minimum length of the national significant number. 2 | export const MIN_LENGTH_FOR_NSN = 2 3 | 4 | // The ITU says the maximum length should be 15, 5 | // but one can find longer numbers in Germany. 6 | export const MAX_LENGTH_FOR_NSN = 17 7 | 8 | // The maximum length of the country calling code. 9 | export const MAX_LENGTH_COUNTRY_CODE = 3 10 | 11 | // Digits accepted in phone numbers 12 | // (ascii, fullwidth, arabic-indic, and eastern arabic digits). 13 | export const VALID_DIGITS = '0-9\uFF10-\uFF19\u0660-\u0669\u06F0-\u06F9' 14 | 15 | // `DASHES` will be right after the opening square bracket of the "character class" 16 | const DASHES = '-\u2010-\u2015\u2212\u30FC\uFF0D' 17 | const SLASHES = '\uFF0F/' 18 | const DOTS = '\uFF0E.' 19 | export const WHITESPACE = ' \u00A0\u00AD\u200B\u2060\u3000' 20 | const BRACKETS = '()\uFF08\uFF09\uFF3B\uFF3D\\[\\]' 21 | // export const OPENING_BRACKETS = '(\uFF08\uFF3B\\\[' 22 | const TILDES = '~\u2053\u223C\uFF5E' 23 | 24 | // Regular expression of acceptable punctuation found in phone numbers. This 25 | // excludes punctuation found as a leading character only. This consists of dash 26 | // characters, white space characters, full stops, slashes, square brackets, 27 | // parentheses and tildes. Full-width variants are also present. 28 | export const VALID_PUNCTUATION = `${DASHES}${SLASHES}${DOTS}${WHITESPACE}${BRACKETS}${TILDES}` 29 | 30 | export const PLUS_CHARS = '+\uFF0B' 31 | // const LEADING_PLUS_CHARS_PATTERN = new RegExp('^[' + PLUS_CHARS + ']+') -------------------------------------------------------------------------------- /source/findNumbers/LRUCache.js: -------------------------------------------------------------------------------- 1 | // https://medium.com/dsinjs/implementing-lru-cache-in-javascript-94ba6755cda9 2 | 3 | class Node { 4 | constructor(key, value, next = null, prev = null) { 5 | this.key = key; 6 | this.value = value; 7 | this.next = next; 8 | this.prev = prev; 9 | } 10 | } 11 | 12 | export default class LRUCache { 13 | //set default limit of 10 if limit is not passed. 14 | constructor(limit = 10) { 15 | this.size = 0; 16 | this.limit = limit; 17 | this.head = null; 18 | this.tail = null; 19 | this.cache = {}; 20 | } 21 | 22 | // Write Node to head of LinkedList 23 | // update cache with Node key and Node reference 24 | put(key, value){ 25 | this.ensureLimit(); 26 | 27 | if(!this.head){ 28 | this.head = this.tail = new Node(key, value); 29 | }else{ 30 | const node = new Node(key, value, this.head); 31 | this.head.prev = node; 32 | this.head = node; 33 | } 34 | 35 | //Update the cache map 36 | this.cache[key] = this.head; 37 | this.size++; 38 | } 39 | 40 | // Read from cache map and make that node as new Head of LinkedList 41 | get(key){ 42 | if(this.cache[key]){ 43 | const value = this.cache[key].value; 44 | 45 | // node removed from it's position and cache 46 | this.remove(key) 47 | // write node again to the head of LinkedList to make it most recently used 48 | this.put(key, value); 49 | 50 | return value; 51 | } 52 | 53 | console.log(`Item not available in cache for key ${key}`); 54 | } 55 | 56 | ensureLimit(){ 57 | if(this.size === this.limit){ 58 | this.remove(this.tail.key) 59 | } 60 | } 61 | 62 | remove(key){ 63 | const node = this.cache[key]; 64 | 65 | if(node.prev !== null){ 66 | node.prev.next = node.next; 67 | }else{ 68 | this.head = node.next; 69 | } 70 | 71 | if(node.next !== null){ 72 | node.next.prev = node.prev; 73 | }else{ 74 | this.tail = node.prev 75 | } 76 | 77 | delete this.cache[key]; 78 | this.size--; 79 | } 80 | 81 | clear() { 82 | this.head = null; 83 | this.tail = null; 84 | this.size = 0; 85 | this.cache = {}; 86 | } 87 | 88 | // // Invokes the callback function with every node of the chain and the index of the node. 89 | // forEach(fn) { 90 | // let node = this.head; 91 | // let counter = 0; 92 | // while (node) { 93 | // fn(node, counter); 94 | // node = node.next; 95 | // counter++; 96 | // } 97 | // } 98 | 99 | // // To iterate over LRU with a 'for...of' loop 100 | // *[Symbol.iterator]() { 101 | // let node = this.head; 102 | // while (node) { 103 | // yield node; 104 | // node = node.next; 105 | // } 106 | // } 107 | } -------------------------------------------------------------------------------- /source/findNumbers/Leniency.test.js: -------------------------------------------------------------------------------- 1 | /* 2 | import { containsMoreThanOneSlashInNationalNumber } from './Leniency.js' 3 | 4 | describe('Leniency', () => { 5 | it('testContainsMoreThanOneSlashInNationalNumber', () => { 6 | // A date should return true. 7 | number.setCountryCode(1) 8 | number.setCountryCodeSource(CountryCodeSource.FROM_DEFAULT_COUNTRY) 9 | containsMoreThanOneSlashInNationalNumber(number, '1/05/2013').should.equal(true) 10 | 11 | // Here, the country code source thinks it started with a country calling code, but this is not 12 | // the same as the part before the slash, so it's still true. 13 | number.setCountryCode(274) 14 | number.setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITHOUT_PLUS_SIGN) 15 | containsMoreThanOneSlashInNationalNumber(number, '27/4/2013').should.equal(true) 16 | 17 | // Now it should be false, because the first slash is after the country calling code. 18 | number.setCountryCode(49) 19 | number.setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITH_PLUS_SIGN) 20 | containsMoreThanOneSlashInNationalNumber(number, '49/69/2013').should.equal(false) 21 | 22 | number.setCountryCode(49) 23 | number.setCountryCodeSource(CountryCodeSource.FROM_NUMBER_WITHOUT_PLUS_SIGN) 24 | containsMoreThanOneSlashInNationalNumber(number, '+49/69/2013').should.equal(false) 25 | containsMoreThanOneSlashInNationalNumber(number, '+ 49/69/2013').should.equal(false) 26 | containsMoreThanOneSlashInNationalNumber(number, '+ 49/69/20/13').should.equal(true) 27 | 28 | // Here, the first group is not assumed to be the country calling code, even though it is the 29 | // same as it, so this should return true. 30 | number.setCountryCode(49) 31 | number.setCountryCodeSource(CountryCodeSource.FROM_DEFAULT_COUNTRY) 32 | containsMoreThanOneSlashInNationalNumber(number, '49/69/2013').should.equal(true) 33 | }) 34 | }) 35 | */ -------------------------------------------------------------------------------- /source/findNumbers/RegExpCache.js: -------------------------------------------------------------------------------- 1 | import LRUCache from './LRUCache.js' 2 | 3 | // A cache for frequently used country-specific regular expressions. Set to 32 to cover ~2-3 4 | // countries being used for the same doc with ~10 patterns for each country. Some pages will have 5 | // a lot more countries in use, but typically fewer numbers for each so expanding the cache for 6 | // that use-case won't have a lot of benefit. 7 | export default class RegExpCache { 8 | constructor(size) { 9 | this.cache = new LRUCache(size) 10 | } 11 | 12 | getPatternForRegExp(pattern) { 13 | let regExp = this.cache.get(pattern) 14 | if (!regExp) { 15 | regExp = new RegExp('^' + pattern) 16 | this.cache.put(pattern, regExp) 17 | } 18 | return regExp 19 | } 20 | } -------------------------------------------------------------------------------- /source/findNumbers/isValidCandidate.js: -------------------------------------------------------------------------------- 1 | // Copy-pasted from `PhoneNumberMatcher.js`. 2 | 3 | import { PLUS_CHARS } from '../constants.js' 4 | import { limit } from './util.js' 5 | 6 | import { 7 | isLatinLetter, 8 | isInvalidPunctuationSymbol 9 | } from './utf-8.js' 10 | 11 | const OPENING_PARENS = '(\\[\uFF08\uFF3B' 12 | const CLOSING_PARENS = ')\\]\uFF09\uFF3D' 13 | const NON_PARENS = `[^${OPENING_PARENS}${CLOSING_PARENS}]` 14 | 15 | export const LEAD_CLASS = `[${OPENING_PARENS}${PLUS_CHARS}]` 16 | 17 | // Punctuation that may be at the start of a phone number - brackets and plus signs. 18 | const LEAD_CLASS_LEADING = new RegExp('^' + LEAD_CLASS) 19 | 20 | // Limit on the number of pairs of brackets in a phone number. 21 | const BRACKET_PAIR_LIMIT = limit(0, 3) 22 | 23 | /** 24 | * Pattern to check that brackets match. Opening brackets should be closed within a phone number. 25 | * This also checks that there is something inside the brackets. Having no brackets at all is also 26 | * fine. 27 | * 28 | * An opening bracket at the beginning may not be closed, but subsequent ones should be. It's 29 | * also possible that the leading bracket was dropped, so we shouldn't be surprised if we see a 30 | * closing bracket first. We limit the sets of brackets in a phone number to four. 31 | */ 32 | const MATCHING_BRACKETS_ENTIRE = new RegExp 33 | ( 34 | '^' 35 | + "(?:[" + OPENING_PARENS + "])?" + "(?:" + NON_PARENS + "+" + "[" + CLOSING_PARENS + "])?" 36 | + NON_PARENS + "+" 37 | + "(?:[" + OPENING_PARENS + "]" + NON_PARENS + "+[" + CLOSING_PARENS + "])" + BRACKET_PAIR_LIMIT 38 | + NON_PARENS + "*" 39 | + '$' 40 | ) 41 | 42 | /** 43 | * Matches strings that look like publication pages. Example: 44 | *
Computing Complete Answers to Queries in the Presence of Limited Access Patterns.
45 |  * Chen Li. VLDB J. 12(3): 211-227 (2003).
46 | * 47 | * The string "211-227 (2003)" is not a telephone number. 48 | */ 49 | const PUB_PAGES = /\d{1,5}-+\d{1,5}\s{0,4}\(\d{1,4}/ 50 | 51 | export default function isValidCandidate(candidate, offset, text, leniency) 52 | { 53 | // Check the candidate doesn't contain any formatting 54 | // which would indicate that it really isn't a phone number. 55 | if (!MATCHING_BRACKETS_ENTIRE.test(candidate) || PUB_PAGES.test(candidate)) { 56 | return 57 | } 58 | 59 | // If leniency is set to VALID or stricter, we also want to skip numbers that are surrounded 60 | // by Latin alphabetic characters, to skip cases like abc8005001234 or 8005001234def. 61 | if (leniency !== 'POSSIBLE') 62 | { 63 | // If the candidate is not at the start of the text, 64 | // and does not start with phone-number punctuation, 65 | // check the previous character. 66 | if (offset > 0 && !LEAD_CLASS_LEADING.test(candidate)) 67 | { 68 | const previousChar = text[offset - 1] 69 | // We return null if it is a latin letter or an invalid punctuation symbol. 70 | if (isInvalidPunctuationSymbol(previousChar) || isLatinLetter(previousChar)) { 71 | return false 72 | } 73 | } 74 | 75 | const lastCharIndex = offset + candidate.length 76 | if (lastCharIndex < text.length) 77 | { 78 | const nextChar = text[lastCharIndex] 79 | if (isInvalidPunctuationSymbol(nextChar) || isLatinLetter(nextChar)) { 80 | return false 81 | } 82 | } 83 | } 84 | 85 | return true 86 | } -------------------------------------------------------------------------------- /source/findNumbers/isValidPreCandidate.js: -------------------------------------------------------------------------------- 1 | // Matches strings that look like dates using "/" as a separator. 2 | // Examples: 3/10/2011, 31/10/96 or 08/31/95. 3 | const SLASH_SEPARATED_DATES = /(?:(?:[0-3]?\d\/[01]?\d)|(?:[01]?\d\/[0-3]?\d))\/(?:[12]\d)?\d{2}/ 4 | 5 | // Matches timestamps. 6 | // Examples: "2012-01-02 08:00". 7 | // Note that the reg-ex does not include the 8 | // trailing ":\d\d" -- that is covered by TIME_STAMPS_SUFFIX. 9 | const TIME_STAMPS = /[12]\d{3}[-/]?[01]\d[-/]?[0-3]\d +[0-2]\d$/ 10 | const TIME_STAMPS_SUFFIX_LEADING = /^:[0-5]\d/ 11 | 12 | export default function isValidPreCandidate(candidate, offset, text) 13 | { 14 | // Skip a match that is more likely to be a date. 15 | if (SLASH_SEPARATED_DATES.test(candidate)) { 16 | return false 17 | } 18 | 19 | // Skip potential time-stamps. 20 | if (TIME_STAMPS.test(candidate)) 21 | { 22 | const followingText = text.slice(offset + candidate.length) 23 | if (TIME_STAMPS_SUFFIX_LEADING.test(followingText)) { 24 | return false 25 | } 26 | } 27 | 28 | return true 29 | } -------------------------------------------------------------------------------- /source/findNumbers/matchPhoneNumberStringAgainstPhoneNumber.js: -------------------------------------------------------------------------------- 1 | import parsePhoneNumber from '../parsePhoneNumber.js' 2 | 3 | /** 4 | * Matches a phone number object against a phone number string. 5 | * @param {string} phoneNumberString 6 | * @param {PhoneNumber} phoneNumber 7 | * @param {object} metadata — Metadata JSON 8 | * @return {'INVALID_NUMBER'|'NO_MATCH'|'SHORT_NSN_MATCH'|'NSN_MATCH'|'EXACT_MATCH'} 9 | */ 10 | export default function matchPhoneNumberStringAgainstPhoneNumber(phoneNumberString, phoneNumber, metadata) { 11 | // Parse `phoneNumberString`. 12 | let phoneNumberStringContainsCallingCode = true 13 | let parsedPhoneNumber = parsePhoneNumber(phoneNumberString, metadata) 14 | if (!parsedPhoneNumber) { 15 | // If `phoneNumberString` didn't contain a country calling code 16 | // then substitute it with the `phoneNumber`'s country calling code. 17 | phoneNumberStringContainsCallingCode = false 18 | parsedPhoneNumber = parsePhoneNumber(phoneNumberString, { defaultCallingCode: phoneNumber.countryCallingCode }, metadata) 19 | } 20 | if (!parsedPhoneNumber) { 21 | return 'INVALID_NUMBER' 22 | } 23 | 24 | // Check that the extensions match. 25 | if (phoneNumber.ext) { 26 | if (parsedPhoneNumber.ext !== phoneNumber.ext) { 27 | return 'NO_MATCH' 28 | } 29 | } else { 30 | if (parsedPhoneNumber.ext) { 31 | return 'NO_MATCH' 32 | } 33 | } 34 | 35 | // Check that country calling codes match. 36 | if (phoneNumberStringContainsCallingCode) { 37 | if (phoneNumber.countryCallingCode !== parsedPhoneNumber.countryCallingCode) { 38 | return 'NO_MATCH' 39 | } 40 | } 41 | 42 | // Check if the whole numbers match. 43 | if (phoneNumber.number === parsedPhoneNumber.number) { 44 | if (phoneNumberStringContainsCallingCode) { 45 | return 'EXACT_MATCH' 46 | } else { 47 | return 'NSN_MATCH' 48 | } 49 | } 50 | 51 | // Check if one national number is a "suffix" of the other. 52 | if ( 53 | phoneNumber.nationalNumber.indexOf(parsedPhoneNumber.nationalNumber) === 0 || 54 | parsedPhoneNumber.nationalNumber.indexOf(phoneNumber.nationalNumber) === 0 55 | ) { 56 | // "A SHORT_NSN_MATCH occurs if there is a difference because of the 57 | // presence or absence of an 'Italian leading zero', the presence or 58 | // absence of an extension, or one NSN being a shorter variant of the 59 | // other." 60 | return 'SHORT_NSN_MATCH' 61 | } 62 | 63 | return 'NO_MATCH' 64 | } -------------------------------------------------------------------------------- /source/findNumbers/parsePreCandidate.js: -------------------------------------------------------------------------------- 1 | import { trimAfterFirstMatch } from './util.js' 2 | 3 | // Regular expression of characters typically used to start a second phone number for the purposes 4 | // of parsing. This allows us to strip off parts of the number that are actually the start of 5 | // another number, such as for: (530) 583-6985 x302/x2303 -> the second extension here makes this 6 | // actually two phone numbers, (530) 583-6985 x302 and (530) 583-6985 x2303. We remove the second 7 | // extension so that the first number is parsed correctly. 8 | // 9 | // Matches a slash (\ or /) followed by a space followed by an `x`. 10 | // 11 | const SECOND_NUMBER_START_PATTERN = /[\\/] *x/ 12 | 13 | export default function parsePreCandidate(candidate) 14 | { 15 | // Check for extra numbers at the end. 16 | // TODO: This is the place to start when trying to support extraction of multiple phone number 17 | // from split notations (+41 79 123 45 67 / 68). 18 | return trimAfterFirstMatch(SECOND_NUMBER_START_PATTERN, candidate) 19 | } -------------------------------------------------------------------------------- /source/findNumbers/util.js: -------------------------------------------------------------------------------- 1 | /** Returns a regular expression quantifier with an upper and lower limit. */ 2 | export function limit(lower, upper) 3 | { 4 | if ((lower < 0) || (upper <= 0) || (upper < lower)) { 5 | throw new TypeError() 6 | } 7 | return `{${lower},${upper}}` 8 | } 9 | 10 | /** 11 | * Trims away any characters after the first match of {@code pattern} in {@code candidate}, 12 | * returning the trimmed version. 13 | */ 14 | export function trimAfterFirstMatch(regexp, string) 15 | { 16 | const index = string.search(regexp) 17 | 18 | if (index >= 0) { 19 | return string.slice(0, index) 20 | } 21 | 22 | return string 23 | } 24 | 25 | export function startsWith(string, substring) 26 | { 27 | return string.indexOf(substring) === 0 28 | } 29 | 30 | export function endsWith(string, substring) 31 | { 32 | return string.indexOf(substring, string.length - substring.length) === string.length - substring.length 33 | } 34 | -------------------------------------------------------------------------------- /source/findNumbers/util.test.js: -------------------------------------------------------------------------------- 1 | import { 2 | limit, 3 | trimAfterFirstMatch, 4 | startsWith, 5 | endsWith 6 | } from './util.js' 7 | 8 | describe('findNumbers/util', () => 9 | { 10 | it('should generate regexp limit', () => 11 | { 12 | let thrower = () => limit(1, 0) 13 | thrower.should.throw() 14 | 15 | thrower = () => limit(-1, 1) 16 | thrower.should.throw() 17 | 18 | thrower = () => limit(0, 0) 19 | thrower.should.throw() 20 | }) 21 | 22 | it('should trimAfterFirstMatch', () => 23 | { 24 | trimAfterFirstMatch(/\d/, 'abc123').should.equal('abc') 25 | trimAfterFirstMatch(/\d/, 'abc').should.equal('abc') 26 | }) 27 | 28 | it('should determine if a string starts with a substring', () => 29 | { 30 | startsWith('𐍈123', '𐍈').should.equal(true) 31 | startsWith('1𐍈', '𐍈').should.equal(false) 32 | }) 33 | 34 | it('should determine if a string ends with a substring', () => 35 | { 36 | endsWith('123𐍈', '𐍈').should.equal(true) 37 | endsWith('𐍈1', '𐍈').should.equal(false) 38 | }) 39 | }) -------------------------------------------------------------------------------- /source/findPhoneNumbersInText.js: -------------------------------------------------------------------------------- 1 | import PhoneNumberMatcher from './PhoneNumberMatcher.js' 2 | import normalizeArguments from './normalizeArguments.js' 3 | 4 | export default function findPhoneNumbersInText() { 5 | const { text, options, metadata } = normalizeArguments(arguments) 6 | const matcher = new PhoneNumberMatcher(text, { ...options, v2: true }, metadata) 7 | const results = [] 8 | while (matcher.hasNext()) { 9 | results.push(matcher.next()) 10 | } 11 | return results 12 | } -------------------------------------------------------------------------------- /source/formatIncompletePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import AsYouType from './AsYouType.js' 2 | 3 | /** 4 | * Formats a (possibly incomplete) phone number. 5 | * The phone number can be either in E.164 format 6 | * or in a form of national number digits. 7 | * @param {string} value - A possibly incomplete phone number. Either in E.164 format or in a form of national number digits. 8 | * @param {string|object} [optionsOrDefaultCountry] - A two-letter ("ISO 3166-1 alpha-2") country code, or an object of shape `{ defaultCountry?: string, defaultCallingCode?: string }`. 9 | * @return {string} Formatted (possibly incomplete) phone number. 10 | */ 11 | export default function formatIncompletePhoneNumber(value, optionsOrDefaultCountry, metadata) { 12 | if (!metadata) { 13 | metadata = optionsOrDefaultCountry 14 | optionsOrDefaultCountry = undefined 15 | } 16 | return new AsYouType(optionsOrDefaultCountry, metadata).input(value) 17 | } -------------------------------------------------------------------------------- /source/formatIncompletePhoneNumber.test.js: -------------------------------------------------------------------------------- 1 | import formatIncompletePhoneNumber from './formatIncompletePhoneNumber.js' 2 | 3 | import metadata from '../metadata.min.json' assert { type: 'json' } 4 | 5 | describe('formatIncompletePhoneNumber', () => { 6 | it('should format parsed input value', () => { 7 | let result 8 | 9 | // National input. 10 | formatIncompletePhoneNumber('880055535', 'RU', metadata).should.equal('8 (800) 555-35') 11 | 12 | // International input, no country. 13 | formatIncompletePhoneNumber('+780055535', null, metadata).should.equal('+7 800 555 35') 14 | 15 | // International input, no country argument. 16 | formatIncompletePhoneNumber('+780055535', metadata).should.equal('+7 800 555 35') 17 | 18 | // International input, with country. 19 | formatIncompletePhoneNumber('+780055535', 'RU', metadata).should.equal('+7 800 555 35') 20 | }) 21 | 22 | it('should support an object argument', () => { 23 | formatIncompletePhoneNumber('880055535', { defaultCountry: 'RU' }, metadata).should.equal('8 (800) 555-35') 24 | formatIncompletePhoneNumber('880055535', { defaultCallingCode: '7' }, metadata).should.equal('8 (800) 555-35') 25 | }) 26 | }) -------------------------------------------------------------------------------- /source/formatPhoneNumberForMobileDialing.test.js: -------------------------------------------------------------------------------- 1 | // Google's tests: 2 | // https://github.com/googlei18n/libphonenumber/blob/597983dc4d56ed7e5337a8e74316dc7a3d02d794/javascript/i18n/phonenumbers/phonenumberutil_test.js 3 | 4 | // import metadata from '../metadata.min.json' assert { type: 'json' } 5 | 6 | // import formatPhoneNumberForMobileDialing from './formatPhoneNumberForMobileDialing.js' 7 | 8 | // describe('formatPhoneNumberForMobileDialing', () => 9 | // { 10 | // it('should format for mobile dialing', () => 11 | // { 12 | // formatPhoneNumberForMobileDialing({ phone: '8005553535', country: 'RU' }, 'US', true, metadata).should.equal('+7 800 555 3535') 13 | // formatPhoneNumberForMobileDialing({ phone: '8005553535', country: 'RU' }, 'US', false, metadata).should.equal('+78005553535') 14 | // formatPhoneNumberForMobileDialing({ phone: '8005553535', country: 'RU' }, 'RU', false, metadata).should.equal('8005553535') 15 | // }) 16 | // }) -------------------------------------------------------------------------------- /source/getCountries.js: -------------------------------------------------------------------------------- 1 | import Metadata from './metadata.js' 2 | 3 | export default function getCountries(metadata) { 4 | return new Metadata(metadata).getCountries() 5 | } -------------------------------------------------------------------------------- /source/getCountries.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../metadata.min.json' assert { type: 'json' } 2 | 3 | import getCountries from './getCountries.js' 4 | 5 | describe('getCountries', () => { 6 | it('should get countries list', () => { 7 | expect(getCountries(metadata).indexOf('RU') > 0).to.be.true; 8 | }) 9 | }) -------------------------------------------------------------------------------- /source/getCountryCallingCode.js: -------------------------------------------------------------------------------- 1 | // Deprecated. Import from 'metadata.js' directly instead. 2 | export { getCountryCallingCode as default } from './metadata.js' -------------------------------------------------------------------------------- /source/getCountryCallingCode.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../metadata.min.json' assert { type: 'json' } 2 | 3 | import getCountryCallingCode from './getCountryCallingCode.js' 4 | 5 | describe('getCountryCallingCode', () => { 6 | it('should get country calling code', () => { 7 | getCountryCallingCode('US', metadata).should.equal('1') 8 | }) 9 | 10 | it('should throw if country is unknown', () => { 11 | expect(() => getCountryCallingCode('ZZ', metadata)).to.throw('Unknown country: ZZ') 12 | }) 13 | }) -------------------------------------------------------------------------------- /source/getExampleNumber.js: -------------------------------------------------------------------------------- 1 | import PhoneNumber from './PhoneNumber.js' 2 | 3 | export default function getExampleNumber(country, examples, metadata) { 4 | if (examples[country]) { 5 | return new PhoneNumber(country, examples[country], metadata) 6 | } 7 | } -------------------------------------------------------------------------------- /source/getExampleNumber.test.js: -------------------------------------------------------------------------------- 1 | import examples from '../examples.mobile.json' assert { type: 'json' } 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | import getExampleNumber from './getExampleNumber.js' 4 | 5 | describe('getExampleNumber', () => { 6 | it('should get an example number', () => { 7 | const phoneNumber = getExampleNumber('RU', examples, metadata) 8 | phoneNumber.nationalNumber.should.equal('9123456789') 9 | phoneNumber.number.should.equal('+79123456789') 10 | phoneNumber.countryCallingCode.should.equal('7') 11 | phoneNumber.country.should.equal('RU') 12 | }) 13 | 14 | it('should handle a non-existing country', () => { 15 | expect(getExampleNumber('XX', examples, metadata)).to.be.undefined 16 | }) 17 | }) -------------------------------------------------------------------------------- /source/helpers/RFC3966.js: -------------------------------------------------------------------------------- 1 | import isViablePhoneNumber from './isViablePhoneNumber.js' 2 | 3 | // https://www.ietf.org/rfc/rfc3966.txt 4 | 5 | /** 6 | * @param {string} text - Phone URI (RFC 3966). 7 | * @return {object} `{ ?number, ?ext }`. 8 | */ 9 | export function parseRFC3966(text) { 10 | let number 11 | let ext 12 | 13 | // Replace "tel:" with "tel=" for parsing convenience. 14 | text = text.replace(/^tel:/, 'tel=') 15 | 16 | for (const part of text.split(';')) { 17 | const [name, value] = part.split('=') 18 | switch (name) { 19 | case 'tel': 20 | number = value 21 | break 22 | case 'ext': 23 | ext = value 24 | break 25 | case 'phone-context': 26 | // Only "country contexts" are supported. 27 | // "Domain contexts" are ignored. 28 | if (value[0] === '+') { 29 | number = value + number 30 | } 31 | break 32 | } 33 | } 34 | 35 | // If the phone number is not viable, then abort. 36 | if (!isViablePhoneNumber(number)) { 37 | return {} 38 | } 39 | 40 | const result = { number } 41 | if (ext) { 42 | result.ext = ext 43 | } 44 | return result 45 | } 46 | 47 | /** 48 | * @param {object} - `{ ?number, ?extension }`. 49 | * @return {string} Phone URI (RFC 3966). 50 | */ 51 | export function formatRFC3966({ number, ext }) { 52 | if (!number) { 53 | return '' 54 | } 55 | if (number[0] !== '+') { 56 | throw new Error(`"formatRFC3966()" expects "number" to be in E.164 format.`) 57 | } 58 | return `tel:${number}${ext ? ';ext=' + ext : ''}` 59 | } -------------------------------------------------------------------------------- /source/helpers/RFC3966.test.js: -------------------------------------------------------------------------------- 1 | import { parseRFC3966, formatRFC3966 } from './RFC3966.js' 2 | 3 | describe('RFC3966', () => { 4 | it('should format', () => { 5 | expect(() => formatRFC3966({ number: '123' })).to.throw('expects "number" to be in E.164 format') 6 | formatRFC3966({}).should.equal('') 7 | formatRFC3966({ number: '+78005553535' }).should.equal('tel:+78005553535') 8 | formatRFC3966({ number: '+78005553535', ext: '123' }).should.equal('tel:+78005553535;ext=123') 9 | }) 10 | 11 | it('should parse', () => { 12 | parseRFC3966('tel:+78005553535').should.deep.equal({ 13 | number : '+78005553535' 14 | }) 15 | 16 | parseRFC3966('tel:+78005553535;ext=123').should.deep.equal({ 17 | number : '+78005553535', 18 | ext : '123' 19 | }) 20 | 21 | // With `phone-context` 22 | parseRFC3966('tel:8005553535;ext=123;phone-context=+7').should.deep.equal({ 23 | number : '+78005553535', 24 | ext : '123' 25 | }) 26 | 27 | // "Domain contexts" are ignored 28 | parseRFC3966('tel:8005553535;ext=123;phone-context=www.leningrad.spb.ru').should.deep.equal({ 29 | number : '8005553535', 30 | ext : '123' 31 | }) 32 | 33 | // Not a viable phone number. 34 | parseRFC3966('tel:3').should.deep.equal({}) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /source/helpers/applyInternationalSeparatorStyle.js: -------------------------------------------------------------------------------- 1 | import { VALID_PUNCTUATION } from '../constants.js' 2 | 3 | // Removes brackets and replaces dashes with spaces. 4 | // 5 | // E.g. "(999) 111-22-33" -> "999 111 22 33" 6 | // 7 | // For some reason Google's metadata contains ``s with brackets and dashes. 8 | // Meanwhile, there's no single opinion about using punctuation in international phone numbers. 9 | // 10 | // For example, Google's `` for USA is `+1 213-373-4253`. 11 | // And here's a quote from WikiPedia's "North American Numbering Plan" page: 12 | // https://en.wikipedia.org/wiki/North_American_Numbering_Plan 13 | // 14 | // "The country calling code for all countries participating in the NANP is 1. 15 | // In international format, an NANP number should be listed as +1 301 555 01 00, 16 | // where 301 is an area code (Maryland)." 17 | // 18 | // I personally prefer the international format without any punctuation. 19 | // For example, brackets are remnants of the old age, meaning that the 20 | // phone number part in brackets (so called "area code") can be omitted 21 | // if dialing within the same "area". 22 | // And hyphens were clearly introduced for splitting local numbers into memorizable groups. 23 | // For example, remembering "5553535" is difficult but "555-35-35" is much simpler. 24 | // Imagine a man taking a bus from home to work and seeing an ad with a phone number. 25 | // He has a couple of seconds to memorize that number until it passes by. 26 | // If it were spaces instead of hyphens the man wouldn't necessarily get it, 27 | // but with hyphens instead of spaces the grouping is more explicit. 28 | // I personally think that hyphens introduce visual clutter, 29 | // so I prefer replacing them with spaces in international numbers. 30 | // In the modern age all output is done on displays where spaces are clearly distinguishable 31 | // so hyphens can be safely replaced with spaces without losing any legibility. 32 | // 33 | export default function applyInternationalSeparatorStyle(formattedNumber) { 34 | return formattedNumber.replace(new RegExp(`[${VALID_PUNCTUATION}]+`, 'g'), ' ').trim() 35 | } -------------------------------------------------------------------------------- /source/helpers/applyInternationalSeparatorStyle.test.js: -------------------------------------------------------------------------------- 1 | import applyInternationalSeparatorStyle from './applyInternationalSeparatorStyle.js' 2 | 3 | describe('applyInternationalSeparatorStyle', () => { 4 | it('should change Google\'s international format style', () => { 5 | applyInternationalSeparatorStyle('(xxx) xxx-xx-xx').should.equal('xxx xxx xx xx') 6 | applyInternationalSeparatorStyle('(xxx)xxx').should.equal('xxx xxx') 7 | }) 8 | }) -------------------------------------------------------------------------------- /source/helpers/checkNumberLength.test.js: -------------------------------------------------------------------------------- 1 | import Metadata from '../metadata.js' 2 | import metadata from '../../metadata.max.json' assert { type: 'json' } 3 | import oldMetadata from '../../test/metadata/1.0.0/metadata.min.json' assert { type: 'json' } 4 | 5 | import { checkNumberLengthForType } from './checkNumberLength.js' 6 | 7 | describe('checkNumberLength', () => { 8 | it('should check phone number length', () => { 9 | // Too short. 10 | checkNumberLength('800555353', 'FIXED_LINE', 'RU').should.equal('TOO_SHORT') 11 | // Normal. 12 | checkNumberLength('8005553535', 'FIXED_LINE', 'RU').should.equal('IS_POSSIBLE') 13 | // Too long. 14 | checkNumberLength('80055535355', 'FIXED_LINE', 'RU').should.equal('TOO_LONG') 15 | 16 | // No such type. 17 | checkNumberLength('169454850', 'VOIP', 'AC').should.equal('INVALID_LENGTH') 18 | // No such possible length. 19 | checkNumberLength('1694548', undefined, 'AD').should.equal('INVALID_LENGTH') 20 | 21 | // FIXED_LINE_OR_MOBILE 22 | checkNumberLength('1694548', 'FIXED_LINE_OR_MOBILE', 'AD').should.equal('INVALID_LENGTH') 23 | // No mobile phones. 24 | checkNumberLength('8123', 'FIXED_LINE_OR_MOBILE', 'TA').should.equal('IS_POSSIBLE') 25 | // No "possible lengths" for "mobile". 26 | checkNumberLength('81234567', 'FIXED_LINE_OR_MOBILE', 'SZ').should.equal('IS_POSSIBLE') 27 | }) 28 | 29 | it('should work for old metadata', function() { 30 | const _oldMetadata = new Metadata(oldMetadata) 31 | _oldMetadata.country('RU') 32 | checkNumberLengthForType('8005553535', 'FIXED_LINE', _oldMetadata).should.equal('IS_POSSIBLE') 33 | }) 34 | }) 35 | 36 | function checkNumberLength(number, type, country) { 37 | const _metadata = new Metadata(metadata) 38 | _metadata.country(country) 39 | return checkNumberLengthForType(number, type, _metadata) 40 | } -------------------------------------------------------------------------------- /source/helpers/extension/extractExtension.js: -------------------------------------------------------------------------------- 1 | import createExtensionPattern from './createExtensionPattern.js' 2 | 3 | // Regexp of all known extension prefixes used by different regions followed by 4 | // 1 or more valid digits, for use when parsing. 5 | const EXTN_PATTERN = new RegExp('(?:' + createExtensionPattern() + ')$', 'i') 6 | 7 | // Strips any extension (as in, the part of the number dialled after the call is 8 | // connected, usually indicated with extn, ext, x or similar) from the end of 9 | // the number, and returns it. 10 | export default function extractExtension(number) { 11 | const start = number.search(EXTN_PATTERN) 12 | if (start < 0) { 13 | return {} 14 | } 15 | // If we find a potential extension, and the number preceding this is a viable 16 | // number, we assume it is an extension. 17 | const numberWithoutExtension = number.slice(0, start) 18 | const matches = number.match(EXTN_PATTERN) 19 | let i = 1 20 | while (i < matches.length) { 21 | if (matches[i]) { 22 | return { 23 | number: numberWithoutExtension, 24 | ext: matches[i] 25 | } 26 | } 27 | i++ 28 | } 29 | } -------------------------------------------------------------------------------- /source/helpers/extractCountryCallingCode.test.js: -------------------------------------------------------------------------------- 1 | import extractCountryCallingCode from './extractCountryCallingCode.js' 2 | import metadata from '../../metadata.min.json' assert { type: 'json' } 3 | 4 | describe('extractCountryCallingCode', () => { 5 | it('should extract country calling code from a number', () => { 6 | extractCountryCallingCode('+78005553535', null, null, metadata).should.deep.equal({ 7 | countryCallingCodeSource: 'FROM_NUMBER_WITH_PLUS_SIGN', 8 | countryCallingCode: '7', 9 | number: '8005553535' 10 | }) 11 | 12 | extractCountryCallingCode('+7800', null, null, metadata).should.deep.equal({ 13 | countryCallingCodeSource: 'FROM_NUMBER_WITH_PLUS_SIGN', 14 | countryCallingCode: '7', 15 | number: '800' 16 | }) 17 | 18 | extractCountryCallingCode('', null, null, metadata).should.deep.equal({}) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /source/helpers/extractCountryCallingCodeFromInternationalNumberWithoutPlusSign.js: -------------------------------------------------------------------------------- 1 | import Metadata from '../metadata.js' 2 | import matchesEntirely from './matchesEntirely.js' 3 | import extractNationalNumber from './extractNationalNumber.js' 4 | import checkNumberLength from './checkNumberLength.js' 5 | import getCountryCallingCode from '../getCountryCallingCode.js' 6 | 7 | /** 8 | * Sometimes some people incorrectly input international phone numbers 9 | * without the leading `+`. This function corrects such input. 10 | * @param {string} number — Phone number digits. 11 | * @param {string?} country 12 | * @param {string?} callingCode 13 | * @param {object} metadata 14 | * @return {object} `{ countryCallingCode: string?, number: string }`. 15 | */ 16 | export default function extractCountryCallingCodeFromInternationalNumberWithoutPlusSign( 17 | number, 18 | country, 19 | callingCode, 20 | metadata 21 | ) { 22 | const countryCallingCode = country ? getCountryCallingCode(country, metadata) : callingCode 23 | if (number.indexOf(countryCallingCode) === 0) { 24 | metadata = new Metadata(metadata) 25 | metadata.selectNumberingPlan(country, callingCode) 26 | const possibleShorterNumber = number.slice(countryCallingCode.length) 27 | const { 28 | nationalNumber: possibleShorterNationalNumber, 29 | } = extractNationalNumber( 30 | possibleShorterNumber, 31 | metadata 32 | ) 33 | const { 34 | nationalNumber 35 | } = extractNationalNumber( 36 | number, 37 | metadata 38 | ) 39 | // If the number was not valid before but is valid now, 40 | // or if it was too long before, we consider the number 41 | // with the country calling code stripped to be a better result 42 | // and keep that instead. 43 | // For example, in Germany (+49), `49` is a valid area code, 44 | // so if a number starts with `49`, it could be both a valid 45 | // national German number or an international number without 46 | // a leading `+`. 47 | if ( 48 | ( 49 | !matchesEntirely(nationalNumber, metadata.nationalNumberPattern()) 50 | && 51 | matchesEntirely(possibleShorterNationalNumber, metadata.nationalNumberPattern()) 52 | ) 53 | || 54 | checkNumberLength(nationalNumber, metadata) === 'TOO_LONG' 55 | ) { 56 | return { 57 | countryCallingCode, 58 | number: possibleShorterNumber 59 | } 60 | } 61 | } 62 | return { number } 63 | } -------------------------------------------------------------------------------- /source/helpers/extractFormattedPhoneNumberFromPossibleRfc3966NumberUri.js: -------------------------------------------------------------------------------- 1 | import extractPhoneContext, { 2 | isPhoneContextValid, 3 | PLUS_SIGN, 4 | RFC3966_PREFIX_, 5 | RFC3966_PHONE_CONTEXT_, 6 | RFC3966_ISDN_SUBADDRESS_ 7 | } from './extractPhoneContext.js' 8 | 9 | import ParseError from '../ParseError.js' 10 | 11 | /** 12 | * @param {string} numberToParse 13 | * @param {string} nationalNumber 14 | * @return {} 15 | */ 16 | export default function extractFormattedPhoneNumberFromPossibleRfc3966NumberUri(numberToParse, { 17 | extractFormattedPhoneNumber 18 | }) { 19 | const phoneContext = extractPhoneContext(numberToParse) 20 | if (!isPhoneContextValid(phoneContext)) { 21 | throw new ParseError('NOT_A_NUMBER') 22 | } 23 | 24 | let phoneNumberString 25 | 26 | if (phoneContext === null) { 27 | // Extract a possible number from the string passed in. 28 | // (this strips leading characters that could not be the start of a phone number) 29 | phoneNumberString = extractFormattedPhoneNumber(numberToParse) || '' 30 | } else { 31 | phoneNumberString = '' 32 | 33 | // If the phone context contains a phone number prefix, we need to capture 34 | // it, whereas domains will be ignored. 35 | if (phoneContext.charAt(0) === PLUS_SIGN) { 36 | phoneNumberString += phoneContext 37 | } 38 | 39 | // Now append everything between the "tel:" prefix and the phone-context. 40 | // This should include the national number, an optional extension or 41 | // isdn-subaddress component. Note we also handle the case when "tel:" is 42 | // missing, as we have seen in some of the phone number inputs. 43 | // In that case, we append everything from the beginning. 44 | const indexOfRfc3966Prefix = numberToParse.indexOf(RFC3966_PREFIX_) 45 | let indexOfNationalNumber 46 | // RFC 3966 "tel:" prefix is preset at this stage because 47 | // `isPhoneContextValid()` requires it to be present. 48 | /* istanbul ignore else */ 49 | if (indexOfRfc3966Prefix >= 0) { 50 | indexOfNationalNumber = indexOfRfc3966Prefix + RFC3966_PREFIX_.length 51 | } else { 52 | indexOfNationalNumber = 0 53 | } 54 | const indexOfPhoneContext = numberToParse.indexOf(RFC3966_PHONE_CONTEXT_) 55 | phoneNumberString += numberToParse.substring(indexOfNationalNumber, indexOfPhoneContext) 56 | } 57 | 58 | // Delete the isdn-subaddress and everything after it if it is present. 59 | // Note extension won't appear at the same time with isdn-subaddress 60 | // according to paragraph 5.3 of the RFC3966 spec. 61 | const indexOfIsdn = phoneNumberString.indexOf(RFC3966_ISDN_SUBADDRESS_) 62 | if (indexOfIsdn > 0) { 63 | phoneNumberString = phoneNumberString.substring(0, indexOfIsdn) 64 | } 65 | // If both phone context and isdn-subaddress are absent but other 66 | // parameters are present, the parameters are left in nationalNumber. 67 | // This is because we are concerned about deleting content from a potential 68 | // number string when there is no strong evidence that the number is 69 | // actually written in RFC3966. 70 | 71 | if (phoneNumberString !== '') { 72 | return phoneNumberString 73 | } 74 | } -------------------------------------------------------------------------------- /source/helpers/extractNationalNumber.test.js: -------------------------------------------------------------------------------- 1 | import extractNationalNumber from './extractNationalNumber.js' 2 | 3 | import Metadata from '../metadata.js' 4 | import oldMetadata from '../../test/metadata/1.0.0/metadata.min.json' assert { type: 'json' } 5 | 6 | describe('extractNationalNumber', function() { 7 | it('should extract a national number when using old metadata', function() { 8 | const _oldMetadata = new Metadata(oldMetadata) 9 | _oldMetadata.selectNumberingPlan('RU') 10 | extractNationalNumber('88005553535', _oldMetadata).should.deep.equal({ 11 | nationalNumber: '8005553535', 12 | carrierCode: undefined 13 | }) 14 | }) 15 | }) -------------------------------------------------------------------------------- /source/helpers/extractNationalNumberFromPossiblyIncompleteNumber.test.js: -------------------------------------------------------------------------------- 1 | import Metadata from '../metadata.js' 2 | import metadata from '../../metadata.min.json' assert { type: 'json' } 3 | import extractNationalNumberFromPossiblyIncompleteNumber from './extractNationalNumberFromPossiblyIncompleteNumber.js' 4 | 5 | describe('extractNationalNumberFromPossiblyIncompleteNumber', () => { 6 | it('should parse a carrier code when there is no national prefix transform rule', () => { 7 | const meta = new Metadata(metadata) 8 | meta.country('AU') 9 | extractNationalNumberFromPossiblyIncompleteNumber('18311800123', meta).should.deep.equal({ 10 | nationalPrefix: undefined, 11 | carrierCode: '1831', 12 | nationalNumber: '1800123' 13 | }) 14 | }) 15 | }) -------------------------------------------------------------------------------- /source/helpers/formatNationalNumberUsingFormat.js: -------------------------------------------------------------------------------- 1 | import applyInternationalSeparatorStyle from './applyInternationalSeparatorStyle.js' 2 | 3 | // This was originally set to $1 but there are some countries for which the 4 | // first group is not used in the national pattern (e.g. Argentina) so the $1 5 | // group does not match correctly. Therefore, we use `\d`, so that the first 6 | // group actually used in the pattern will be matched. 7 | export const FIRST_GROUP_PATTERN = /(\$\d)/ 8 | 9 | export default function formatNationalNumberUsingFormat( 10 | number, 11 | format, 12 | { 13 | useInternationalFormat, 14 | withNationalPrefix, 15 | carrierCode, 16 | metadata 17 | } 18 | ) { 19 | const formattedNumber = number.replace( 20 | new RegExp(format.pattern()), 21 | useInternationalFormat 22 | ? format.internationalFormat() 23 | : ( 24 | // This library doesn't use `domestic_carrier_code_formatting_rule`, 25 | // because that one is only used when formatting phone numbers 26 | // for dialing from a mobile phone, and this is not a dialing library. 27 | // carrierCode && format.domesticCarrierCodeFormattingRule() 28 | // // First, replace the $CC in the formatting rule with the desired carrier code. 29 | // // Then, replace the $FG in the formatting rule with the first group 30 | // // and the carrier code combined in the appropriate way. 31 | // ? format.format().replace(FIRST_GROUP_PATTERN, format.domesticCarrierCodeFormattingRule().replace('$CC', carrierCode)) 32 | // : ( 33 | // withNationalPrefix && format.nationalPrefixFormattingRule() 34 | // ? format.format().replace(FIRST_GROUP_PATTERN, format.nationalPrefixFormattingRule()) 35 | // : format.format() 36 | // ) 37 | withNationalPrefix && format.nationalPrefixFormattingRule() 38 | ? format.format().replace(FIRST_GROUP_PATTERN, format.nationalPrefixFormattingRule()) 39 | : format.format() 40 | ) 41 | ) 42 | if (useInternationalFormat) { 43 | return applyInternationalSeparatorStyle(formattedNumber) 44 | } 45 | return formattedNumber 46 | } -------------------------------------------------------------------------------- /source/helpers/getCountryByCallingCode.js: -------------------------------------------------------------------------------- 1 | import getCountryByNationalNumber from './getCountryByNationalNumber.js' 2 | 3 | const USE_NON_GEOGRAPHIC_COUNTRY_CODE = false 4 | 5 | export default function getCountryByCallingCode(callingCode, { 6 | nationalNumber: nationalPhoneNumber, 7 | defaultCountry, 8 | metadata 9 | }) { 10 | /* istanbul ignore if */ 11 | if (USE_NON_GEOGRAPHIC_COUNTRY_CODE) { 12 | if (metadata.isNonGeographicCallingCode(callingCode)) { 13 | return '001' 14 | } 15 | } 16 | const possibleCountries = metadata.getCountryCodesForCallingCode(callingCode) 17 | if (!possibleCountries) { 18 | return 19 | } 20 | // If there's just one country corresponding to the country code, 21 | // then just return it, without further phone number digits validation. 22 | if (possibleCountries.length === 1) { 23 | return possibleCountries[0] 24 | } 25 | return getCountryByNationalNumber(nationalPhoneNumber, { 26 | countries: possibleCountries, 27 | defaultCountry, 28 | metadata: metadata.metadata 29 | }) 30 | } -------------------------------------------------------------------------------- /source/helpers/getCountryByNationalNumber.js: -------------------------------------------------------------------------------- 1 | import Metadata from '../metadata.js' 2 | import getNumberType from './getNumberType.js' 3 | 4 | export default function getCountryByNationalNumber(nationalPhoneNumber, { 5 | countries, 6 | defaultCountry, 7 | metadata 8 | }) { 9 | // Re-create `metadata` because it will be selecting a `country`. 10 | metadata = new Metadata(metadata) 11 | 12 | // const matchingCountries = [] 13 | 14 | for (const country of countries) { 15 | metadata.country(country) 16 | // "Leading digits" patterns are only defined for about 20% of all countries. 17 | // By definition, matching "leading digits" is a sufficient but not a necessary 18 | // condition for a phone number to belong to a country. 19 | // The point of "leading digits" check is that it's the fastest one to get a match. 20 | // https://gitlab.com/catamphetamine/libphonenumber-js/blob/master/METADATA.md#leading_digits 21 | // I'd suppose that "leading digits" patterns are mutually exclusive for different countries 22 | // because of the intended use of that feature. 23 | if (metadata.leadingDigits()) { 24 | if (nationalPhoneNumber && 25 | nationalPhoneNumber.search(metadata.leadingDigits()) === 0) { 26 | return country 27 | } 28 | } 29 | // Else perform full validation with all of those 30 | // fixed-line/mobile/etc regular expressions. 31 | else if (getNumberType({ phone: nationalPhoneNumber, country }, undefined, metadata.metadata)) { 32 | // If both the `defaultCountry` and the "main" one match the phone number, 33 | // don't prefer the `defaultCountry` over the "main" one. 34 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/154 35 | return country 36 | // // If the `defaultCountry` is among the `matchingCountries` then return it. 37 | // if (defaultCountry) { 38 | // if (country === defaultCountry) { 39 | // return country 40 | // } 41 | // matchingCountries.push(country) 42 | // } else { 43 | // return country 44 | // } 45 | } 46 | } 47 | 48 | // // Return the first ("main") one of the `matchingCountries`. 49 | // if (matchingCountries.length > 0) { 50 | // return matchingCountries[0] 51 | // } 52 | } -------------------------------------------------------------------------------- /source/helpers/getIddPrefix.js: -------------------------------------------------------------------------------- 1 | import Metadata from '../metadata.js' 2 | 3 | /** 4 | * Pattern that makes it easy to distinguish whether a region has a single 5 | * international dialing prefix or not. If a region has a single international 6 | * prefix (e.g. 011 in USA), it will be represented as a string that contains 7 | * a sequence of ASCII digits, and possibly a tilde, which signals waiting for 8 | * the tone. If there are multiple available international prefixes in a 9 | * region, they will be represented as a regex string that always contains one 10 | * or more characters that are not ASCII digits or a tilde. 11 | */ 12 | const SINGLE_IDD_PREFIX_REG_EXP = /^[\d]+(?:[~\u2053\u223C\uFF5E][\d]+)?$/ 13 | 14 | // For regions that have multiple IDD prefixes 15 | // a preferred IDD prefix is returned. 16 | export default function getIddPrefix(country, callingCode, metadata) { 17 | const countryMetadata = new Metadata(metadata) 18 | countryMetadata.selectNumberingPlan(country, callingCode) 19 | if (countryMetadata.defaultIDDPrefix()) { 20 | return countryMetadata.defaultIDDPrefix() 21 | } 22 | if (SINGLE_IDD_PREFIX_REG_EXP.test(countryMetadata.IDDPrefix())) { 23 | return countryMetadata.IDDPrefix() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /source/helpers/getNumberType.test.js: -------------------------------------------------------------------------------- 1 | import getNumberType from './getNumberType.js' 2 | 3 | import oldMetadata from '../../test/metadata/1.0.0/metadata.min.json' assert { type: 'json' } 4 | 5 | import Metadata from '../metadata.js' 6 | 7 | describe('getNumberType', function() { 8 | it('should get number type when using old metadata', function() { 9 | getNumberType( 10 | { 11 | nationalNumber: '2133734253', 12 | country: 'US' 13 | }, 14 | { v2: true }, 15 | oldMetadata 16 | ).should.equal('FIXED_LINE_OR_MOBILE') 17 | }) 18 | 19 | it('should return `undefined` when the phone number is a malformed one', function() { 20 | expect(getNumberType( 21 | {}, 22 | { v2: true }, 23 | oldMetadata 24 | )).to.equal(undefined) 25 | }) 26 | }) -------------------------------------------------------------------------------- /source/helpers/getPossibleCountriesForNumber.js: -------------------------------------------------------------------------------- 1 | import Metadata from '../metadata.js' 2 | 3 | /** 4 | * Returns a list of countries that the phone number could potentially belong to. 5 | * @param {string} callingCode — Calling code. 6 | * @param {string} nationalNumber — National (significant) number. 7 | * @param {object} metadata — Metadata. 8 | * @return {string[]} A list of possible countries. 9 | */ 10 | export default function getPossibleCountriesForNumber(callingCode, nationalNumber, metadata) { 11 | const _metadata = new Metadata(metadata) 12 | let possibleCountries = _metadata.getCountryCodesForCallingCode(callingCode) 13 | if (!possibleCountries) { 14 | return [] 15 | } 16 | return possibleCountries.filter((country) => { 17 | return couldNationalNumberBelongToCountry(nationalNumber, country, metadata) 18 | }) 19 | } 20 | 21 | function couldNationalNumberBelongToCountry(nationalNumber, country, metadata) { 22 | const _metadata = new Metadata(metadata) 23 | _metadata.selectNumberingPlan(country) 24 | if (_metadata.numberingPlan.possibleLengths().indexOf(nationalNumber.length) >= 0) { 25 | return true 26 | } 27 | return false 28 | } -------------------------------------------------------------------------------- /source/helpers/isObject.js: -------------------------------------------------------------------------------- 1 | const objectConstructor = {}.constructor; 2 | 3 | export default function isObject(object) { 4 | return object !== undefined && object !== null && object.constructor === objectConstructor; 5 | } 6 | -------------------------------------------------------------------------------- /source/helpers/matchesEntirely.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Checks whether the entire input sequence can be matched 3 | * against the regular expression. 4 | * @return {boolean} 5 | */ 6 | export default function matchesEntirely(text, regular_expression) { 7 | // If assigning the `''` default value is moved to the arguments above, 8 | // code coverage would decrease for some weird reason. 9 | text = text || '' 10 | return new RegExp('^(?:' + regular_expression + ')$').test(text) 11 | } -------------------------------------------------------------------------------- /source/helpers/matchesEntirely.test.js: -------------------------------------------------------------------------------- 1 | import matchesEntirely from './matchesEntirely.js' 2 | 3 | describe('matchesEntirely', () => { 4 | it('should work in edge cases', () => { 5 | // No text. 6 | matchesEntirely(undefined, '').should.equal(true) 7 | 8 | // "OR" in regexp. 9 | matchesEntirely('911231231', '4\d{8}|[1-9]\d{7}').should.equal(false) 10 | }) 11 | }) -------------------------------------------------------------------------------- /source/helpers/mergeArrays.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Merges two arrays. 3 | * @param {*} a 4 | * @param {*} b 5 | * @return {*} 6 | */ 7 | export default function mergeArrays(a, b) { 8 | const merged = a.slice() 9 | 10 | for (const element of b) { 11 | if (a.indexOf(element) < 0) { 12 | merged.push(element) 13 | } 14 | } 15 | 16 | return merged.sort((a, b) => a - b) 17 | 18 | // ES6 version, requires Set polyfill. 19 | // let merged = new Set(a) 20 | // for (const element of b) { 21 | // merged.add(i) 22 | // } 23 | // return Array.from(merged).sort((a, b) => a - b) 24 | } -------------------------------------------------------------------------------- /source/helpers/mergeArrays.test.js: -------------------------------------------------------------------------------- 1 | import mergeArrays from './mergeArrays.js' 2 | 3 | describe('mergeArrays', () => { 4 | it('should merge arrays', () => { 5 | mergeArrays([1, 2], [2, 3]).should.deep.equal([1, 2, 3]) 6 | }) 7 | }) -------------------------------------------------------------------------------- /source/helpers/parseDigits.js: -------------------------------------------------------------------------------- 1 | // These mappings map a character (key) to a specific digit that should 2 | // replace it for normalization purposes. Non-European digits that 3 | // may be used in phone numbers are mapped to a European equivalent. 4 | // 5 | // E.g. in Iraq they don't write `+442323234` but rather `+٤٤٢٣٢٣٢٣٤`. 6 | // 7 | export const DIGITS = { 8 | '0': '0', 9 | '1': '1', 10 | '2': '2', 11 | '3': '3', 12 | '4': '4', 13 | '5': '5', 14 | '6': '6', 15 | '7': '7', 16 | '8': '8', 17 | '9': '9', 18 | '\uFF10': '0', // Fullwidth digit 0 19 | '\uFF11': '1', // Fullwidth digit 1 20 | '\uFF12': '2', // Fullwidth digit 2 21 | '\uFF13': '3', // Fullwidth digit 3 22 | '\uFF14': '4', // Fullwidth digit 4 23 | '\uFF15': '5', // Fullwidth digit 5 24 | '\uFF16': '6', // Fullwidth digit 6 25 | '\uFF17': '7', // Fullwidth digit 7 26 | '\uFF18': '8', // Fullwidth digit 8 27 | '\uFF19': '9', // Fullwidth digit 9 28 | '\u0660': '0', // Arabic-indic digit 0 29 | '\u0661': '1', // Arabic-indic digit 1 30 | '\u0662': '2', // Arabic-indic digit 2 31 | '\u0663': '3', // Arabic-indic digit 3 32 | '\u0664': '4', // Arabic-indic digit 4 33 | '\u0665': '5', // Arabic-indic digit 5 34 | '\u0666': '6', // Arabic-indic digit 6 35 | '\u0667': '7', // Arabic-indic digit 7 36 | '\u0668': '8', // Arabic-indic digit 8 37 | '\u0669': '9', // Arabic-indic digit 9 38 | '\u06F0': '0', // Eastern-Arabic digit 0 39 | '\u06F1': '1', // Eastern-Arabic digit 1 40 | '\u06F2': '2', // Eastern-Arabic digit 2 41 | '\u06F3': '3', // Eastern-Arabic digit 3 42 | '\u06F4': '4', // Eastern-Arabic digit 4 43 | '\u06F5': '5', // Eastern-Arabic digit 5 44 | '\u06F6': '6', // Eastern-Arabic digit 6 45 | '\u06F7': '7', // Eastern-Arabic digit 7 46 | '\u06F8': '8', // Eastern-Arabic digit 8 47 | '\u06F9': '9' // Eastern-Arabic digit 9 48 | } 49 | 50 | export function parseDigit(character) { 51 | return DIGITS[character] 52 | } 53 | 54 | /** 55 | * Parses phone number digits from a string. 56 | * Drops all punctuation leaving only digits. 57 | * Also converts wide-ascii and arabic-indic numerals to conventional numerals. 58 | * E.g. in Iraq they don't write `+442323234` but rather `+٤٤٢٣٢٣٢٣٤`. 59 | * @param {string} string 60 | * @return {string} 61 | * @example 62 | * ```js 63 | * parseDigits('8 (800) 555') 64 | * // Outputs '8800555'. 65 | * ``` 66 | */ 67 | export default function parseDigits(string) { 68 | let result = '' 69 | // Using `.split('')` here instead of normal `for ... of` 70 | // because the importing application doesn't neccessarily include an ES6 polyfill. 71 | // The `.split('')` approach discards "exotic" UTF-8 characters 72 | // (the ones consisting of four bytes) but digits 73 | // (including non-European ones) don't fall into that range 74 | // so such "exotic" characters would be discarded anyway. 75 | for (const character of string.split('')) { 76 | const digit = parseDigit(character) 77 | if (digit) { 78 | result += digit 79 | } 80 | } 81 | return result 82 | } -------------------------------------------------------------------------------- /source/helpers/parseDigits.test.js: -------------------------------------------------------------------------------- 1 | import parseDigits from './parseDigits.js' 2 | 3 | describe('parseDigits', () => { 4 | it('should parse digits', () => { 5 | parseDigits('+٤٤٢٣٢٣٢٣٤').should.equal('442323234') 6 | }) 7 | }) -------------------------------------------------------------------------------- /source/helpers/stripIddPrefix.js: -------------------------------------------------------------------------------- 1 | import Metadata from '../metadata.js' 2 | import { VALID_DIGITS } from '../constants.js' 3 | 4 | const CAPTURING_DIGIT_PATTERN = new RegExp('([' + VALID_DIGITS + '])') 5 | 6 | export default function stripIddPrefix(number, country, callingCode, metadata) { 7 | if (!country) { 8 | return 9 | } 10 | // Check if the number is IDD-prefixed. 11 | const countryMetadata = new Metadata(metadata) 12 | countryMetadata.selectNumberingPlan(country, callingCode) 13 | const IDDPrefixPattern = new RegExp(countryMetadata.IDDPrefix()) 14 | if (number.search(IDDPrefixPattern) !== 0) { 15 | return 16 | } 17 | // Strip IDD prefix. 18 | number = number.slice(number.match(IDDPrefixPattern)[0].length) 19 | // If there're any digits after an IDD prefix, 20 | // then those digits are a country calling code. 21 | // Since no country code starts with a `0`, 22 | // the code below validates that the next digit (if present) is not `0`. 23 | const matchedGroups = number.match(CAPTURING_DIGIT_PATTERN) 24 | if (matchedGroups && matchedGroups[1] != null && matchedGroups[1].length > 0) { 25 | if (matchedGroups[1] === '0') { 26 | return 27 | } 28 | } 29 | return number 30 | } -------------------------------------------------------------------------------- /source/helpers/stripIddPrefix.test.js: -------------------------------------------------------------------------------- 1 | import stripIddPrefix from './stripIddPrefix.js' 2 | 3 | import metadata from '../../metadata.min.json' assert { type: 'json' } 4 | 5 | describe('stripIddPrefix', () => { 6 | it('should strip a valid IDD prefix', () => { 7 | stripIddPrefix('01178005553535', 'US', '1', metadata).should.equal('78005553535') 8 | }) 9 | 10 | it('should strip a valid IDD prefix (no country calling code)', () => { 11 | stripIddPrefix('011', 'US', '1', metadata).should.equal('') 12 | }) 13 | 14 | it('should strip a valid IDD prefix (valid country calling code)', () => { 15 | stripIddPrefix('0117', 'US', '1', metadata).should.equal('7') 16 | }) 17 | 18 | it('should strip a valid IDD prefix (not a valid country calling code)', () => { 19 | expect(stripIddPrefix('0110', 'US', '1', metadata)).to.be.undefined 20 | }) 21 | }) -------------------------------------------------------------------------------- /source/isPossible.js: -------------------------------------------------------------------------------- 1 | import Metadata from './metadata.js' 2 | import checkNumberLength from './helpers/checkNumberLength.js' 3 | 4 | /** 5 | * Checks if a phone number is "possible" (basically just checks its length). 6 | * 7 | * isPossible(phoneNumberInstance, { ..., v2: true }, metadata) 8 | * 9 | * isPossible({ phone: '8005553535', country: 'RU' }, { ... }, metadata) 10 | * isPossible({ phone: '8005553535', country: 'RU' }, undefined, metadata) 11 | * 12 | * @param {object|PhoneNumber} input — If `options.v2: true` flag is passed, the `input` should be a `PhoneNumber` instance. Otherwise, it should be an object of shape `{ phone: '...', country: '...' }`. 13 | * @param {object} [options] 14 | * @param {object} metadata 15 | * @return {string} 16 | */ 17 | export default function isPossiblePhoneNumber(input, options, metadata) { 18 | /* istanbul ignore if */ 19 | if (options === undefined) { 20 | options = {} 21 | } 22 | 23 | metadata = new Metadata(metadata) 24 | 25 | if (options.v2) { 26 | if (!input.countryCallingCode) { 27 | throw new Error('Invalid phone number object passed') 28 | } 29 | metadata.selectNumberingPlan(input.countryCallingCode) 30 | } else { 31 | if (!input.phone) { 32 | return false 33 | } 34 | if (input.country) { 35 | if (!metadata.hasCountry(input.country)) { 36 | throw new Error(`Unknown country: ${input.country}`) 37 | } 38 | metadata.country(input.country) 39 | } else { 40 | if (!input.countryCallingCode) { 41 | throw new Error('Invalid phone number object passed') 42 | } 43 | metadata.selectNumberingPlan(input.countryCallingCode) 44 | } 45 | } 46 | 47 | // Old metadata (< 1.0.18) had no "possible length" data. 48 | if (metadata.possibleLengths()) { 49 | return isPossibleNumber(input.phone || input.nationalNumber, metadata) 50 | } else { 51 | // There was a bug between `1.7.35` and `1.7.37` where "possible_lengths" 52 | // were missing for "non-geographical" numbering plans. 53 | // Just assume the number is possible in such cases: 54 | // it's unlikely that anyone generated their custom metadata 55 | // in that short period of time (one day). 56 | // This code can be removed in some future major version update. 57 | if (input.countryCallingCode && metadata.isNonGeographicCallingCode(input.countryCallingCode)) { 58 | // "Non-geographic entities" did't have `possibleLengths` 59 | // due to a bug in metadata generation process. 60 | return true 61 | } else { 62 | throw new Error('Missing "possibleLengths" in metadata. Perhaps the metadata has been generated before v1.0.18.'); 63 | } 64 | } 65 | } 66 | 67 | export function isPossibleNumber(nationalNumber, metadata) { //, isInternational) { 68 | switch (checkNumberLength(nationalNumber, metadata)) { 69 | case 'IS_POSSIBLE': 70 | return true 71 | // This library ignores "local-only" phone numbers (for simplicity). 72 | // See the readme for more info on what are "local-only" phone numbers. 73 | // case 'IS_POSSIBLE_LOCAL_ONLY': 74 | // return !isInternational 75 | default: 76 | return false 77 | } 78 | } -------------------------------------------------------------------------------- /source/isPossible.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../metadata.min.json' assert { type: 'json' } 2 | import _isPossibleNumber from './isPossible.js' 3 | import parsePhoneNumber from './parsePhoneNumber.js' 4 | 5 | function isPossibleNumber(...parameters) { 6 | let v2 7 | if (parameters.length < 1) { 8 | // `input` parameter. 9 | parameters.push(undefined) 10 | } else { 11 | // Convert string `input` to a `PhoneNumber` instance. 12 | if (typeof parameters[0] === 'string') { 13 | v2 = true 14 | parameters[0] = parsePhoneNumber(parameters[0], { 15 | ...parameters[1], 16 | extract: false 17 | }, metadata) 18 | } 19 | } 20 | if (parameters.length < 2) { 21 | // `options` parameter. 22 | parameters.push(undefined) 23 | } 24 | // Set `v2` flag. 25 | parameters[1] = { 26 | v2, 27 | ...parameters[1] 28 | } 29 | // Add `metadata` parameter. 30 | parameters.push(metadata) 31 | // Call the function. 32 | return _isPossibleNumber.apply(this, parameters) 33 | } 34 | 35 | describe('isPossible', () => { 36 | it('should work', function() 37 | { 38 | isPossibleNumber('+79992223344').should.equal(true) 39 | 40 | isPossibleNumber({ phone: '1112223344', country: 'RU' }).should.equal(true) 41 | isPossibleNumber({ phone: '111222334', country: 'RU' }).should.equal(false) 42 | isPossibleNumber({ phone: '11122233445', country: 'RU' }).should.equal(false) 43 | 44 | isPossibleNumber({ phone: '1112223344', countryCallingCode: 7 }).should.equal(true) 45 | }) 46 | 47 | it('should work v2', () => { 48 | isPossibleNumber({ nationalNumber: '111222334', countryCallingCode: 7 }, { v2: true }).should.equal(false) 49 | isPossibleNumber({ nationalNumber: '1112223344', countryCallingCode: 7 }, { v2: true }).should.equal(true) 50 | isPossibleNumber({ nationalNumber: '11122233445', countryCallingCode: 7 }, { v2: true }).should.equal(false) 51 | }) 52 | 53 | it('should work in edge cases', () => { 54 | // Invalid `PhoneNumber` argument. 55 | expect(() => isPossibleNumber({}, { v2: true })).to.throw('Invalid phone number object passed') 56 | 57 | // Empty input is passed. 58 | // This is just to support `isValidNumber({})` 59 | // for cases when `parseNumber()` returns `{}`. 60 | isPossibleNumber({}).should.equal(false) 61 | expect(() => isPossibleNumber({ phone: '1112223344' })).to.throw('Invalid phone number object passed') 62 | 63 | // Incorrect country. 64 | expect(() => isPossibleNumber({ phone: '1112223344', country: 'XX' })).to.throw('Unknown country') 65 | }) 66 | }) -------------------------------------------------------------------------------- /source/isPossiblePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import normalizeArguments from './normalizeArguments.js' 2 | import parsePhoneNumber from './parsePhoneNumber_.js' 3 | 4 | export default function isPossiblePhoneNumber() { 5 | let { text, options, metadata } = normalizeArguments(arguments) 6 | options = { 7 | ...options, 8 | extract: false 9 | } 10 | const phoneNumber = parsePhoneNumber(text, options, metadata) 11 | return phoneNumber && phoneNumber.isPossible() || false 12 | } -------------------------------------------------------------------------------- /source/isPossiblePhoneNumber.test.js: -------------------------------------------------------------------------------- 1 | import _isPossiblePhoneNumber from './isPossiblePhoneNumber.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | import oldMetadata from '../test/metadata/1.0.0/metadata.min.json' assert { type: 'json' } 4 | 5 | function isPossiblePhoneNumber(...parameters) { 6 | parameters.push(metadata) 7 | return _isPossiblePhoneNumber.apply(this, parameters) 8 | } 9 | 10 | describe('isPossiblePhoneNumber', () => { 11 | it('should detect whether a phone number is possible', () => { 12 | isPossiblePhoneNumber('8 (800) 555 35 35', 'RU').should.equal(true) 13 | isPossiblePhoneNumber('8 (800) 555 35 35 0', 'RU').should.equal(false) 14 | isPossiblePhoneNumber('Call: 8 (800) 555 35 35', 'RU').should.equal(false) 15 | isPossiblePhoneNumber('8 (800) 555 35 35', { defaultCountry: 'RU' }).should.equal(true) 16 | isPossiblePhoneNumber('+7 (800) 555 35 35').should.equal(true) 17 | isPossiblePhoneNumber('+7 1 (800) 555 35 35').should.equal(false) 18 | isPossiblePhoneNumber(' +7 (800) 555 35 35').should.equal(false) 19 | isPossiblePhoneNumber(' ').should.equal(false) 20 | }) 21 | 22 | it('should detect whether a phone number is possible when using old metadata', () => { 23 | expect(() => _isPossiblePhoneNumber('8 (800) 555 35 35', 'RU', oldMetadata)) 24 | .to.throw('Missing "possibleLengths" in metadata.') 25 | _isPossiblePhoneNumber('+888 123 456 78901', oldMetadata).should.equal(true) 26 | }) 27 | }) -------------------------------------------------------------------------------- /source/isValid.js: -------------------------------------------------------------------------------- 1 | import Metadata from './metadata.js' 2 | import matchesEntirely from './helpers/matchesEntirely.js' 3 | import getNumberType from './helpers/getNumberType.js' 4 | 5 | /** 6 | * Checks if a given phone number is valid. 7 | * 8 | * isValid(phoneNumberInstance, { ..., v2: true }, metadata) 9 | * 10 | * isPossible({ phone: '8005553535', country: 'RU' }, { ... }, metadata) 11 | * isPossible({ phone: '8005553535', country: 'RU' }, undefined, metadata) 12 | * 13 | * If the `number` is a string, it will be parsed to an object, 14 | * but only if it contains only valid phone number characters (including punctuation). 15 | * If the `number` is an object, it is used as is. 16 | * 17 | * The optional `defaultCountry` argument is the default country. 18 | * I.e. it does not restrict to just that country, 19 | * e.g. in those cases where several countries share 20 | * the same phone numbering rules (NANPA, Britain, etc). 21 | * For example, even though the number `07624 369230` 22 | * belongs to the Isle of Man ("IM" country code) 23 | * calling `isValidNumber('07624369230', 'GB', metadata)` 24 | * still returns `true` because the country is not restricted to `GB`, 25 | * it's just that `GB` is the default one for the phone numbering rules. 26 | * For restricting the country see `isValidNumberForRegion()` 27 | * though restricting a country might not be a good idea. 28 | * https://github.com/googlei18n/libphonenumber/blob/master/FAQ.md#when-should-i-use-isvalidnumberforregion 29 | * 30 | * Examples: 31 | * 32 | * ```js 33 | * isValidNumber('+78005553535', metadata) 34 | * isValidNumber('8005553535', 'RU', metadata) 35 | * isValidNumber('88005553535', 'RU', metadata) 36 | * isValidNumber({ phone: '8005553535', country: 'RU' }, metadata) 37 | * ``` 38 | */ 39 | export default function isValidNumber(input, options, metadata) 40 | { 41 | // If assigning the `{}` default value is moved to the arguments above, 42 | // code coverage would decrease for some weird reason. 43 | options = options || {} 44 | 45 | metadata = new Metadata(metadata) 46 | 47 | metadata.selectNumberingPlan(input.country, input.countryCallingCode) 48 | 49 | // By default, countries only have type regexps when it's required for 50 | // distinguishing different countries having the same `countryCallingCode`. 51 | if (metadata.hasTypes()) { 52 | return getNumberType(input, options, metadata.metadata) !== undefined 53 | } 54 | 55 | // If there are no type regexps for this country in metadata then use 56 | // `nationalNumberPattern` as a "better than nothing" replacement. 57 | const nationalNumber = options.v2 ? input.nationalNumber : input.phone 58 | return matchesEntirely(nationalNumber, metadata.nationalNumberPattern()) 59 | } -------------------------------------------------------------------------------- /source/isValidPhoneNumber.js: -------------------------------------------------------------------------------- 1 | import normalizeArguments from './normalizeArguments.js' 2 | import parsePhoneNumber from './parsePhoneNumber_.js' 3 | 4 | export default function isValidPhoneNumber() { 5 | let { text, options, metadata } = normalizeArguments(arguments) 6 | options = { 7 | ...options, 8 | extract: false 9 | } 10 | const phoneNumber = parsePhoneNumber(text, options, metadata) 11 | return phoneNumber && phoneNumber.isValid() || false 12 | } -------------------------------------------------------------------------------- /source/isValidPhoneNumber.test.js: -------------------------------------------------------------------------------- 1 | import _isValidPhoneNumber from './isValidPhoneNumber.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | function isValidPhoneNumber(...parameters) { 5 | parameters.push(metadata) 6 | return _isValidPhoneNumber.apply(this, parameters) 7 | } 8 | 9 | describe('isValidPhoneNumber', () => { 10 | it('should detect whether a phone number is valid', () => { 11 | isValidPhoneNumber('8 (800) 555 35 35', 'RU').should.equal(true) 12 | isValidPhoneNumber('8 (800) 555 35 35 0', 'RU').should.equal(false) 13 | isValidPhoneNumber('Call: 8 (800) 555 35 35', 'RU').should.equal(false) 14 | isValidPhoneNumber('8 (800) 555 35 35', { defaultCountry: 'RU' }).should.equal(true) 15 | isValidPhoneNumber('+7 (800) 555 35 35').should.equal(true) 16 | isValidPhoneNumber('+7 1 (800) 555 35 35').should.equal(false) 17 | isValidPhoneNumber(' +7 (800) 555 35 35').should.equal(false) 18 | isValidPhoneNumber(' ').should.equal(false) 19 | }) 20 | }) -------------------------------------------------------------------------------- /source/legacy/findNumbers.js: -------------------------------------------------------------------------------- 1 | import PhoneNumberMatcher from '../PhoneNumberMatcher.js' 2 | import normalizeArguments from '../normalizeArguments.js' 3 | 4 | export default function findNumbers() { 5 | const { text, options, metadata } = normalizeArguments(arguments) 6 | const matcher = new PhoneNumberMatcher(text, options, metadata) 7 | const results = [] 8 | while (matcher.hasNext()) { 9 | results.push(matcher.next()) 10 | } 11 | return results 12 | } -------------------------------------------------------------------------------- /source/legacy/findPhoneNumbers.js: -------------------------------------------------------------------------------- 1 | // This is a legacy function. 2 | // Use `findNumbers()` instead. 3 | 4 | import _findPhoneNumbers, { searchPhoneNumbers as _searchPhoneNumbers } from './findPhoneNumbersInitialImplementation.js' 5 | import normalizeArguments from '../normalizeArguments.js' 6 | 7 | export default function findPhoneNumbers() 8 | { 9 | const { text, options, metadata } = normalizeArguments(arguments) 10 | return _findPhoneNumbers(text, options, metadata) 11 | } 12 | 13 | /** 14 | * @return ES6 `for ... of` iterator. 15 | */ 16 | export function searchPhoneNumbers() 17 | { 18 | const { text, options, metadata } = normalizeArguments(arguments) 19 | return _searchPhoneNumbers(text, options, metadata) 20 | } -------------------------------------------------------------------------------- /source/legacy/format.js: -------------------------------------------------------------------------------- 1 | import _formatNumber from '../format.js' 2 | import parse from '../parse.js' 3 | import isObject from '../helpers/isObject.js' 4 | 5 | export default function formatNumber() { 6 | const { 7 | input, 8 | format, 9 | options, 10 | metadata 11 | } = normalizeArguments(arguments) 12 | 13 | return _formatNumber(input, format, options, metadata) 14 | } 15 | 16 | // Sort out arguments 17 | function normalizeArguments(args) 18 | { 19 | const [arg_1, arg_2, arg_3, arg_4, arg_5] = Array.prototype.slice.call(args) 20 | 21 | let input 22 | let format 23 | let options 24 | let metadata 25 | 26 | // Sort out arguments. 27 | 28 | // If the phone number is passed as a string. 29 | // `format('8005553535', ...)`. 30 | if (typeof arg_1 === 'string') 31 | { 32 | // If country code is supplied. 33 | // `format('8005553535', 'RU', 'NATIONAL', [options], metadata)`. 34 | if (typeof arg_3 === 'string') 35 | { 36 | format = arg_3 37 | 38 | if (arg_5) 39 | { 40 | options = arg_4 41 | metadata = arg_5 42 | } 43 | else 44 | { 45 | metadata = arg_4 46 | } 47 | 48 | input = parse(arg_1, { defaultCountry: arg_2, extended: true }, metadata) 49 | } 50 | // Just an international phone number is supplied 51 | // `format('+78005553535', 'NATIONAL', [options], metadata)`. 52 | else 53 | { 54 | if (typeof arg_2 !== 'string') 55 | { 56 | throw new Error('`format` argument not passed to `formatNumber(number, format)`') 57 | } 58 | 59 | format = arg_2 60 | 61 | if (arg_4) 62 | { 63 | options = arg_3 64 | metadata = arg_4 65 | } 66 | else 67 | { 68 | metadata = arg_3 69 | } 70 | 71 | input = parse(arg_1, { extended: true }, metadata) 72 | } 73 | } 74 | // If the phone number is passed as a parsed number object. 75 | // `format({ phone: '8005553535', country: 'RU' }, 'NATIONAL', [options], metadata)`. 76 | else if (isObject(arg_1)) 77 | { 78 | input = arg_1 79 | format = arg_2 80 | 81 | if (arg_4) 82 | { 83 | options = arg_3 84 | metadata = arg_4 85 | } 86 | else 87 | { 88 | metadata = arg_3 89 | } 90 | } 91 | else throw new TypeError('A phone number must either be a string or an object of shape { phone, [country] }.') 92 | 93 | // Legacy lowercase formats. 94 | if (format === 'International') { 95 | format = 'INTERNATIONAL' 96 | } else if (format === 'National') { 97 | format = 'NATIONAL' 98 | } 99 | 100 | return { 101 | input, 102 | format, 103 | options, 104 | metadata 105 | } 106 | } -------------------------------------------------------------------------------- /source/legacy/getNumberType.js: -------------------------------------------------------------------------------- 1 | import isViablePhoneNumber from '../helpers/isViablePhoneNumber.js' 2 | import _getNumberType from '../helpers/getNumberType.js' 3 | import isObject from '../helpers/isObject.js' 4 | import parse from '../parse.js' 5 | 6 | // Finds out national phone number type (fixed line, mobile, etc) 7 | export default function getNumberType() { 8 | const { input, options, metadata } = normalizeArguments(arguments) 9 | // `parseNumber()` would return `{}` when no phone number could be parsed from the input. 10 | if (!input.phone) { 11 | return 12 | } 13 | return _getNumberType(input, options, metadata) 14 | } 15 | 16 | // Sort out arguments 17 | export function normalizeArguments(args) 18 | { 19 | const [arg_1, arg_2, arg_3, arg_4] = Array.prototype.slice.call(args) 20 | 21 | let input 22 | let options = {} 23 | let metadata 24 | 25 | // If the phone number is passed as a string. 26 | // `getNumberType('88005553535', ...)`. 27 | if (typeof arg_1 === 'string') 28 | { 29 | // If "default country" argument is being passed 30 | // then convert it to an `options` object. 31 | // `getNumberType('88005553535', 'RU', metadata)`. 32 | if (!isObject(arg_2)) 33 | { 34 | if (arg_4) 35 | { 36 | options = arg_3 37 | metadata = arg_4 38 | } 39 | else 40 | { 41 | metadata = arg_3 42 | } 43 | 44 | // `parse` extracts phone numbers from raw text, 45 | // therefore it will cut off all "garbage" characters, 46 | // while this `validate` function needs to verify 47 | // that the phone number contains no "garbage" 48 | // therefore the explicit `isViablePhoneNumber` check. 49 | if (isViablePhoneNumber(arg_1)) 50 | { 51 | input = parse(arg_1, { defaultCountry: arg_2 }, metadata) 52 | } 53 | else 54 | { 55 | input = {} 56 | } 57 | } 58 | // No "resrict country" argument is being passed. 59 | // International phone number is passed. 60 | // `getNumberType('+78005553535', metadata)`. 61 | else 62 | { 63 | if (arg_3) 64 | { 65 | options = arg_2 66 | metadata = arg_3 67 | } 68 | else 69 | { 70 | metadata = arg_2 71 | } 72 | 73 | // `parse` extracts phone numbers from raw text, 74 | // therefore it will cut off all "garbage" characters, 75 | // while this `validate` function needs to verify 76 | // that the phone number contains no "garbage" 77 | // therefore the explicit `isViablePhoneNumber` check. 78 | if (isViablePhoneNumber(arg_1)) 79 | { 80 | input = parse(arg_1, undefined, metadata) 81 | } 82 | else 83 | { 84 | input = {} 85 | } 86 | } 87 | } 88 | // If the phone number is passed as a parsed phone number. 89 | // `getNumberType({ phone: '88005553535', country: 'RU' }, ...)`. 90 | else if (isObject(arg_1)) 91 | { 92 | input = arg_1 93 | 94 | if (arg_3) 95 | { 96 | options = arg_2 97 | metadata = arg_3 98 | } 99 | else 100 | { 101 | metadata = arg_2 102 | } 103 | } 104 | else throw new TypeError('A phone number must either be a string or an object of shape { phone, [country] }.') 105 | 106 | return { 107 | input, 108 | options, 109 | metadata 110 | } 111 | } -------------------------------------------------------------------------------- /source/legacy/getNumberType.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../../metadata.max.json' assert { type: 'json' } 2 | import Metadata from '../metadata.js' 3 | import _getNumberType from './getNumberType.js' 4 | 5 | function getNumberType(...parameters) { 6 | parameters.push(metadata) 7 | return _getNumberType.apply(this, parameters) 8 | } 9 | 10 | describe('getNumberType', () => { 11 | it('should infer phone number type MOBILE', () => { 12 | getNumberType('9150000000', 'RU').should.equal('MOBILE') 13 | getNumberType('7912345678', 'GB').should.equal('MOBILE') 14 | getNumberType('51234567', 'EE').should.equal('MOBILE') 15 | }) 16 | 17 | it('should infer phone number types', () => { 18 | getNumberType('88005553535', 'RU').should.equal('TOLL_FREE') 19 | getNumberType('8005553535', 'RU').should.equal('TOLL_FREE') 20 | getNumberType('4957777777', 'RU').should.equal('FIXED_LINE') 21 | getNumberType('8030000000', 'RU').should.equal('PREMIUM_RATE') 22 | 23 | getNumberType('2133734253', 'US').should.equal('FIXED_LINE_OR_MOBILE') 24 | getNumberType('5002345678', 'US').should.equal('PERSONAL_NUMBER') 25 | }) 26 | 27 | it('should work when no country is passed', () => { 28 | getNumberType('+79150000000').should.equal('MOBILE') 29 | }) 30 | 31 | it('should return FIXED_LINE_OR_MOBILE when there is ambiguity', () => { 32 | // (no such country in the metadata, therefore no unit test for this `if`) 33 | }) 34 | 35 | it('should work in edge cases', function() { 36 | let thrower 37 | 38 | // // No metadata 39 | // thrower = () => _getNumberType({ phone: '+78005553535' }) 40 | // thrower.should.throw('`metadata` argument not passed') 41 | 42 | // Parsed phone number 43 | getNumberType({ phone: '8005553535', country: 'RU' }).should.equal('TOLL_FREE') 44 | 45 | // Invalid phone number 46 | type(getNumberType('123', 'RU')).should.equal('undefined') 47 | 48 | // Invalid country 49 | thrower = () => getNumberType({ phone: '8005553535', country: 'RUS' }) 50 | thrower.should.throw('Unknown country') 51 | 52 | // Numerical `value` 53 | thrower = () => getNumberType(89150000000, 'RU') 54 | thrower.should.throw('A phone number must either be a string or an object of shape { phone, [country] }.') 55 | 56 | // When `options` argument is passed. 57 | getNumberType('8005553535', 'RU', {}).should.equal('TOLL_FREE') 58 | getNumberType('+78005553535', {}).should.equal('TOLL_FREE') 59 | getNumberType({ phone: '8005553535', country: 'RU' }, {}).should.equal('TOLL_FREE') 60 | }) 61 | }) 62 | 63 | function type(something) { 64 | return typeof something 65 | } -------------------------------------------------------------------------------- /source/legacy/isPossibleNumber.js: -------------------------------------------------------------------------------- 1 | import { normalizeArguments } from './getNumberType.js' 2 | import _isPossibleNumber from '../isPossible.js' 3 | 4 | /** 5 | * Checks if a given phone number is possible. 6 | * Which means it only checks phone number length 7 | * and doesn't test any regular expressions. 8 | * 9 | * Examples: 10 | * 11 | * ```js 12 | * isPossibleNumber('+78005553535', metadata) 13 | * isPossibleNumber('8005553535', 'RU', metadata) 14 | * isPossibleNumber('88005553535', 'RU', metadata) 15 | * isPossibleNumber({ phone: '8005553535', country: 'RU' }, metadata) 16 | * ``` 17 | */ 18 | export default function isPossibleNumber() { 19 | const { input, options, metadata } = normalizeArguments(arguments) 20 | // `parseNumber()` would return `{}` when no phone number could be parsed from the input. 21 | if (!input.phone && !(options && options.v2)) { 22 | return false 23 | } 24 | return _isPossibleNumber(input, options, metadata) 25 | } -------------------------------------------------------------------------------- /source/legacy/isPossibleNumber.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../../metadata.min.json' assert { type: 'json' } 2 | import _isPossibleNumber from './isPossibleNumber.js' 3 | 4 | function isPossibleNumber(...parameters) { 5 | parameters.push(metadata) 6 | return _isPossibleNumber.apply(this, parameters) 7 | } 8 | 9 | describe('isPossibleNumber', () => { 10 | it('should work', function() 11 | { 12 | isPossibleNumber('+79992223344').should.equal(true) 13 | 14 | isPossibleNumber({ phone: '1112223344', country: 'RU' }).should.equal(true) 15 | isPossibleNumber({ phone: '111222334', country: 'RU' }).should.equal(false) 16 | isPossibleNumber({ phone: '11122233445', country: 'RU' }).should.equal(false) 17 | 18 | isPossibleNumber({ phone: '1112223344', countryCallingCode: 7 }).should.equal(true) 19 | }) 20 | 21 | it('should work v2', () => { 22 | isPossibleNumber({ nationalNumber: '111222334', countryCallingCode: 7 }, { v2: true }).should.equal(false) 23 | isPossibleNumber({ nationalNumber: '1112223344', countryCallingCode: 7 }, { v2: true }).should.equal(true) 24 | isPossibleNumber({ nationalNumber: '11122233445', countryCallingCode: 7 }, { v2: true }).should.equal(false) 25 | }) 26 | 27 | it('should work in edge cases', () => { 28 | // Invalid `PhoneNumber` argument. 29 | expect(() => isPossibleNumber({}, { v2: true })).to.throw('Invalid phone number object passed') 30 | 31 | // Empty input is passed. 32 | // This is just to support `isValidNumber({})` 33 | // for cases when `parseNumber()` returns `{}`. 34 | isPossibleNumber({}).should.equal(false) 35 | expect(() => isPossibleNumber({ phone: '1112223344' })).to.throw('Invalid phone number object passed') 36 | 37 | // Incorrect country. 38 | expect(() => isPossibleNumber({ phone: '1112223344', country: 'XX' })).to.throw('Unknown country') 39 | }) 40 | }) -------------------------------------------------------------------------------- /source/legacy/isValidNumber.js: -------------------------------------------------------------------------------- 1 | import _isValidNumber from '../isValid.js' 2 | import { normalizeArguments } from './getNumberType.js' 3 | 4 | // Finds out national phone number type (fixed line, mobile, etc) 5 | export default function isValidNumber() { 6 | const { input, options, metadata } = normalizeArguments(arguments) 7 | // `parseNumber()` would return `{}` when no phone number could be parsed from the input. 8 | if (!input.phone) { 9 | return false 10 | } 11 | return _isValidNumber(input, options, metadata) 12 | } -------------------------------------------------------------------------------- /source/legacy/isValidNumberForRegion.js: -------------------------------------------------------------------------------- 1 | import isViablePhoneNumber from '../helpers/isViablePhoneNumber.js' 2 | import parseNumber from '../parse.js' 3 | import _isValidNumberForRegion from './isValidNumberForRegion_.js' 4 | 5 | // This function has been deprecated and is not exported as 6 | // `isValidPhoneNumberForCountry()` or `isValidPhoneNumberForRegion()`. 7 | // 8 | // The rationale is: 9 | // 10 | // * We don't use the "region" word, so "country" would be better. 11 | // 12 | // * It could be substituted with: 13 | // 14 | // ```js 15 | // export default function isValidPhoneNumberForCountry(phoneNumberString, country) { 16 | // const phoneNumber = parsePhoneNumber(phoneNumberString, { 17 | // defaultCountry: country, 18 | // // Demand that the entire input string must be a phone number. 19 | // // Otherwise, it would "extract" a phone number from an input string. 20 | // extract: false 21 | // }) 22 | // if (!phoneNumber) { 23 | // return false 24 | // } 25 | // if (phoneNumber.country !== country) { 26 | // return false 27 | // } 28 | // return phoneNumber.isValid() 29 | // } 30 | // ``` 31 | // 32 | // * Same function could be used for `isPossiblePhoneNumberForCountry()` 33 | // by replacing `isValid()` with `isPossible()`. 34 | // 35 | // * The reason why this function is not exported is because its result is ambiguous. 36 | // Suppose `false` is returned. It could mean any of: 37 | // * Not a phone number. 38 | // * The phone number is valid but belongs to another country or another calling code. 39 | // * The phone number belongs to the correct country but is not valid digit-wise. 40 | // All those three cases should be handled separately from a "User Experience" standpoint. 41 | // Simply showing "Invalid phone number" error in all of those cases would be lazy UX. 42 | 43 | export default function isValidNumberForRegion(number, country, metadata) { 44 | if (typeof number !== 'string') { 45 | throw new TypeError('number must be a string') 46 | } 47 | if (typeof country !== 'string') { 48 | throw new TypeError('country must be a string') 49 | } 50 | // `parse` extracts phone numbers from raw text, 51 | // therefore it will cut off all "garbage" characters, 52 | // while this `validate` function needs to verify 53 | // that the phone number contains no "garbage" 54 | // therefore the explicit `isViablePhoneNumber` check. 55 | let input 56 | if (isViablePhoneNumber(number)) { 57 | input = parseNumber(number, { defaultCountry: country }, metadata) 58 | } else { 59 | input = {} 60 | } 61 | return _isValidNumberForRegion(input, country, undefined, metadata) 62 | } -------------------------------------------------------------------------------- /source/legacy/isValidNumberForRegion.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../../metadata.min.json' assert { type: 'json' } 2 | import isValidNumberForRegionCustom from './isValidNumberForRegion.js' 3 | import _isValidNumberForRegion from './isValidNumberForRegion_.js' 4 | 5 | function isValidNumberForRegion(...parameters) { 6 | parameters.push(metadata) 7 | return isValidNumberForRegionCustom.apply(this, parameters) 8 | } 9 | 10 | describe('isValidNumberForRegion', () => { 11 | it('should detect if is valid number for region', () => { 12 | isValidNumberForRegion('07624369230', 'GB').should.equal(false) 13 | isValidNumberForRegion('07624369230', 'IM').should.equal(true) 14 | }) 15 | 16 | it('should validate arguments', () => { 17 | expect(() => isValidNumberForRegion({ phone: '7624369230', country: 'GB' })).to.throw('number must be a string') 18 | expect(() => isValidNumberForRegion('7624369230')).to.throw('country must be a string') 19 | }) 20 | 21 | it('should work in edge cases', () => { 22 | // Not a "viable" phone number. 23 | isValidNumberForRegion('7', 'GB').should.equal(false) 24 | 25 | // `options` argument `if/else` coverage. 26 | _isValidNumberForRegion('07624369230', 'GB', {}, metadata).should.equal(false) 27 | }) 28 | }) -------------------------------------------------------------------------------- /source/legacy/isValidNumberForRegion_.js: -------------------------------------------------------------------------------- 1 | import isValidNumber from '../isValid.js' 2 | 3 | /** 4 | * Checks if a given phone number is valid within a given region. 5 | * Is just an alias for `phoneNumber.isValid() && phoneNumber.country === country`. 6 | * https://github.com/googlei18n/libphonenumber/blob/master/FAQ.md#when-should-i-use-isvalidnumberforregion 7 | */ 8 | export default function isValidNumberForRegion(input, country, options, metadata) { 9 | // If assigning the `{}` default value is moved to the arguments above, 10 | // code coverage would decrease for some weird reason. 11 | options = options || {} 12 | return input.country === country && isValidNumber(input, options, metadata) 13 | } -------------------------------------------------------------------------------- /source/legacy/parse.js: -------------------------------------------------------------------------------- 1 | import _parseNumber from '../parse.js' 2 | import normalizeArguments from '../normalizeArguments.js' 3 | 4 | export default function parseNumber() { 5 | const { text, options, metadata } = normalizeArguments(arguments) 6 | return _parseNumber(text, options, metadata) 7 | } -------------------------------------------------------------------------------- /source/legacy/searchNumbers.js: -------------------------------------------------------------------------------- 1 | import normalizeArguments from '../normalizeArguments.js' 2 | import PhoneNumberMatcher from '../PhoneNumberMatcher.js' 3 | 4 | /** 5 | * @return ES6 `for ... of` iterator. 6 | */ 7 | export default function searchNumbers() 8 | { 9 | const { text, options, metadata } = normalizeArguments(arguments) 10 | 11 | const matcher = new PhoneNumberMatcher(text, options, metadata) 12 | 13 | return { 14 | [Symbol.iterator]() { 15 | return { 16 | next: () => { 17 | if (matcher.hasNext()) { 18 | return { 19 | done: false, 20 | value: matcher.next() 21 | } 22 | } 23 | return { 24 | done: true 25 | } 26 | } 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /source/legacy/searchNumbers.test.js: -------------------------------------------------------------------------------- 1 | import searchNumbers from './searchNumbers.js' 2 | import metadata from '../../metadata.min.json' assert { type: 'json' } 3 | 4 | describe('searchNumbers', () => { 5 | it('should iterate', () => { 6 | const expectedNumbers =[{ 7 | country : 'RU', 8 | phone : '8005553535', 9 | // number : '+7 (800) 555-35-35', 10 | startsAt : 14, 11 | endsAt : 32 12 | }, { 13 | country : 'US', 14 | phone : '2133734253', 15 | // number : '(213) 373-4253', 16 | startsAt : 41, 17 | endsAt : 55 18 | }] 19 | 20 | for (const number of searchNumbers('The number is +7 (800) 555-35-35 and not (213) 373-4253 as written in the document.', 'US', metadata)) { 21 | number.should.deep.equal(expectedNumbers.shift()) 22 | } 23 | 24 | expectedNumbers.length.should.equal(0) 25 | }) 26 | }) -------------------------------------------------------------------------------- /source/normalizeArguments.js: -------------------------------------------------------------------------------- 1 | import isObject from './helpers/isObject.js' 2 | 3 | // Extracts the following properties from function arguments: 4 | // * input `text` 5 | // * `options` object 6 | // * `metadata` JSON 7 | export default function normalizeArguments(args) { 8 | const [arg_1, arg_2, arg_3, arg_4] = Array.prototype.slice.call(args) 9 | 10 | let text 11 | let options 12 | let metadata 13 | 14 | // If the phone number is passed as a string. 15 | // `parsePhoneNumber('88005553535', ...)`. 16 | if (typeof arg_1 === 'string') { 17 | text = arg_1 18 | } 19 | else throw new TypeError('A text for parsing must be a string.') 20 | 21 | // If "default country" argument is being passed then move it to `options`. 22 | // `parsePhoneNumber('88005553535', 'RU', [options], metadata)`. 23 | if (!arg_2 || typeof arg_2 === 'string') 24 | { 25 | if (arg_4) { 26 | options = arg_3 27 | metadata = arg_4 28 | } else { 29 | options = undefined 30 | metadata = arg_3 31 | } 32 | 33 | if (arg_2) { 34 | options = { defaultCountry: arg_2, ...options } 35 | } 36 | } 37 | // `defaultCountry` is not passed. 38 | // Example: `parsePhoneNumber('+78005553535', [options], metadata)`. 39 | else if (isObject(arg_2)) 40 | { 41 | if (arg_3) { 42 | options = arg_2 43 | metadata = arg_3 44 | } else { 45 | metadata = arg_2 46 | } 47 | } 48 | else throw new Error(`Invalid second argument: ${arg_2}`) 49 | 50 | return { 51 | text, 52 | options, 53 | metadata 54 | } 55 | } -------------------------------------------------------------------------------- /source/parseIncompletePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import { parseDigit } from './helpers/parseDigits.js' 2 | 3 | /** 4 | * Parses phone number characters from a string. 5 | * Drops all punctuation leaving only digits and the leading `+` sign (if any). 6 | * Also converts wide-ascii and arabic-indic numerals to conventional numerals. 7 | * E.g. in Iraq they don't write `+442323234` but rather `+٤٤٢٣٢٣٢٣٤`. 8 | * @param {string} string 9 | * @return {string} 10 | * @example 11 | * ```js 12 | * // Outputs '8800555'. 13 | * parseIncompletePhoneNumber('8 (800) 555') 14 | * // Outputs '+7800555'. 15 | * parseIncompletePhoneNumber('+7 800 555') 16 | * ``` 17 | */ 18 | export default function parseIncompletePhoneNumber(string) { 19 | let result = '' 20 | // Using `.split('')` here instead of normal `for ... of` 21 | // because the importing application doesn't neccessarily include an ES6 polyfill. 22 | // The `.split('')` approach discards "exotic" UTF-8 characters 23 | // (the ones consisting of four bytes) but digits 24 | // (including non-European ones) don't fall into that range 25 | // so such "exotic" characters would be discarded anyway. 26 | for (const character of string.split('')) { 27 | result += parsePhoneNumberCharacter(character, result) || '' 28 | } 29 | return result 30 | } 31 | 32 | /** 33 | * Parses next character while parsing phone number digits (including a `+`) 34 | * from text: discards everything except `+` and digits, and `+` is only allowed 35 | * at the start of a phone number. 36 | * For example, is used in `react-phone-number-input` where it uses 37 | * [`input-format`](https://gitlab.com/catamphetamine/input-format). 38 | * @param {string} character - Yet another character from raw input string. 39 | * @param {string?} prevParsedCharacters - Previous parsed characters. 40 | * @param {function?} emitEvent - An optional "emit event" function. 41 | * @return {string?} The parsed character. 42 | */ 43 | export function parsePhoneNumberCharacter(character, prevParsedCharacters, emitEvent) { 44 | // Only allow a leading `+`. 45 | if (character === '+') { 46 | // If this `+` is not the first parsed character 47 | // then discard it. 48 | if (prevParsedCharacters) { 49 | // `emitEvent` argument was added to this `export`ed function on Dec 26th, 2023. 50 | // Any 3rd-party code that used to `import` and call this function before that 51 | // won't be passing any `emitEvent` argument. 52 | // 53 | // The addition of the `emitEvent` argument was to fix the slightly-weird behavior 54 | // of parsing an input string when the user inputs something like `"2+7" 55 | // https://github.com/catamphetamine/react-phone-number-input/issues/437 56 | // 57 | // If the parser encounters an unexpected `+` in a string being parsed 58 | // then it simply discards that out-of-place `+` and any following characters. 59 | // 60 | if (typeof emitEvent === 'function') { 61 | emitEvent('end') 62 | } 63 | return 64 | } 65 | return '+' 66 | } 67 | // Allow digits. 68 | return parseDigit(character) 69 | } -------------------------------------------------------------------------------- /source/parseIncompletePhoneNumber.test.js: -------------------------------------------------------------------------------- 1 | import parseIncompletePhoneNumber, { parsePhoneNumberCharacter } from './parseIncompletePhoneNumber.js' 2 | 3 | describe('parseIncompletePhoneNumber', () => { 4 | it('should parse phone number character', () => { 5 | // Accepts leading `+`. 6 | parsePhoneNumberCharacter('+').should.equal('+') 7 | 8 | // Doesn't accept non-leading `+`. 9 | expect(parsePhoneNumberCharacter('+', '+')).to.be.undefined 10 | 11 | // Parses digits. 12 | parsePhoneNumberCharacter('1').should.equal('1') 13 | 14 | // Parses non-European digits. 15 | parsePhoneNumberCharacter('٤').should.equal('4') 16 | 17 | // Dismisses other characters. 18 | expect(parsePhoneNumberCharacter('-')).to.be.undefined 19 | }) 20 | 21 | it('should parse incomplete phone number', () => { 22 | parseIncompletePhoneNumber('').should.equal('') 23 | 24 | // Doesn't accept non-leading `+`. 25 | parseIncompletePhoneNumber('++').should.equal('+') 26 | 27 | // Accepts leading `+`. 28 | parseIncompletePhoneNumber('+7 800 555').should.equal('+7800555') 29 | 30 | // Parses digits. 31 | parseIncompletePhoneNumber('8 (800) 555').should.equal('8800555') 32 | 33 | // Parses non-European digits. 34 | parseIncompletePhoneNumber('+٤٤٢٣٢٣٢٣٤').should.equal('+442323234') 35 | }) 36 | 37 | it('should work with a new `context` argument in `parsePhoneNumberCharacter()` function (international number)', () => { 38 | let stopped = false 39 | 40 | const emit = (event) => { 41 | switch (event) { 42 | case 'end': 43 | stopped = true 44 | break 45 | } 46 | } 47 | 48 | parsePhoneNumberCharacter('+', undefined, emit).should.equal('+') 49 | expect(stopped).to.equal(false) 50 | 51 | parsePhoneNumberCharacter('1', '+', emit).should.equal('1') 52 | expect(stopped).to.equal(false) 53 | 54 | expect(parsePhoneNumberCharacter('+', '+1', emit)).to.equal(undefined) 55 | expect(stopped).to.equal(true) 56 | 57 | expect(parsePhoneNumberCharacter('2', '+1', emit)).to.equal('2') 58 | expect(stopped).to.equal(true) 59 | }) 60 | 61 | it('should work with a new `context` argument in `parsePhoneNumberCharacter()` function (national number)', () => { 62 | let stopped = false 63 | 64 | const emit = (event) => { 65 | switch (event) { 66 | case 'end': 67 | stopped = true 68 | break 69 | } 70 | } 71 | 72 | parsePhoneNumberCharacter('2', undefined, emit).should.equal('2') 73 | expect(stopped).to.equal(false) 74 | 75 | expect(parsePhoneNumberCharacter('+', '2', emit)).to.equal(undefined) 76 | expect(stopped).to.equal(true) 77 | 78 | expect(parsePhoneNumberCharacter('1', '2', emit)).to.equal('1') 79 | expect(stopped).to.equal(true) 80 | }) 81 | }) -------------------------------------------------------------------------------- /source/parsePhoneNumber.js: -------------------------------------------------------------------------------- 1 | import normalizeArguments from './normalizeArguments.js' 2 | import parsePhoneNumber_ from './parsePhoneNumber_.js' 3 | 4 | export default function parsePhoneNumber() { 5 | const { text, options, metadata } = normalizeArguments(arguments) 6 | return parsePhoneNumber_(text, options, metadata) 7 | } 8 | -------------------------------------------------------------------------------- /source/parsePhoneNumber.test.js: -------------------------------------------------------------------------------- 1 | import _parsePhoneNumber from './parsePhoneNumber.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | function parsePhoneNumber(...parameters) { 5 | parameters.push(metadata) 6 | return _parsePhoneNumber.apply(this, parameters) 7 | } 8 | 9 | const USE_NON_GEOGRAPHIC_COUNTRY_CODE = false 10 | 11 | describe('parsePhoneNumber', () => { 12 | it('should parse phone numbers from string', () => { 13 | parsePhoneNumber('Phone: 8 (800) 555 35 35.', 'RU').nationalNumber.should.equal('8005553535') 14 | expect(parsePhoneNumber('3', 'RU')).to.be.undefined 15 | }) 16 | 17 | it('should work in edge cases', () => { 18 | expect(parsePhoneNumber('')).to.be.undefined 19 | }) 20 | 21 | it('should parse phone numbers when invalid country code is passed', () => { 22 | parsePhoneNumber('Phone: +7 (800) 555 35 35.', 'XX').nationalNumber.should.equal('8005553535') 23 | expect(parsePhoneNumber('Phone: 8 (800) 555-35-35.', 'XX')).to.be.undefined 24 | }) 25 | 26 | 27 | it('should parse non-geographic numbering plan phone numbers (extended)', () => { 28 | const phoneNumber = parsePhoneNumber('+870773111632') 29 | phoneNumber.number.should.equal('+870773111632') 30 | if (USE_NON_GEOGRAPHIC_COUNTRY_CODE) { 31 | phoneNumber.country.should.equal('001') 32 | } else { 33 | expect(phoneNumber.country).to.be.undefined 34 | } 35 | phoneNumber.countryCallingCode.should.equal('870') 36 | }) 37 | 38 | it('should parse non-geographic numbering plan phone numbers (default country code) (extended)', () => { 39 | const phoneNumber = parsePhoneNumber('773111632', { defaultCallingCode: '870' }) 40 | phoneNumber.number.should.equal('+870773111632') 41 | if (USE_NON_GEOGRAPHIC_COUNTRY_CODE) { 42 | phoneNumber.country.should.equal('001') 43 | } else { 44 | expect(phoneNumber.country).to.be.undefined 45 | } 46 | phoneNumber.countryCallingCode.should.equal('870') 47 | }) 48 | 49 | it('should determine the possibility of non-geographic phone numbers', () => { 50 | const phoneNumber = parsePhoneNumber('+870773111632') 51 | phoneNumber.isPossible().should.equal(true) 52 | const phoneNumber2 = parsePhoneNumber('+8707731116321') 53 | phoneNumber2.isPossible().should.equal(false) 54 | }) 55 | 56 | it('should support `extract: false` flag', () => { 57 | const testCorrectness = (number, expectedResult) => { 58 | const result = expect(parsePhoneNumber(number, { extract: false, defaultCountry: 'US' })) 59 | if (expectedResult) { 60 | result.to.not.be.undefined 61 | } else { 62 | result.to.be.undefined 63 | } 64 | } 65 | testCorrectness('Call: (213) 373-4253', false) 66 | testCorrectness('(213) 373-4253x', false) 67 | testCorrectness('(213) 373-4253', true) 68 | testCorrectness('- (213) 373-4253 -', true) 69 | testCorrectness('+1 (213) 373-4253', true) 70 | testCorrectness(' +1 (213) 373-4253', false) 71 | }) 72 | 73 | it('should not prematurely strip a possible national prefix from Chinese numbers', () => { 74 | // https://gitlab.com/catamphetamine/libphonenumber-js/-/issues/57 75 | const phoneNumber = parsePhoneNumber('+86123456789') 76 | phoneNumber.isPossible().should.equal(true) 77 | phoneNumber.isValid().should.equal(false) 78 | phoneNumber.nationalNumber.should.equal('123456789') 79 | }) 80 | }) 81 | -------------------------------------------------------------------------------- /source/parsePhoneNumberWithError.js: -------------------------------------------------------------------------------- 1 | import parsePhoneNumberWithError_ from './parsePhoneNumberWithError_.js' 2 | import normalizeArguments from './normalizeArguments.js' 3 | 4 | export default function parsePhoneNumberWithError() { 5 | const { text, options, metadata } = normalizeArguments(arguments) 6 | return parsePhoneNumberWithError_(text, options, metadata) 7 | } -------------------------------------------------------------------------------- /source/parsePhoneNumberWithError_.js: -------------------------------------------------------------------------------- 1 | import parse from './parse.js' 2 | 3 | export default function parsePhoneNumberWithError(text, options, metadata) { 4 | return parse(text, { ...options, v2: true }, metadata) 5 | } -------------------------------------------------------------------------------- /source/parsePhoneNumber_.js: -------------------------------------------------------------------------------- 1 | import parsePhoneNumberWithError from './parsePhoneNumberWithError_.js' 2 | import ParseError from './ParseError.js' 3 | import { isSupportedCountry } from './metadata.js' 4 | 5 | export default function parsePhoneNumber(text, options, metadata) { 6 | // Validate `defaultCountry`. 7 | if (options && options.defaultCountry && !isSupportedCountry(options.defaultCountry, metadata)) { 8 | options = { 9 | ...options, 10 | defaultCountry: undefined 11 | } 12 | } 13 | // Parse phone number. 14 | try { 15 | return parsePhoneNumberWithError(text, options, metadata) 16 | } catch (error) { 17 | /* istanbul ignore else */ 18 | if (error instanceof ParseError) { 19 | // 20 | } else { 21 | throw error 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /source/searchPhoneNumbersInText.js: -------------------------------------------------------------------------------- 1 | import PhoneNumberMatcher from './PhoneNumberMatcher.js' 2 | import normalizeArguments from './normalizeArguments.js' 3 | 4 | export default function searchPhoneNumbersInText() { 5 | const { text, options, metadata } = normalizeArguments(arguments) 6 | const matcher = new PhoneNumberMatcher(text, { ...options, v2: true }, metadata) 7 | return { 8 | [Symbol.iterator]() { 9 | return { 10 | next: () => { 11 | if (matcher.hasNext()) { 12 | return { 13 | done: false, 14 | value: matcher.next() 15 | } 16 | } 17 | return { 18 | done: true 19 | } 20 | } 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /source/searchPhoneNumbersInText.test.js: -------------------------------------------------------------------------------- 1 | import searchPhoneNumbersInText from './searchPhoneNumbersInText.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | describe('searchPhoneNumbersInText', () => { 5 | it('should find phone numbers (with default country)', () => { 6 | const NUMBERS = ['+78005553535', '+12133734253'] 7 | for (const number of searchPhoneNumbersInText('The number is +7 (800) 555-35-35 and not (213) 373-4253 as written in the document.', 'US', metadata)) { 8 | number.number.number.should.equal(NUMBERS[0]) 9 | NUMBERS.shift() 10 | } 11 | }) 12 | 13 | it('should find phone numbers', () => { 14 | const NUMBERS = ['+78005553535', '+12133734253'] 15 | for (const number of searchPhoneNumbersInText('The number is +7 (800) 555-35-35 and not (213) 373-4253 as written in the document.', metadata)) { 16 | number.number.number.should.equal(NUMBERS[0]) 17 | NUMBERS.shift() 18 | } 19 | }) 20 | 21 | it('should find phone numbers in text', () => { 22 | const expectedNumbers = [{ 23 | country: 'RU', 24 | nationalNumber: '8005553535', 25 | startsAt: 14, 26 | endsAt: 32 27 | }, { 28 | country: 'US', 29 | nationalNumber: '2133734253', 30 | startsAt: 41, 31 | endsAt: 55 32 | }] 33 | 34 | for (const number of searchPhoneNumbersInText('The number is +7 (800) 555-35-35 and not (213) 373-4253 as written in the document.', 'US', metadata)) { 35 | const expected = expectedNumbers.shift() 36 | number.startsAt.should.equal(expected.startsAt) 37 | number.endsAt.should.equal(expected.endsAt) 38 | number.number.nationalNumber.should.equal(expected.nationalNumber) 39 | number.number.country.should.equal(expected.country) 40 | } 41 | 42 | expectedNumbers.length.should.equal(0) 43 | }) 44 | }) -------------------------------------------------------------------------------- /source/tools/semver-compare.js: -------------------------------------------------------------------------------- 1 | // Copy-pasted from: 2 | // https://github.com/substack/semver-compare/blob/master/index.js 3 | // 4 | // Inlining this function because some users reported issues with 5 | // importing from `semver-compare` in a browser with ES6 "native" modules. 6 | // 7 | // Fixes `semver-compare` not being able to compare versions with alpha/beta/etc "tags". 8 | // https://github.com/catamphetamine/libphonenumber-js/issues/381 9 | export default function(a, b) { 10 | a = a.split('-') 11 | b = b.split('-') 12 | var pa = a[0].split('.') 13 | var pb = b[0].split('.') 14 | for (var i = 0; i < 3; i++) { 15 | var na = Number(pa[i]) 16 | var nb = Number(pb[i]) 17 | if (na > nb) return 1 18 | if (nb > na) return -1 19 | if (!isNaN(na) && isNaN(nb)) return 1 20 | if (isNaN(na) && !isNaN(nb)) return -1 21 | } 22 | if (a[1] && b[1]) { 23 | return a[1] > b[1] ? 1 : (a[1] < b[1] ? -1 : 0) 24 | } 25 | return !a[1] && b[1] ? 1 : (a[1] && !b[1] ? -1 : 0) 26 | } -------------------------------------------------------------------------------- /source/tools/semver-compare.test.js: -------------------------------------------------------------------------------- 1 | import semverCompare from './semver-compare.js' 2 | 3 | describe('semver-compare', () => { 4 | it('should compare versions', () => { 5 | const versions = [ 6 | '1.2.3', 7 | '4.11.6', 8 | '4.2.0', 9 | '1.5.19', 10 | '1.5.6', 11 | '1.5.4', 12 | '1.5.5-alpha.beta', 13 | '1.5.5-alpha', 14 | '1.5.5', 15 | '1.5.5-rc.1', 16 | '1.5.5-beta.0', 17 | '4.1.3', 18 | '2.3.1', 19 | '10.5.5', 20 | '11.3.0' 21 | ] 22 | versions.sort(semverCompare).should.deep.equal([ 23 | '1.2.3', 24 | '1.5.4', 25 | '1.5.5-alpha', 26 | '1.5.5-alpha.beta', 27 | '1.5.5-beta.0', 28 | '1.5.5-rc.1', 29 | '1.5.5', 30 | '1.5.6', 31 | '1.5.19', 32 | '2.3.1', 33 | '4.1.3', 34 | '4.2.0', 35 | '4.11.6', 36 | '10.5.5', 37 | '11.3.0' 38 | ]) 39 | }) 40 | }) -------------------------------------------------------------------------------- /source/validatePhoneNumberLength.js: -------------------------------------------------------------------------------- 1 | import normalizeArguments from './normalizeArguments.js' 2 | import parsePhoneNumberWithError from './parsePhoneNumberWithError_.js' 3 | import ParseError from './ParseError.js' 4 | import Metadata from './metadata.js' 5 | import checkNumberLength from './helpers/checkNumberLength.js' 6 | 7 | export default function validatePhoneNumberLength() { 8 | let { text, options, metadata } = normalizeArguments(arguments) 9 | options = { 10 | ...options, 11 | extract: false 12 | } 13 | 14 | // Parse phone number. 15 | try { 16 | const phoneNumber = parsePhoneNumberWithError(text, options, metadata) 17 | metadata = new Metadata(metadata) 18 | metadata.selectNumberingPlan(phoneNumber.countryCallingCode) 19 | const result = checkNumberLength(phoneNumber.nationalNumber, metadata) 20 | if (result !== 'IS_POSSIBLE') { 21 | return result 22 | } 23 | } catch (error) { 24 | /* istanbul ignore else */ 25 | if (error instanceof ParseError) { 26 | return error.message 27 | } else { 28 | throw error 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /source/validatePhoneNumberLength.test.js: -------------------------------------------------------------------------------- 1 | import _validatePhoneNumberLength from './validatePhoneNumberLength.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | function validatePhoneNumberLength(...parameters) { 5 | parameters.push(metadata) 6 | return _validatePhoneNumberLength.apply(this, parameters) 7 | } 8 | 9 | describe('validatePhoneNumberLength', () => { 10 | it('should detect whether a phone number length is valid', () => { 11 | // Not a phone number. 12 | validatePhoneNumberLength('+').should.equal('NOT_A_NUMBER') 13 | validatePhoneNumberLength('abcde').should.equal('NOT_A_NUMBER') 14 | 15 | // No country supplied for a national number. 16 | validatePhoneNumberLength('123').should.equal('INVALID_COUNTRY') 17 | 18 | // Too short while the number is not considered "viable" 19 | // by Google's `libphonenumber`. 20 | validatePhoneNumberLength('2', 'US').should.equal('TOO_SHORT') 21 | validatePhoneNumberLength('+1', 'US').should.equal('TOO_SHORT') 22 | validatePhoneNumberLength('+12', 'US').should.equal('TOO_SHORT') 23 | 24 | // Test national (significant) number length. 25 | validatePhoneNumberLength('444 1 44', 'TR').should.equal('TOO_SHORT') 26 | expect(validatePhoneNumberLength('444 1 444', 'TR')).to.be.undefined 27 | validatePhoneNumberLength('444 1 4444', 'TR').should.equal('INVALID_LENGTH') 28 | validatePhoneNumberLength('444 1 4444444444', 'TR').should.equal('TOO_LONG') 29 | }) 30 | }) -------------------------------------------------------------------------------- /test/metadata/1.0.0/isPossibleNumber.test.js: -------------------------------------------------------------------------------- 1 | import metadata from './metadata.min.json' assert { type: 'json' } 2 | import _isPossibleNumber from '../../../source/legacy/isPossibleNumber.js' 3 | 4 | function isPossibleNumber(...parameters) 5 | { 6 | parameters.push(metadata) 7 | return _isPossibleNumber.apply(this, parameters) 8 | } 9 | 10 | describe('isPossibleNumber', () => 11 | { 12 | it('should work in edge cases', function() 13 | { 14 | expect(() => isPossibleNumber({ phone: '1112223344', country: 'RU' })).to.throw('Missing "possibleLengths" in metadata.') 15 | }) 16 | }) -------------------------------------------------------------------------------- /test/metadata/1.0.0/metadata.test.js: -------------------------------------------------------------------------------- 1 | import metadata from './metadata.min.json' assert { type: 'json' } 2 | 3 | import Metadata from '../../../source/metadata.js' 4 | 5 | describe('metadata', () => 6 | { 7 | it('should return undefined for non-defined types', function() 8 | { 9 | const FR = new Metadata(metadata).country('FR') 10 | type(FR.type('FIXED_LINE')).should.equal('undefined') 11 | }) 12 | }) 13 | 14 | function type(something) 15 | { 16 | return typeof something 17 | } -------------------------------------------------------------------------------- /test/metadata/1.0.0/types.test.js: -------------------------------------------------------------------------------- 1 | import metadata from './metadata.max.json' assert { type: 'json' } 2 | import get_number_type_custom from '../../../source/legacy/getNumberType.js' 3 | 4 | function get_number_type(...parameters) 5 | { 6 | parameters.push(metadata) 7 | return get_number_type_custom.apply(this, parameters) 8 | } 9 | 10 | describe('get_number_type', () => 11 | { 12 | it('should infer phone number type MOBILE', function() 13 | { 14 | get_number_type('9150000000', 'RU').should.equal('MOBILE') 15 | get_number_type('7912345678', 'GB').should.equal('MOBILE') 16 | get_number_type('91187654321', 'AR').should.equal('MOBILE') 17 | // get_number_type('15123456789', 'DE').should.equal('MOBILE') 18 | get_number_type('51234567', 'EE').should.equal('MOBILE') 19 | }) 20 | 21 | it('should infer phone number types', function() 22 | { 23 | get_number_type('88005553535', 'RU').should.equal('TOLL_FREE') 24 | get_number_type('8005553535', 'RU').should.equal('TOLL_FREE') 25 | get_number_type('4957777777', 'RU').should.equal('FIXED_LINE') 26 | get_number_type('8030000000', 'RU').should.equal('PREMIUM_RATE') 27 | 28 | get_number_type('2133734253', 'US').should.equal('FIXED_LINE_OR_MOBILE') 29 | get_number_type('5002345678', 'US').should.equal('PERSONAL_NUMBER') 30 | }) 31 | 32 | it('should return FIXED_LINE_OR_MOBILE when there is ambiguity', () => 33 | { 34 | // (no such country in the metadata, therefore no unit test for this `if`) 35 | }) 36 | 37 | it('should work in edge cases', function() 38 | { 39 | let thrower 40 | 41 | // // No metadata 42 | // thrower = () => get_number_type_custom({ phone: '+78005553535' }) 43 | // thrower.should.throw('`metadata` argument not passed') 44 | 45 | // Parsed phone number 46 | get_number_type({ phone: '8005553535', country: 'RU' }).should.equal('TOLL_FREE') 47 | 48 | // Invalid phone number 49 | type(get_number_type('123', 'RU')).should.equal('undefined') 50 | 51 | // Numerical `value` 52 | thrower = () => get_number_type(89150000000, 'RU') 53 | thrower.should.throw('A phone number must either be a string or an object of shape { phone, [country] }.') 54 | }) 55 | }) 56 | 57 | function type(something) 58 | { 59 | return typeof something 60 | } 61 | -------------------------------------------------------------------------------- /test/metadata/1.0.0/validate.test.js: -------------------------------------------------------------------------------- 1 | import metadata from './metadata.min.json' assert { type: 'json' } 2 | import validate from '../../../source/legacy/isValidNumber.js' 3 | 4 | function is_valid_number(...parameters) 5 | { 6 | parameters.push(metadata) 7 | return validate.apply(this, parameters) 8 | } 9 | 10 | describe('validate', () => 11 | { 12 | it('should validate phone numbers', function() 13 | { 14 | is_valid_number('+1-213-373-4253').should.equal(true) 15 | is_valid_number('+1-213-373').should.equal(false) 16 | 17 | is_valid_number('+1-213-373-4253', undefined).should.equal(true) 18 | 19 | is_valid_number('(213) 373-4253', 'US').should.equal(true) 20 | is_valid_number('(213) 37', 'US').should.equal(false) 21 | 22 | is_valid_number({ country: 'US', phone: '2133734253' }).should.equal(true) 23 | }) 24 | 25 | it('should refine phone number validation in case extended regular expressions are set for a country', () => 26 | { 27 | // Germany general validation must pass 28 | is_valid_number('123456', 'DE').should.equal(true) 29 | is_valid_number('0123456', 'DE').should.equal(true) 30 | 31 | // Extra regular expressions for precise national number validation. 32 | // `types` index in compressed array is `9` 33 | metadata.countries.DE[9] = 34 | [ 35 | "[246]\\d{5,13}|3(?:0\\d{3,13}|2\\d{9}|[3-9]\\d{4,13})|5(?:0[2-8]|[1256]\\d|[38][0-8]|4\\d{0,2}|[79][0-7])\\d{3,11}|7(?:0[2-8]|[1-9]\\d)\\d{3,10}|8(?:0[2-9]|[1-9]\\d)\\d{3,10}|9(?:0[6-9]\\d{3,10}|1\\d{4,12}|[2-9]\\d{4,11})", 36 | "1(?:5[0-25-9]\\d{8}|6[023]\\d{7,8}|7(?:[0-57-9]\\d?|6\\d)\\d{7})", 37 | "800\\d{7,12}", 38 | "137[7-9]\\d{6}|900(?:[135]\\d{6}|9\\d{7})", 39 | "700\\d{8}", 40 | "1(?:5(?:(?:2\\d55|7\\d99|9\\d33)\\d{7}|(?:[034568]00|113)\\d{8})|6(?:013|255|399)\\d{7,8}|7(?:[015]13|[234]55|[69]33|[78]99)\\d{7,8})", 41 | "18(?:1\\d{5,11}|[2-9]\\d{8})", 42 | "16(?:4\\d{1,10}|[89]\\d{1,11})" 43 | ] 44 | 45 | // Germany extended validation must not pass for an invalid phone number 46 | is_valid_number('123456', 'DE').should.equal(false) 47 | is_valid_number('0123456', 'DE').should.equal(false) 48 | 49 | // // Germany extended validation must pass for a valid phone number, 50 | // // but still must demand the national prefix (`0`). 51 | // // https://github.com/catamphetamine/libphonenumber-js/issues/6 52 | // is_valid_number('30123456', 'DE').should.equal(false) 53 | is_valid_number('030123456', 'DE').should.equal(true) 54 | }) 55 | 56 | it('should work in edge cases', function() 57 | { 58 | // No metadata 59 | let thrower = () => validate('+78005553535') 60 | thrower.should.throw('`metadata` argument not passed') 61 | 62 | // Non-phone-number characters in a phone number 63 | is_valid_number('+499821958a').should.equal(false) 64 | is_valid_number('88005553535x', 'RU').should.equal(false) 65 | 66 | // Numerical `value` 67 | thrower = () => is_valid_number(88005553535, 'RU') 68 | thrower.should.throw('A phone number must either be a string or an object of shape { phone, [country] }.') 69 | 70 | // Long country phone code 71 | is_valid_number('+3725555555').should.equal(true) 72 | }) 73 | 74 | it('should accept phone number extensions', function() 75 | { 76 | // International 77 | is_valid_number('+12133734253 ext. 123').should.equal(true) 78 | // National 79 | is_valid_number('88005553535 x123', 'RU').should.equal(true) 80 | }) 81 | }) -------------------------------------------------------------------------------- /test/metadata/1.1.11/test/getCountryCallingCode.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../metadata.min.json' assert { type: 'json' } 2 | 3 | import getCountryCallingCode from '../../../../source/getCountryCallingCode.js' 4 | 5 | describe('getCountryCallingCode', () => 6 | { 7 | it('should get country calling code', () => 8 | { 9 | getCountryCallingCode('US', metadata).should.equal('1') 10 | }) 11 | 12 | it('should throw if country is unknown', () => 13 | { 14 | expect(() => getCountryCallingCode('ZZ', metadata)).to.throw('Unknown country: ZZ') 15 | }) 16 | }) -------------------------------------------------------------------------------- /test/metadata/1.1.11/test/metadata.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../metadata.min.json' assert { type: 'json' } 2 | 3 | import Metadata, { validateMetadata } from '../../../../source/metadata.js' 4 | 5 | describe('metadata', () => 6 | { 7 | it('should return undefined for non-defined types', function() 8 | { 9 | const FR = new Metadata(metadata).country('FR') 10 | type(FR.type('FIXED_LINE')).should.equal('undefined') 11 | }) 12 | 13 | it('should validate country', function() 14 | { 15 | const thrower = () => new Metadata(metadata).country('RUS') 16 | thrower.should.throw('Unknown country') 17 | }) 18 | 19 | it('should validate metadata', function() 20 | { 21 | let thrower = () => validateMetadata() 22 | thrower.should.throw('`metadata` argument not passed') 23 | 24 | thrower = () => validateMetadata(123) 25 | thrower.should.throw('Got a number: 123.') 26 | 27 | thrower = () => validateMetadata('abc') 28 | thrower.should.throw('Got a string: abc.') 29 | 30 | thrower = () => validateMetadata({ a: true, b: 2 }) 31 | thrower.should.throw('Got an object of shape: { a, b }.') 32 | 33 | thrower = () => validateMetadata({ a: true, countries: 2 }) 34 | thrower.should.throw('Got an object of shape: { a, countries }.') 35 | 36 | thrower = () => validateMetadata({ country_calling_codes: true, countries: 2 }) 37 | thrower.should.throw('Got an object of shape') 38 | 39 | thrower = () => validateMetadata({ country_calling_codes: {}, countries: 2 }) 40 | thrower.should.throw('Got an object of shape') 41 | 42 | validateMetadata({ country_calling_codes: {}, countries: {}, b: 3 }) 43 | }) 44 | 45 | it('should support new metadata', () => 46 | { 47 | const US = new Metadata(metadata).country('US') 48 | expect(US.IDDPrefix()).to.be.undefined 49 | expect(US.defaultIDDPrefix()).to.be.undefined 50 | }) 51 | }) 52 | 53 | function type(something) 54 | { 55 | return typeof something 56 | } -------------------------------------------------------------------------------- /test/metadata/1.1.11/test/parsePhoneNumberFromString.test.js: -------------------------------------------------------------------------------- 1 | import _parsePhoneNumberFromString from '../../../../source/parsePhoneNumber.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | function parsePhoneNumberFromString(...parameters) { 5 | parameters.push(metadata) 6 | return _parsePhoneNumberFromString.apply(this, parameters) 7 | } 8 | 9 | describe('parsePhoneNumberFromString', () => { 10 | it('should parse phone numbers from string', () => { 11 | parsePhoneNumberFromString('Phone: 8 (800) 555 35 35.', 'RU').nationalNumber.should.equal('8005553535') 12 | expect(parsePhoneNumberFromString('3', 'RU')).to.be.undefined 13 | }) 14 | 15 | it('should work in edge cases', () => { 16 | expect(parsePhoneNumberFromString('')).to.be.undefined 17 | }) 18 | 19 | it('should parse phone numbers when invalid country code is passed', () => { 20 | parsePhoneNumberFromString('Phone: +7 (800) 555 35 35.', 'XX').nationalNumber.should.equal('8005553535') 21 | expect(parsePhoneNumberFromString('Phone: 8 (800) 555-35-35.', 'XX')).to.be.undefined 22 | }) 23 | 24 | it('should handle old metadata and non-geographic calling codes', () => { 25 | const phoneNumber = parsePhoneNumberFromString('+870773111632') 26 | expect(phoneNumber.country).to.be.undefined 27 | phoneNumber.countryCallingCode.should.equal('870') 28 | phoneNumber.isPossible().should.equal(true) 29 | phoneNumber.isNonGeographic().should.equal(true) 30 | }) 31 | 32 | it('should cover all cases in metadata', () => { 33 | const phoneNumber = parsePhoneNumberFromString('+78005553535') 34 | phoneNumber.isNonGeographic().should.equal(false) 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /test/metadata/1.7.34/test/findPhoneNumbersInText.test.js: -------------------------------------------------------------------------------- 1 | import findPhoneNumbersInText from '../../../../source/findPhoneNumbersInText.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | describe('findPhoneNumbersInText', () => { 5 | it('should find phone numbers in text (with default country)', () => { 6 | findPhoneNumbersInText('+7 (800) 555-35-35', 'US', metadata)[0].number.number.should.equal('+78005553535') 7 | }) 8 | 9 | it('should find phone numbers in text (with default country in options)', () => { 10 | findPhoneNumbersInText('+7 (800) 555-35-35', { defaultCountry: 'US' }, metadata)[0].number.number.should.equal('+78005553535') 11 | }) 12 | 13 | it('should find phone numbers in text (with default country and options)', () => { 14 | findPhoneNumbersInText('+7 (800) 555-35-35', 'US', {}, metadata)[0].number.number.should.equal('+78005553535') 15 | }) 16 | 17 | it('should find phone numbers in text (without default country, with options)', () => { 18 | findPhoneNumbersInText('+7 (800) 555-35-35', undefined, {}, metadata)[0].number.number.should.equal('+78005553535') 19 | }) 20 | 21 | it('should find phone numbers in text (with default country, without options)', () => { 22 | findPhoneNumbersInText('+7 (800) 555-35-35', 'US', undefined, metadata)[0].number.number.should.equal('+78005553535') 23 | }) 24 | 25 | it('should find phone numbers in text (with empty default country)', () => { 26 | findPhoneNumbersInText('+7 (800) 555-35-35', undefined, metadata)[0].number.number.should.equal('+78005553535') 27 | }) 28 | 29 | it('should find phone numbers in text', () => { 30 | const NUMBERS = ['+78005553535', '+12133734253'] 31 | const results = findPhoneNumbersInText('The number is +7 (800) 555-35-35 and not (213) 373-4253 as written in the document.', metadata) 32 | let i = 0 33 | while (i < results.length) { 34 | results[i].number.number.should.equal(NUMBERS[i]) 35 | i++ 36 | } 37 | }) 38 | }) -------------------------------------------------------------------------------- /test/metadata/1.7.34/test/isPossibleNumber.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../metadata.min.json' assert { type: 'json' } 2 | import _isPossibleNumber from '../../../../source/legacy/isPossibleNumber.js' 3 | 4 | function isPossibleNumber(...parameters) { 5 | parameters.push(metadata) 6 | return _isPossibleNumber.apply(this, parameters) 7 | } 8 | 9 | describe('isPossibleNumber', () => { 10 | it('should work', function() 11 | { 12 | isPossibleNumber('+79992223344').should.equal(true) 13 | 14 | isPossibleNumber({ phone: '1112223344', country: 'RU' }).should.equal(true) 15 | isPossibleNumber({ phone: '111222334', country: 'RU' }).should.equal(false) 16 | isPossibleNumber({ phone: '11122233445', country: 'RU' }).should.equal(false) 17 | 18 | isPossibleNumber({ phone: '1112223344', countryCallingCode: 7 }).should.equal(true) 19 | }) 20 | 21 | it('should work v2', () => { 22 | isPossibleNumber({ nationalNumber: '111222334', countryCallingCode: 7 }, { v2: true }).should.equal(false) 23 | isPossibleNumber({ nationalNumber: '1112223344', countryCallingCode: 7 }, { v2: true }).should.equal(true) 24 | isPossibleNumber({ nationalNumber: '11122233445', countryCallingCode: 7 }, { v2: true }).should.equal(false) 25 | }) 26 | 27 | it('should work in edge cases', () => { 28 | // Invalid `PhoneNumber` argument. 29 | expect(() => isPossibleNumber({}, { v2: true })).to.throw('Invalid phone number object passed') 30 | 31 | // Empty input is passed. 32 | // This is just to support `isValidNumber({})` 33 | // for cases when `parseNumber()` returns `{}`. 34 | isPossibleNumber({}).should.equal(false) 35 | expect(() => isPossibleNumber({ phone: '1112223344' })).to.throw('Invalid phone number object passed') 36 | 37 | // Incorrect country. 38 | expect(() => isPossibleNumber({ phone: '1112223344', country: 'XX' })).to.throw('Unknown country') 39 | }) 40 | }) -------------------------------------------------------------------------------- /test/metadata/1.7.34/test/metadata.test.js: -------------------------------------------------------------------------------- 1 | import metadata from '../metadata.min.json' assert { type: 'json' } 2 | 3 | import Metadata from '../../../../source/metadata.js' 4 | 5 | describe('metadata', () => { 6 | it('should cover non-occuring edge cases', () => { 7 | new Metadata(metadata).getNumberingPlanMetadata('999') 8 | }) 9 | }) -------------------------------------------------------------------------------- /test/metadata/1.7.34/test/parseIncompletePhoneNumber.test.js: -------------------------------------------------------------------------------- 1 | import parseIncompletePhoneNumber, { parsePhoneNumberCharacter } from '../../../../source/parseIncompletePhoneNumber.js' 2 | 3 | describe('parseIncompletePhoneNumber', () => { 4 | it('should parse phone number character', () => { 5 | // Accepts leading `+`. 6 | parsePhoneNumberCharacter('+').should.equal('+') 7 | 8 | // Doesn't accept non-leading `+`. 9 | expect(parsePhoneNumberCharacter('+', '+')).to.be.undefined 10 | 11 | // Parses digits. 12 | parsePhoneNumberCharacter('1').should.equal('1') 13 | 14 | // Parses non-European digits. 15 | parsePhoneNumberCharacter('٤').should.equal('4') 16 | 17 | // Dismisses other characters. 18 | expect(parsePhoneNumberCharacter('-')).to.be.undefined 19 | }) 20 | 21 | it('should parse incomplete phone number', () => { 22 | parseIncompletePhoneNumber('').should.equal('') 23 | 24 | // Doesn't accept non-leading `+`. 25 | parseIncompletePhoneNumber('++').should.equal('+') 26 | 27 | // Accepts leading `+`. 28 | parseIncompletePhoneNumber('+7 800 555').should.equal('+7800555') 29 | 30 | // Parses digits. 31 | parseIncompletePhoneNumber('8 (800) 555').should.equal('8800555') 32 | 33 | // Parses non-European digits. 34 | parseIncompletePhoneNumber('+٤٤٢٣٢٣٢٣٤').should.equal('+442323234') 35 | }) 36 | }) -------------------------------------------------------------------------------- /test/metadata/1.7.34/test/parsePhoneNumberFromString.test.js: -------------------------------------------------------------------------------- 1 | import _parsePhoneNumberFromString from '../../../../source/parsePhoneNumber.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | function parsePhoneNumberFromString(...parameters) { 5 | parameters.push(metadata) 6 | return _parsePhoneNumberFromString.apply(this, parameters) 7 | } 8 | 9 | describe('parsePhoneNumberFromString', () => { 10 | it('should parse phone numbers from string', () => { 11 | parsePhoneNumberFromString('Phone: 8 (800) 555 35 35.', 'RU').nationalNumber.should.equal('8005553535') 12 | expect(parsePhoneNumberFromString('3', 'RU')).to.be.undefined 13 | }) 14 | 15 | it('should work in edge cases', () => { 16 | expect(parsePhoneNumberFromString('')).to.be.undefined 17 | }) 18 | 19 | it('should parse phone numbers when invalid country code is passed', () => { 20 | parsePhoneNumberFromString('Phone: +7 (800) 555 35 35.', 'XX').nationalNumber.should.equal('8005553535') 21 | expect(parsePhoneNumberFromString('Phone: 8 (800) 555-35-35.', 'XX')).to.be.undefined 22 | }) 23 | }) 24 | -------------------------------------------------------------------------------- /test/metadata/1.7.37/test/parsePhoneNumberFromString.test.js: -------------------------------------------------------------------------------- 1 | import _parsePhoneNumberFromString from '../../../../source/parsePhoneNumber.js' 2 | import metadata from '../metadata.min.json' assert { type: 'json' } 3 | 4 | function parsePhoneNumberFromString(...parameters) { 5 | parameters.push(metadata) 6 | return _parsePhoneNumberFromString.apply(this, parameters) 7 | } 8 | 9 | describe('parsePhoneNumberFromString', () => { 10 | it('should handle the bug when non-geographic numbering plans didn\'t have "possible_lengths" set', () => { 11 | const phoneNumber = parsePhoneNumberFromString('+870773111632') 12 | expect(phoneNumber.country).to.be.undefined 13 | phoneNumber.countryCallingCode.should.equal('870') 14 | phoneNumber.isPossible().should.equal(true) 15 | // All numbers are assumed being possible. 16 | const phoneNumber2 = parsePhoneNumberFromString('+8707731116321') 17 | phoneNumber2.isPossible().should.equal(true) 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /test/setup.js: -------------------------------------------------------------------------------- 1 | import chai, { expect } from 'chai' 2 | 3 | global.expect = expect 4 | chai.should() -------------------------------------------------------------------------------- /website/lib/input-format.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).InputFormat={})}(this,(function(e){"use strict";function t(e,t){var n=0,r=t.split(""),i=Array.isArray(r),a=0;for(r=i?r:r[Symbol.iterator]();;){var o;if(i){if(a>=r.length)break;o=r[a++]}else{if((a=r.next()).done)break;o=a.value}o===e&&n++}return n}function n(e,n){for(var r=arguments.length>2&&void 0!==arguments[2]?arguments[2]:"x",i=arguments.length>3&&void 0!==arguments[3]?arguments[3]:" ",a=e.length,o=t("(",e),u=t(")",e),c=o-u;c>0&&a1&&void 0!==arguments[1]?arguments[1]:"x",i=arguments.length>2?arguments[2]:void 0;if(!e)return function(e){return{text:e}};var a=t(r,e);return function(t){if(!t)return{text:"",template:e};var o=0,u="",c=e.split(""),f=Array.isArray(c),l=0;for(c=f?c:c[Symbol.iterator]();;){var s;if(f){if(l>=c.length)break;s=c[l++]}else{if((l=c.next()).done)break;s=l.value}var v=s;if(v===r){if(u+=t[o],++o===t.length&&t.lengtha&&(i=r.length))),a++}return void 0===t&&(i=r.length),{value:r,caret:i}}function o(e,t,n){"string"==typeof n&&(n=r(n));var i=n(e)||{},a=i.text,o=i.template;if(void 0===a&&(a=e),o)if(void 0===t)t=a.length;else{for(var u=0,c=!1,f=-1;u0&&(e=e.slice(0,t-1)+e.slice(t),t--);break;case"Delete":e=e.slice(0,t)+e.slice(t+1)}return{value:e,caret:t}}(c,f,r);c=s.value,f=s.caret}var v=o(c,f,n),d=v.text;f=v.caret,e.value=d,l(e,f),i(c)}e.format=o,e.onChange=function(e,t,n,r,i){d(t,n,r,void 0,i)},e.onCut=function(e,t,n,r,i){setTimeout((function(){return d(t,n,r,void 0,i)}),0)},e.onKeyDown=function(e,t,n,r,i){var a=function(e){switch(e.keyCode){case c:return"Backspace";case f:return"Delete"}}(e);switch(a){case"Delete":case"Backspace":e.preventDefault();var o=u(t);return o?(v(t,o),d(t,n,r,void 0,i)):d(t,n,r,a,i)}},e.onPaste=function(e,t,n,r,i){var a=u(t);a&&v(t,a),d(t,n,r,void 0,i)},e.parse=a,e.parseDigit=function(e,t){return i[e]},e.templateFormatter=r,e.templateParser=function(e,n,r){"function"==typeof n&&(r=n,n="x");var i=t(n,e);return function(e,t){if(t.length