FIX: BBCode contents can span multiple lines
This commit is contained in:
parent
5454c1ed24
commit
300dbdc88b
|
@ -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+$/);
|
||||
|
||||
|
|
|
@ -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)
|
||||
];
|
||||
});
|
||||
|
|
|
@ -62,8 +62,8 @@ test("size tags", function() {
|
|||
format("[size=asdf]regular[/size]",
|
||||
"<span class=\"bbcode-size-1\">regular</span>",
|
||||
"it only supports numbers in bbcode");
|
||||
format("[size=35]\nNEWLINE\n[/size]",
|
||||
"<span class=\"bbcode-size-35\"><br>NEWLINE<br></span>",
|
||||
format("[size=35]NEWLINE\n\ntest[/size]",
|
||||
"<span class=\"bbcode-size-35\"><p>NEWLINE</p><p>test</p></span>",
|
||||
"works with newlines");
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue