Use same lock values as mobile clients
[ProtonMail-WebClient.git] / packages / shared / lib / helpers / string.ts
blob1361f9b144064da3c19ee5ab3900fd1c2b82460c
1 enum CURRENCIES {
2     USD = '$',
3     EUR = '€',
4     CHF = 'CHF',
7 export const normalize = (value = '', removeDiacritics = false) => {
8     let normalized = value.toLowerCase().trim();
9     if (removeDiacritics) {
10         normalized = normalized.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
11     }
12     return normalized;
15 export const replaceLineBreak = (content = '') => content.replace(/(?:\r\n|\r|\n)/g, '<br />');
17 export const toCRLF = (str: string) => str.replace(/\n/g, '\r\n');
19 export const toPrice = (amount = 0, currency: keyof typeof CURRENCIES = 'EUR', divisor = 100) => {
20     const symbol = CURRENCIES[currency] || currency;
21     const value = Number(amount / divisor).toFixed(2);
22     const prefix = +value < 0 ? '-' : '';
23     const absValue = Math.abs(+value);
25     if (currency === 'USD') {
26         return `${prefix}${symbol}${absValue}`;
27     }
29     return `${prefix}${absValue} ${symbol}`;
32 export const addPlus = ([first = '', ...rest] = []) => {
33     return [first, rest.length && `+${rest.length}`].filter(Boolean).join(', ');
36 export const DEFAULT_TRUNCATE_OMISSION = '…';
38 /**
39  * Given a maximum number of characters to capture from a string at the start and end of it,
40  * truncate the string by adding omission if too long. If only a maximum number of characters
41  * is passed, the string is truncated by adding omission in the middle of it if too long
42  */
43 export const truncateMore = ({
44     string,
45     charsToDisplay,
46     charsToDisplayStart = 0,
47     charsToDisplayEnd = 0,
48     omission = '…',
49     skewEnd = false,
50 }: {
51     string: string;
52     charsToDisplay?: number;
53     charsToDisplayStart?: number;
54     charsToDisplayEnd?: number;
55     omission?: string;
56     skewEnd?: boolean;
57 }): string => {
58     if (string.length === 0) {
59         return string;
60     }
62     if (charsToDisplay !== undefined) {
63         // truncate symmetrically
64         const visibleChars = charsToDisplay - omission.length;
65         const charsToDisplayStart = skewEnd ? Math.floor(visibleChars / 2) : Math.ceil(visibleChars / 2);
66         const charsToDisplayEnd = visibleChars - charsToDisplayStart;
68         return truncateMore({ string, charsToDisplayStart, charsToDisplayEnd, omission });
69     }
71     if (string.length <= charsToDisplayStart + charsToDisplayEnd + omission.length) {
72         return string;
73     }
75     const strBegin = string.substring(0, charsToDisplayStart);
76     const strEnd = string.substring(string.length - charsToDisplayEnd, string.length);
78     return strBegin + omission + strEnd;
81 export const truncatePossiblyQuotedString = (string: string, charsToDisplay: number) => {
82     const match = string.match(/^"(.+)"$/);
84     if (!match) {
85         return truncateMore({ string, charsToDisplay });
86     }
88     const [, quotedString] = match;
90     return `"${truncateMore({ string: quotedString, charsToDisplay: charsToDisplay - 2 })}"`;
93 export const getInitials = (fullName = '') => {
94     const [first, ...rest] = fullName
95         .replace(/\s{2,}/g, ' ')
96         .split(' ')
97         .filter((word = '') => !/^[.,/#!$@%^&*;:{}=\-_`~()]/g.test(word));
98     const last = rest[rest.length - 1];
100     const initials = [first, last]
101         .filter(Boolean)
102         .map((letter = '') => [...letter.toUpperCase()][0]) // We use the spread operator to support Unicode characters
103         .join('');
105     if (!initials) {
106         return '?';
107     }
109     return initials;
112 export const hasProtonDomain = (email = '') => {
113     return /@(protonmail\.(com|ch)|proton\.(me|ch)|pm\.me|)$/i.test(email);
116 const getMatchingCharacters = (string: string, substring: string) => {
117     let i;
118     for (i = 0; i < substring.length; ++i) {
119         if (string[i] !== substring[i]) {
120             return i;
121         }
122     }
123     return i > 0 ? i : 0;
126 export const findLongestMatchingIndex = (strings: string[] = [], substring = '') => {
127     let max = 0;
128     let i = -1;
130     strings.forEach((string, idx) => {
131         const numberOfMatches = getMatchingCharacters(string, substring);
132         if (numberOfMatches > max) {
133             max = numberOfMatches;
134             i = idx;
135         }
136     });
138     return i;
141 export const stripLeadingSlash = (str: string) => str.replace(/^\/+/g, '');
142 export const stripTrailingSlash = (str: string) => str.replace(/\/+$/g, '');
143 export const stripLeadingAndTrailingSlash = (str: string) => str.replace(/^\/+|\/+$/g, '');
145 export const removeDiacritics = (str: string) => {
146     return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
150  * Replace LTR and RTL override unicode chars which can lead to security issues on filenames
151  * 202D and 202E should be the only unicode chars concerned
152  * https://jira.protontech.ch/browse/SEC-644
153  */
154 export const rtlSanitize = (str: string) => {
155     return str.replace(/[\u202D\u202E]/g, '_');
158 export const removeHTMLComments = (str: string) => {
159     return str.replace(/<!--[\s\S]*?-->/g, '');