views:

183

answers:

1

I have coded a Javascript bbcode similar to the one I'm using to write this message. It also incorporates a live preview box like the one I see below. The only problem I'm facing at the moment is that some nested bbcode is not parsing.

For example:

[quote]
   [quote][/quote]
[/quote]

Is not parsing correctly.

This is my Javascript currently.

function preview() {

    var txt = $('#editor').val();
    txt = txt.replace(/</g,'&lt;');
    txt = txt.replace(/>/g,'&gt;');

    txt = txt.replace(/[\r\n]/g,'%lb%');

    var find    = [
                   /\[quote\](.*?)\[\/quote\]/gi,
                   /\[quote author="(.*?)" date="(.*?)"\](.*?)\[\/quote\]/gi,
                   /\[b\](.*?)\[\/b\]/gi,
                   /\[i\](.*?)\[\/i\]/gi,
                   /\[u\](.*?)\[\/u\]/gi,
                   /\[left\](.*?)\[\/left\]/gi,
                   /\[center\](.*?)\[\/center\]/gi,
                   /\[right\](.*?)\[\/right\]/gi,
                   /\[size=(10|12|24|30)](.*?)\[\/size\]/gi,
                   /\[font=(.*?)](.*?)\[\/font\]/gi,
                   /\[color=(.*?)](.*?)\[\/color\]/gi,
                   /\[url(?:\=?)(.*?)\](.*?)\[\/url\]/gi,
                   /\[email=(.*?)\](.*?)\[\/email\]/gi,
                   /\[email\](.*?)\[\/email\]/gi,
                   /\[img(.*?)\](.*?)\[\/img\]/gi,
                   /(?:%lb%|\s)*\[code(?:\=?)(?:.*?)\](?:%lb%|\s)*(.*?)(?:%lb%|\s)*\[\/code\](?:%lb%|\s)*/gi,

                   /\[list(.*?)\](.*?)\[\*\](.*?)(?:%lb%|\s)*(\[\*\].*?\[\/list\]|\[\/list\])/i,
                   /(?:%lb%|\s)*\[list\](?:%lb%|\s)*(.*?)(?:%lb%|\s)*\[\/list\](?:%lb%|\s)*/gi,
                   /(?:%lb%|\s)*\[list=(\d)\](?:%lb%|\s)*(.*?)(?:%lb%|\s)*\[\/list\](?:%lb%|\s)*/gi,
                   /(?:%lb%){3,}/g

                   ];
    var replace = [
                   '<blockquote><div class="quote"><div class="quote_body">$1</div></div></blockquote>',
                   '<blockquote><div class="quote"><div class="quote_author"><span class="quote_from">Quote from</span> <span class="author">$1</span> on <span class="date">$2</span></div><div class="quote_body">$3</div></div></blockquote>',
                   '<b>$1<\/b>',
                   '<i>$1<\/i>',
                   '<u>$1<\/u>',
                   '<div class="align_left">$1<\/div>',
                   '<div class="align_center">$1<\/div>',
                   '<div class="align_right">$1<\/div>',
                   '<span style="font-size:$1px;">$2</span>',
                   '<span style="font-family:$1;">$2</span>',
                   '<span style="color:$1;">$2</span>',
                   '<a href="$1">$2</a>',
                   '<a href="mailto:$1">$2</a>',
                   '<a href="mailto:$1">$1</a>',                   
                   '<img $1 src="$2" />',
                   '<pre><code>$1</code></pre>',
                   '[list$1]$2<li>$3</li>$4',
                   '<ul>$1</ul>',
                   '<ol start=$1>$2</ol>',
                   '%lb%%lb%'

                   ];

    // fix [*] so that they only work inside [/list]
    for(var i in find)
    {
        txt = txt.replace(find[i],replace[i]);
        if(i == 17) while(txt.match(find[i],replace[i])) txt = txt.replace(find[i],replace[i]);
    }

    // Fix Smilies
    txt = txt.replace(/%lb%/g,'<br />');
    txt = txt.replace(/\:\)/g, '<img class="smiley" src="/img/smilies/smile.gif">');
    txt = txt.replace(/\:-\)/g, '<img class="smiley" src="/img/smilies/happy.gif">');
    txt = txt.replace(/\:D/g, '<img class="smiley" src="/img/smilies/biggrin.gif">');
    txt = txt.replace(/\:\(/g, '<img class="smiley" src="/img/smilies/sad.gif">');
    txt = txt.replace(/8\)/g, '<img class="smiley" src="/img/smilies/cool.gif">');
    txt = txt.replace(/=O/g, '<img class="smiley" src="/img/smilies/surprised.gif">');
    txt = txt.replace(/\:-\|\|/g, '<img class="smiley" src="/img/smilies/mad.gif">');
    txt = txt.replace(/\:P/g, '<img class="smiley" src="/img/smilies/stongue.gif">');
    txt = txt.replace(/\}\-\(/g, '<img class="smiley" src="/img/smilies/confused.gif">');

    // Format Dates
    txt = txt.replace(/\d{10}/g, function($0) {
        var d = new Date($0*1000);
        var months = new Array('January','February','March','April','May','June','July','August','September','October','November','December');
        return "" + months[d.getMonth()] + " " + d.getDate() + ", " + d.getFullYear() + ", " + (d.getHours()%12) + ":" + d.getMinutes() + " " + (d.getHours()<12 ? 'AM' : 'PM');
    });

    // Update the preview box
    $('.preview').html(txt);
}

This flips most all my bbcode tags. The quote tag is problematic in that it sometimes does not flip nested quote tags or parse the contents of the inner quote tags.

If anyone is able to offer a solution I would be very grateful. Thank you!

+1  A: 

Three solutions:

  1. Write a parser. This will produce the best solution but takes a non-trivial amount of effort.

  2. Find a BBCode parsing library. Probably as good as #1 in quality and substantially easier.

  3. Add a negative lookahead to the inside of each tag regex and continuously apply until no match. E.g.:

    \[quote\]((?:[^](?!\[quote\]))*?)\[\/quote\]
    

    This will capture the inner quote, then once its replaced, the outer one. Not nearly as clean as the other two but probably the quickest fix.

Max Shawabkeh
I strongly recommend #2: an existing library will probably be less buggy than your own, at least in the first place.
Marcel Korpel