JavaScript BB-Code transformation I am doing a lot of stuff with JavaScript and jQuery lately, but am not heading for clean code too much, as I am usually writing userscripts or small applications for myself.

Recently, a community I am part of announced that it will change their supported BB-Codes, as the tabmenu BB-Code had a possible safety issue. By now, hundreds of posts have been done with tabmenus and a lot of users were complaining that editing all these posts will take forever.

As I am a guy who likes to automate as much stuff as possible, I took the task and developed a small application that changes posts with the tabmenu BB-Code into a format that is supported by the forum.

Here are two examples of what the application does:

Original BB-Code:



[tabmenu]
[tab=Tab1]Tab1 Text
[tab=Tab2]Tab2 Text
[/tabmenu]


Note: tabmenu has a closing tag, tab does not and will contain all text until the next tab or until the closing [/tabmenu].

Result:

[h]Tab1[/h]Tab1 Text
[h]Tab2[/h]Tab2 Text


h turn into headlines, just as in HTML and force a linebreak.

As the tabmenu does not only support tabs, but also subtabs, here is the second example:

Original BB-Code:

[tabmenu]
[tab=Tab1]
[subtab=Subtab11]Subtab11 Text
[subtab=Subtab12]Subtab12 Text
[tab=Tab2]
[subtab=Subtab21]Subtab21 Text
[/tabmenu]


Note: If you have subtabs in you tabmenu, you can't write text into tab areas. Apart from that subtabs behave like tabs when getting their content.

Result:

[h]Tab1[/h]
[spoiler=Subtab11]Subtab11 Text
[/spoiler][spoiler=Subtab12]Subtab12 Text
[/spoiler][h]Tab2[/h]
[spoiler=Subtab21]Subtab21 Text
[/spoiler]


tabs are still being replaced with h. The behavior for subtabs is a bit different though. Here my application is taking the title from the subtab, turning it into a spoiler-Tag (not comparable with the spoiler-codes of Stack Exchange, the spoiler-BB-Code is more like an accordion) and placing the content of the subtab between the opening spoiler-Tag and the closing one.

$(function () {
    $('#buttonGenerate').on('click', function () {
        // The format of titles can be like this: [tab=Title], [tab="Title"] &amp; [tab='Title']
        // the user can choose the format with this checkbox
        var bbFormat = $('input[name=bbCodeFormat]:checked').val();
        var titleTemplate;
        var regexTab;
        var regexSubTab;

        switch (bbFormat) {
            // Depending on which format was chosen, different regex patterns are used
            case '1':
            // Format: [tab=Title], allows certain special chars in the title
                regexTab = /\[tab=((\w|[ äöüÄÖÜ,.\-;:_#'+*!"§$%&amp;/()=?ß^°])*)\]/g;
                regexSubTab = /\[subtab=((\w|[ äöüÄÖÜ,.\-;:_#'+*!"§$%&amp;/()=?ß^°])*)\]((.|\s)+?((?=\[subtab)|(?=\[\/tabmenu)|(?=\[tab)))/g;
                titleTemplate = '###';
                break;
            // Format: [tab="Title"] allows any special chars in the title
            case '2':
                regexTab = /\[tab="((.| )*)"\]/g;
                regexSubTab = /\[subtab="((.| )*)"\]((.|\s)+?((?=\[subtab)|(?=\[\/tabmenu)|(?=\[tab)))/g;
                titleTemplate = '"###"';
                break;
            // Format: [tab='Title'] allows any special chars in the title
            case '3':
                regexTab = /\[tab='((.| )*)'\]/g;
                regexSubTab = /\[subtab='((.| )*)'\]((.|\s)+?((?=\[subtab)|(?=\[\/tabmenu)|(?=\[tab)))/g;
                titleTemplate = "'###'";
                break;
        }

        // Gather original BB-Code from textarea
        var input = $('#inputArea').val();

        // Replace the tabmenu BB-Code via Regex
        input = input.replace(regexSubTab, '[spoiler=' + titleTemplate.replace('###', '$1') + ']$3[/spoiler]');
        input = input.replace(regexTab, '[h]$1[/h]');
        input = input.replace('[tabmenu]', '').replace('[/tabmenu]', '');
        $('#outputArea').val(input.trim());
    });
});
Your code is good, but it could be improved:



Using an enum

If you use an enum, you can avoid Magic Numbers in your switch statement:


    switch (bbFormat) {
        // Depending on which format was chosen, different regex patterns are used
        case '1':



Whereas, doing:

var BB_FORMAT_TYPES = {
    NoQuotes: 1,
    DoubleQuotes: 2,
    SingleQuotes: 3
};


with:

switch (bbFormat) {
    case BB_FORMAT_TYPES.NoQuotes:
        break;
    case BB_FORMAT_TYPES.SingleQuotes:
        break;
    case BB_FORMAT_TYPES.DoubleQuotes:
        break;
}


However, using an enum is usually suited more for when the contents of each have different execution styles. In your case, it'd be better to move the code to an object, and have a single execution path referencing said object.

    var object = {
        1: {
            regexTab: /\[tab=((\w|[ äöüÄÖÜ,.\-;:_#'+*!"§$%&amp;/()=?ß^°])*)\]/g,
            regexSubTab: /\[subtab=((\w|[ äöüÄÖÜ,.\-;:_#'+*!"§$%&amp;/()=?ß^°])*)\]((.|\s)+?((?=\[subtab)|(?=\[\/tabmenu)|(?=\[tab)))/g,
            titleTemplate: '###'
        },
        2: {
            regexTab: /\[tab="((.| )*)"\]/g,
            regexSubTab: /\[subtab="((.| )*)"\]((.|\s)+?((?=\[subtab)|(?=\[\/tabmenu)|(?=\[tab)))/g,
            titleTemplate: '"###"'
        },
        3: {
            regexTab: /\[tab='((.| )*)'\]/g,
            regexSubTab: /\[subtab='((.| )*)'\]((.|\s)+?((?=\[subtab)|(?=\[\/tabmenu)|(?=\[tab)))/g,
            titleTemplate: "'###'"
        }
    };




Chaining:

Instead of having the replace on each line, try using chaining:

    input = input.replace(regexSubTab, '[spoiler=' + titleTemplate.replace('###', '$1') + ']$3[/spoiler]');
                 .replace(regexTab, '[h]$1[/h]');
                 .replace('[tabmenu]', '')
                 .replace('[/tabmenu]', '');


that way you can avoid looking up and writing to the input variable each time.