2022-03-04 09:23:40 +00:00
|
|
|
import { marked } from 'marked';
|
2021-01-15 09:10:50 +00:00
|
|
|
import DOMPurify from 'dompurify';
|
2020-06-02 17:29:02 +00:00
|
|
|
import { escapeHtml } from './HTMLSanitizer';
|
2021-01-15 09:10:50 +00:00
|
|
|
|
2020-08-11 04:27:42 +00:00
|
|
|
const TWITTER_USERNAME_REGEX = /(^|[^@\w])@(\w{1,15})\b/g;
|
|
|
|
const TWITTER_USERNAME_REPLACEMENT =
|
|
|
|
'$1<a href="http://twitter.com/$2" target="_blank" rel="noreferrer nofollow noopener">@$2</a>';
|
|
|
|
|
|
|
|
const TWITTER_HASH_REGEX = /(^|\s)#(\w+)/g;
|
|
|
|
const TWITTER_HASH_REPLACEMENT =
|
|
|
|
'$1<a href="https://twitter.com/hashtag/$2" target="_blank" rel="noreferrer nofollow noopener">#$2</a>';
|
2020-06-02 17:29:02 +00:00
|
|
|
|
2021-02-03 12:25:27 +00:00
|
|
|
const USER_MENTIONS_REGEX = /mention:\/\/(user|team)\/(\d+)\/(.+)/gm;
|
2021-01-26 18:34:11 +00:00
|
|
|
|
2019-11-23 18:59:55 +00:00
|
|
|
class MessageFormatter {
|
2020-08-11 04:27:42 +00:00
|
|
|
constructor(message, isATweet = false) {
|
2021-01-26 18:34:11 +00:00
|
|
|
this.message = DOMPurify.sanitize(escapeHtml(message || ''));
|
2020-08-11 04:27:42 +00:00
|
|
|
this.isATweet = isATweet;
|
2021-01-15 09:10:50 +00:00
|
|
|
this.marked = marked;
|
|
|
|
|
|
|
|
const renderer = {
|
|
|
|
heading(text) {
|
|
|
|
return `<strong>${text}</strong>`;
|
|
|
|
},
|
|
|
|
link(url, title, text) {
|
2021-01-26 18:34:11 +00:00
|
|
|
const mentionRegex = new RegExp(USER_MENTIONS_REGEX);
|
|
|
|
if (url.match(mentionRegex)) {
|
|
|
|
return `<span class="prosemirror-mention-node">${text}</span>`;
|
|
|
|
}
|
2021-01-15 09:10:50 +00:00
|
|
|
return `<a rel="noreferrer noopener nofollow" href="${url}" class="link" title="${title ||
|
|
|
|
''}" target="_blank">${text}</a>`;
|
|
|
|
},
|
|
|
|
};
|
|
|
|
this.marked.use({ renderer });
|
2019-11-23 18:59:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
formatMessage() {
|
2020-08-11 04:27:42 +00:00
|
|
|
if (this.isATweet) {
|
2021-01-15 09:10:50 +00:00
|
|
|
const withUserName = this.message.replace(
|
2020-08-11 04:27:42 +00:00
|
|
|
TWITTER_USERNAME_REGEX,
|
|
|
|
TWITTER_USERNAME_REPLACEMENT
|
|
|
|
);
|
2021-01-15 09:10:50 +00:00
|
|
|
const withHash = withUserName.replace(
|
2020-08-11 04:27:42 +00:00
|
|
|
TWITTER_HASH_REGEX,
|
|
|
|
TWITTER_HASH_REPLACEMENT
|
|
|
|
);
|
2021-01-15 09:10:50 +00:00
|
|
|
const markedDownOutput = marked(withHash);
|
|
|
|
return markedDownOutput;
|
2020-08-11 04:27:42 +00:00
|
|
|
}
|
2022-03-14 12:43:21 +00:00
|
|
|
DOMPurify.addHook('afterSanitizeAttributes', node => {
|
|
|
|
if ('target' in node) node.setAttribute('target', '_blank');
|
|
|
|
});
|
|
|
|
return DOMPurify.sanitize(
|
|
|
|
marked(this.message, { breaks: true, gfm: true })
|
|
|
|
);
|
2019-11-23 18:59:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
get formattedMessage() {
|
|
|
|
return this.formatMessage();
|
|
|
|
}
|
2021-01-15 09:10:50 +00:00
|
|
|
|
|
|
|
get plainText() {
|
|
|
|
const strippedOutHtml = new DOMParser().parseFromString(
|
|
|
|
this.formattedMessage,
|
|
|
|
'text/html'
|
|
|
|
);
|
|
|
|
return strippedOutHtml.body.textContent || '';
|
|
|
|
}
|
2019-11-23 18:59:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
export default MessageFormatter;
|