extractNationalNumberFromPossiblyIncompleteNumber.js 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. /**
  2. * Strips any national prefix (such as 0, 1) present in a
  3. * (possibly incomplete) number provided.
  4. * "Carrier codes" are only used in Colombia and Brazil,
  5. * and only when dialing within those countries from a mobile phone to a fixed line number.
  6. * Sometimes it won't actually strip national prefix
  7. * and will instead prepend some digits to the `number`:
  8. * for example, when number `2345678` is passed with `VI` country selected,
  9. * it will return `{ number: "3402345678" }`, because `340` area code is prepended.
  10. * @param {string} number — National number digits.
  11. * @param {object} metadata — Metadata with country selected.
  12. * @return {object} `{ nationalNumber: string, nationalPrefix: string? carrierCode: string? }`. Even if a national prefix was extracted, it's not necessarily present in the returned object, so don't rely on its presence in the returned object in order to find out whether a national prefix has been extracted or not.
  13. */
  14. export default function extractNationalNumberFromPossiblyIncompleteNumber(number, metadata) {
  15. if (number && metadata.numberingPlan.nationalPrefixForParsing()) {
  16. // See METADATA.md for the description of
  17. // `national_prefix_for_parsing` and `national_prefix_transform_rule`.
  18. // Attempt to parse the first digits as a national prefix.
  19. const prefixPattern = new RegExp('^(?:' + metadata.numberingPlan.nationalPrefixForParsing() + ')')
  20. const prefixMatch = prefixPattern.exec(number)
  21. if (prefixMatch) {
  22. let nationalNumber
  23. let carrierCode
  24. // https://gitlab.com/catamphetamine/libphonenumber-js/-/blob/master/METADATA.md#national_prefix_for_parsing--national_prefix_transform_rule
  25. // If a `national_prefix_for_parsing` has any "capturing groups"
  26. // then it means that the national (significant) number is equal to
  27. // those "capturing groups" transformed via `national_prefix_transform_rule`,
  28. // and nothing could be said about the actual national prefix:
  29. // what is it and was it even there.
  30. // If a `national_prefix_for_parsing` doesn't have any "capturing groups",
  31. // then everything it matches is a national prefix.
  32. // To determine whether `national_prefix_for_parsing` matched any
  33. // "capturing groups", the value of the result of calling `.exec()`
  34. // is looked at, and if it has non-undefined values where there're
  35. // "capturing groups" in the regular expression, then it means
  36. // that "capturing groups" have been matched.
  37. // It's not possible to tell whether there'll be any "capturing gropus"
  38. // before the matching process, because a `national_prefix_for_parsing`
  39. // could exhibit both behaviors.
  40. const capturedGroupsCount = prefixMatch.length - 1
  41. const hasCapturedGroups = capturedGroupsCount > 0 && prefixMatch[capturedGroupsCount]
  42. if (metadata.nationalPrefixTransformRule() && hasCapturedGroups) {
  43. nationalNumber = number.replace(
  44. prefixPattern,
  45. metadata.nationalPrefixTransformRule()
  46. )
  47. // If there's more than one captured group,
  48. // then carrier code is the second one.
  49. if (capturedGroupsCount > 1) {
  50. carrierCode = prefixMatch[1]
  51. }
  52. }
  53. // If there're no "capturing groups",
  54. // or if there're "capturing groups" but no
  55. // `national_prefix_transform_rule`,
  56. // then just strip the national prefix from the number,
  57. // and possibly a carrier code.
  58. // Seems like there could be more.
  59. else {
  60. // `prefixBeforeNationalNumber` is the whole substring matched by
  61. // the `national_prefix_for_parsing` regular expression.
  62. // There seem to be no guarantees that it's just a national prefix.
  63. // For example, if there's a carrier code, it's gonna be a
  64. // part of `prefixBeforeNationalNumber` too.
  65. const prefixBeforeNationalNumber = prefixMatch[0]
  66. nationalNumber = number.slice(prefixBeforeNationalNumber.length)
  67. // If there's at least one captured group,
  68. // then carrier code is the first one.
  69. if (hasCapturedGroups) {
  70. carrierCode = prefixMatch[1]
  71. }
  72. }
  73. // Tries to guess whether a national prefix was present in the input.
  74. // This is not something copy-pasted from Google's library:
  75. // they don't seem to have an equivalent for that.
  76. // So this isn't an "officially approved" way of doing something like that.
  77. // But since there seems no other existing method, this library uses it.
  78. let nationalPrefix
  79. if (hasCapturedGroups) {
  80. const possiblePositionOfTheFirstCapturedGroup = number.indexOf(prefixMatch[1])
  81. const possibleNationalPrefix = number.slice(0, possiblePositionOfTheFirstCapturedGroup)
  82. // Example: an Argentinian (AR) phone number `0111523456789`.
  83. // `prefixMatch[0]` is `01115`, and `$1` is `11`,
  84. // and the rest of the phone number is `23456789`.
  85. // The national number is transformed via `9$1` to `91123456789`.
  86. // National prefix `0` is detected being present at the start.
  87. // if (possibleNationalPrefix.indexOf(metadata.numberingPlan.nationalPrefix()) === 0) {
  88. if (possibleNationalPrefix === metadata.numberingPlan.nationalPrefix()) {
  89. nationalPrefix = metadata.numberingPlan.nationalPrefix()
  90. }
  91. } else {
  92. nationalPrefix = prefixMatch[0]
  93. }
  94. return {
  95. nationalNumber,
  96. nationalPrefix,
  97. carrierCode
  98. }
  99. }
  100. }
  101. return {
  102. nationalNumber: number
  103. }
  104. }