Skip to content

Commit

Permalink
Merge pull request #6694 from ehkasper/markdown-coffee
Browse files Browse the repository at this point in the history
Convert markdown to js
  • Loading branch information
rodrigok authored Apr 18, 2017
2 parents efef8ce + 2ed4062 commit bb173ea
Show file tree
Hide file tree
Showing 8 changed files with 225 additions and 185 deletions.
84 changes: 0 additions & 84 deletions packages/rocketchat-markdown/markdown.coffee

This file was deleted.

93 changes: 93 additions & 0 deletions packages/rocketchat-markdown/markdown.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Markdown is a named function that will parse markdown syntax
* @param {Object} message - The message object
*/

class MarkdownClass {
parse(text) {
return this.parseNotEscaped(_.escapeHTML(text));
}

parseNotEscaped(msg) {
const schemes = RocketChat.settings.get('Markdown_SupportSchemesForLink').split(',').join('|');

// Support ![alt text](http://image url)
msg = msg.replace(new RegExp(`!\\[([^\\]]+)\\]\\(((?:${ schemes }):\\/\\/[^\\)]+)\\)`, 'gm'), function(match, title, url) {
const target = url.indexOf(Meteor.absoluteUrl()) === 0 ? '' : '_blank';
return `<a href="${ _.escapeHTML(url) }" title="${ _.escapeHTML(title) }" target="${ _.escapeHTML(target) }"><div class="inline-image" style="background-image: url(${ _.escapeHTML(url) });"></div></a>`;
});

// Support [Text](http://link)
msg = msg.replace(new RegExp(`\\[([^\\]]+)\\]\\(((?:${ schemes }):\\/\\/[^\\)]+)\\)`, 'gm'), function(match, title, url) {
const target = url.indexOf(Meteor.absoluteUrl()) === 0 ? '' : '_blank';
return `<a href="${ _.escapeHTML(url) }" target="${ _.escapeHTML(target) }">${ _.escapeHTML(title) }</a>`;
});

// Support <http://link|Text>
msg = msg.replace(new RegExp(`(?:<|&lt;)((?:${ schemes }):\\/\\/[^\\|]+)\\|(.+?)(?=>|&gt;)(?:>|&gt;)`, 'gm'), (match, url, title) => {
const target = url.indexOf(Meteor.absoluteUrl()) === 0 ? '' : '_blank';
return `<a href="${ _.escapeHTML(url) }" target="${ _.escapeHTML(target) }">${ _.escapeHTML(title) }</a>`;
});

if (RocketChat.settings.get('Markdown_Headers')) {
// Support # Text for h1
msg = msg.replace(/^# (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm, '<h1>$1</h1>');

// Support # Text for h2
msg = msg.replace(/^## (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm, '<h2>$1</h2>');

// Support # Text for h3
msg = msg.replace(/^### (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm, '<h3>$1</h3>');

// Support # Text for h4
msg = msg.replace(/^#### (([\S\w\d-_\/\*\.,\\][ \u00a0\u1680\u180e\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]?)+)/gm, '<h4>$1</h4>');
}

// Support *text* to make bold
msg = msg.replace(/(^|&gt;|[ >_~`])\*{1,2}([^\*\r\n]+)\*{1,2}([<_~`]|\B|\b|$)/gm, '$1<span class="copyonly">*</span><strong>$2</strong><span class="copyonly">*</span>$3');

// Support _text_ to make italics
msg = msg.replace(/(^|&gt;|[ >*~`])\_([^\_\r\n]+)\_([<*~`]|\B|\b|$)/gm, '$1<span class="copyonly">_</span><em>$2</em><span class="copyonly">_</span>$3');

// Support ~text~ to strike through text
msg = msg.replace(/(^|&gt;|[ >_*`])\~{1,2}([^~\r\n]+)\~{1,2}([<_*`]|\B|\b|$)/gm, '$1<span class="copyonly">~</span><strike>$2</strike><span class="copyonly">~</span>$3');

// Support for block quote
// >>>
// Text
// <<<
msg = msg.replace(/(?:&gt;){3}\n+([\s\S]*?)\n+(?:&lt;){3}/g, '<blockquote class="background-transparent-darker-before"><span class="copyonly">&gt;&gt;&gt;</span>$1<span class="copyonly">&lt;&lt;&lt;</span></blockquote>');

// Support >Text for quote
msg = msg.replace(/^&gt;(.*)$/gm, '<blockquote class="background-transparent-darker-before"><span class="copyonly">&gt;</span>$1</blockquote>');

// Remove white-space around blockquote (prevent <br>). Because blockquote is block element.
msg = msg.replace(/\s*<blockquote class="background-transparent-darker-before">/gm, '<blockquote class="background-transparent-darker-before">');
msg = msg.replace(/<\/blockquote>\s*/gm, '</blockquote>');

// Remove new-line between blockquotes.
msg = msg.replace(/<\/blockquote>\n<blockquote/gm, '</blockquote><blockquote');

if (typeof window !== 'undefined' && window !== null ? window.rocketDebug : undefined) { console.log('Markdown', msg); }

return msg;
}
}

const Markdown = new MarkdownClass;
RocketChat.Markdown = Markdown;

// renderMessage already did html escape
const MarkdownMessage = (message) => {
if (_.trim(message != null ? message.html : undefined)) {
message.html = Markdown.parseNotEscaped(message.html);
}

return message;
};

RocketChat.callbacks.add('renderMessage', MarkdownMessage, RocketChat.callbacks.priority.HIGH, 'markdown');

if (Meteor.isClient) {
Blaze.registerHelper('RocketChatMarkdown', text => Markdown.parse(text));
}
88 changes: 0 additions & 88 deletions packages/rocketchat-markdown/markdowncode.coffee

This file was deleted.

112 changes: 112 additions & 0 deletions packages/rocketchat-markdown/markdowncode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* MarkdownCode is a named function that will parse `inline code` and ```codeblock``` syntaxes
* @param {Object} message - The message object
*/
import hljs from 'highlight.js';

class MarkdownCode {
constructor(message) {

if (s.trim(message.html)) {
if (message.tokens == null) {
message.tokens = [];
}

MarkdownCode.handle_codeblocks(message);
MarkdownCode.handle_inlinecode(message);

if (window && window.rocketDebug) {
console.log('Markdown', message);
}
}

return message;
}

static handle_inlinecode(message) {
// Support `text`
return message.html = message.html.replace(/(^|&gt;|[ >_*~])\`([^`\r\n]+)\`([<_*~]|\B|\b|$)/gm, (match, p1, p2, p3) => {
const token = `=!=${ Random.id() }=!=`;

message.tokens.push({
token,
text: `${ p1 }<span class=\"copyonly\">\`</span><span><code class=\"code-colors inline\">${ p2 }</code></span><span class=\"copyonly\">\`</span>${ p3 }`,
noHtml: match
});

return token;
});
}

static handle_codeblocks(message) {
// Count occurencies of ```
const count = (message.html.match(/```/g) || []).length;

if (count) {

// Check if we need to add a final ```
if ((count % 2) > 0) {
message.html = `${ message.html }\n\`\`\``;
message.msg = `${ message.msg }\n\`\`\``;
}

// Separate text in code blocks and non code blocks
const msgParts = message.html.split(/(^.*)(```(?:[a-zA-Z]+)?(?:(?:.|\n)*?)```)(.*\n?)$/gm);

for (let index = 0; index < msgParts.length; index++) {
// Verify if this part is code
const part = msgParts[index];
const codeMatch = part.match(/^```(.*[\n\ ]?)([\s\S]*?)```+?$/);

if (codeMatch != null) {
// Process highlight if this part is code
let code;
let lang;
let result;
const singleLine = codeMatch[0].indexOf('\n') === -1;

if (singleLine) {
lang = '';
code = _.unescapeHTML(codeMatch[1] + codeMatch[2]);
} else {
lang = codeMatch[1];
code = _.unescapeHTML(codeMatch[2]);
}

if (s.trim(lang) === '') {
lang = '';
}

if (!Array.from(hljs.listLanguages()).includes(s.trim(lang))) {
result = hljs.highlightAuto((lang + code));
} else {
result = hljs.highlight(s.trim(lang), code);
}

const token = `=!=${ Random.id() }=!=`;

message.tokens.push({
highlight: true,
token,
text: `<pre><code class='code-colors hljs ${ result.language }'><span class='copyonly'>\`\`\`<br></span>${ result.value }<span class='copyonly'><br>\`\`\`</span></code></pre>`,
noHtml: `\`\`\`\n${ s.stripTags(result.value) }\n\`\`\``
});

msgParts[index] = token;
} else {
msgParts[index] = part;
}
}

// Re-mount message
return message.html = msgParts.join('');
}
}
}

RocketChat.MarkdownCode = MarkdownCode;

const MarkdownCodeCB = (message) => new MarkdownCode(message);

// MarkdownCode gets higher priority over Markdown so it's possible place a callback in between (katex for exmaple)
RocketChat.callbacks.add('renderMessage', MarkdownCodeCB, RocketChat.callbacks.priority.HIGH - 2, 'markdowncode');
Loading

0 comments on commit bb173ea

Please sign in to comment.