{"id":1078,"date":"2011-05-07T15:52:11","date_gmt":"2011-05-07T19:52:11","guid":{"rendered":"http:\/\/patorjk.com\/blog\/?p=1078"},"modified":"2015-02-12T09:19:20","modified_gmt":"2015-02-12T13:19:20","slug":"extendible-bbcode-parser-in-javascript","status":"publish","type":"post","link":"https:\/\/patorjk.com\/blog\/2011\/05\/07\/extendible-bbcode-parser-in-javascript\/","title":{"rendered":"Extendible BBCode Parser in JavaScript"},"content":{"rendered":"<div id=\"attachment_1100\" class=\"wp-caption alignright\" style=\"width: 260px\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/patorjk.com\/blog\/wp-content\/uploads\/2011\/05\/strange.png\" alt=\"\" title=\"Avatar\" width=\"250\" height=\"149\" class=\"size-full wp-image-1100\" \/><\/p>\n<p class=\"wp-caption-text\">Photo By <a href='http:\/\/www.flickr.com\/photos\/therefore\/475120436\/in\/faves-40423570@N07\/'>Dean Terry<\/a><\/p>\n<\/div>\n<p>I decided to try my hand at implementing a <a href=\"https:\/\/secure.wikimedia.org\/wikipedia\/en\/wiki\/BBCode\">BBCode<\/a> parser in JavaScript. You can play around with it online <a href=\"http:\/\/patorjk.com\/bbcode-previewer\/\">here<\/a>, and download the source <a href=\"https:\/\/github.com\/patorjk\/Extendible-BBCode-Parser\">here<\/a>.<\/p>\n<p>I had looked around a little bit and noticed that the existing JavaScript BBCode parsers had at least a few of the following issues:<\/p>\n<ul>\n<li>They didn&#8217;t report errors on misaligned tags (e.g., [b][u]test[\/b][\/u]).<\/li>\n<li>They couldn&#8217;t handle tags of the same type that were nested within each other (e.g., [color=red]red[color=blue]blue[\/color]red again[\/color]). This happens because their regex will look for the first closing tag it can find.<\/li>\n<li>They couldn&#8217;t handle BBCode&#8217;s list format (e.g., [list][*]item 1[*]item 2[\/list]).<\/li>\n<li>They didn&#8217;t report errors on incorrect parent-child relationships (e.g., [list][td]item 1?[\/td][\/list]).<\/li>\n<li>They weren&#8217;t easily extendible.<\/li>\n<\/ul>\n<p>I naively thought it&#8217;d be easy to quickly whip up a parser, and at first it was. Most BBCode tags can be implemented with a simple find and replace. However, I quickly ran into the issues of dealing with nested tags of the same type, the noparse tag, and the list tag&#8217;s annoying [*] tag (which doesn&#8217;t have a closing tag). Luckily, I came across a neat blog post on <a href=\"http:\/\/blog.stevenlevithan.com\/archives\/reverse-recursive-pattern\">finding nested patterns in JavaScript<\/a>, which came in handy for isolating tag pairs, from the inner-most on up. Taking the idea from that post, one can do something like this to process the inner tags first and avoid the nested tag problem:<\/p>\n<pre class=\"brush:js\">\r\nvar str = \"[list][list]test[\/list][\/list]\",\r\n    re = \/\\[([^\\]]*?)\\](.*?)\\[\\\/\\1\\]\/gi;\r\nwhile (str !== (str = str.replace(re, function(strMatch, subM1, subM2) {\r\n    return \"<some-html-tag>\" + subM2 + \"<\/some-html-tag>\";\r\n})));\r\n\/\/ str = \"<some-html-tag><some-html-tag>test<\/some-html-tag><\/some-html-tag>\"\r\n<\/pre>\n<p>That idea works well, though you can&#8217;t implement a noparse tag if you process the inner-most tags first. So I decided to pre-process the BBCode with something similar to the idea above and add in nested-depth information to each open and close tag. Once all of the tags had that, I could  parse the processed code with a regex that could easily match-up the correct open and close tags.<\/p>\n<p>To get around the issue of the [*] tag having no closing tag, I wrote code that inserted [\/*] tags where they were supposed to go during the pre-processing period. I wont go into the algorithm here, but you can dig into the code if you&#8217;re interested.<\/p>\n<p>Also, I should note that the fact that JavaScript allows you to use a function as the second parameter to the <a href=\"https:\/\/developer.mozilla.org\/en\/JavaScript\/Reference\/Global_Objects\/String\/replace\">replace method<\/a> makes processing the tags really easy. Once you match a set of tags, you can recursively call the parse function on that tag&#8217;s contents from inside of the function you passed to replace.<\/p>\n<p><strong>Using the parser<\/strong><\/p>\n<p>To use the use the parser, you&#8217;d simply include xbbcode.js and xbbcode.css files somewhere on your page (which are contained in the zip file linked above), and then call the XBBCODE object from somewhere in your JavaScript:<\/p>\n<pre class=\"brush:js\">\r\nvar result = XBBCODE.process({\r\n    text: \"Some bbcode to process here\",\r\n    removeMisalignedTags: false,\r\n    addInLineBreaks: false\r\n});\r\nconsole.log(\"Errors: \" + result.error);\r\nconsole.dir(result.errorQueue);\r\nconsole.log(result.html);\/\/ the HTML form of your BBCode\r\n<\/pre>\n<p><strong>Adding new tags<\/strong><\/p>\n<p>To add a new tag to your BBCode, add properties to the &#8220;tags&#8221; object inside of the XBBCODE object. For example, say you wanted to add a tag called [googleit] which would change its contents into a link of its google search results. You&#8217;d implement that by adding this to the tags object:<\/p>\n<pre class=\"brush:js\">\r\n\"googleit\": {\r\n    openTag: function(params,content) {\r\n        var website = \"\\\"http:\/\/www.google.com\/#q=\" + content + '\"';\r\n        return '&lt;a href=' + website + '>';\r\n    },\r\n    closeTag: function(params,content) {\r\n        return '&lt;\/a>';\r\n    }\r\n}\r\n<\/pre>\n<p>Then you could have BBCode like this: &#8220;[googleit]ta-da![\/googleit]&#8221; which would be transformed into this: &#8220;&lt;a href=&#8221;http:\/\/www.google.com\/#q=ta-da!&#8221;>ta-da!&lt;\/a>&#8221;<\/p>\n<p>If you have any suggestions or find any bugs let me know.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Photo By Dean Terry I decided to try my hand at implementing a BBCode parser in JavaScript. You can play around with it online here, and download the source here. I had looked around a little bit and noticed that the existing JavaScript BBCode parsers had at least a few of the following issues: They &hellip; <a href=\"https:\/\/patorjk.com\/blog\/2011\/05\/07\/extendible-bbcode-parser-in-javascript\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Extendible BBCode Parser in JavaScript<\/span> <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[21,26,32],"tags":[],"class_list":["post-1078","post","type-post","status-publish","format-standard","hentry","category-javascript","category-web-apps","category-web-development"],"_links":{"self":[{"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/posts\/1078","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/comments?post=1078"}],"version-history":[{"count":62,"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/posts\/1078\/revisions"}],"predecessor-version":[{"id":3299,"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/posts\/1078\/revisions\/3299"}],"wp:attachment":[{"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/media?parent=1078"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/categories?post=1078"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/patorjk.com\/blog\/wp-json\/wp\/v2\/tags?post=1078"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}