Skip to content

Instantly share code, notes, and snippets.

@nfroidure
Last active August 29, 2015 14:09
Show Gist options
  • Save nfroidure/37af8ffa1eeb22d1e877 to your computer and use it in GitHub Desktop.
Save nfroidure/37af8ffa1eeb22d1e877 to your computer and use it in GitHub Desktop.
Convert XBBCodes to XHTML
module.exports = function xbbcode2html(str, options) {
var all = options.all || false;
var cleanup = options.cleanup || false;
var secure = options.secure || false;
var tags = [
'span', 'kbd', 'var', 'del', 'ins', 'div', 'strong', 'em', 'dfn', 'cite',
'q', 'blockquote', 'p', 'br', 'a', 'ol', 'ul', 'li', 'abbr', 'acronym',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'pre', 'address', 'img', 'tr',
'th', 'td', 'table', 'caption', 'thead', 'tfoot', 'tbody', 'dl', 'dd', 'dt',
'map', 'area', 'code', 'samp', 'sub', 'sup'
];
if(!secure) {
tags.push('script', 'noscript', 'object', 'param');
}
var attributes = [
'class', 'id', 'name', 'dir', 'title', 'lang', 'style', 'href', 'hreflang',
'rel', 'rev', 'tabindex', 'type', 'accesskey', 'charset', 'datetime',
'cite', 'alt', 'longdesc', 'usemap', 'src', 'coords', 'shape', 'nohref',
'summary', 'scope'
];
if(!secure) {
attributes.push(
'onclick', 'ondblclick', 'onkeydown', 'onkeypress', 'onkeyup', 'onload',
'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup',
'onunload', 'onblur', 'onfocus', 'defer', 'value', 'data', 'width', 'height'
);
}
var tagChars = 'abcdefghijklmnopqrstuvwxyz0123456789';
var attributeChars = 'abcdefghijklmnopqrstuvwxyz';
var curTextNode = '';
var curHtml = '';
var curTag = '';
var curTagBegin = -1;
var opClTag = false;
var curAtt = '';
var curAttBegin = -1;
var curAttVal = '';
var curAttValBegin = -1;
var lastSpaceInAtt = -1;
str = str.replace('[[', '[');
str = str.replace(']]', ']');
for(i = 0; i < str.length; i++) {
curTag = '';
curTagBegin = -1;
opClTag = false;
curAtt = '';
curAttBegin = -1;
curAttVal = '';
curAttValBegin = -1;
lastSpaceInAtt = -1;
if(str[i] == '[') {
if(curTextNode) {
curHtml += curTextNode;
}
curTextNode = '';
curTagBegin = i;
if(str[i+1] == '/') {
// Tag close loop
for(i = i+2; i < str.length; i++) {
if(-1 !== tagChars.indexOf(str[i])) {
curTag += str[i];
} else {
break;
}
}
if(!(curTag && (all || -1 !== tags.indexOf(curTag)))) {
curTextNode += '[/' + curTag + str[i];
continue;
} else if(i >= str.length || str[i] != ']') {
curTextNode += '[/' + curTag + str[i];
continue;
}
curHtml += '</' + curTag + '>';
} else {
curTextNode='';
for(i = i+1; i < str.length; i++) {
if(-1 !== tagChars.indexOf(str[i])) {
curTag+=str[i];
} else {
break;
}
}
if(curTag && ( all || -1 !== tags.indexOf(curTag))) {
curHtml += '<' + curTag;
if(i+1 < str.length && str[i] == '/' && str[i+1] == ']') {
opClTag = true;
} else if(i+2 < str.length && str[i] == ' ' && str[i+1] == '/' && str[i+2] == ']') {
opClTag = true;
} else if(i+1 < str.length && str[i] == ' ' && -1 !== attributeChars.indexOf(str[i+1])) {
while(str[i] == ' ') {
curAttBegin = i + 1;
for(i = i+1; i < str.length; i++) {
if(-1 !== attributeChars.indexOf(str[i])) {
curAtt += str[i];
continue;
} else {
if(-1 !== attributes.indexOf(curAtt)) {
curHtml += ' ' + curAtt + '="';
curAttValBegin = i+1;
} else {
curAtt = '';
curAttValBegin = i+1;
}
break;
}
}
if(str[i] == '=') {
for(i = i+1; i < str.length; i++) {
if(str[i] == ']') {
break;
} else if(i < str.length && str[i] == '/' && str[i+1] == ']' &&
curAtt != 'href' && curAtt != 'src' && curAtt != 'cite') {
opClTag = true;
break;
} else if(i < str.length && str[i] == ' ' && str[i+1] == '/' && str[i+2] == ']') {
opClTag = true;
break;
} else if(str[i] != ' ') {
if(lastSpaceInAtt >= 0) {
if(str[i] == '=') {
i = lastSpaceInAtt;
break;
} else if(-1 === attributeChars.indexOf(str[i])) {
lastSpaceInAtt = -1;
}
}
continue;
} else {
lastSpaceInAtt = i;
}
}
curAttVal += str.substr(curAttValBegin, i-curAttValBegin);
} else {
if(i < str.length && str[i] == '/' && str[i+1] == ']') {
opClTag = true;
} else if(i < str.length && str[i] == ' ' && str[i+1] == '/' && str[i+2] == ']') {
opClTag = true;
}
}
if(curAtt) {
curHtml += curAttVal + '"';
}
curAtt = '';
curAttBegin = -1;
curAttVal = '';
curAttValBegin = -1;
lastSpaceInAtt = -1;
if(opClTag && str[i] == ' ') {
i++;
}
}
} else if(str[i] != ']') {
curHtml = curHtml.substr(0, curHtml.length - curTag.length - 1);
curTextNode += '[' + curTag + str[i];
continue;
}
while(opClTag && str[i] != ']')
i++;
if(opClTag) {
curHtml += ' /';
opClTag = false;
}
curHtml += '>';
} else {
curTextNode += '[' + curTag + str[i];
continue;
}
}
} else {
curTextNode += str[i];
}
}
if(curTextNode) {
curHtml += curTextNode;
}
if(cleanup) {
curHtml = curHtml.replace(/\[(?:[^\]]+)\]/i, '');
}
return curHtml;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment