FIX: Allow intra-word underscores.

This commit is contained in:
Robin Ward 2013-08-27 12:24:17 -04:00
parent 89265c3a8b
commit 73489b652e
4 changed files with 48 additions and 79 deletions

View File

@ -10,23 +10,34 @@ Discourse.Dialect.on("register", function(event) {
var dialect = event.dialect, var dialect = event.dialect,
MD = event.MD; MD = event.MD;
/**
Handles simultaneous bold and italics
@method parseMentions var inlineBuilder = function(symbol, tag, surround) {
@param {String} text the text match return function(text, match, prev) {
@param {Array} match the match found if (prev && (prev.length > 0)) {
@param {Array} prev the previous jsonML var last = prev[prev.length - 1];
@return {Array} an array containing how many chars we've replaced and the jsonML content for it. if (typeof last === "string" && (!last.match(/\W$/))) { return; }
@namespace Discourse.Dialect }
**/
dialect.inline['***'] = function boldItalics(text, match, prev) {
var regExp = /^\*{3}([^\*]+)\*{3}/,
m = regExp.exec(text);
if (m) { var regExp = new RegExp("^\\" + symbol + "([^\\" + symbol + "]+)" + "\\" + symbol, "igm"),
return [m[0].length, ['strong', ['em'].concat(this.processInline(m[1]))]]; m = regExp.exec(text);
}
if (m) {
var contents = [tag].concat(this.processInline(m[1]));
if (surround) {
contents = [surround, contents];
}
return [m[0].length, contents];
}
};
}; };
dialect.inline['***'] = inlineBuilder('**', 'em', 'strong');
dialect.inline['**'] = inlineBuilder('**', 'strong');
dialect.inline['*'] = inlineBuilder('*', 'em');
dialect.inline['_'] = inlineBuilder('_', 'em');
}); });

View File

@ -1,3 +1,18 @@
/*
This is a fork of markdown-js with a few changes to support discourse:
* We have replaced the strong/em handlers because we prefer them only to work on word
boundaries.
* We removed the maraku support as we don't use it.
* We don't escape the contents of HTML as we prefer to use a whitelist.
* Note the name BetterMarkdown doesn't mean it's *better* than markdown-js, it refers
to it being better than our previous markdown parser!
*/
// Released under MIT license // Released under MIT license
// Copyright (c) 2009-2010 Dominic Baggott // Copyright (c) 2009-2010 Dominic Baggott
// Copyright (c) 2009-2010 Ash Berlin // Copyright (c) 2009-2010 Ash Berlin
@ -1004,69 +1019,6 @@ Markdown.dialects.Gruber.inline = {
}; };
// Meta Helper/generator method for em and strong handling
function strong_em( tag, md ) {
var state_slot = tag + "_state",
other_slot = tag == "strong" ? "em_state" : "strong_state";
function CloseTag(len) {
this.len_after = len;
this.name = "close_" + md;
}
return function ( text, orig_match ) {
if ( this[state_slot][0] == md ) {
// Most recent em is of this type
//D:this.debug("closing", md);
this[state_slot].shift();
// "Consume" everything to go back to the recrusion in the else-block below
return[ text.length, new CloseTag(text.length-md.length) ];
}
else {
// Store a clone of the em/strong states
var other = this[other_slot].slice(),
state = this[state_slot].slice();
this[state_slot].unshift(md);
//D:this.debug_indent += " ";
// Recurse
var res = this.processInline( text.substr( md.length ) );
//D:this.debug_indent = this.debug_indent.substr(2);
var last = res[res.length - 1];
//D:this.debug("processInline from", tag + ": ", uneval( res ) );
var check = this[state_slot].shift();
if ( last instanceof CloseTag ) {
res.pop();
// We matched! Huzzah.
var consumed = text.length - last.len_after;
return [ consumed, [ tag ].concat(res) ];
}
else {
// Restore the state of the other kind. We might have mistakenly closed it.
this[other_slot] = other;
this[state_slot] = state;
// We can't reuse the processed result as it could have wrong parsing contexts in it.
return [ md.length, md ];
}
}
}; // End returned function
}
Markdown.dialects.Gruber.inline["**"] = strong_em("strong", "**");
Markdown.dialects.Gruber.inline["__"] = strong_em("strong", "__");
Markdown.dialects.Gruber.inline["*"] = strong_em("em", "*");
Markdown.dialects.Gruber.inline["_"] = strong_em("em", "_");
// Build default order from insertion order. // Build default order from insertion order.
Markdown.buildBlockOrder = function(d) { Markdown.buildBlockOrder = function(d) {
var ord = []; var ord = [];

View File

@ -105,7 +105,7 @@ module PrettyText
ctx.eval("var I18n = {}; I18n.t = function(a,b){ return helpers.t(a,b); }"); ctx.eval("var I18n = {}; I18n.t = function(a,b){ return helpers.t(a,b); }");
ctx_load(ctx, ctx_load(ctx,
"app/assets/javascripts/external/markdown.js", "app/assets/javascripts/external/better_markdown.js",
"app/assets/javascripts/discourse/dialects/dialect.js", "app/assets/javascripts/discourse/dialects/dialect.js",
"app/assets/javascripts/discourse/components/utilities.js", "app/assets/javascripts/discourse/components/utilities.js",
"app/assets/javascripts/discourse/components/markdown.js") "app/assets/javascripts/discourse/components/markdown.js")

View File

@ -17,7 +17,13 @@ var cookedOptions = function(input, opts, expected, text) {
test("basic cooking", function() { test("basic cooking", function() {
cooked("hello", "<p>hello</p>", "surrounds text with paragraphs"); cooked("hello", "<p>hello</p>", "surrounds text with paragraphs");
cooked("**evil**", "<p><strong>evil</strong></p>", "it bolds text.");
cooked("*trout*", "<p><em>trout</em></p>", "it italicizes text.");
cooked("_trout_", "<p><em>trout</em></p>", "it italicizes text.");
cooked("***hello***", "<p><strong><em>hello</em></strong></p>", "it can do bold and italics at once."); cooked("***hello***", "<p><strong><em>hello</em></strong></p>", "it can do bold and italics at once.");
cooked("word_with_underscores", "<p>word_with_underscores</p>", "it doesn't do intraword italics");
cooked("hello \\*evil\\*", "<p>hello *evil*</p>", "it supports escaping of asterisks");
cooked("hello \\_evil\\_", "<p>hello _evil_</p>", "it supports escaping of italics");
}); });
test("Traditional Line Breaks", function() { test("Traditional Line Breaks", function() {