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");
});