Like ojdo has pointed out, using plain regexes to parse something like BBCode is too complicated of a route to take.
It would be too long of a task to educate you on building proper parsers, but here's a short simplified example, of how one would go on parsing something like BBCode.
First tokenize
First off, we transform the input text into some higher level data structure which we can then work on more easily. For the BBCode, let's tokenize the input into begin tags, end tags, and text in between:
function tokenize(input) {
var m;
var tokens = [];
while (input.length > 0) {
if ((m = input.match(/^\[([a-z]+)(=([^\]]+))?\]/i))) {
// start tag [foo=*]
tokens.push({type: m[1].toLowerCase() + "_start", argument: m[3], raw: m[0]});
}
else if ((m = input.match(/^\[\/([a-z]+)\]/i))) {
// end tag [/foo]
tokens.push({type: m[1].toLowerCase() + "_end", raw: m[0]});
}
else {
// text between tags (this regex always matches)
m = input.match(/^\[?[^\[]*/);
tokens.push({type: "text", raw: m[0]});
}
// skip forward
input = input.slice(m[0].length);
}
return tokens;
}
Given an input like "[URL=google.com]click here[/URL]"
, the tokenize
function produces an array of tokens like this:
[
{ type: 'url_start', argument: 'google.com', raw: '[URL=google.com]' },
{ type: 'text', raw: 'click here' },
{ type: 'url_end', raw: '[/URL]' }
]
Then produce HTML
Normally one would now transform this list of tokens into a tree structure, but as the BBCode-to-HTML conversion is very one-to-one, we don't really need to, and can pass the list of tokens directly to another function that converts it to HTML:
function tokensToHtml(tokens) {
var output = "";
for (var i=0; i<tokens.length; i++) {
var tok = tokens[i];
if (tok.type === "url_start") {
var url = tok.argument;
// prepend URL-s with http:// if needed
if (!/^https?:\/\//.test(url)) {
url = "http://" + url;
}
output += "<a href='" + escapeHtml(url) + "'>";
}
else if (tok.type === "url_end") {
output += "</a>";
}
else {
// anything else is a text token or an unknown tag
// which we also treat just as text
output += escapeHtml(tok.raw);
}
}
return output;
}
function escapeHtml(str) {
return str.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"');
}
As can be see, we simply replace each token with corresponding HTML. When writing out the <a href=...
we also take care of prepending the http://
as needed. Additionally we escape all HTML special chars in URL and within rest of the text - using a little escapeHtml
utility function.
To tie it all together:
function bbCodeToHtml(input) {
return tokensToHtml(tokenize(input));
}
It should be fairly obvious how to extend the tokensToHtml
to support additional BBCode tags.
One missing feature is to automatically append close tags if one would input something like "[URL=google.com]bla bla"
. This is left as an exercise for the reader :)