extractPhoneContext.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103
  1. // When phone numbers are written in `RFC3966` format — `"tel:+12133734253"` —
  2. // they can have their "calling code" part written separately in a `phone-context` parameter.
  3. // Example: `"tel:12133734253;phone-context=+1"`.
  4. // This function parses the full phone number from the local number and the `phone-context`
  5. // when the `phone-context` contains a `+` sign.
  6. import {
  7. VALID_DIGITS,
  8. // PLUS_CHARS
  9. } from '../constants.js'
  10. export const PLUS_SIGN = '+'
  11. const RFC3966_VISUAL_SEPARATOR_ = '[\\-\\.\\(\\)]?'
  12. const RFC3966_PHONE_DIGIT_ = '(' + '[' + VALID_DIGITS + ']' + '|' + RFC3966_VISUAL_SEPARATOR_ + ')'
  13. const RFC3966_GLOBAL_NUMBER_DIGITS_ =
  14. '^' +
  15. '\\' +
  16. PLUS_SIGN +
  17. RFC3966_PHONE_DIGIT_ +
  18. '*' +
  19. '[' + VALID_DIGITS + ']' +
  20. RFC3966_PHONE_DIGIT_ +
  21. '*' +
  22. '$'
  23. /**
  24. * Regular expression of valid global-number-digits for the phone-context
  25. * parameter, following the syntax defined in RFC3966.
  26. */
  27. const RFC3966_GLOBAL_NUMBER_DIGITS_PATTERN_ = new RegExp(RFC3966_GLOBAL_NUMBER_DIGITS_, 'g')
  28. // In this port of Google's library, we don't accept alpha characters in phone numbers.
  29. // const ALPHANUM_ = VALID_ALPHA_ + VALID_DIGITS
  30. const ALPHANUM_ = VALID_DIGITS
  31. const RFC3966_DOMAINLABEL_ = '[' + ALPHANUM_ + ']+((\\-)*[' + ALPHANUM_ + '])*'
  32. const VALID_ALPHA_ = 'a-zA-Z'
  33. const RFC3966_TOPLABEL_ = '[' + VALID_ALPHA_ + ']+((\\-)*[' + ALPHANUM_ + '])*'
  34. const RFC3966_DOMAINNAME_ = '^(' + RFC3966_DOMAINLABEL_ + '\\.)*' + RFC3966_TOPLABEL_ + '\\.?$'
  35. /**
  36. * Regular expression of valid domainname for the phone-context parameter,
  37. * following the syntax defined in RFC3966.
  38. */
  39. const RFC3966_DOMAINNAME_PATTERN_ = new RegExp(RFC3966_DOMAINNAME_, 'g')
  40. export const RFC3966_PREFIX_ = 'tel:'
  41. export const RFC3966_PHONE_CONTEXT_ = ';phone-context='
  42. export const RFC3966_ISDN_SUBADDRESS_ = ';isub='
  43. /**
  44. * Extracts the value of the phone-context parameter of `numberToExtractFrom`,
  45. * following the syntax defined in RFC3966.
  46. *
  47. * @param {string} numberToExtractFrom
  48. * @return {string|null} the extracted string (possibly empty), or `null` if no phone-context parameter is found.
  49. */
  50. export default function extractPhoneContext(numberToExtractFrom) {
  51. const indexOfPhoneContext = numberToExtractFrom.indexOf(RFC3966_PHONE_CONTEXT_)
  52. // If no phone-context parameter is present
  53. if (indexOfPhoneContext < 0) {
  54. return null
  55. }
  56. const phoneContextStart = indexOfPhoneContext + RFC3966_PHONE_CONTEXT_.length
  57. // If phone-context parameter is empty
  58. if (phoneContextStart >= numberToExtractFrom.length) {
  59. return ''
  60. }
  61. const phoneContextEnd = numberToExtractFrom.indexOf(';', phoneContextStart)
  62. // If phone-context is not the last parameter
  63. if (phoneContextEnd >= 0) {
  64. return numberToExtractFrom.substring(phoneContextStart, phoneContextEnd)
  65. } else {
  66. return numberToExtractFrom.substring(phoneContextStart)
  67. }
  68. }
  69. /**
  70. * Returns whether the value of phoneContext follows the syntax defined in RFC3966.
  71. *
  72. * @param {string|null} phoneContext
  73. * @return {boolean}
  74. */
  75. export function isPhoneContextValid(phoneContext) {
  76. if (phoneContext === null) {
  77. return true
  78. }
  79. if (phoneContext.length === 0) {
  80. return false
  81. }
  82. // Does phone-context value match pattern of global-number-digits or domainname.
  83. return RFC3966_GLOBAL_NUMBER_DIGITS_PATTERN_.test(phoneContext) ||
  84. RFC3966_DOMAINNAME_PATTERN_.test(phoneContext)
  85. }