summaryrefslogtreecommitdiffstats
path: root/web
diff options
context:
space:
mode:
authorhmhealey <harrisonmhealey@gmail.com>2015-10-28 14:21:47 -0400
committerhmhealey <harrisonmhealey@gmail.com>2015-10-28 14:31:41 -0400
commit0356130ca6d4e3457769946d7dbf383ffe291098 (patch)
treee9d25019563f9d6aea10133dd9aaafb1b937d96a /web
parent429e25864b86b7b5976348860a302c2843687ef4 (diff)
downloadchat-0356130ca6d4e3457769946d7dbf383ffe291098.tar.gz
chat-0356130ca6d4e3457769946d7dbf383ffe291098.tar.bz2
chat-0356130ca6d4e3457769946d7dbf383ffe291098.zip
Updated markdown regexes to better handle underscores and links
Diffstat (limited to 'web')
-rw-r--r--web/react/utils/markdown.jsx52
-rw-r--r--web/react/utils/text_formatting.jsx23
2 files changed, 42 insertions, 33 deletions
diff --git a/web/react/utils/markdown.jsx b/web/react/utils/markdown.jsx
index 4fb4c6089..e34f3d00a 100644
--- a/web/react/utils/markdown.jsx
+++ b/web/react/utils/markdown.jsx
@@ -10,9 +10,21 @@ class MattermostInlineLexer extends marked.InlineLexer {
constructor(links, options) {
super(links, options);
- // modified version of the regex that doesn't break up words in snake_case
- // the original is /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
- this.rules.text = /^[\s\S]+?(?=__|\b_|[\\<!\[*`]| {2,}\n|$)/;
+ this.rules = Object.assign({}, this.rules);
+
+ // modified version of the regex that doesn't break up words in snake_case,
+ // allows for links starting with www, and allows links succounded by parentheses
+ // the original is /^[\s\S]+?(?=[\\<!\[_*`~]|https?:\/\/| {2,}\n|$)/
+ this.rules.text = /^[\s\S]+?(?=[^\w\/]_|[\\<!\[*`~]|https?:\/\/|www\.|\(| {2,}\n|$)/;
+
+ // modified version of the regex that allows links starting with www and those surrounded
+ // by parentheses
+ // the original is /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/
+ this.rules.url = /^(\(?(?:https?:\/\/|www\.)[^\s<.][^\s<]*[^<.,:;"'\]\s])/;
+
+ // modified version of the regex that allows <links> starting with www.
+ // the original is /^<([^ >]+(@|:\/)[^ >]+)>/
+ this.rules.autolink = /^<((?:[^ >]+(@|:\/)|www\.)[^ >]+)>/;
}
}
@@ -56,8 +68,20 @@ class MattermostMarkdownRenderer extends marked.Renderer {
link(href, title, text) {
let outHref = href;
+ let outText = text;
+ let prefix = '';
+ let suffix = '';
+
+ // some links like https://en.wikipedia.org/wiki/Rendering_(computer_graphics) contain brackets
+ // and we try our best to differentiate those from ones just wrapped in brackets when autolinking
+ if (outHref.startsWith('(') && outHref.endsWith(')') && text === outHref) {
+ prefix = '(';
+ suffix = ')';
+ outText = text.substring(1, text.length - 1);
+ outHref = outHref.substring(1, outHref.length - 1);
+ }
- if (!(/^(mailto|https?|ftp)/.test(outHref))) {
+ if (!(/[a-z+.-]+:/i).test(outHref)) {
outHref = `http://${outHref}`;
}
@@ -72,26 +96,17 @@ class MattermostMarkdownRenderer extends marked.Renderer {
output += ' target="_blank">';
}
- output += text + '</a>';
+ output += outText + '</a>';
- return output;
+ return prefix + output + suffix;
}
paragraph(text) {
- let outText = text;
-
- // required so markdown does not strip '_' from @user_names
- outText = TextFormatting.doFormatMentions(text);
-
- if (!('emoticons' in this.options) || this.options.emoticon) {
- outText = TextFormatting.doFormatEmoticons(outText);
- }
-
if (this.formattingOptions.singleline) {
- return `<p class="markdown__paragraph-inline">${outText}</p>`;
+ return `<p class="markdown__paragraph-inline">${text}</p>`;
}
- return super.paragraph(outText);
+ return super.paragraph(text);
}
table(header, body) {
@@ -106,7 +121,8 @@ class MattermostMarkdownRenderer extends marked.Renderer {
export function format(text, options) {
const markdownOptions = {
renderer: new MattermostMarkdownRenderer(null, options),
- sanitize: true
+ sanitize: true,
+ gfm: true
};
const tokens = marked.lexer(text, markdownOptions);
diff --git a/web/react/utils/text_formatting.jsx b/web/react/utils/text_formatting.jsx
index 9c87d2693..2de858a17 100644
--- a/web/react/utils/text_formatting.jsx
+++ b/web/react/utils/text_formatting.jsx
@@ -43,7 +43,7 @@ export function doFormatText(text, options) {
// replace important words and phrases with tokens
output = autolinkAtMentions(output, tokens);
- output = autolinkUrls(output, tokens);
+ output = autolinkEmails(output, tokens);
output = autolinkHashtags(output, tokens);
if (!('emoticons' in options) || options.emoticon) {
@@ -93,28 +93,21 @@ export function sanitizeHtml(text) {
return output;
}
-// Convert URLs into tokens
-function autolinkUrls(text, tokens) {
- function replaceUrlWithToken(autolinker, match) {
+// Convert emails into tokens
+function autolinkEmails(text, tokens) {
+ function replaceEmailWithToken(autolinker, match) {
const linkText = match.getMatchedText();
let url = linkText;
if (match.getType() === 'email') {
url = `mailto:${url}`;
- } else if (!(/^(mailto|https?|ftp)/.test(url))) {
- url = `http://${url}`;
}
const index = tokens.size;
- const alias = `MM_LINK${index}`;
-
- var target = 'target="_blank"';
- if (url.lastIndexOf(Utils.getTeamURLFromAddressBar(), 0) === 0) {
- target = '';
- }
+ const alias = `MM_EMAIL${index}`;
tokens.set(alias, {
- value: `<a class="theme" ${target} href="${url}">${linkText}</a>`,
+ value: `<a class="theme" href="${url}">${linkText}</a>`,
originalText: linkText
});
@@ -123,12 +116,12 @@ function autolinkUrls(text, tokens) {
// we can't just use a static autolinker because we need to set replaceFn
const autolinker = new Autolinker({
- urls: true,
+ urls: false,
email: true,
phone: false,
twitter: false,
hashtag: false,
- replaceFn: replaceUrlWithToken
+ replaceFn: replaceEmailWithToken
});
return autolinker.link(text);