findPhoneNumbersInitialImplementation.js 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  2. function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
  3. function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
  4. function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
  5. // This is a legacy function.
  6. // Use `findNumbers()` instead.
  7. import { PLUS_CHARS, VALID_PUNCTUATION, VALID_DIGITS, WHITESPACE } from '../constants.js';
  8. import parse from '../parse.js';
  9. import { VALID_PHONE_NUMBER_WITH_EXTENSION } from '../helpers/isViablePhoneNumber.js';
  10. import createExtensionPattern from '../helpers/extension/createExtensionPattern.js';
  11. import parsePreCandidate from '../findNumbers/parsePreCandidate.js';
  12. import isValidPreCandidate from '../findNumbers/isValidPreCandidate.js';
  13. import isValidCandidate from '../findNumbers/isValidCandidate.js';
  14. /**
  15. * Regexp of all possible ways to write extensions, for use when parsing. This
  16. * will be run as a case-insensitive regexp match. Wide character versions are
  17. * also provided after each ASCII version. There are three regular expressions
  18. * here. The first covers RFC 3966 format, where the extension is added using
  19. * ';ext='. The second more generic one starts with optional white space and
  20. * ends with an optional full stop (.), followed by zero or more spaces/tabs
  21. * /commas and then the numbers themselves. The other one covers the special
  22. * case of American numbers where the extension is written with a hash at the
  23. * end, such as '- 503#'. Note that the only capturing groups should be around
  24. * the digits that you want to capture as part of the extension, or else parsing
  25. * will fail! We allow two options for representing the accented o - the
  26. * character itself, and one in the unicode decomposed form with the combining
  27. * acute accent.
  28. */
  29. export var EXTN_PATTERNS_FOR_PARSING = createExtensionPattern('parsing');
  30. var WHITESPACE_IN_THE_BEGINNING_PATTERN = new RegExp('^[' + WHITESPACE + ']+');
  31. var PUNCTUATION_IN_THE_END_PATTERN = new RegExp('[' + VALID_PUNCTUATION + ']+$'); // // Regular expression for getting opening brackets for a valid number
  32. // // found using `PHONE_NUMBER_START_PATTERN` for prepending those brackets to the number.
  33. // const BEFORE_NUMBER_DIGITS_PUNCTUATION = new RegExp('[' + OPENING_BRACKETS + ']+' + '[' + WHITESPACE + ']*' + '$')
  34. var VALID_PRECEDING_CHARACTER_PATTERN = /[^a-zA-Z0-9]/;
  35. export default function findPhoneNumbers(text, options, metadata) {
  36. /* istanbul ignore if */
  37. if (options === undefined) {
  38. options = {};
  39. }
  40. var search = new PhoneNumberSearch(text, options, metadata);
  41. var phones = [];
  42. while (search.hasNext()) {
  43. phones.push(search.next());
  44. }
  45. return phones;
  46. }
  47. /**
  48. * @return ES6 `for ... of` iterator.
  49. */
  50. export function searchPhoneNumbers(text, options, metadata) {
  51. /* istanbul ignore if */
  52. if (options === undefined) {
  53. options = {};
  54. }
  55. var search = new PhoneNumberSearch(text, options, metadata);
  56. return _defineProperty({}, Symbol.iterator, function () {
  57. return {
  58. next: function next() {
  59. if (search.hasNext()) {
  60. return {
  61. done: false,
  62. value: search.next()
  63. };
  64. }
  65. return {
  66. done: true
  67. };
  68. }
  69. };
  70. });
  71. }
  72. /**
  73. * Extracts a parseable phone number including any opening brackets, etc.
  74. * @param {string} text - Input.
  75. * @return {object} `{ ?number, ?startsAt, ?endsAt }`.
  76. */
  77. export var PhoneNumberSearch = /*#__PURE__*/function () {
  78. function PhoneNumberSearch(text, options, metadata) {
  79. _classCallCheck(this, PhoneNumberSearch);
  80. this.text = text; // If assigning the `{}` default value is moved to the arguments above,
  81. // code coverage would decrease for some weird reason.
  82. this.options = options || {};
  83. this.metadata = metadata; // Iteration tristate.
  84. this.state = 'NOT_READY';
  85. this.regexp = new RegExp(VALID_PHONE_NUMBER_WITH_EXTENSION, 'ig');
  86. }
  87. _createClass(PhoneNumberSearch, [{
  88. key: "find",
  89. value: function find() {
  90. var matches = this.regexp.exec(this.text);
  91. if (!matches) {
  92. return;
  93. }
  94. var number = matches[0];
  95. var startsAt = matches.index;
  96. number = number.replace(WHITESPACE_IN_THE_BEGINNING_PATTERN, '');
  97. startsAt += matches[0].length - number.length; // Fixes not parsing numbers with whitespace in the end.
  98. // Also fixes not parsing numbers with opening parentheses in the end.
  99. // https://github.com/catamphetamine/libphonenumber-js/issues/252
  100. number = number.replace(PUNCTUATION_IN_THE_END_PATTERN, '');
  101. number = parsePreCandidate(number);
  102. var result = this.parseCandidate(number, startsAt);
  103. if (result) {
  104. return result;
  105. } // Tail recursion.
  106. // Try the next one if this one is not a valid phone number.
  107. return this.find();
  108. }
  109. }, {
  110. key: "parseCandidate",
  111. value: function parseCandidate(number, startsAt) {
  112. if (!isValidPreCandidate(number, startsAt, this.text)) {
  113. return;
  114. } // Don't parse phone numbers which are non-phone numbers
  115. // due to being part of something else (e.g. a UUID).
  116. // https://github.com/catamphetamine/libphonenumber-js/issues/213
  117. // Copy-pasted from Google's `PhoneNumberMatcher.js` (`.parseAndValidate()`).
  118. if (!isValidCandidate(number, startsAt, this.text, this.options.extended ? 'POSSIBLE' : 'VALID')) {
  119. return;
  120. } // // Prepend any opening brackets left behind by the
  121. // // `PHONE_NUMBER_START_PATTERN` regexp.
  122. // const text_before_number = text.slice(this.searching_from, startsAt)
  123. // const full_number_starts_at = text_before_number.search(BEFORE_NUMBER_DIGITS_PUNCTUATION)
  124. // if (full_number_starts_at >= 0)
  125. // {
  126. // number = text_before_number.slice(full_number_starts_at) + number
  127. // startsAt = full_number_starts_at
  128. // }
  129. //
  130. // this.searching_from = matches.lastIndex
  131. var result = parse(number, this.options, this.metadata);
  132. if (!result.phone) {
  133. return;
  134. }
  135. result.startsAt = startsAt;
  136. result.endsAt = startsAt + number.length;
  137. return result;
  138. }
  139. }, {
  140. key: "hasNext",
  141. value: function hasNext() {
  142. if (this.state === 'NOT_READY') {
  143. this.last_match = this.find();
  144. if (this.last_match) {
  145. this.state = 'READY';
  146. } else {
  147. this.state = 'DONE';
  148. }
  149. }
  150. return this.state === 'READY';
  151. }
  152. }, {
  153. key: "next",
  154. value: function next() {
  155. // Check the state and find the next match as a side-effect if necessary.
  156. if (!this.hasNext()) {
  157. throw new Error('No next element');
  158. } // Don't retain that memory any longer than necessary.
  159. var result = this.last_match;
  160. this.last_match = null;
  161. this.state = 'NOT_READY';
  162. return result;
  163. }
  164. }]);
  165. return PhoneNumberSearch;
  166. }();
  167. //# sourceMappingURL=findPhoneNumbersInitialImplementation.js.map