-
Notifications
You must be signed in to change notification settings - Fork 135
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into chirag-deprecating-sequenceNumber
- Loading branch information
Showing
8 changed files
with
329 additions
and
45 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -64,7 +64,12 @@ export default class ExpensiMark { | |
); | ||
return this.modifyTextForEmailLinks(regex, textToProcess, replacement); | ||
}, | ||
replacement: (match, g1, g2) => `<a href="mailto:${g2}">${g1.trim()}</a>`, | ||
replacement: (match, g1, g2) => { | ||
if (g1.match(CONST.REG_EXP.EMOJIS) || !g1.trim()) { | ||
return match; | ||
} | ||
return `<a href="mailto:${g2}">${g1.trim()}</a>`; | ||
}, | ||
}, | ||
|
||
/** | ||
|
@@ -210,8 +215,8 @@ export default class ExpensiMark { | |
}, | ||
{ | ||
name: 'strikethrough', | ||
regex: /\B~((?=\S)((~~(?!~)|[^\s~]|\s(?!~))+?))~\B(?![^<]*(<\/pre>|<\/code>|<\/a>))/g, | ||
replacement: (match, g1) => (this.containsNonPairTag(g1) ? match : `<del>${g1}</del>`), | ||
regex: /\B~((?![\s~])[\s\S]*?[^\s~])~\B(?![^<]*(<\/pre>|<\/code>|<\/a>))/g, | ||
replacement: (match, g1) => (g1.includes('<pre>') || this.containsNonPairTag(g1) ? match : `<del>${g1}</del>`), | ||
}, | ||
{ | ||
name: 'newline', | ||
|
@@ -282,7 +287,7 @@ export default class ExpensiMark { | |
}, | ||
{ | ||
name: 'heading1', | ||
regex: /[^\S\r\n]*<(h1)(?:"[^"]*"|'[^']*'|[^'">])*>(.*?)<\/\1>(?![^<]*(<\/pre>|<\/code>))[^\S\r\n]*/gi, | ||
regex: /[^\S\r\n]*<(h1)(?:"[^"]*"|'[^']*'|[^'">])*>(.*?)<\/\1>(?![^<]*(<\/pre>|<\/code>))/gi, | ||
replacement: '<h1># $2</h1>', | ||
}, | ||
{ | ||
|
@@ -446,39 +451,72 @@ export default class ExpensiMark { | |
let startIndex = 0; | ||
|
||
while (match !== null) { | ||
// we want to avoid matching email address domains | ||
let abort = false; | ||
if ((match.index !== 0) && (textToCheck[match.index - 1] === '@')) { | ||
abort = true; | ||
// We end the link at the last closing parenthesis that matches an opening parenthesis because unmatched closing parentheses are unlikely to be in the url | ||
// and can be part of markdown for example | ||
let unmatchedOpenParentheses = 0; | ||
let url = match[2]; | ||
for (let i = 0; i < url.length; i++) { | ||
if (url[i] === '(') { | ||
unmatchedOpenParentheses++; | ||
} else if (url[i] === ')') { | ||
// Unmatched closing parenthesis | ||
if (unmatchedOpenParentheses <= 0) { | ||
const numberOfCharsToRemove = url.length - i; | ||
match[0] = match[0].substr(0, match[0].length - numberOfCharsToRemove); | ||
url = url.substr(0, url.length - numberOfCharsToRemove); | ||
break; | ||
} | ||
unmatchedOpenParentheses--; | ||
} | ||
} | ||
|
||
// we want to avoid matching ending ) unless it is a closing parenthesis for the URL | ||
if (textToCheck[(match.index + match[2].length) - 1] === ')' && !match[2].includes('(')) { | ||
match[0] = match[0].substr(0, match[0].length - 1); | ||
match[2] = match[2].substr(0, match[2].length - 1); | ||
} | ||
// Because we are removing ) parenthesis, some special characters that shouldn't be in the href are in the href | ||
// For example google.com/toto.) is accepted by the regular expression above and we remove the ) parenthesis, so the link becomes google.com/toto. which is not a valid link | ||
// In that case we should also remove the "." | ||
// Those characters should only be remove from the url if this url doesn't have a parameter or a fragment | ||
if (!url.includes('?') && !url.includes('#')) { | ||
let numberOfCharsToRemove = 0; | ||
|
||
// remove extra ) parentheses | ||
let brace = 0; | ||
if (match[2][match[2].length - 1] === ')') { | ||
for (let i = 0; i < match[2].length; i++) { | ||
if (match[2][i] === '(') { | ||
brace++; | ||
} | ||
if (match[2][i] === ')') { | ||
brace--; | ||
for (let i = url.length - 1; i >= 0; i--) { | ||
if (CONST.SPECIAL_CHARS_TO_REMOVE.includes(url[i])) { | ||
numberOfCharsToRemove++; | ||
} else { | ||
break; | ||
} | ||
} | ||
if (brace) { | ||
match[0] = match[0].substr(0, match[0].length + brace); | ||
match[2] = match[2].substr(0, match[2].length + brace); | ||
if (numberOfCharsToRemove) { | ||
match[0] = match[0].substring(0, match[0].length - numberOfCharsToRemove); | ||
url = url.substring(0, url.length - numberOfCharsToRemove); | ||
} | ||
} | ||
|
||
replacedText = replacedText.concat(textToCheck.substr(startIndex, (match.index - startIndex))); | ||
|
||
if (abort || match[1].includes('<pre>')) { | ||
// We want to avoid matching domains in email addresses so we don't render them as URLs, | ||
// but we need to check if there are valid URLs after the email address and render them accordingly, | ||
// e.g. [email protected]/https://www.test.com | ||
let isDoneMatching = false; | ||
let shouldApplyAutoLinkAgain = true; | ||
|
||
// If we find a URL with a leading @ sign, we need look for other domains in the rest of the string | ||
if ((match.index !== 0) && (textToCheck[match.index - 1] === '@')) { | ||
const domainRegex = new RegExp('^(([a-z-0-9]+\\.)+[a-z]{2,})(\\S*)', 'i'); | ||
const domainMatch = domainRegex.exec(url); | ||
|
||
// If we find another domain in the remainder of the string, we apply the auto link rule again and set a flag to avoid re-doing below. | ||
if ((domainMatch !== null) && (domainMatch[3] !== '')) { | ||
replacedText = replacedText.concat(domainMatch[1] + this.replace(domainMatch[3], {filterRules: ['autolink']})); | ||
shouldApplyAutoLinkAgain = false; | ||
} else { | ||
// Otherwise, we're done applying rules | ||
isDoneMatching = true; | ||
} | ||
} | ||
|
||
// We don't want to apply link rule if match[1] contains the code block inside the [] of the markdown e.g. [```example```](https://example.com) | ||
if (isDoneMatching || match[1].includes('<pre>')) { | ||
replacedText = replacedText.concat(textToCheck.substr(match.index, (match[0].length))); | ||
} else { | ||
} else if (shouldApplyAutoLinkAgain) { | ||
const urlRegex = new RegExp(`^${LOOSE_URL_REGEX}$|^${URL_REGEX}$`, 'i'); | ||
|
||
// `match[1]` contains the text inside the [] of the markdown e.g. [example](https://example.com) | ||
|
@@ -490,7 +528,7 @@ export default class ExpensiMark { | |
filterRules: ['bold', 'strikethrough', 'italic'], | ||
shouldEscapeText: false, | ||
}); | ||
replacedText = replacedText.concat(replacement(match[0], linkText, match[2])); | ||
replacedText = replacedText.concat(replacement(match[0], linkText, url)); | ||
} | ||
startIndex = match.index + (match[0].length); | ||
|
||
|
@@ -754,11 +792,14 @@ export default class ExpensiMark { | |
*/ | ||
extractLinksInMarkdownComment(comment) { | ||
try { | ||
const escapedComment = _.escape(comment); | ||
const matches = [...escapedComment.matchAll(MARKDOWN_LINK_REGEX)]; | ||
const htmlString = this.replace(comment, {filterRules: ['link']}); | ||
|
||
// We use same anchor tag template as link and autolink rules to extract link | ||
const regex = new RegExp(`<a href="${MARKDOWN_URL_REGEX}" target="_blank" rel="noreferrer noopener">`, 'gi'); | ||
const matches = [...htmlString.matchAll(regex)]; | ||
|
||
// Element 1 from match is the regex group if it exists which contains the link URLs | ||
const links = _.map(matches, match => Str.sanitizeURL(match[2])); | ||
const links = _.map(matches, match => Str.sanitizeURL(match[1])); | ||
return links; | ||
} catch (e) { | ||
// eslint-disable-next-line no-console | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters