From f95c86ac723f39341cb68b579b6c26a7b0ecdbf4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?R=C3=A9gis=20Hanol?= ]*\>)([\s\S]*)/igm,
+ stop: /<\/pre>/igm,
+ rawContents: true,
+ skipIfTradtionalLinebreaks: true,
+
+ emitter: function(blockContents) {
+ return ['p', ['pre', flattenBlocks(blockContents)]];
+ }
+});
+
// Ensure that content in a code block is fully escaped. This way it's not white listed
// and we can use HTML and Javascript examples.
Discourse.Dialect.on('parseNode', function (event) {
@@ -51,7 +62,6 @@ Discourse.Dialect.on('parseNode', function (event) {
if (path && path[path.length-1] && path[path.length-1][0] && path[path.length-1][0] === "pre") {
regexp = / +$/g;
-
} else {
regexp = /^ +| +$/g;
}
@@ -59,17 +69,6 @@ Discourse.Dialect.on('parseNode', function (event) {
}
});
-Discourse.Dialect.replaceBlock({
- start: /(]*\>)([\s\S]*)/igm,
- stop: /<\/pre>/igm,
- rawContents: true,
- skipIfTradtionalLinebreaks: true,
-
- emitter: function(blockContents) {
- return ['p', ['pre', flattenBlocks(blockContents)]];
- }
-});
-
// Whitelist the language classes
var regexpSource = "^lang-(" + acceptableCodeClasses.join('|') + ")$";
Discourse.Markdown.whiteListTag('code', 'class', new RegExp(regexpSource, "i"));
diff --git a/app/assets/javascripts/discourse/dialects/dialect.js b/app/assets/javascripts/discourse/dialects/dialect.js
index 66b72994b4e..245e523916d 100644
--- a/app/assets/javascripts/discourse/dialects/dialect.js
+++ b/app/assets/javascripts/discourse/dialects/dialect.js
@@ -12,7 +12,8 @@ var parser = window.BetterMarkdown,
initialized = false,
emitters = [],
hoisted,
- preProcessors = [];
+ preProcessors = [],
+ escape = Handlebars.Utils.escapeExpression;
/**
Initialize our dialects for processing.
@@ -162,6 +163,10 @@ function hoister(t, target, replacement) {
return t;
}
+function outdent(t) {
+ return t.replace(/^[ ]{4}/gm, "");
+}
+
/**
An object used for rendering our dialects.
@@ -183,14 +188,46 @@ Discourse.Dialect = {
cook: function(text, opts) {
if (!initialized) { initializeDialects(); }
+ dialect.options = opts;
+
// Helps us hoist out HTML
hoisted = {};
+ // pre-hoist all code-blocks
+
+ // ...
blocks
+ text = text.replace(/(\n*)([\s\S]*?)<\/pre>/ig, function(_, before, m) {
+ var hash = md5(m);
+ hoisted[hash] = escape(m.trim());
+ return before + "" + hash + "
";
+ });
+
+ // fenced blocks
+ text = text.replace(/(\n*)```([a-z0-9\-]*)\n([\s\S]*?)\n```/g, function(_, before, language, m) {
+ var hash = md5(m);
+ hoisted[hash] = escape(m.trim());
+ return before + "```" + language + "\n" + hash + "\n```";
+ });
+
+ // inline
+ text = text.replace(/(^|[^`])`([^`]*?)`([^`]|$)/g, function(_, before, m, after) {
+ var hash = md5(m);
+ hoisted[hash] = escape(m);
+ return before + "`" + hash + "`" + after;
+ });
+
+ // markdown blocks
+ text = text.replace(/(\n*)((?:(?:[ ]{4}).*\n+)+)/g, function(_, before, m) {
+ var hash = md5(m);
+ hoisted[hash] = escape(outdent(m).trim());
+ return before + " " + hash + "\n";
+ });
+
+ // pre-processors
preProcessors.forEach(function(p) {
text = p(text, hoister);
});
- dialect.options = opts;
var tree = parser.toHTMLTree(text, 'Discourse'),
result = parser.renderJsonML(parseTree(tree));
@@ -203,12 +240,11 @@ Discourse.Dialect = {
// If we hoisted out anything, put it back
var keys = Object.keys(hoisted);
if (keys.length) {
- keys.forEach(function(k) {
- result = result.replace(new RegExp(k,"g"), hoisted[k]);
+ keys.forEach(function(key) {
+ result = result.replace(new RegExp(key, "g"), hoisted[key]);
});
}
- hoisted = {};
return result.trim();
},
diff --git a/test/javascripts/lib/markdown-test.js.es6 b/test/javascripts/lib/markdown-test.js.es6
index 7c6cf4e4080..79dd7f31d46 100644
--- a/test/javascripts/lib/markdown-test.js.es6
+++ b/test/javascripts/lib/markdown-test.js.es6
@@ -345,12 +345,12 @@ test("Code Blocks", function() {
"{hello: 'world'}
trailing
", "It does not truncate text after a code block."); - cooked("```json\nline 1\n\nline 2\n\n\nline3\n```", - "line 1\n\nline 2\n\n\nline3",
+ cooked("```json\nline 1\n\nline 2\n\n\nline 3\n```",
+ "line 1\n\nline 2\n\n\nline 3",
"it maintains new lines inside a code block.");
- cooked("hello\nworld\n```json\nline 1\n\nline 2\n\n\nline3\n```",
- "hello
world
line 1\n\nline 2\n\n\nline3",
+ cooked("hello\nworld\n```json\nline 1\n\nline 2\n\n\nline 3\n```",
+ "hello
world
line 1\n\nline 2\n\n\nline 3",
"it maintains new lines inside a code block with leading content.");
cooked("```ruby\n