From 6029a77efb3a24e3b07573a37f73ec98a4a3c011 Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Wed, 28 Aug 2013 16:15:11 -0400 Subject: [PATCH] A nicer API for dealing with text replacements in Discourse's parser pipeline --- .../javascripts/discourse/dialects/dialect.js | 105 ++++++++++++------ .../discourse/dialects/github_code_dialect.js | 56 +--------- .../discourse/dialects/newline_dialect.js | 45 ++++---- 3 files changed, 92 insertions(+), 114 deletions(-) diff --git a/app/assets/javascripts/discourse/dialects/dialect.js b/app/assets/javascripts/discourse/dialects/dialect.js index eb26d0cadea..9ee2d24a4f6 100644 --- a/app/assets/javascripts/discourse/dialects/dialect.js +++ b/app/assets/javascripts/discourse/dialects/dialect.js @@ -3,43 +3,6 @@ Discourse uses the Markdown.js as its main parser. `Discourse.Dialect` is the framework for extending it with additional formatting. - To extend the dialect, you can register a handler, and you will receive an `event` object - with a handle to the markdown `Dialect` from Markdown.js that we are defining. Here's - a sample dialect that replaces all occurrences of "evil trout" with a link that says - "EVIL TROUT IS AWESOME": - - ```javascript - - Discourse.Dialect.on("register", function(event) { - var dialect = event.dialect; - - // To see how this works, review one of our samples or the Markdown.js code: - dialect.inline["evil trout"] = function(text) { - return ["evil trout".length, ['a', {href: "http://eviltrout.com"}, "EVIL TROUT IS AWESOME"] ]; - }; - - }); - ``` - - You can also manipulate the JsonML tree that is produced by the parser before it converted to HTML. - This is useful if the markup you want needs a certain structure of HTML elements. Rather than - writing regular expressions to match HTML, consider parsing the tree instead! We use this for - making sure a onebox is on one line, as an example. - - This example changes the content of any `` tags. - - The `event.path` attribute contains the current path to the node. - - ```javascript - Discourse.Dialect.on("parseNode", function(event) { - var node = event.node; - - if (node[0] === 'code') { - node[node.length-1] = "EVIL TROUT HACKED YOUR CODE"; - } - }); - ``` - **/ var parser = window.BetterMarkdown, MD = parser.Markdown, @@ -239,7 +202,75 @@ Discourse.Dialect = { return [endPos+stop.length, contents]; } }; + }, + /** + After the parser has been executed, post process any text nodes in the HTML document. + This is useful if you want to apply a transformation to the text. + + If you are generating HTML from the text, it is preferable to use the replacer + functions and do it in the parsing part of the pipeline. This function is best for + simple transformations or transformations that have to happen after all earlier + processing is done. + + For example, to convert all text to upper case: + + ```javascript + + Discourse.Dialect.postProcessText(function (text) { + return text.toUpperCase(); + }); + + ``` + + @method postProcessText + @param {Function} emitter The function to call with the text. It returns JsonML to modify the tree. + **/ + postProcessText: function(emitter) { + Discourse.Dialect.on("parseNode", function(event) { + var node = event.node; + if (node.length < 2) { return; } + + for (var j=1; j 0) { return; } - - if (node.length > 1) { - for (var j=1; j 0) { - spliceInstructions.push(split[i]); - if (i !== split.length-1) { spliceInstructions.push(['br']); } - } - } - node.splice.apply(node, spliceInstructions); - } - } - } - } - } - } +// 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.postProcessTag('code', function (contents) { + return Handlebars.Utils.escapeExpression(contents); }); diff --git a/app/assets/javascripts/discourse/dialects/newline_dialect.js b/app/assets/javascripts/discourse/dialects/newline_dialect.js index 3653bdcdb3e..1d51d4b9cd2 100644 --- a/app/assets/javascripts/discourse/dialects/newline_dialect.js +++ b/app/assets/javascripts/discourse/dialects/newline_dialect.js @@ -1,37 +1,32 @@ /** - Support for the newline behavior in markdown that most expect. - - @event parseNode - @namespace Discourse.Dialect + Support for the newline behavior in markdown that most expect. Look through all text nodes + in the tree, replace any new lines with `br`s. **/ -Discourse.Dialect.on("parseNode", function(event) { - var node = event.node, - opts = event.dialect.options, +Discourse.Dialect.postProcessText(function (text, event) { + var opts = event.dialect.options, insideCounts = event.insideCounts, linebreaks = opts.traditional_markdown_linebreaks || Discourse.SiteSettings.traditional_markdown_linebreaks; - if (linebreaks || (insideCounts.pre > 0) || (node.length < 1)) { return; } + if (linebreaks || (insideCounts.pre > 0)) { return; } - for (var j=1; j` + return [['br']]; + } else { - if (typeof textContent === "string") { - if (textContent === "\n") { - node[j] = ['br']; - } else { - var split = textContent.split(/\n+/); - if (split.length) { - var spliceInstructions = [j, 1]; - for (var i=0; i 0) { - spliceInstructions.push(split[i]); - if (i !== split.length-1) { spliceInstructions.push(['br']); } - } - } - node.splice.apply(node, spliceInstructions); + // If the text node contains new lines, perhaps with text between them, insert the + // `
` tags. + var split = text.split(/\n+/); + if (split.length) { + var replacement = []; + for (var i=0; i 0) { + replacement.push(split[i]); + if (i !== split.length-1) { replacement.push(['br']); } } } + return replacement; } } -}); +}); \ No newline at end of file