diff --git a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js index eed88c533c..bf83367b3e 100644 --- a/app/assets/javascripts/discourse/dialects/bbcode_dialect.js +++ b/app/assets/javascripts/discourse/dialects/bbcode_dialect.js @@ -13,6 +13,57 @@ @param {Boolean} [opts.wordBoundary] If true, the match must be on a word boundary @param {Boolean} [opts.spaceBoundary] If true, the match must be on a sppace boundary **/ +Discourse.BBCode = {}; + +Discourse.BBCode.register = function(codeName, args, emitter) { + + // Optional second param for args + if (typeof args === "function") { + emitter = args; + args = {}; + } + + Discourse.Dialect.replaceBlock({ + start: new RegExp("\\[" + codeName + "(=[^\\[\\]]+)?\\]([\\s\\S]*)", "igm"), + stop: new RegExp("\\[\\/" + codeName + "\\]", "igm"), + emitter: function(blockContents, matches, options) { + while (blockContents.length && (typeof blockContents[0] === "string" || blockContents[0] instanceof String)) { + blockContents[0] = String(blockContents[0]).replace(/^\s+/, ''); + if (!blockContents[0].length) { + blockContents.shift(); + } else { + break; + } + } + + var contents = []; + if (blockContents.length) { + var self = this; + + var nextContents = blockContents.slice(1); + blockContents = this.processBlock(blockContents[0], nextContents).concat(nextContents); + + blockContents.forEach(function (bc) { + if (typeof bc === "string" || bc instanceof String) { + var processed = self.processInline(String(bc)); + if (processed.length) { + contents.push(['p'].concat(processed)); + } + } else { + contents.push(bc); + } + }); + } + if (!args.singlePara && contents.length === 1) { + contents[0].shift(); + contents = contents[0]; + } + var result = emitter(contents, matches[1] ? matches[1].replace(/^=|\"/g, '') : null, options); + return args.noWrap ? result : ['p', result]; + } + }); +}; + function replaceBBCode(tag, emitter, opts) { opts = opts || {}; opts = _.merge(opts, { start: "[" + tag + "]", stop: "[/" + tag + "]", emitter: emitter }); @@ -69,20 +120,6 @@ function removeEmptyLines(contents) { return result; } -/** - Creates a BBCode handler that accepts parameters. Passes them to the emitter. - Processes the inside recursively so it can be nested. - - @method replaceBBCodeParams - @param {tag} tag the tag we want to match - @param {function} emitter the function that creates JsonML for the tag -**/ -function replaceBBCodeParams(tag, emitter) { - replaceBBCodeParamsRaw(tag, function (param, contents) { - return emitter(param, this.processInline(contents)); - }); -} - replaceBBCode('b', function(contents) { return ['span', {'class': 'bbcode-b'}].concat(contents); }); replaceBBCode('i', function(contents) { return ['span', {'class': 'bbcode-i'}].concat(contents); }); replaceBBCode('u', function(contents) { return ['span', {'class': 'bbcode-u'}].concat(contents); }); @@ -112,8 +149,8 @@ replaceBBCodeParamsRaw("email", function(param, contents) { return ['a', {href: "mailto:" + param, 'data-bbcode': true}, contents]; }); -replaceBBCodeParams("size", function(param, contents) { - return ['span', {'class': "bbcode-size-" + (parseInt(param, 10) || 1)}].concat(contents); +Discourse.BBCode.register('size', function(contents, params) { + return ['span', {'class': "bbcode-size-" + (parseInt(params, 10) || 1)}].concat(contents); }); Discourse.Markdown.whiteListTag('span', 'class', /^bbcode-size-\d+$/); diff --git a/app/assets/javascripts/discourse/dialects/quote_dialect.js b/app/assets/javascripts/discourse/dialects/quote_dialect.js index bb740582e7..b0b8f5bbb8 100644 --- a/app/assets/javascripts/discourse/dialects/quote_dialect.js +++ b/app/assets/javascripts/discourse/dialects/quote_dialect.js @@ -1,87 +1,47 @@ -/** - Support for quoting other users. -**/ - var esc = Handlebars.Utils.escapeExpression; +Discourse.BBCode.register('quote', {noWrap: true, singlePara: true}, function(contents, bbParams, options) { + var params = {'class': 'quote'}, + username = null; -Discourse.Dialect.replaceBlock({ - start: new RegExp("\\[quote(=[^\\[\\]]+)?\\]([\\s\\S]*)", "igm"), - stop: /\[\/quote\]/igm, - emitter: function(blockContents, matches, options) { + if (bbParams) { + var paramsSplit = bbParams.split(/\,\s*/); + username = paramsSplit[0]; - var params = {'class': 'quote'}, - username = null; - - if (matches[1]) { - var paramsString = matches[1].replace(/^=|\"/g, ''), - paramsSplit = paramsString.split(/\,\s*/); - - username = paramsSplit[0]; - - paramsSplit.forEach(function(p,i) { - if (i > 0) { - var assignment = p.split(':'); - if (assignment[0] && assignment[1]) { - var escaped = esc(assignment[0]); - // don't escape attributes, makes no sense - if(escaped === assignment[0]) { - params['data-' + assignment[0]] = esc(assignment[1].trim()); - } + paramsSplit.forEach(function(p,i) { + if (i > 0) { + var assignment = p.split(':'); + if (assignment[0] && assignment[1]) { + var escaped = esc(assignment[0]); + // don't escape attributes, makes no sense + if(escaped === assignment[0]) { + params['data-' + assignment[0]] = esc(assignment[1].trim()); } } - }); - } - - var avatarImg; - if (options.lookupAvatarByPostNumber) { - // client-side, we can retrieve the avatar from the post - var postNumber = parseInt(params['data-post'], 10); - avatarImg = options.lookupAvatarByPostNumber(postNumber); - } else if (options.lookupAvatar) { - // server-side, we need to lookup the avatar from the username - avatarImg = options.lookupAvatar(username); - } - - while (blockContents.length && (typeof blockContents[0] === "string" || blockContents[0] instanceof String)) { - blockContents[0] = String(blockContents[0]).replace(/^\s+/, ''); - if (!blockContents[0].length) { - blockContents.shift(); - } else { - break; } - } - - var contents = ['blockquote']; - if (blockContents.length) { - var self = this; - - var nextContents = blockContents.slice(1); - blockContents = this.processBlock(blockContents[0], nextContents).concat(nextContents); - - blockContents.forEach(function (bc) { - if (typeof bc === "string" || bc instanceof String) { - var processed = self.processInline(String(bc)); - if (processed.length) { - contents.push(['p'].concat(processed)); - } - } else { - contents.push(bc); - } - }); - } - - // If there's no username just return a simple quote - if (!username) { - return ['p', ['aside', params, contents]]; - } - - return ['aside', params, - ['div', {'class': 'title'}, - ['div', {'class': 'quote-controls'}], - avatarImg ? ['__RAW', avatarImg] : "", - username ? I18n.t('user.said', {username: username}) : "" - ], - contents - ]; + }); } + + var avatarImg; + if (options.lookupAvatarByPostNumber) { + // client-side, we can retrieve the avatar from the post + var postNumber = parseInt(params['data-post'], 10); + avatarImg = options.lookupAvatarByPostNumber(postNumber); + } else if (options.lookupAvatar) { + // server-side, we need to lookup the avatar from the username + avatarImg = options.lookupAvatar(username); + } + + // If there's no username just return a simple quote + if (!username) { + return ['p', ['aside', params, ['blockquote'].concat(contents)]]; + } + + return ['aside', params, + ['div', {'class': 'title'}, + ['div', {'class': 'quote-controls'}], + avatarImg ? ['__RAW', avatarImg] : "", + username ? I18n.t('user.said', {username: username}) : "" + ], + ['blockquote'].concat(contents) + ]; }); diff --git a/test/javascripts/lib/bbcode-test.js.es6 b/test/javascripts/lib/bbcode-test.js.es6 index 5790402c36..e38df2414f 100644 --- a/test/javascripts/lib/bbcode-test.js.es6 +++ b/test/javascripts/lib/bbcode-test.js.es6 @@ -62,8 +62,8 @@ test("size tags", function() { format("[size=asdf]regular[/size]", "regular", "it only supports numbers in bbcode"); - format("[size=35]\nNEWLINE\n[/size]", - "
NEWLINE
", + format("[size=35]NEWLINE\n\ntest[/size]", + "

NEWLINE

test

", "works with newlines"); });