-
-
Notifications
You must be signed in to change notification settings - Fork 2k
/
utils.js
238 lines (221 loc) · 9.84 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
goog.provide("i18n.phonenumbers.demo");
// includes
goog.require("i18n.phonenumbers.PhoneNumberFormat");
goog.require("i18n.phonenumbers.PhoneNumberUtil");
goog.require("i18n.phonenumbers.Error");
goog.require("i18n.phonenumbers.AsYouTypeFormatter");
//* Format the number as the user types.
const formatNumberAsYouType = (number, countryCode) => {
try {
//* Have to clean it first, as AYTF stops formatting as soon as it hits any formatting char (even it's own)
//* (it's designed to be fed one char at a time, as opposed to every char every time).
const clean = number.replace(/[^+0-9]/g, "");
const formatter = new i18n.phonenumbers.AsYouTypeFormatter(countryCode);
let result = "";
for (let i = 0; i < clean.length; i++) {
result = formatter.inputDigit(clean.charAt(i));
}
return result;
} catch {
return number;
}
};
//* Format the given number to the given format.
const formatNumber = (number, countryCode, formatArg) => {
try {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);
if (phoneUtil.isPossibleNumber(numberObj)) {
const format =
typeof formatArg === "undefined"
? i18n.phonenumbers.PhoneNumberFormat.E164
: formatArg;
return phoneUtil.format(numberObj, format);
}
return number;
} catch {
return number;
}
};
//* Get an example number for the given country code.
const getExampleNumber = (countryCode, national, numberType, useE164) => {
try {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
const numberObj = phoneUtil.getExampleNumberForType(
countryCode,
numberType,
);
let format;
if (useE164) {
format = i18n.phonenumbers.PhoneNumberFormat.E164;
} else {
format = national
? i18n.phonenumbers.PhoneNumberFormat.NATIONAL
: i18n.phonenumbers.PhoneNumberFormat.INTERNATIONAL;
}
return phoneUtil.format(numberObj, format);
} catch {
return "";
}
};
//* Get the core number, without any international dial code, or national prefix.
const getCoreNumber = (number, countryCode) => {
try {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);
return numberObj.getNationalNumber().toString();
} catch {
return "";
}
};
//* Get the extension from the given number
const getExtension = (number, countryCode) => {
try {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);
return numberObj.getExtension();
} catch {
return "";
}
};
//* Get the type of the given number e.g. fixed-line/mobile.
const getNumberType = (number, countryCode) => {
try {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);
return phoneUtil.getNumberType(numberObj);
} catch {
//* Broken
return -99;
}
};
//* Get more info if the validation has failed e.g. too long/too short.
//* NOTE that isPossibleNumberWithReason returns a i18n.phonenumbers.PhoneNumberUtil.ValidationResult.
const getValidationError = (number, countryCode) => {
if (!countryCode) {
return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.INVALID_COUNTRY_CODE;
}
try {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);
return phoneUtil.isPossibleNumberWithReason(numberObj);
} catch (e) {
//* Here I convert thrown errors into ValidationResult enums (if possible).
//* errors are from i18n.phonenumbers.Error in the file https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js.
if (e.message === i18n.phonenumbers.Error.INVALID_COUNTRY_CODE) {
return i18n.phonenumbers.PhoneNumberUtil.ValidationResult
.INVALID_COUNTRY_CODE;
}
if (
//* Hack to solve issue where parseAndKeepRawInput throws weird error for zero or 1-digit (national) numbers e.g. "3" or "+13" s
number.length <= 3 ||
e.message === i18n.phonenumbers.Error.TOO_SHORT_AFTER_IDD ||
e.message === i18n.phonenumbers.Error.TOO_SHORT_NSN
) {
return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_SHORT;
}
if (e.message === i18n.phonenumbers.Error.TOO_LONG) {
return i18n.phonenumbers.PhoneNumberUtil.ValidationResult.TOO_LONG;
}
//* Broken
return -99;
}
};
//* Check if given number is valid.
const isValidNumber = (number, countryCode, numberTypeNames) => {
try {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);
const isValidNumber = phoneUtil.isValidNumber(numberObj);
if (numberTypeNames) {
const numberTypes = numberTypeNames.map((typeName) => numberType[typeName]);
return isValidNumber && numberTypes.includes(phoneUtil.getNumberType(numberObj));
}
return isValidNumber;
} catch {
return false;
}
};
//* Check if given number is possible.
const isPossibleNumber = (number, countryCode, numberTypeNames) => {
try {
const phoneUtil = i18n.phonenumbers.PhoneNumberUtil.getInstance();
const numberObj = phoneUtil.parseAndKeepRawInput(number, countryCode);
if (numberTypeNames) {
//* FIXED_LINE_OR_MOBILE does not behave how you would expect (e.g. in the UK, calling isPossibleNumberForType() with a mobile number and numberType=FIXED_LINE_OR_MOBILE will return false). This is because it is its own category that is different to either MOBILE or FIXED_LINE (e.g. it is used for US numbers which could be used for either purpose - it should really be called something like FIXED_LINE_SLASH_MOBILE). So here we make it more user friendly by checking if it's a possible number for any of those three categories. NOTE: this is actually in-line with how it behaves in other situations e.g. if you call isPossibleNumberForType with type="MOBILE" and the number set to a US number, it returns VALID even though it's type is technically FIXED_LINE_OR_MOBILE.
if (numberTypeNames.includes("FIXED_LINE_OR_MOBILE")) {
if (!numberTypeNames.includes("MOBILE")) {
numberTypeNames.push("MOBILE");
}
if (!numberTypeNames.includes("FIXED_LINE")) {
numberTypeNames.push("FIXED_LINE");
}
}
for (let typeName of numberTypeNames) {
//* Can't use phoneUtil.isPossibleNumberForType directly as it accepts IS_POSSIBLE_LOCAL_ONLY numbers e.g. local numbers that are much shorter.
const resultForType = phoneUtil.isPossibleNumberForTypeWithReason(numberObj, numberType[typeName]);
if (resultForType === i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE) {
return true;
}
}
return false;
}
//* Can't use phoneUtil.isPossibleNumber directly as it accepts IS_POSSIBLE_LOCAL_ONLY numbers e.g. local numbers that are much shorter.
const result = phoneUtil.isPossibleNumberWithReason(numberObj);
const isPossible = result === i18n.phonenumbers.PhoneNumberUtil.ValidationResult.IS_POSSIBLE;
return isPossible;
} catch {
return false;
}
};
/********************
* NOTE: for following sections, keys must be in quotes to force closure compiler to preserve them
********************/
//* copied this from i18n.phonenumbers.PhoneNumberFormat in the file https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js.
const numberFormat = {
"E164": 0,
"INTERNATIONAL": 1,
"NATIONAL": 2,
"RFC3966": 3,
};
//* copied this from i18n.phonenumbers.PhoneNumberType in https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js and put the keys in quotes to force closure compiler to preserve the keys
// TODO: There must be a way to just tell closure compiler to preserve the keys on i18n.phonenumbers.PhoneNumberType and just export that.
const numberType = {
"FIXED_LINE": 0,
"MOBILE": 1,
"FIXED_LINE_OR_MOBILE": 2,
"TOLL_FREE": 3,
"PREMIUM_RATE": 4,
"SHARED_COST": 5,
"VOIP": 6,
"PERSONAL_NUMBER": 7,
"PAGER": 8,
"UAN": 9,
"VOICEMAIL": 10,
"UNKNOWN": -1,
};
//* copied this from i18n.phonenumbers.PhoneNumberUtil.ValidationResult in https://github.com/googlei18n/libphonenumber/blob/master/javascript/i18n/phonenumbers/phonenumberutil.js and again put the keys in quotes.
const validationError = {
"IS_POSSIBLE": 0,
"INVALID_COUNTRY_CODE": 1,
"TOO_SHORT": 2,
"TOO_LONG": 3,
"IS_POSSIBLE_LOCAL_ONLY": 4,
"INVALID_LENGTH": 5,
};
//* Exports
//* Note: the below code defines window.intlTelInputUtilsTemp, which is so-called because it will be exported (as an ES Module) and then deleted at the end of this file (see output_wrapper in grunt/closure-compiler.js).
goog.exportSymbol("intlTelInputUtilsTemp", {});
goog.exportSymbol("intlTelInputUtilsTemp.formatNumberAsYouType", formatNumberAsYouType);
goog.exportSymbol("intlTelInputUtilsTemp.formatNumber", formatNumber);
goog.exportSymbol("intlTelInputUtilsTemp.getExampleNumber", getExampleNumber);
goog.exportSymbol("intlTelInputUtilsTemp.getExtension", getExtension);
goog.exportSymbol("intlTelInputUtilsTemp.getNumberType", getNumberType);
goog.exportSymbol("intlTelInputUtilsTemp.getValidationError", getValidationError);
goog.exportSymbol("intlTelInputUtilsTemp.isValidNumber", isValidNumber);
goog.exportSymbol("intlTelInputUtilsTemp.isPossibleNumber", isPossibleNumber);
goog.exportSymbol("intlTelInputUtilsTemp.getCoreNumber", getCoreNumber);
//* Enums
goog.exportSymbol("intlTelInputUtilsTemp.numberFormat", numberFormat);
goog.exportSymbol("intlTelInputUtilsTemp.numberType", numberType);
goog.exportSymbol("intlTelInputUtilsTemp.validationError", validationError);