Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need easier way to replace nested custom tags with HTML equivalents

I'm looking for a way to properly replace nested custom tags with their HTML equivalents. For example, suppose we have the following text:

This is {b:bold text}

Which should become:

This is <b>bold text</b>

I'm aware that I can - and probably should - use something like a span with a "bold" class instead of the old "b" tags, but there's a reason I'm sticking with "b" for the purpose of this example. I may have nested tags:

This is {b:bold text and {i:italic}}

which should become:

This is <b>bold text and <i>italic</i></b>

However, I have these regexes (which I use with the replace() function):

/\{b:([\s\S]*?)\}/gm
/\{i:([\s\S]*?)\}/gm

And then the result will become:

This is <b>bold text and <i>italic</b></i>

The tags are not properly nested. Again, using spans can solve this in this case, but that won't work with things like "ul", "li", "h1", ... Greedy regexes will also cause problems if the text looks like this:

This is {b:bold text} and {i:italic}

So my current solution is to replace everything with span first, using a data-tag attribute to indicate what it should actually be, and then use jQuery to find all span nodes and replace them with the proper tags:

{h:This is a header}
becomes
<span data-tag='h1'>This is a header</span>
becomes
<h1>This is a header</h1>

It works well, but i'm wondering if there is an easier way to do this. The intermediate method feels a bit like a duct tape solution and I would like to "beautify" it.

Any suggestions?

like image 754
WebStakker Avatar asked Dec 20 '12 19:12

WebStakker


1 Answers

A simple stack goes a long way

function doReplace( str ) {
    var stack = [],
        ret = [],
        ch;
    for( var i = 0, l = str.length; i < l; ++i ) {
        var ch = str.charAt(i);

        if( ch === "{" ) {
            var pos = str.indexOf( ":", i+1);
            if( pos < 0 ) {
                throw new SyntaxError();
            }
            var tagName = str.substring( i + 1, pos);
            if( /[^A-Za-z0-9]/.test(tagName)) {
                throw new SyntaxError();
            }
            ret.push( "<" + tagName + ">" );
            stack.push( tagName);
            i+= tagName.length + 1;
        }
        else if( ch === "}" ) {
            if( !stack.length ) {
                throw new SyntaxError();
            }
            var tagName = stack.pop();
            ret.push( "</" + tagName + ">" );
        }
        else {
            ret.push( ch );
        }
    }

    if( stack.length ) {
        throw new SyntaxError();
    }

    return ret.join("");
}

doReplace( "This is {b:bold text {i:italic text{i:italic text{i:italic text{i:italic text{i:italic text{i:italic text}}}}}}}")
//"This is <b>bold text <i>italic text<i>italic text<i>italic text<i>italic text<i>italic text<i>italic text</i></i></i></i></i></i></b>"
like image 192
Esailija Avatar answered Oct 20 '22 02:10

Esailija