UNPKG

27 kBJavaScriptView Raw
1import {
2 DIGIT_PLACEHOLDER,
3 countOccurences,
4 repeat,
5 cutAndStripNonPairedParens,
6 closeNonPairedParens,
7 stripNonPairedParens,
8 populateTemplateWithDigits
9} from './AsYouTypeFormatter.util'
10
11import formatCompleteNumber, {
12 canFormatCompleteNumber
13} from './AsYouTypeFormatter.complete'
14
15import parseDigits from './helpers/parseDigits'
16export { DIGIT_PLACEHOLDER } from './AsYouTypeFormatter.util'
17import { FIRST_GROUP_PATTERN } from './helpers/formatNationalNumberUsingFormat'
18import { VALID_PUNCTUATION } from './constants'
19import applyInternationalSeparatorStyle from './helpers/applyInternationalSeparatorStyle'
20
21// Used in phone number format template creation.
22// Could be any digit, I guess.
23const DUMMY_DIGIT = '9'
24// I don't know why is it exactly `15`
25const LONGEST_NATIONAL_PHONE_NUMBER_LENGTH = 15
26// Create a phone number consisting only of the digit 9 that matches the
27// `number_pattern` by applying the pattern to the "longest phone number" string.
28const LONGEST_DUMMY_PHONE_NUMBER = repeat(DUMMY_DIGIT, LONGEST_NATIONAL_PHONE_NUMBER_LENGTH)
29
30// A set of characters that, if found in a national prefix formatting rules, are an indicator to
31// us that we should separate the national prefix from the number when formatting.
32const NATIONAL_PREFIX_SEPARATORS_PATTERN = /[- ]/
33
34// Deprecated: Google has removed some formatting pattern related code from their repo.
35// https://github.com/googlei18n/libphonenumber/commit/a395b4fef3caf57c4bc5f082e1152a4d2bd0ba4c
36// "We no longer have numbers in formatting matching patterns, only \d."
37// Because this library supports generating custom metadata
38// some users may still be using old metadata so the relevant
39// code seems to stay until some next major version update.
40const SUPPORT_LEGACY_FORMATTING_PATTERNS = true
41
42// A pattern that is used to match character classes in regular expressions.
43// An example of a character class is "[1-4]".
44const CREATE_CHARACTER_CLASS_PATTERN = SUPPORT_LEGACY_FORMATTING_PATTERNS && (() => /\[([^\[\]])*\]/g)
45
46// Any digit in a regular expression that actually denotes a digit. For
47// example, in the regular expression "80[0-2]\d{6,10}", the first 2 digits
48// (8 and 0) are standalone digits, but the rest are not.
49// Two look-aheads are needed because the number following \\d could be a
50// two-digit number, since the phone number can be as long as 15 digits.
51const CREATE_STANDALONE_DIGIT_PATTERN = SUPPORT_LEGACY_FORMATTING_PATTERNS && (() => /\d(?=[^,}][^,}])/g)
52
53// A regular expression that is used to determine if a `format` is
54// suitable to be used in the "as you type formatter".
55// A `format` is suitable when the resulting formatted number has
56// the same digits as the user has entered.
57//
58// In the simplest case, that would mean that the format
59// doesn't add any additional digits when formatting a number.
60// Google says that it also shouldn't add "star" (`*`) characters,
61// like it does in some Israeli formats.
62// Such basic format would only contain "valid punctuation"
63// and "captured group" identifiers ($1, $2, etc).
64//
65// An example of a format that adds additional digits:
66//
67// Country: `AR` (Argentina).
68// Format:
69// {
70// "pattern": "(\\d)(\\d{2})(\\d{4})(\\d{4})",
71// "leading_digits_patterns": ["91"],
72// "national_prefix_formatting_rule": "0$1",
73// "format": "$2 15-$3-$4",
74// "international_format": "$1 $2 $3-$4"
75// }
76//
77// In the format above, the `format` adds `15` to the digits when formatting a number.
78// A sidenote: this format actually is suitable because `national_prefix_for_parsing`
79// has previously removed `15` from a national number, so re-adding `15` in `format`
80// doesn't actually result in any extra digits added to user's input.
81// But verifying that would be a complex procedure, so the code chooses a simpler path:
82// it simply filters out all `format`s that contain anything but "captured group" ids.
83//
84// This regular expression is called `ELIGIBLE_FORMAT_PATTERN` in Google's
85// `libphonenumber` code.
86//
87const NON_ALTERING_FORMAT_REG_EXP = new RegExp(
88 '^' +
89 '[' + VALID_PUNCTUATION + ']*' +
90 '(\\$\\d[' + VALID_PUNCTUATION + ']*)+' +
91 '$'
92)
93
94// This is the minimum length of the leading digits of a phone number
95// to guarantee the first "leading digits pattern" for a phone number format
96// to be preemptive.
97const MIN_LEADING_DIGITS_LENGTH = 3
98
99export default class AsYouTypeFormatter {
100 constructor({
101 state,
102 metadata
103 }) {
104 this.metadata = metadata
105 this.resetFormat()
106 }
107
108 resetFormat() {
109 this.chosenFormat = undefined
110 this.template = undefined
111 this.nationalNumberTemplate = undefined
112 this.populatedNationalNumberTemplate = undefined
113 this.populatedNationalNumberTemplatePosition = -1
114 }
115
116 reset(numberingPlan, state) {
117 this.resetFormat()
118 if (numberingPlan) {
119 this.isNANP = numberingPlan.callingCode() === '1'
120 this.matchingFormats = numberingPlan.formats()
121 if (state.nationalSignificantNumber) {
122 this.narrowDownMatchingFormats(state)
123 }
124 } else {
125 this.isNANP = undefined
126 this.matchingFormats = []
127 }
128 }
129
130 format(nextDigits, state) {
131 // See if the phone number digits can be formatted as a complete phone number.
132 // If not, use the results from `formatNationalNumberWithNextDigits()`,
133 // which formats based on the chosen formatting pattern.
134 //
135 // Attempting to format complete phone number first is how it's done
136 // in Google's `libphonenumber`, so this library just follows it.
137 // Google's `libphonenumber` code doesn't explain in detail why does it
138 // attempt to format digits as a complete phone number
139 // instead of just going with a previoulsy (or newly) chosen `format`:
140 //
141 // "Checks to see if there is an exact pattern match for these digits.
142 // If so, we should use this instead of any other formatting template
143 // whose leadingDigitsPattern also matches the input."
144 //
145 if (canFormatCompleteNumber(state.nationalSignificantNumber, this.metadata)) {
146 for (const format of this.matchingFormats) {
147 const formattedCompleteNumber = formatCompleteNumber(
148 state,
149 format,
150 {
151 metadata: this.metadata,
152 shouldTryNationalPrefixFormattingRule: format => this.shouldTryNationalPrefixFormattingRule(format, {
153 international: state.international,
154 nationalPrefix: state.nationalPrefix
155 }),
156 getSeparatorAfterNationalPrefix: this.getSeparatorAfterNationalPrefix
157 }
158 )
159 if (formattedCompleteNumber) {
160 this.resetFormat()
161 this.chosenFormat = format
162 this.setNationalNumberTemplate(formattedCompleteNumber.replace(/\d/g, DIGIT_PLACEHOLDER), state)
163 this.populatedNationalNumberTemplate = formattedCompleteNumber
164 // With a new formatting template, the matched position
165 // using the old template needs to be reset.
166 this.populatedNationalNumberTemplatePosition = this.template.lastIndexOf(DIGIT_PLACEHOLDER)
167 return formattedCompleteNumber
168 }
169
170 }
171 }
172 // Format the digits as a partial (incomplete) phone number
173 // using the previously chosen formatting pattern (or a newly chosen one).
174 return this.formatNationalNumberWithNextDigits(nextDigits, state)
175 }
176
177 // Formats the next phone number digits.
178 formatNationalNumberWithNextDigits(nextDigits, state) {
179 const previouslyChosenFormat = this.chosenFormat
180 // Choose a format from the list of matching ones.
181 const newlyChosenFormat = this.chooseFormat(state)
182 if (newlyChosenFormat) {
183 if (newlyChosenFormat === previouslyChosenFormat) {
184 // If it can format the next (current) digits
185 // using the previously chosen phone number format
186 // then return the updated formatted number.
187 return this.formatNextNationalNumberDigits(nextDigits)
188 } else {
189 // If a more appropriate phone number format
190 // has been chosen for these "leading digits",
191 // then re-format the national phone number part
192 // using the newly selected format.
193 return this.formatNextNationalNumberDigits(state.getNationalDigits())
194 }
195 }
196 }
197
198 narrowDownMatchingFormats({
199 nationalSignificantNumber,
200 nationalPrefix,
201 international
202 }) {
203 const leadingDigits = nationalSignificantNumber
204
205 // "leading digits" pattern list starts with a
206 // "leading digits" pattern fitting a maximum of 3 leading digits.
207 // So, after a user inputs 3 digits of a national (significant) phone number
208 // this national (significant) number can already be formatted.
209 // The next "leading digits" pattern is for 4 leading digits max,
210 // and the "leading digits" pattern after it is for 5 leading digits max, etc.
211
212 // This implementation is different from Google's
213 // in that it searches for a fitting format
214 // even if the user has entered less than
215 // `MIN_LEADING_DIGITS_LENGTH` digits of a national number.
216 // Because some leading digit patterns already match for a single first digit.
217 let leadingDigitsPatternIndex = leadingDigits.length - MIN_LEADING_DIGITS_LENGTH
218 if (leadingDigitsPatternIndex < 0) {
219 leadingDigitsPatternIndex = 0
220 }
221
222 this.matchingFormats = this.matchingFormats.filter(
223 format => this.formatSuits(format, international, nationalPrefix)
224 && this.formatMatches(format, leadingDigits, leadingDigitsPatternIndex)
225 )
226
227 // If there was a phone number format chosen
228 // and it no longer holds given the new leading digits then reset it.
229 // The test for this `if` condition is marked as:
230 // "Reset a chosen format when it no longer holds given the new leading digits".
231 // To construct a valid test case for this one can find a country
232 // in `PhoneNumberMetadata.xml` yielding one format for 3 `<leadingDigits>`
233 // and yielding another format for 4 `<leadingDigits>` (Australia in this case).
234 if (this.chosenFormat && this.matchingFormats.indexOf(this.chosenFormat) === -1) {
235 this.resetFormat()
236 }
237 }
238
239 formatSuits(format, international, nationalPrefix) {
240 // When a prefix before a national (significant) number is
241 // simply a national prefix, then it's parsed as `this.nationalPrefix`.
242 // In more complex cases, a prefix before national (significant) number
243 // could include a national prefix as well as some "capturing groups",
244 // and in that case there's no info whether a national prefix has been parsed.
245 // If national prefix is not used when formatting a phone number
246 // using this format, but a national prefix has been entered by the user,
247 // and was extracted, then discard such phone number format.
248 // In Google's "AsYouType" formatter code, the equivalent would be this part:
249 // https://github.com/google/libphonenumber/blob/0a45cfd96e71cad8edb0e162a70fcc8bd9728933/java/libphonenumber/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java#L175-L184
250 if (nationalPrefix &&
251 !format.usesNationalPrefix() &&
252 // !format.domesticCarrierCodeFormattingRule() &&
253 !format.nationalPrefixIsOptionalWhenFormattingInNationalFormat()) {
254 return false
255 }
256 // If national prefix is mandatory for this phone number format
257 // and there're no guarantees that a national prefix is present in user input
258 // then discard this phone number format as not suitable.
259 // In Google's "AsYouType" formatter code, the equivalent would be this part:
260 // https://github.com/google/libphonenumber/blob/0a45cfd96e71cad8edb0e162a70fcc8bd9728933/java/libphonenumber/src/com/google/i18n/phonenumbers/AsYouTypeFormatter.java#L185-L193
261 if (!international &&
262 !nationalPrefix &&
263 format.nationalPrefixIsMandatoryWhenFormattingInNationalFormat()) {
264 return false
265 }
266 return true
267 }
268
269 formatMatches(format, leadingDigits, leadingDigitsPatternIndex) {
270 const leadingDigitsPatternsCount = format.leadingDigitsPatterns().length
271 // If this format is not restricted to a certain
272 // leading digits pattern then it fits.
273 if (leadingDigitsPatternsCount === 0) {
274 return true
275 }
276 // Start excluding any non-matching formats only when the
277 // national number entered so far is at least 3 digits long,
278 // otherwise format matching would give false negatives.
279 // For example, when the digits entered so far are `2`
280 // and the leading digits pattern is `21` –
281 // it's quite obvious in this case that the format could be the one
282 // but due to the absence of further digits it would give false negative.
283 if (leadingDigits.length < MIN_LEADING_DIGITS_LENGTH) {
284 return true
285 }
286 // If at least `MIN_LEADING_DIGITS_LENGTH` digits of a national number are available
287 // then format matching starts narrowing down the list of possible formats
288 // (only previously matched formats are considered for next digits).
289 leadingDigitsPatternIndex = Math.min(leadingDigitsPatternIndex, leadingDigitsPatternsCount - 1)
290 const leadingDigitsPattern = format.leadingDigitsPatterns()[leadingDigitsPatternIndex]
291 // Brackets are required for `^` to be applied to
292 // all or-ed (`|`) parts, not just the first one.
293 return new RegExp(`^(${leadingDigitsPattern})`).test(leadingDigits)
294 }
295
296 getFormatFormat(format, international) {
297 return international ? format.internationalFormat() : format.format()
298 }
299
300 chooseFormat(state) {
301 // When there are multiple available formats, the formatter uses the first
302 // format where a formatting template could be created.
303 for (const format of this.matchingFormats.slice()) {
304 // If this format is currently being used
305 // and is still suitable, then stick to it.
306 if (this.chosenFormat === format) {
307 break
308 }
309 // Sometimes, a formatting rule inserts additional digits in a phone number,
310 // and "as you type" formatter can't do that: it should only use the digits
311 // that the user has input.
312 //
313 // For example, in Argentina, there's a format for mobile phone numbers:
314 //
315 // {
316 // "pattern": "(\\d)(\\d{2})(\\d{4})(\\d{4})",
317 // "leading_digits_patterns": ["91"],
318 // "national_prefix_formatting_rule": "0$1",
319 // "format": "$2 15-$3-$4",
320 // "international_format": "$1 $2 $3-$4"
321 // }
322 //
323 // In that format, `international_format` is used instead of `format`
324 // because `format` inserts `15` in the formatted number,
325 // and `AsYouType` formatter should only use the digits
326 // the user has actually input, without adding any extra digits.
327 // In this case, it wouldn't make a difference, because the `15`
328 // is first stripped when applying `national_prefix_for_parsing`
329 // and then re-added when using `format`, so in reality it doesn't
330 // add any new digits to the number, but to detect that, the code
331 // would have to be more complex: it would have to try formatting
332 // the digits using the format and then see if any digits have
333 // actually been added or removed, and then, every time a new digit
334 // is input, it should re-check whether the chosen format doesn't
335 // alter the digits.
336 //
337 // Google's code doesn't go that far, and so does this library:
338 // it simply requires that a `format` doesn't add any additonal
339 // digits to user's input.
340 //
341 // Also, people in general should move from inputting phone numbers
342 // in national format (possibly with national prefixes)
343 // and use international phone number format instead:
344 // it's a logical thing in the modern age of mobile phones,
345 // globalization and the internet.
346 //
347 /* istanbul ignore if */
348 if (!NON_ALTERING_FORMAT_REG_EXP.test(this.getFormatFormat(format, state.international))) {
349 continue
350 }
351 if (!this.createTemplateForFormat(format, state)) {
352 // Remove the format if it can't generate a template.
353 this.matchingFormats = this.matchingFormats.filter(_ => _ !== format)
354 continue
355 }
356 this.chosenFormat = format
357 break
358 }
359 if (!this.chosenFormat) {
360 // No format matches the national (significant) phone number.
361 this.resetFormat()
362 }
363 return this.chosenFormat
364 }
365
366 createTemplateForFormat(format, state) {
367 // The formatter doesn't format numbers when numberPattern contains '|', e.g.
368 // (20|3)\d{4}. In those cases we quickly return.
369 // (Though there's no such format in current metadata)
370 /* istanbul ignore if */
371 if (SUPPORT_LEGACY_FORMATTING_PATTERNS && format.pattern().indexOf('|') >= 0) {
372 return
373 }
374 // Get formatting template for this phone number format
375 const template = this.getTemplateForFormat(format, state)
376 // If the national number entered is too long
377 // for any phone number format, then abort.
378 if (template) {
379 this.setNationalNumberTemplate(template, state)
380 return true
381 }
382 }
383
384 getSeparatorAfterNationalPrefix = (format) => {
385 // `US` metadata doesn't have a `national_prefix_formatting_rule`,
386 // so the `if` condition below doesn't apply to `US`,
387 // but in reality there shoudl be a separator
388 // between a national prefix and a national (significant) number.
389 // So `US` national prefix separator is a "special" "hardcoded" case.
390 if (this.isNANP) {
391 return ' '
392 }
393 // If a `format` has a `national_prefix_formatting_rule`
394 // and that rule has a separator after a national prefix,
395 // then it means that there should be a separator
396 // between a national prefix and a national (significant) number.
397 if (format &&
398 format.nationalPrefixFormattingRule() &&
399 NATIONAL_PREFIX_SEPARATORS_PATTERN.test(format.nationalPrefixFormattingRule())) {
400 return ' '
401 }
402 // At this point, there seems to be no clear evidence that
403 // there should be a separator between a national prefix
404 // and a national (significant) number. So don't insert one.
405 return ''
406 }
407
408 getInternationalPrefixBeforeCountryCallingCode({ IDDPrefix, missingPlus }, options) {
409 if (IDDPrefix) {
410 return options && options.spacing === false ? IDDPrefix : IDDPrefix + ' '
411 }
412 if (missingPlus) {
413 return ''
414 }
415 return '+'
416 }
417
418 getTemplate(state) {
419 if (!this.template) {
420 return
421 }
422 // `this.template` holds the template for a "complete" phone number.
423 // The currently entered phone number is most likely not "complete",
424 // so trim all non-populated digits.
425 let index = -1
426 let i = 0
427 const internationalPrefix = state.international ? this.getInternationalPrefixBeforeCountryCallingCode(state, { spacing: false }) : ''
428 while (i < internationalPrefix.length + state.getDigitsWithoutInternationalPrefix().length) {
429 index = this.template.indexOf(DIGIT_PLACEHOLDER, index + 1)
430 i++
431 }
432 return cutAndStripNonPairedParens(this.template, index + 1)
433 }
434
435 setNationalNumberTemplate(template, state) {
436 this.nationalNumberTemplate = template
437 this.populatedNationalNumberTemplate = template
438 // With a new formatting template, the matched position
439 // using the old template needs to be reset.
440 this.populatedNationalNumberTemplatePosition = -1
441 // For convenience, the public `.template` property
442 // contains the whole international number
443 // if the phone number being input is international:
444 // 'x' for the '+' sign, 'x'es for the country phone code,
445 // a spacebar and then the template for the formatted national number.
446 if (state.international) {
447 this.template =
448 this.getInternationalPrefixBeforeCountryCallingCode(state).replace(/[\d\+]/g, DIGIT_PLACEHOLDER) +
449 repeat(DIGIT_PLACEHOLDER, state.callingCode.length) +
450 ' ' +
451 template
452 } else {
453 this.template = template
454 }
455 }
456
457 /**
458 * Generates formatting template for a national phone number,
459 * optionally containing a national prefix, for a format.
460 * @param {Format} format
461 * @param {string} nationalPrefix
462 * @return {string}
463 */
464 getTemplateForFormat(format, {
465 nationalSignificantNumber,
466 international,
467 nationalPrefix,
468 complexPrefixBeforeNationalSignificantNumber
469 }) {
470 let pattern = format.pattern()
471
472 /* istanbul ignore else */
473 if (SUPPORT_LEGACY_FORMATTING_PATTERNS) {
474 pattern = pattern
475 // Replace anything in the form of [..] with \d
476 .replace(CREATE_CHARACTER_CLASS_PATTERN(), '\\d')
477 // Replace any standalone digit (not the one in `{}`) with \d
478 .replace(CREATE_STANDALONE_DIGIT_PATTERN(), '\\d')
479 }
480
481 // Generate a dummy national number (consisting of `9`s)
482 // that fits this format's `pattern`.
483 //
484 // This match will always succeed,
485 // because the "longest dummy phone number"
486 // has enough length to accomodate any possible
487 // national phone number format pattern.
488 //
489 let digits = LONGEST_DUMMY_PHONE_NUMBER.match(pattern)[0]
490
491 // If the national number entered is too long
492 // for any phone number format, then abort.
493 if (nationalSignificantNumber.length > digits.length) {
494 return
495 }
496
497 // Get a formatting template which can be used to efficiently format
498 // a partial number where digits are added one by one.
499
500 // Below `strictPattern` is used for the
501 // regular expression (with `^` and `$`).
502 // This wasn't originally in Google's `libphonenumber`
503 // and I guess they don't really need it
504 // because they're not using "templates" to format phone numbers
505 // but I added `strictPattern` after encountering
506 // South Korean phone number formatting bug.
507 //
508 // Non-strict regular expression bug demonstration:
509 //
510 // this.nationalSignificantNumber : `111111111` (9 digits)
511 //
512 // pattern : (\d{2})(\d{3,4})(\d{4})
513 // format : `$1 $2 $3`
514 // digits : `9999999999` (10 digits)
515 //
516 // '9999999999'.replace(new RegExp(/(\d{2})(\d{3,4})(\d{4})/g), '$1 $2 $3') = "99 9999 9999"
517 //
518 // template : xx xxxx xxxx
519 //
520 // But the correct template in this case is `xx xxx xxxx`.
521 // The template was generated incorrectly because of the
522 // `{3,4}` variability in the `pattern`.
523 //
524 // The fix is, if `this.nationalSignificantNumber` has already sufficient length
525 // to satisfy the `pattern` completely then `this.nationalSignificantNumber`
526 // is used instead of `digits`.
527
528 const strictPattern = new RegExp('^' + pattern + '$')
529 const nationalNumberDummyDigits = nationalSignificantNumber.replace(/\d/g, DUMMY_DIGIT)
530
531 // If `this.nationalSignificantNumber` has already sufficient length
532 // to satisfy the `pattern` completely then use it
533 // instead of `digits`.
534 if (strictPattern.test(nationalNumberDummyDigits)) {
535 digits = nationalNumberDummyDigits
536 }
537
538 let numberFormat = this.getFormatFormat(format, international)
539 let nationalPrefixIncludedInTemplate
540
541 // If a user did input a national prefix (and that's guaranteed),
542 // and if a `format` does have a national prefix formatting rule,
543 // then see if that national prefix formatting rule
544 // prepends exactly the same national prefix the user has input.
545 // If that's the case, then use the `format` with the national prefix formatting rule.
546 // Otherwise, use the `format` without the national prefix formatting rule,
547 // and prepend a national prefix manually to it.
548 if (this.shouldTryNationalPrefixFormattingRule(format, { international, nationalPrefix })) {
549 const numberFormatWithNationalPrefix = numberFormat.replace(
550 FIRST_GROUP_PATTERN,
551 format.nationalPrefixFormattingRule()
552 )
553 // If `national_prefix_formatting_rule` of a `format` simply prepends
554 // national prefix at the start of a national (significant) number,
555 // then such formatting can be used with `AsYouType` formatter.
556 // There seems to be no `else` case: everywhere in metadata,
557 // national prefix formatting rule is national prefix + $1,
558 // or `($1)`, in which case such format isn't even considered
559 // when the user has input a national prefix.
560 /* istanbul ignore else */
561 if (parseDigits(format.nationalPrefixFormattingRule()) === (nationalPrefix || '') + parseDigits('$1')) {
562 numberFormat = numberFormatWithNationalPrefix
563 nationalPrefixIncludedInTemplate = true
564 // Replace all digits of the national prefix in the formatting template
565 // with `DIGIT_PLACEHOLDER`s.
566 if (nationalPrefix) {
567 let i = nationalPrefix.length
568 while (i > 0) {
569 numberFormat = numberFormat.replace(/\d/, DIGIT_PLACEHOLDER)
570 i--
571 }
572 }
573 }
574 }
575
576 // Generate formatting template for this phone number format.
577 let template = digits
578 // Format the dummy phone number according to the format.
579 .replace(new RegExp(pattern), numberFormat)
580 // Replace each dummy digit with a DIGIT_PLACEHOLDER.
581 .replace(new RegExp(DUMMY_DIGIT, 'g'), DIGIT_PLACEHOLDER)
582
583 // If a prefix of a national (significant) number is not as simple
584 // as just a basic national prefix, then just prepend such prefix
585 // before the national (significant) number, optionally spacing
586 // the two with a whitespace.
587 if (!nationalPrefixIncludedInTemplate) {
588 if (complexPrefixBeforeNationalSignificantNumber) {
589 // Prepend the prefix to the template manually.
590 template = repeat(DIGIT_PLACEHOLDER, complexPrefixBeforeNationalSignificantNumber.length) +
591 ' ' +
592 template
593 } else if (nationalPrefix) {
594 // Prepend national prefix to the template manually.
595 template = repeat(DIGIT_PLACEHOLDER, nationalPrefix.length) +
596 this.getSeparatorAfterNationalPrefix(format) +
597 template
598 }
599 }
600
601 if (international) {
602 template = applyInternationalSeparatorStyle(template)
603 }
604
605 return template
606 }
607
608 formatNextNationalNumberDigits(digits) {
609 const result = populateTemplateWithDigits(
610 this.populatedNationalNumberTemplate,
611 this.populatedNationalNumberTemplatePosition,
612 digits
613 )
614
615 if (!result) {
616 // Reset the format.
617 this.resetFormat()
618 return
619 }
620
621 this.populatedNationalNumberTemplate = result[0]
622 this.populatedNationalNumberTemplatePosition = result[1]
623
624 // Return the formatted phone number so far.
625 return cutAndStripNonPairedParens(this.populatedNationalNumberTemplate, this.populatedNationalNumberTemplatePosition + 1)
626
627 // The old way which was good for `input-format` but is not so good
628 // for `react-phone-number-input`'s default input (`InputBasic`).
629 // return closeNonPairedParens(this.populatedNationalNumberTemplate, this.populatedNationalNumberTemplatePosition + 1)
630 // .replace(new RegExp(DIGIT_PLACEHOLDER, 'g'), ' ')
631 }
632
633 shouldTryNationalPrefixFormattingRule = (format, { international, nationalPrefix }) => {
634 if (format.nationalPrefixFormattingRule()) {
635 // In some countries, `national_prefix_formatting_rule` is `($1)`,
636 // so it applies even if the user hasn't input a national prefix.
637 // `format.usesNationalPrefix()` detects such cases.
638 const usesNationalPrefix = format.usesNationalPrefix()
639 if ((usesNationalPrefix && nationalPrefix) ||
640 (!usesNationalPrefix && !international)) {
641 return true
642 }
643 }
644 }
645}
\No newline at end of file