From 23002ae01cde0fdf1b119d3ae50d5ba2cce11306 Mon Sep 17 00:00:00 2001 From: Jens Maier Date: Thu, 14 Aug 2014 01:58:01 +0200 Subject: [PATCH] Fix: block parser now uses regexes for end tags. solves code block case, where end tag must be on a line of its own. --- .../discourse/dialects/bbcode_dialect.js | 2 +- .../discourse/dialects/code_dialect.js | 4 ++-- .../javascripts/discourse/dialects/dialect.js | 21 +++++++++++-------- .../discourse/dialects/quote_dialect.js | 2 +- test/javascripts/lib/markdown-test.js.es6 | 6 +++++- 5 files changed, 21 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js index d04f675de68..e1460edef21 100644 --- a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js +++ b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js @@ -120,7 +120,7 @@ Discourse.Markdown.whiteListTag('span', 'class', /^bbcode-size-\d+$/); // Handles `[code] ... [/code]` blocks Discourse.Dialect.replaceBlock({ start: /(\[code\])([\s\S]*)/igm, - stop: '[/code]', + stop: /\[\/code\]/igm, rawContents: true, emitter: function(blockContents) { diff --git a/app/assets/javascripts/discourse/dialects/code_dialect.js b/app/assets/javascripts/discourse/dialects/code_dialect.js index 5bb5d6b8b00..275b138fe65 100644 --- a/app/assets/javascripts/discourse/dialects/code_dialect.js +++ b/app/assets/javascripts/discourse/dialects/code_dialect.js @@ -21,7 +21,7 @@ function flattenBlocks(blocks) { Discourse.Dialect.replaceBlock({ start: /^`{3}([^\n\[\]]+)?\n?([\s\S]*)?/gm, - stop: '```', + stop: /^```$/gm, emitter: function(blockContents, matches) { var klass = Discourse.SiteSettings.default_code_lang; @@ -54,7 +54,7 @@ Discourse.Dialect.on('parseNode', function (event) { Discourse.Dialect.replaceBlock({ start: /(]*\>)([\s\S]*)/igm, - stop: '', + stop: /<\/pre>/igm, rawContents: true, skipIfTradtionalLinebreaks: true, diff --git a/app/assets/javascripts/discourse/dialects/dialect.js b/app/assets/javascripts/discourse/dialects/dialect.js index 663e7205c6b..ddb84b45a8d 100644 --- a/app/assets/javascripts/discourse/dialects/dialect.js +++ b/app/assets/javascripts/discourse/dialects/dialect.js @@ -379,7 +379,7 @@ Discourse.Dialect = { if (!match) { return; } var lastChance = function() { - return !next.some(function(e) { return e.indexOf(args.stop) !== -1; }); + return !next.some(function(blk) { return blk.match(args.stop); }); }; // shave off start tag and leading text, if any. @@ -387,7 +387,8 @@ Discourse.Dialect = { leading = block.slice(0, pos), trailing = match[2] ? match[2].replace(/^\n*/, "") : ""; // just give up if there's no stop tag in this or any next block - if (block.indexOf(args.stop, pos + args.stop.length) === -1 && lastChance()) { return; } + args.stop.lastIndex = block.length - trailing.length; + if (!args.stop.exec(block) && lastChance()) { return; } if (leading.length > 0) { result.push(['p'].concat(this.processInline(leading))); } if (trailing.length > 0) { next.unshift(MD.mk_block(trailing, block.trailing, @@ -405,10 +406,10 @@ Discourse.Dialect = { startPos.push(args.start.lastIndex - m[0].length); args.start.lastIndex = args.start.lastIndex - (m[2] ? m[2].length : 0); } - var endPos = [], offset = 0; - while ((pos = currentBlock.indexOf(args.stop, offset)) !== -1) { - endPos.push(pos); - offset += (pos + args.stop.length); + args.stop.lastIndex = 0; + var endPos = []; + while (m = (args.stop).exec(currentBlock)) { + endPos.push(args.stop.lastIndex - m[0].length); } // go through the available end tags: @@ -421,7 +422,8 @@ Discourse.Dialect = { // found an end tag, but we must go up a level first. ep++; nesting--; } else { - // found an end tag and we're at the top: done! + // found an end tag and we're at the top: done! -- or: start tag and end tag are + // identical, (i.e. startPos[sp] == endPos[ep]), so we don't do nesting at all. actualEndPos = endPos[ep]; break blockloop; } @@ -442,8 +444,9 @@ Discourse.Dialect = { contentBlocks.push(currentBlock); } - var before = currentBlock.slice(0, actualEndPos).replace(/\n*$/, ""), - after = currentBlock.slice(actualEndPos + args.stop.length).replace(/^\n*/, ""); + var stopLen = currentBlock.match(args.stop)[0].length, + before = currentBlock.slice(0, actualEndPos).replace(/\n*$/, ""), + after = currentBlock.slice(actualEndPos + stopLen).replace(/^\n*/, ""); if (before.length > 0) contentBlocks.push(MD.mk_block(before, "", currentBlock.lineNumber)); if (after.length > 0) next.unshift(MD.mk_block(after, "", currentBlock.lineNumber + countLines(before))); diff --git a/app/assets/javascripts/discourse/dialects/quote_dialect.js b/app/assets/javascripts/discourse/dialects/quote_dialect.js index 69ac28dcb29..bb740582e73 100644 --- a/app/assets/javascripts/discourse/dialects/quote_dialect.js +++ b/app/assets/javascripts/discourse/dialects/quote_dialect.js @@ -6,7 +6,7 @@ var esc = Handlebars.Utils.escapeExpression; Discourse.Dialect.replaceBlock({ start: new RegExp("\\[quote(=[^\\[\\]]+)?\\]([\\s\\S]*)", "igm"), - stop: '[/quote]', + stop: /\[\/quote\]/igm, emitter: function(blockContents, matches, options) { var params = {'class': 'quote'}, diff --git a/test/javascripts/lib/markdown-test.js.es6 b/test/javascripts/lib/markdown-test.js.es6 index f8b22858138..2a06be5ae70 100644 --- a/test/javascripts/lib/markdown-test.js.es6 +++ b/test/javascripts/lib/markdown-test.js.es6 @@ -365,13 +365,17 @@ test("Code Blocks", function() { "

hello

", "it doesn't not whitelist all classes"); - cooked("```[quote=\"sam, post:1, topic:9441, full:true\"]This is `` a bug.[/quote]```", + cooked("```\n[quote=\"sam, post:1, topic:9441, full:true\"]This is `` a bug.[/quote]\n```", "

[quote="sam, post:1, topic:9441, full:true"]This is `<not>` a bug.[/quote]

", "it allows code with backticks in it"); cooked(" hello\n
test
", "
hello
\n\n
test
", "it allows an indented code block to by followed by a `
`"); + + cooked("``` foo bar ```", + "

foo bar

", + "it tolerates misuse of code block tags as inline code"); }); test("sanitize", function() {