The communications platform that puts data protection first.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
Rocket.Chat/app/markdown/lib/markdown.js

122 lines
3.3 KiB

/*
* Markdown is a named function that will parse markdown syntax
* @param {Object} message - The message object
*/
import { Meteor } from 'meteor/meteor';
import { marked } from './parser/marked/marked';
import { original } from './parser/original/original';
import { filtered } from './parser/filtered/filtered';
import { code } from './parser/original/code';
import { settings } from '../../settings';
import { escapeHTML } from '../../../lib/escapeHTML';
const parsers = {
original,
marked,
filtered,
};
class MarkdownClass {
parse(text) {
const message = {
html: escapeHTML(text),
};
return this.mountTokensBack(this.parseMessageNotEscaped(message)).html;
}
parseNotEscaped(text) {
const message = {
html: text,
};
return this.mountTokensBack(this.parseMessageNotEscaped(message)).html;
}
parseMessageNotEscaped(message) {
const parser = settings.get('Markdown_Parser');
if (parser === 'disabled') {
return message;
}
const options = {
supportSchemesForLink: settings.get('Markdown_SupportSchemesForLink'),
headers: settings.get('Markdown_Headers'),
rootUrl: Meteor.absoluteUrl(),
marked: {
gfm: settings.get('Markdown_Marked_GFM'),
tables: settings.get('Markdown_Marked_Tables'),
breaks: settings.get('Markdown_Marked_Breaks'),
pedantic: settings.get('Markdown_Marked_Pedantic'),
smartLists: settings.get('Markdown_Marked_SmartLists'),
smartypants: settings.get('Markdown_Marked_Smartypants'),
},
};
const parse = typeof parsers[parser] === 'function' ? parsers[parser] : parsers.original;
return parse(message, options);
}
mountTokensBackRecursively(message, tokenList, useHtml = true) {
const missingTokens = [];
if (tokenList.length > 0) {
for (const { token, text, noHtml } of tokenList) {
if (message.html.indexOf(token) >= 0) {
message.html = message.html.replace(token, () => (useHtml ? text : noHtml)); // Uses lambda so doesn't need to escape $
} else {
missingTokens.push({ token, text, noHtml });
}
}
}
// If there are tokens that were missing from the string, but the last iteration replaced at least one token, then go again
// this is done because one of the tokens may have been hidden by another one
if (missingTokens.length > 0 && missingTokens.length < tokenList.length) {
this.mountTokensBackRecursively(message, missingTokens, useHtml);
}
}
mountTokensBack(message, useHtml = true) {
if (message.tokens) {
this.mountTokensBackRecursively(message, message.tokens, useHtml);
}
return message;
}
code(...args) {
return code(...args);
}
filterMarkdownFromMessage(message) {
return parsers.filtered(message, {
supportSchemesForLink: settings.get('Markdown_SupportSchemesForLink'),
});
}
}
export const Markdown = new MarkdownClass();
export const filterMarkdown = (message) => Markdown.filterMarkdownFromMessage(message);
export const createMarkdownMessageRenderer = ({ parser, ...options }) => {
if (!parser || parser === 'disabled') {
return (message) => message;
}
const parse = typeof parsers[parser] === 'function' ? parsers[parser] : parsers.original;
return (message) => {
if (!message?.html?.trim()) {
return message;
}
return parse(message, options);
};
};
export const createMarkdownNotificationRenderer = (options) =>
(message) =>
parsers.filtered(message, options);