UNPKG

22.4 kBJavaScriptView Raw
1"use strict";
2
3Object.defineProperty(exports, "__esModule", {
4 value: true
5});
6exports.extractFormattedDigitsAndPlus = extractFormattedDigitsAndPlus;
7exports["default"] = void 0;
8
9var _extractCountryCallingCode2 = _interopRequireDefault(require("./helpers/extractCountryCallingCode"));
10
11var _extractCountryCallingCodeFromInternationalNumberWithoutPlusSign = _interopRequireDefault(require("./helpers/extractCountryCallingCodeFromInternationalNumberWithoutPlusSign"));
12
13var _extractNationalNumberFromPossiblyIncompleteNumber = _interopRequireDefault(require("./helpers/extractNationalNumberFromPossiblyIncompleteNumber"));
14
15var _stripIddPrefix = _interopRequireDefault(require("./helpers/stripIddPrefix"));
16
17var _parseDigits = _interopRequireDefault(require("./helpers/parseDigits"));
18
19var _constants = require("./constants");
20
21function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
22
23function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
24
25function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance"); }
26
27function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
28
29function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
30
31function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
32
33function _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); } }
34
35function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
36
37var VALID_FORMATTED_PHONE_NUMBER_PART = '[' + _constants.VALID_PUNCTUATION + _constants.VALID_DIGITS + ']+';
38var VALID_FORMATTED_PHONE_NUMBER_PART_PATTERN = new RegExp('^' + VALID_FORMATTED_PHONE_NUMBER_PART + '$', 'i');
39var VALID_PHONE_NUMBER = '(?:' + '[' + _constants.PLUS_CHARS + ']' + '[' + _constants.VALID_PUNCTUATION + _constants.VALID_DIGITS + ']*' + '|' + '[' + _constants.VALID_PUNCTUATION + _constants.VALID_DIGITS + ']+' + ')';
40var AFTER_PHONE_NUMBER_DIGITS_END_PATTERN = new RegExp('[^' + _constants.VALID_PUNCTUATION + _constants.VALID_DIGITS + ']+' + '.*' + '$'); // Tests whether `national_prefix_for_parsing` could match
41// different national prefixes.
42// Matches anything that's not a digit or a square bracket.
43
44var COMPLEX_NATIONAL_PREFIX = /[^\d\[\]]/;
45
46var AsYouTypeParser =
47/*#__PURE__*/
48function () {
49 function AsYouTypeParser(_ref) {
50 var defaultCountry = _ref.defaultCountry,
51 defaultCallingCode = _ref.defaultCallingCode,
52 metadata = _ref.metadata,
53 onNationalSignificantNumberChange = _ref.onNationalSignificantNumberChange;
54
55 _classCallCheck(this, AsYouTypeParser);
56
57 this.defaultCountry = defaultCountry;
58 this.defaultCallingCode = defaultCallingCode;
59 this.metadata = metadata;
60 this.onNationalSignificantNumberChange = onNationalSignificantNumberChange;
61 }
62
63 _createClass(AsYouTypeParser, [{
64 key: "input",
65 value: function input(text, state) {
66 var _extractFormattedDigi = extractFormattedDigitsAndPlus(text),
67 _extractFormattedDigi2 = _slicedToArray(_extractFormattedDigi, 2),
68 formattedDigits = _extractFormattedDigi2[0],
69 hasPlus = _extractFormattedDigi2[1];
70
71 var digits = (0, _parseDigits["default"])(formattedDigits); // Checks for a special case: just a leading `+` has been entered.
72
73 var justLeadingPlus;
74
75 if (hasPlus) {
76 if (!state.digits) {
77 state.startInternationalNumber();
78
79 if (!digits) {
80 justLeadingPlus = true;
81 }
82 }
83 }
84
85 if (digits) {
86 this.inputDigits(digits, state);
87 }
88
89 return {
90 digits: digits,
91 justLeadingPlus: justLeadingPlus
92 };
93 }
94 /**
95 * Inputs "next" phone number digits.
96 * @param {string} digits
97 * @return {string} [formattedNumber] Formatted national phone number (if it can be formatted at this stage). Returning `undefined` means "don't format the national phone number at this stage".
98 */
99
100 }, {
101 key: "inputDigits",
102 value: function inputDigits(nextDigits, state) {
103 var digits = state.digits;
104 var hasReceivedThreeLeadingDigits = digits.length < 3 && digits.length + nextDigits.length >= 3; // Append phone number digits.
105
106 state.appendDigits(nextDigits); // Attempt to extract IDD prefix:
107 // Some users input their phone number in international format,
108 // but in an "out-of-country" dialing format instead of using the leading `+`.
109 // https://github.com/catamphetamine/libphonenumber-js/issues/185
110 // Detect such numbers as soon as there're at least 3 digits.
111 // Google's library attempts to extract IDD prefix at 3 digits,
112 // so this library just copies that behavior.
113 // I guess that's because the most commot IDD prefixes are
114 // `00` (Europe) and `011` (US).
115 // There exist really long IDD prefixes too:
116 // for example, in Australia the default IDD prefix is `0011`,
117 // and it could even be as long as `14880011`.
118 // An IDD prefix is extracted here, and then every time when
119 // there's a new digit and the number couldn't be formatted.
120
121 if (hasReceivedThreeLeadingDigits) {
122 this.extractIddPrefix(state);
123 }
124
125 if (this.isWaitingForCountryCallingCode(state)) {
126 if (!this.extractCountryCallingCode(state)) {
127 return;
128 }
129 } else {
130 state.appendNationalSignificantNumberDigits(nextDigits);
131 } // If a phone number is being input in international format,
132 // then it's not valid for it to have a national prefix.
133 // Still, some people incorrectly input such numbers with a national prefix.
134 // In such cases, only attempt to strip a national prefix if the number becomes too long.
135 // (but that is done later, not here)
136
137
138 if (!state.international) {
139 if (!this.hasExtractedNationalSignificantNumber) {
140 this.extractNationalSignificantNumber(state.getNationalDigits(), state.update);
141 }
142 }
143 }
144 }, {
145 key: "isWaitingForCountryCallingCode",
146 value: function isWaitingForCountryCallingCode(_ref2) {
147 var international = _ref2.international,
148 callingCode = _ref2.callingCode;
149 return international && !callingCode;
150 } // Extracts a country calling code from a number
151 // being entered in internatonal format.
152
153 }, {
154 key: "extractCountryCallingCode",
155 value: function extractCountryCallingCode(state) {
156 var _extractCountryCallin = (0, _extractCountryCallingCode2["default"])('+' + state.getDigitsWithoutInternationalPrefix(), this.defaultCountry, this.defaultCallingCode, this.metadata.metadata),
157 countryCallingCode = _extractCountryCallin.countryCallingCode,
158 number = _extractCountryCallin.number;
159
160 if (countryCallingCode) {
161 state.setCallingCode(countryCallingCode);
162 state.update({
163 nationalSignificantNumber: number
164 });
165 return true;
166 }
167 }
168 }, {
169 key: "reset",
170 value: function reset(numberingPlan) {
171 if (numberingPlan) {
172 this.hasSelectedNumberingPlan = true;
173
174 var nationalPrefixForParsing = numberingPlan._nationalPrefixForParsing();
175
176 this.couldPossiblyExtractAnotherNationalSignificantNumber = nationalPrefixForParsing && COMPLEX_NATIONAL_PREFIX.test(nationalPrefixForParsing);
177 } else {
178 this.hasSelectedNumberingPlan = undefined;
179 this.couldPossiblyExtractAnotherNationalSignificantNumber = undefined;
180 }
181 }
182 /**
183 * Extracts a national (significant) number from user input.
184 * Google's library is different in that it only applies `national_prefix_for_parsing`
185 * and doesn't apply `national_prefix_transform_rule` after that.
186 * https://github.com/google/libphonenumber/blob/a3d70b0487875475e6ad659af404943211d26456/java/libphonenumber/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java#L539
187 * @return {boolean} [extracted]
188 */
189
190 }, {
191 key: "extractNationalSignificantNumber",
192 value: function extractNationalSignificantNumber(nationalDigits, setState) {
193 if (!this.hasSelectedNumberingPlan) {
194 return;
195 }
196
197 var _extractNationalNumbe = (0, _extractNationalNumberFromPossiblyIncompleteNumber["default"])(nationalDigits, this.metadata),
198 nationalPrefix = _extractNationalNumbe.nationalPrefix,
199 nationalNumber = _extractNationalNumbe.nationalNumber,
200 carrierCode = _extractNationalNumbe.carrierCode;
201
202 if (nationalNumber === nationalDigits) {
203 return;
204 }
205
206 this.onExtractedNationalNumber(nationalPrefix, carrierCode, nationalNumber, nationalDigits, setState);
207 return true;
208 }
209 /**
210 * In Google's code this function is called "attempt to extract longer NDD".
211 * "Some national prefixes are a substring of others", they say.
212 * @return {boolean} [result] — Returns `true` if extracting a national prefix produced different results from what they were.
213 */
214
215 }, {
216 key: "extractAnotherNationalSignificantNumber",
217 value: function extractAnotherNationalSignificantNumber(nationalDigits, prevNationalSignificantNumber, setState) {
218 if (!this.hasExtractedNationalSignificantNumber) {
219 return this.extractNationalSignificantNumber(nationalDigits, setState);
220 }
221
222 if (!this.couldPossiblyExtractAnotherNationalSignificantNumber) {
223 return;
224 }
225
226 var _extractNationalNumbe2 = (0, _extractNationalNumberFromPossiblyIncompleteNumber["default"])(nationalDigits, this.metadata),
227 nationalPrefix = _extractNationalNumbe2.nationalPrefix,
228 nationalNumber = _extractNationalNumbe2.nationalNumber,
229 carrierCode = _extractNationalNumbe2.carrierCode; // If a national prefix has been extracted previously,
230 // then it's always extracted as additional digits are added.
231 // That's assuming `extractNationalNumberFromPossiblyIncompleteNumber()`
232 // doesn't do anything different from what it currently does.
233 // So, just in case, here's this check, though it doesn't occur.
234
235 /* istanbul ignore if */
236
237
238 if (nationalNumber === prevNationalSignificantNumber) {
239 return;
240 }
241
242 this.onExtractedNationalNumber(nationalPrefix, carrierCode, nationalNumber, nationalDigits, setState);
243 return true;
244 }
245 }, {
246 key: "onExtractedNationalNumber",
247 value: function onExtractedNationalNumber(nationalPrefix, carrierCode, nationalSignificantNumber, nationalDigits, setState) {
248 var complexPrefixBeforeNationalSignificantNumber;
249 var nationalSignificantNumberMatchesInput; // This check also works with empty `this.nationalSignificantNumber`.
250
251 var nationalSignificantNumberIndex = nationalDigits.lastIndexOf(nationalSignificantNumber); // If the extracted national (significant) number is the
252 // last substring of the `digits`, then it means that it hasn't been altered:
253 // no digits have been removed from the national (significant) number
254 // while applying `national_prefix_transform_rule`.
255 // https://gitlab.com/catamphetamine/libphonenumber-js/-/blob/master/METADATA.md#national_prefix_for_parsing--national_prefix_transform_rule
256
257 if (nationalSignificantNumberIndex >= 0 && nationalSignificantNumberIndex === nationalDigits.length - nationalSignificantNumber.length) {
258 nationalSignificantNumberMatchesInput = true; // If a prefix of a national (significant) number is not as simple
259 // as just a basic national prefix, then such prefix is stored in
260 // `this.complexPrefixBeforeNationalSignificantNumber` property and will be
261 // prepended "as is" to the national (significant) number to produce
262 // a formatted result.
263
264 var prefixBeforeNationalNumber = nationalDigits.slice(0, nationalSignificantNumberIndex); // `prefixBeforeNationalNumber` is always non-empty,
265 // because `onExtractedNationalNumber()` isn't called
266 // when a national (significant) number hasn't been actually "extracted":
267 // when a national (significant) number is equal to the national part of `digits`,
268 // then `onExtractedNationalNumber()` doesn't get called.
269
270 if (prefixBeforeNationalNumber !== nationalPrefix) {
271 complexPrefixBeforeNationalSignificantNumber = prefixBeforeNationalNumber;
272 }
273 }
274
275 setState({
276 nationalPrefix: nationalPrefix,
277 carrierCode: carrierCode,
278 nationalSignificantNumber: nationalSignificantNumber,
279 nationalSignificantNumberMatchesInput: nationalSignificantNumberMatchesInput,
280 complexPrefixBeforeNationalSignificantNumber: complexPrefixBeforeNationalSignificantNumber
281 }); // `onExtractedNationalNumber()` is only called when
282 // the national (significant) number actually did change.
283
284 this.hasExtractedNationalSignificantNumber = true;
285 this.onNationalSignificantNumberChange();
286 }
287 }, {
288 key: "reExtractNationalSignificantNumber",
289 value: function reExtractNationalSignificantNumber(state) {
290 // Attempt to extract a national prefix.
291 //
292 // Some people incorrectly input national prefix
293 // in an international phone number.
294 // For example, some people write British phone numbers as `+44(0)...`.
295 //
296 // Also, in some rare cases, it is valid for a national prefix
297 // to be a part of an international phone number.
298 // For example, mobile phone numbers in Mexico are supposed to be
299 // dialled internationally using a `1` national prefix,
300 // so the national prefix will be part of an international number.
301 //
302 // Quote from:
303 // https://www.mexperience.com/dialing-cell-phones-in-mexico/
304 //
305 // "Dialing a Mexican cell phone from abroad
306 // When you are calling a cell phone number in Mexico from outside Mexico,
307 // it’s necessary to dial an additional “1” after Mexico’s country code
308 // (which is “52”) and before the area code.
309 // You also ignore the 045, and simply dial the area code and the
310 // cell phone’s number.
311 //
312 // If you don’t add the “1”, you’ll receive a recorded announcement
313 // asking you to redial using it.
314 //
315 // For example, if you are calling from the USA to a cell phone
316 // in Mexico City, you would dial +52 – 1 – 55 – 1234 5678.
317 // (Note that this is different to calling a land line in Mexico City
318 // from abroad, where the number dialed would be +52 – 55 – 1234 5678)".
319 //
320 // Google's demo output:
321 // https://libphonenumber.appspot.com/phonenumberparser?number=%2b5215512345678&country=MX
322 //
323 if (this.extractAnotherNationalSignificantNumber(state.getNationalDigits(), state.nationalSignificantNumber, state.update)) {
324 return true;
325 } // If no format matches the phone number, then it could be
326 // "a really long IDD" (quote from a comment in Google's library).
327 // An IDD prefix is first extracted when the user has entered at least 3 digits,
328 // and then here — every time when there's a new digit and the number
329 // couldn't be formatted.
330 // For example, in Australia the default IDD prefix is `0011`,
331 // and it could even be as long as `14880011`.
332 //
333 // Could also check `!hasReceivedThreeLeadingDigits` here
334 // to filter out the case when this check duplicates the one
335 // already performed when there're 3 leading digits,
336 // but it's not a big deal, and in most cases there
337 // will be a suitable `format` when there're 3 leading digits.
338 //
339
340
341 if (this.extractIddPrefix(state)) {
342 this.extractCallingCodeAndNationalSignificantNumber(state);
343 return true;
344 } // Google's AsYouType formatter supports sort of an "autocorrection" feature
345 // when it "autocorrects" numbers that have been input for a country
346 // with that country's calling code.
347 // Such "autocorrection" feature looks weird, but different people have been requesting it:
348 // https://github.com/catamphetamine/libphonenumber-js/issues/376
349 // https://github.com/catamphetamine/libphonenumber-js/issues/375
350 // https://github.com/catamphetamine/libphonenumber-js/issues/316
351
352
353 if (this.fixMissingPlus(state)) {
354 this.extractCallingCodeAndNationalSignificantNumber(state);
355 return true;
356 }
357 }
358 }, {
359 key: "extractIddPrefix",
360 value: function extractIddPrefix(state) {
361 // An IDD prefix can't be present in a number written with a `+`.
362 // Also, don't re-extract an IDD prefix if has already been extracted.
363 var international = state.international,
364 IDDPrefix = state.IDDPrefix,
365 digits = state.digits,
366 nationalSignificantNumber = state.nationalSignificantNumber;
367
368 if (international || IDDPrefix) {
369 return;
370 } // Some users input their phone number in "out-of-country"
371 // dialing format instead of using the leading `+`.
372 // https://github.com/catamphetamine/libphonenumber-js/issues/185
373 // Detect such numbers.
374
375
376 var numberWithoutIDD = (0, _stripIddPrefix["default"])(digits, this.defaultCountry, this.defaultCallingCode, this.metadata.metadata);
377
378 if (numberWithoutIDD !== undefined && numberWithoutIDD !== digits) {
379 // If an IDD prefix was stripped then convert the IDD-prefixed number
380 // to international number for subsequent parsing.
381 state.update({
382 IDDPrefix: digits.slice(0, digits.length - numberWithoutIDD.length)
383 });
384 this.startInternationalNumber(state);
385 return true;
386 }
387 }
388 }, {
389 key: "fixMissingPlus",
390 value: function fixMissingPlus(state) {
391 if (!state.international) {
392 var _extractCountryCallin2 = (0, _extractCountryCallingCodeFromInternationalNumberWithoutPlusSign["default"])(state.digits, this.defaultCountry, this.defaultCallingCode, this.metadata.metadata),
393 newCallingCode = _extractCountryCallin2.countryCallingCode,
394 number = _extractCountryCallin2.number;
395
396 if (newCallingCode) {
397 state.update({
398 missingPlus: true
399 });
400 this.startInternationalNumber(state);
401 return true;
402 }
403 }
404 }
405 }, {
406 key: "startInternationalNumber",
407 value: function startInternationalNumber(state) {
408 state.startInternationalNumber(); // If a national (significant) number has been extracted before, reset it.
409
410 if (state.nationalSignificantNumber) {
411 state.resetNationalSignificantNumber();
412 this.onNationalSignificantNumberChange();
413 this.hasExtractedNationalSignificantNumber = undefined;
414 }
415 }
416 }, {
417 key: "extractCallingCodeAndNationalSignificantNumber",
418 value: function extractCallingCodeAndNationalSignificantNumber(state) {
419 if (this.extractCountryCallingCode(state)) {
420 // `this.extractCallingCode()` is currently called when the number
421 // couldn't be formatted during the standard procedure.
422 // Normally, the national prefix would be re-extracted
423 // for an international number if such number couldn't be formatted,
424 // but since it's already not able to be formatted,
425 // there won't be yet another retry, so also extract national prefix here.
426 this.extractNationalSignificantNumber(state.getNationalDigits(), state.update);
427 }
428 }
429 }]);
430
431 return AsYouTypeParser;
432}();
433/**
434 * Extracts formatted phone number from text (if there's any).
435 * @param {string} text
436 * @return {string} [formattedPhoneNumber]
437 */
438
439
440exports["default"] = AsYouTypeParser;
441
442function extractFormattedPhoneNumber(text) {
443 // Attempt to extract a possible number from the string passed in.
444 var startsAt = text.search(VALID_PHONE_NUMBER);
445
446 if (startsAt < 0) {
447 return;
448 } // Trim everything to the left of the phone number.
449
450
451 text = text.slice(startsAt); // Trim the `+`.
452
453 var hasPlus;
454
455 if (text[0] === '+') {
456 hasPlus = true;
457 text = text.slice('+'.length);
458 } // Trim everything to the right of the phone number.
459
460
461 text = text.replace(AFTER_PHONE_NUMBER_DIGITS_END_PATTERN, ''); // Re-add the previously trimmed `+`.
462
463 if (hasPlus) {
464 text = '+' + text;
465 }
466
467 return text;
468}
469/**
470 * Extracts formatted phone number digits (and a `+`) from text (if there're any).
471 * @param {string} text
472 * @return {any[]}
473 */
474
475
476function _extractFormattedDigitsAndPlus(text) {
477 // Extract a formatted phone number part from text.
478 var extractedNumber = extractFormattedPhoneNumber(text) || ''; // Trim a `+`.
479
480 if (extractedNumber[0] === '+') {
481 return [extractedNumber.slice('+'.length), true];
482 }
483
484 return [extractedNumber];
485}
486/**
487 * Extracts formatted phone number digits (and a `+`) from text (if there're any).
488 * @param {string} text
489 * @return {any[]}
490 */
491
492
493function extractFormattedDigitsAndPlus(text) {
494 var _extractFormattedDigi3 = _extractFormattedDigitsAndPlus(text),
495 _extractFormattedDigi4 = _slicedToArray(_extractFormattedDigi3, 2),
496 formattedDigits = _extractFormattedDigi4[0],
497 hasPlus = _extractFormattedDigi4[1]; // If the extracted phone number part
498 // can possibly be a part of some valid phone number
499 // then parse phone number characters from a formatted phone number.
500
501
502 if (!VALID_FORMATTED_PHONE_NUMBER_PART_PATTERN.test(formattedDigits)) {
503 formattedDigits = '';
504 }
505
506 return [formattedDigits, hasPlus];
507}
508//# sourceMappingURL=AsYouTypeParser.js.map
\No newline at end of file