1 export const normalize = (value = '', removeDiacritics = false) => {
2 let normalized = value.toLowerCase().trim();
3 if (removeDiacritics) {
4 normalized = normalized.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
9 export const replaceLineBreak = (content = '') => content.replace(/(?:\r\n|\r|\n)/g, '<br />');
11 export const toCRLF = (str: string) => str.replace(/\n/g, '\r\n');
13 export const addPlus = ([first = '', ...rest] = []) => {
14 return [first, rest.length && `+${rest.length}`].filter(Boolean).join(', ');
17 export const DEFAULT_TRUNCATE_OMISSION = '…';
20 * Given a maximum number of characters to capture from a string at the start and end of it,
21 * truncate the string by adding omission if too long. If only a maximum number of characters
22 * is passed, the string is truncated by adding omission in the middle of it if too long
24 export const truncateMore = ({
27 charsToDisplayStart = 0,
28 charsToDisplayEnd = 0,
33 charsToDisplay?: number;
34 charsToDisplayStart?: number;
35 charsToDisplayEnd?: number;
39 if (string.length === 0) {
43 if (charsToDisplay !== undefined) {
44 // truncate symmetrically
45 const visibleChars = charsToDisplay - omission.length;
46 const charsToDisplayStart = skewEnd ? Math.floor(visibleChars / 2) : Math.ceil(visibleChars / 2);
47 const charsToDisplayEnd = visibleChars - charsToDisplayStart;
49 return truncateMore({ string, charsToDisplayStart, charsToDisplayEnd, omission });
52 if (string.length <= charsToDisplayStart + charsToDisplayEnd + omission.length) {
56 const strBegin = string.substring(0, charsToDisplayStart);
57 const strEnd = string.substring(string.length - charsToDisplayEnd, string.length);
59 return strBegin + omission + strEnd;
62 export const truncatePossiblyQuotedString = (string: string, charsToDisplay: number) => {
63 const match = string.match(/^"(.+)"$/);
66 return truncateMore({ string, charsToDisplay });
69 const [, quotedString] = match;
71 return `"${truncateMore({ string: quotedString, charsToDisplay: charsToDisplay - 2 })}"`;
74 export const getInitials = (fullName = '') => {
75 const [first, ...rest] = fullName
76 .replace(/\s{2,}/g, ' ')
78 .filter((word = '') => !/^[.,/#!$@%^&*;:{}=\-_`~()]/g.test(word));
79 const last = rest[rest.length - 1];
81 const initials = [first, last]
83 .map((letter = '') => [...letter.toUpperCase()][0]) // We use the spread operator to support Unicode characters
93 export const hasProtonDomain = (email = '') => {
94 return /@(protonmail\.(com|ch)|proton\.(me|ch)|pm\.me|)$/i.test(email);
97 const getMatchingCharacters = (string: string, substring: string) => {
99 for (i = 0; i < substring.length; ++i) {
100 if (string[i] !== substring[i]) {
104 return i > 0 ? i : 0;
107 export const findLongestMatchingIndex = (strings: string[] = [], substring = '') => {
111 strings.forEach((string, idx) => {
112 const numberOfMatches = getMatchingCharacters(string, substring);
113 if (numberOfMatches > max) {
114 max = numberOfMatches;
122 export const stripLeadingSlash = (str: string) => str.replace(/^\/+/g, '');
123 export const stripTrailingSlash = (str: string) => str.replace(/\/+$/g, '');
124 export const stripLeadingAndTrailingSlash = (str: string) => str.replace(/^\/+|\/+$/g, '');
126 export const removeDiacritics = (str: string) => {
127 return str.normalize('NFD').replace(/[\u0300-\u036f]/g, '');
131 * Replace LTR and RTL override unicode chars which can lead to security issues on filenames
132 * 202D and 202E should be the only unicode chars concerned
133 * https://jira.protontech.ch/browse/SEC-644
135 export const rtlSanitize = (str: string) => {
136 return str.replace(/[\u202D\u202E]/g, '_');
139 export const removeHTMLComments = (str: string) => {
140 return str.replace(/<!--[\s\S]*?-->/g, '');