(function (name, definition) { if (typeof exports !== 'undefined' && typeof module !== 'undefined') { module.exports = definition(); } else if (typeof define === 'function' && typeof define.amd === 'object') { define(definition); } else { this[name] = definition(); } })('html2bbcode', function (html2bbcode) { 'use strict'; html2bbcode = { version: '1.2.3' }; //function HTMLAttribute() function HTMLTag() { this.name = ''; this.length = 0; //this.attr = null; //this.content = null; } HTMLTag.duptags = ['div', 'span']; HTMLTag.headingtags = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']; HTMLTag.selfendtags = ['!doctype', 'meta', 'link','img', 'br']; HTMLTag.newlinetags = ['div', 'p', 'br', 'li', 'tr'].concat(HTMLTag.headingtags); HTMLTag.noemptytags = ['head', 'style', 'script', 'span', 'a', 'font', 'color', 'size', 'face', 'strong', 'b', 'em', 'i', 'del', 's', 'ins', 'u']; HTMLTag.noemptyattrtags = ['img']; HTMLTag.prototype.findquoteend = function (script, start, multiline) { var end = -1; var i = start ? start : 0; var len = script.length; var d = script[i] === '\"'; i++; while (i < len) { if (script[i] === '\\') { i++; switch (script[i]) { case 'u': // \uXXXX i += 5; break; case 'x': // \xXX i += 3; break; default: // \n ... i++; break; } } else if ((d && script[i] === '\"') || (!d && script[i] === '\'')) { end = i; break; } else if (script[i] === '\n' && !multiline) { // not allow change line break; } else { i++; } } return end; }; HTMLTag.prototype.findscriptend = function (script, start) { var end = -1; var i = start ? start : 0; var len = script.length; var freg = /(['"]|<\s*?\/\s*?script\s*?>)/ig; while (i < len) { if (script[i] === '\"' || script[i] === '\'') { var qi = this.findquoteend(script, i, true); if (qi === -1) { break; } i = qi + 1; } else { freg.lastIndex = i; var m = freg.exec(script); if (!m || m.length <= 0) { break; } else if (m[0][0] === '<') { //script here end = freg.lastIndex - m[0].length; break; } // quote i = freg.lastIndex - 1; //console.log(i, script.substr(i, 5)); continue; } } return end; }; HTMLTag.prototype.quote = function (quotation) { // convert string type if (quotation[0] === '\'') { var s = '"'; var i = 1; var len = quotation.length - 1; // last is \' var start = i; while (i < len) { if (quotation[i] === '\\') { i++; switch (quotation[i]) { case 'u': // \uXXXX i += 5; break; case 'x': // \xXX i += 3; break; default: // \n ... i++; break; } } else if (quotation[i] === '\"') { s += quotation.substr(start, i - start); s += '\\"'; i++; start = i; break; } else { i++; } } if (start < len) { s += quotation.substr(start, len - start); } s += '"'; return s; } else { return quotation; } }; HTMLTag.prototype.parseStyle = function (style) { var ss = style.split(';'); var r_style = {}; var count = 0; for (var i = 0; i < ss.length; i++) { var s = ss[i].split(':'); if (s.length >= 2) { count++; var val; if (s.length > 2) { // eg. url(http://example.com) val = s.slice(1).join(':').trim(); } else { val = s[1].trim(); } if (val[0] === '\'' && val[val.length - 1] === '\'') { try { val = JSON.parse(this.quote(val)); } catch (err) { } } r_style[s[0].trim().toLowerCase()] = val; } } if (count > 0) { return r_style; } else { return undefined; } }; HTMLTag.prototype.parseAttributes = function (attr) { attr = attr.trim(); var blank = /\s/; var i = 0; var len = attr.length; var start = i; var lastkey = null; var invalue = false; var r_attr = {}; var add_attr = function (k, v) { if (typeof v === 'undefined') { v = null; } k = k.trim().toLowerCase(); r_attr[k] = v; }; while (i < len) { if (attr[i] === '=') { // TODO: check lastkey, currently drop previous lastkey lastkey = attr.substr(start, i - start); invalue = false; } else if (blank.test(attr[i])) { if (lastkey && invalue) { add_attr(lastkey, attr.substr(start, i - start)); invalue = false; lastkey = null; } else if (i - start > 0) { lastkey = attr.substr(start, i - start); add_attr(lastkey); lastkey = null; } start = i + 1; } else if (lastkey && !invalue) { start = i; if (attr[i] === '"' || attr[i] === '\'') { var b = attr[i] === '\''; i = this.findquoteend(attr, i); if (i === -1) { break; } var v = attr.substr(start, i + 1 - start); if (b) { v = this.quote(v); } try { v = JSON.parse(v); } catch (e) { } add_attr(lastkey, v); lastkey = null; start = i + 1; } else { invalue = true; } } i++; } if (start < len) { var d = attr.substr(start); if (lastkey) { add_attr(lastkey, d); } else { add_attr(d); } lastkey = null; } var count = 0; for (var k in r_attr) { count++; } if (count > 0) { if (r_attr.style) { r_attr.style = this.parseStyle(r_attr.style); } this.attr = r_attr; } }; HTMLTag.prototype.parse = function (html) { var i = 0; if (html[i] !== '<') { throw new Error('not a tag'); } var len = html.length; var blank = /\s/; //var htmltagq = /[<>]/; // strip tagname head blank while (i < len) { if (html[i] === '<') { i++; } else if (html[i] === '>') { // drop this empty tag this.length = i + 1; return this; } else if (blank.test(html[i])) { i++; } else { break; } } if (i >= len) { // drop this this.length = len; return this; } // name var start = i; var tagheadend = false; while (i < len && !blank.test(html[i])) { if (html[i] === '>') { tagheadend = true; break; } else if (html[i] === '/') { break; } i++; } if (i >= len) { // drop this this.length = i; return this; } this.name = html.substr(start, i - start).trim().toLowerCase(); if (this.name.length > 0 && this.name[0] === '/') { this.length = i; this.name = this.name.substr(1); this.selfend = true; return this; } if (HTMLTag.selfendtags.indexOf(this.name) >= 0) { this.selfend = true; } // attr if (!tagheadend) { start = i; while (i < len && html[i] !== '>') { i++; } if (i >= len) { // drop this this.length = i; return this; } else if (i - start > 0) { var sattr = html.substr(start, i - start).trim(); var attrlen = sattr.length; if (attrlen > 0 && sattr[attrlen - 1] === '/') { this.selfend = true; sattr = sattr.substr(0, attrlen - 1); } this.parseAttributes(sattr); } } i++; // skip '>' if (this.selfend) { this.length = i; return this; } // content var that = this; var add_content = function (html) { var hstack = new HTMLStack().parse(html); if (that.content) { that.content.append(hstack); } else { that.content = hstack; } return hstack.length; }; if (this.name === 'script') { var script_len = this.findscriptend(html.substr(i)); if (script_len < 0) { this.length = len; return this; } this.content = new HTMLStack(); var script = html.substr(i, script_len); this.content.length = script_len; this.content.stack = [ script ]; i += script_len; // script tag end start = html.indexOf('>', i); if (start < 0) { // no possible this.length = len; return this; } this.length = start + 1; return this; } var j = 0; while (i < len) { // loop to tag end j++; start = i; while (i < len && blank.test(html[i])) { i++; } while (i < len && html[i] !== '<') { i++; } var i_tagend = i; i++; while (i < len && blank.test(html[i])) { i++; } if (i >= len) { // drop this this.content = new HTMLStack().parse(html.substr(start)); this.length = len; return this; } else { if (i < len && html[i] === '/') { i++; while (i < len && blank.test(html[i])) { i++; } if (i >= len) { // drop this i += add_content(html.substr(start)); this.length = len; return this; } else { var t_start = i; var t_tagheadend = false; while (i < len && !blank.test(html[i])) { if (html[i] === '>') { t_tagheadend = true; break; } i++; } if (i > t_start) { if (!t_tagheadend) { while (i < len && html[i] !== '>') { i++; } } var ename = html.substr(t_start, i - t_start).trim().toLowerCase(); i++; //skip '>' // force stop current tag /*if (ename === this.name)*/ { // end of tag this.length = i; if (i_tagend > start) { // add content add_content(html.substr(start, i_tagend - start)); } return this; } } } } } i = start + add_content(html.substr(start)); } this.length = i; return this; }; function HTMLStack() { this.stack = []; this.length = 0; } HTMLStack.prototype.parse = function (html) { // check first... if (!html) { return this; } var i = 0; var len = html.length; var lasttagend = 0; var blank = /\s/; var that = this; var push_plaintext = function (start, end) { if (start < end) { that.push(html.substr(start, end - start)); } }; while (i < len) { switch (html[i]) { case '<': push_plaintext(lasttagend, i); // check end & drop var t_i = i + 1; while (t_i < len && blank.test(html[t_i])) { t_i++; } if (t_i < len && html[t_i] === '/') { return this; } var tag = new HTMLTag().parse(html.substr(i)); this.push(tag); i += tag.length; lasttagend = i; break; case '>': // TODO: drop the > i++; break; default: i++; break; } } push_plaintext(lasttagend, len); return this; }; HTMLStack.prototype.push = function (data) { this.length += data.length; this.stack.push(data); }; HTMLStack.prototype.pop = function () { return this.stack.pop(); }; HTMLStack.prototype.append = function (hstack) { this.stack = this.stack.concat(hstack.stack); this.length += hstack.length; }; (function () { var dupRegex = new RegExp( '<\\s*?(' + HTMLTag.duptags.join('|') + ')\\s*?>\\s*?' + '<\\s*?\\1\\s*?>' + '(((?!<\\s*?\\1\\s*?>)[\\S\\s])*?)' + '<\\s*?/\\s*?\\1\\s*?>\\s*?' + '<\\s*?/\\s*?\\1\\s*?>', 'ig'); var nlsRegex = new RegExp( '(<\\s*?(' + HTMLTag.newlinetags.join('|') + ')(\\s[^>]*?)?>)\\s+', 'ig'); var nleRegex = new RegExp( '\\s+(<\\s*?/\\s*?(' + HTMLTag.newlinetags.join('|') + ')\\s*?>)', 'ig'); var empRegex = new RegExp( '<\\s*?(' + HTMLTag.noemptytags.join('|') + ')(\\s[^>]*?)?>' + '<\\s*?/\\s*?\\1\\s*?>', 'ig'); HTMLStack.minify = function (html) { var preRegex = //ig; var endPreRegex = /<\/pre>/ig; var emptyRegex = /\s{2,}/g; var m, newHtml = '', preMarkIndex = -1; html = html.replace(empRegex, ''); html = html.replace(nlsRegex, '$1'); html = html.replace(nleRegex, '$1'); while (m = preRegex.exec(html)) { if (preMarkIndex < 0) { preMarkIndex = 0; } newHtml += html.substr(preMarkIndex, preRegex.lastIndex - preMarkIndex).replace(emptyRegex, ' '); preMarkIndex = preRegex.lastIndex; endPreRegex.lastIndex = preRegex.lastIndex; if (m = endPreRegex.exec(html)) { preRegex.lastIndex = endPreRegex.lastIndex; // no replace for pre content newHtml += html.substr(preMarkIndex, m.index - preMarkIndex); preMarkIndex = m.index; } } if (preMarkIndex >= 0) { html = newHtml + html.substr(preMarkIndex).replace(emptyRegex, ' '); } else { html = html.replace(emptyRegex, ' '); } while (dupRegex.test(html)) { html = html.replace(dupRegex, '<$1>$2'); } return html; }; })(); var escapeMap = { '&': 'amp', '<': 'lt', '>': 'gt', '"': 'quot', "'": '#x27', '`': '#x60' }; var unescapeMap = { 'nbsp': ' ', 'amp': '&', 'lt': '<', 'gt': '>', 'quot': '"' }; HTMLStack.unescape = function (str, nonbsp) { var src = '&([a-zA-Z]+?|#[xX][\\da-fA-F]+?|#\\d+?);'; var testRegexp = new RegExp(src); var escaper = function (match, m1) { m1 = m1.toLowerCase(); if (nonbsp && m1 === 'nbsp') { return ' '; } var m = unescapeMap[m1]; if (m) { return m; } else if (m1[0] === '#') { var code = 0; if (m1[1] == 'x') { code = parseInt(m1.substr(2), 16); } else { code = parseInt(m1.substr(1)); } if (code) { return String.fromCharCode(code); } } return ''; }; if (testRegexp.test(str)) { var replaceRegexp = new RegExp(src, 'g'); str = str.replace(replaceRegexp, escaper); } return str; }; HTMLStack.prototype.decode = function (nonbsp) { for (var i = 0; i < this.stack.length; i++) { var s = this.stack[i]; if (typeof s === 'string') { this.stack[i] = HTMLStack.unescape(s, nonbsp); } else if (s instanceof HTMLTag && s.content) { s.content.decode(nonbsp); } } return this; }; HTMLStack.prototype.dedup = function () { for (var i = 0; i < this.stack.length; i++) { var s = this.stack[i]; if (s instanceof HTMLTag && s.content) { if (HTMLTag.duptags.indexOf(s.name) >= 0 && !s.attr && s.content.stack.length === 1) { var ts = s.content.stack[0]; if (ts.name === s.name) { this.stack[i] = ts; i--; continue; } } s.content.dedup(); } } return this; }; HTMLStack.prototype.strip = function (parent, afternewline) { if (!afternewline) { afternewline = (parent && !afternewline) ? (HTMLTag.newlinetags.indexOf(parent.name) >= 0) : true; } var blanks = /^\s*$/; var k = 0; var stag = true; // first recursive for (var i = 0; i < this.stack.length; i++) { var s = this.stack[i]; if (s instanceof HTMLTag) { stag = true; if (s.content) { //check if is after newline var anl; if (k <= 0) { anl = afternewline; } else { anl = false; // fine previous one for (var j = i - 1; j >= 0; j--) { var ts = this.stack[j]; if (ts instanceof HTMLTag) { anl = (HTMLTag.newlinetags.indexOf(ts.name) >= 0); //anl = true; break; } else if (typeof ts === 'string' && blanks.test(ts)) { //continue; } else { break; } } } s.content.strip(s, anl); } } else if (typeof s === 'string' && blanks.test(s)) { if (stag) { continue; } } k++; } stag = true; var new_stack = []; var new_len = 0; for (var i = 0; i < this.stack.length; i++) { var s = this.stack[i]; if (typeof s === 'string' && blanks.test(s) && afternewline) { if (stag) { continue; } afternewline = false; } else if (s instanceof HTMLTag) { stag = true; if (HTMLTag.noemptyattrtags.indexOf(s.name) >= 0) { // strip like if (!s.attr) { continue; } var exists = false; for (var k1 in s.attr) { if (s.attr[k1]) { exists = true; break; } } if (!exists) { continue; } } if (HTMLTag.noemptytags.indexOf(s.name) >= 0 && !s.content) { // null span continue; } else if (HTMLTag.newlinetags.indexOf(s.name) >= 0) { afternewline = true; /*} else if (s.name === 'span' && afternewline) {*/ // keep newline flag } else { afternewline = false; } } else { // not full empty string if (afternewline) { // removehead space after newline s = s.replace(/^\s+/g, ''); if (!s) { // empty string continue; } } s = s.replace(/\s+/g, ' '); stag = false; afternewline = false; } new_len++; new_stack.push(s); } // check last one is empty string var s = new_stack[new_len - 1]; if (typeof s === 'string') { if (new_len >= 2 && blanks.test(s)) { // remove last empty string new_stack.splice(new_len - 1, 1); new_len--; } else if (/\S\s+$/.test(s)) { // space follow with a non-space string new_stack[new_len - 1] = s.replace(/\s+$/, ''); } } if (new_len <= 0 && parent) { delete parent.content; return; } this.stack = new_stack; return this; }; HTMLStack.prototype.showtree = function (tab, depth) { if (!tab) tab = ''; if (!depth) depth = 0; for (var i = 0; i < this.stack.length; i++) { var d = this.stack[i]; if (d instanceof HTMLTag) { console.log(tab, d.name, d.attr ? JSON.stringify(d.attr) : ''); if (d.content) { d.content.showtree(tab + '--', depth + 1); } } else if (typeof d === 'string') { console.log(tab, JSON.stringify(d)); } } }; function BBCode() { this.s = ''; this.weaknewline = true; this.stack = []; } BBCode.maps = { 'a': { section: 'url', attr: 'href' }, 'img': { section: 'img', data: 'src', empty: true }, 'em': { section: 'i' }, 'i': { section: 'i' }, 'strong': { section: 'b' }, 'b': { section: 'b' }, 'del': { section: 's' }, 's': { section: 's' }, 'ins': { section: 'u' }, 'u': { section: 'u' }, 'center': { section: 'center' }, 'ul': { section: 'ul' }, // may need to treat as 'list' 'ol': { section: 'ol' }, // may need to treat as 'list' 'li': { section: 'li', newline: 1 }, 'blockquote': { section: 'quote' }, 'code': { section: 'b' }, 'font': { extend: ['color', 'face', 'size'] }, 'span': { extend: ['color', 'face', 'size'] }, 'color': { section: 'color', attr: 'color' }, 'size': { section: 'size', attr: 'size' }, 'face': { section: 'font', attr: 'face' }, // new line tags 'h1': { section: 'h1', newline: 1 }, 'h2': { section: 'h2', newline: 1 }, 'h3': { section: 'h3', newline: 1 }, 'h4': { section: 'h4', newline: 1 }, 'h5': { section: 'h5', newline: 1 }, 'h6': { section: 'h6', newline: 1 }, 'p': { newline: 1 }, 'br': { newline: 2, empty: true }, 'table': { section: 'table', newline: 1 }, 'tr': { section: 'tr', newline: 1 }, 'th': { section: 'td', newline: 1 }, 'td': { section: 'td', newline: 1 }, 'pre': { section: 'code', newline: 1 }, 'div': { newline: 0 }, // ignore tags '!doctype': { ignore: true }, 'head': { ignore: true }, 'style': { ignore: true }, 'script': { ignore: true }, 'meta': { ignore: true }, 'link': { ignore: true }, }; BBCode.prototype.open = function (section, attr, data) { if (!section) { return; } if (section instanceof Array) { this.stack = this.stack.concat(section); } else { this.stack.push({ section: section, attr: attr, data: data }); } }; BBCode.prototype.append = function (str) { this.solidify(); this._append(str); }; BBCode.prototype._append = function (str) { if (str) { this.s += str; this.weaknewline = false; } }; BBCode.prototype.solidify = function () { // write back stack var i; for (i = 0; i < this.stack.length; i++) { var st = this.stack[i]; var section = st.section; var attr = st.attr; var data = st.data; var s = '[' + section; if (typeof attr === 'string') { s += '=' + attr; } else { for (var k in attr) { s += ' ' + k + '=' + attr[k]; } } s += ']'; if (data) { s += data; } this._append(s); } if (i > 0) { this.stack = []; } }; BBCode.prototype.close = function (section) { if (!section) { return; } this.solidify(); this._append('[/' + section + ']'); }; BBCode.prototype.rollback = function () { this.stack = []; }; BBCode.prototype.newline = function (n) { if (n === 2) { // br this.append('\n'); this.weaknewline = true; } else if (n === 1) { // div, p if (!this.weaknewline) { this.append('\n'); this.weaknewline = true; } } else if (!this.weaknewline) { this.append('\n'); this.weaknewline = true; } }; BBCode.prototype.toString = function () { return this.s; }; // opts: transsize, imagescale function HTML2BBCode(opts) { this.opts = opts ? opts : {}; } HTML2BBCode.prototype.color = function (c) { if (!c) return; var c1Regex = /rgba?\s*?\(\s*?(\d{1,3})\s*?,\s*?(\d{1,3})\s*?,\s*?(\d{1,3})\s*?.*?\)/i; if (c1Regex.test(c)) { var pad2 = function (s) { if (s.length < 2) { s = '0' + s; } return s; } c = c.replace(c1Regex, function (match, r, g, b) { r = pad2(parseInt(r).toString(16)); g = pad2(parseInt(g).toString(16)); b = pad2(parseInt(b).toString(16)); return '#' + r + g + b; }); } return c; }; HTML2BBCode.prototype.size = function (size) { if (!size) return; var px2size = [0, 12, 14, 16, 18, 24, 32, 48]; var name2size = [null, 'smaller', 'small', 'medium', 'large', 'x-large', 'xx-large', '-webkit-xxx-large']; if (/^\d+$/.test(size)) { return size; } else if (/^\d+?px$/.test(size)) { size = parseInt(size); if (!size || size < 0) { return; } if (this.opts.transsize) { for (var i = px2size.length; i >= 0; i--) { if (i === 0) { // smallest return '1'; } if (size >= px2size[i]) { return i.toString(); } } } else { return size.toString(); } } else { var ns = name2size.indexOf(size); if (ns > 0) { if (this.opts.transsize) { return ns.toString(); } else { return px2size[ns].toString(); } } // TODO: support other type return; } return size ? size.toString() : undefined; }; HTML2BBCode.prototype.px = function (px) { if (!px) return; px = parseInt(px); return px ? px.toString() : undefined; }; HTML2BBCode.prototype.convertStyle = function (htag, sec) { if (!sec) { return; } var bbs = []; var that = this; var opts = this.opts; var addbb = function (sec) { if (!sec || sec.ignore || !(sec.section || (sec.extend && sec.extend.length > 0))) { return; } var tsec = { section: sec.section }; if (sec.attr) { if (htag.attr) { switch (sec.section) { case 'size': tsec.attr = that.size(htag.attr[sec.attr]); break; case 'color': tsec.attr = that.color(htag.attr[sec.attr]); break; default: tsec.attr = htag.attr[sec.attr]; break; } if (htag.attr.style) { var ra; switch (sec.section) { case 'size': ra = htag.attr.style['font-size']; if (ra) ra = that.size(ra); break; case 'color': ra = htag.attr.style['color']; if (ra) ra = that.color(ra); break; case 'font': ra = htag.attr.style['font-family']; break; } if (ra) { tsec.attr = ra; } } if (!tsec.attr) { return; } } else { return; } } else if (sec.section === 'img' && opts.imagescale) { // image attr var w, h; if (htag.attr) { w = that.px(htag.attr['width']); h = that.px(htag.attr['height']); if (htag.attr.style) { var w1, h1; w1 = that.px(htag.attr.style['width']); h1 = that.px(htag.attr.style['height']); if (w1) w = w1; if (h1) h = h1; } if (w && h) { tsec.attr = w + 'x' + h; } else if (w || h) { if (w) { tsec.attr = { width: w }; } else { tsec.attr = { height: h }; } } } } if (sec.data) { tsec.data = htag.attr[sec.data]; } bbs.push(tsec); }; // check font-weight & text-align if (htag.attr && htag.attr.style) { if (htag.name !== 'b' && htag.name !== 'strong') { var att = htag.attr.style['font-weight']; if (att === 'bold' || (/^\d+$/.test(att) && parseInt(att) >= 700)) { addbb(BBCode.maps['b']); } } if (htag.name !== 'center') { var att = htag.attr.style['text-align']; if (att === 'center' && !opts.noalign) { addbb(BBCode.maps['center']); } } if (htag.name !== 'em' && htag.name !== 'i') { var att = htag.attr.style['font-style']; if (att === 'italic' || att === 'oblique') { // italic style addbb(BBCode.maps['i']); } } } if (sec.section === 'list' || sec.section === 'ul' || sec.section === 'ol' || sec.section === 'li') { if (opts.nolist) { return []; } } else if (sec.section === 'center') { if (opts.noalign) { return []; } } else if (/^h\d+$/.test(sec.section)) { // HTML Headings if (opts.noheadings) { // 18.5 -> 19 var headings2size = [ null, '32px', '24px', '19px', '16px', '14px', '12px' ]; var m = sec.section.match(/^h(\d+)$/); var hi = parseInt(m[1]); if (hi <= 0) { return []; } else if (hi >= headings2size.length) { hi = headings2size.length; } bbs.push({ section: 'size', attr: that.size(headings2size[hi]) }); return bbs; } } if ('extend' in sec) { for (var i = 0; i < sec.extend.length; i++) { var tag = sec.extend[i]; addbb(BBCode.maps[tag]); } } else { addbb(sec); } return bbs; }; HTML2BBCode.prototype.convert = function (hstack) { var bbcode = new BBCode(); if (!hstack) { return bbcode; } var that = this; var recursive = function (hs, anl) { for (var i = 0; i < hs.length; i++) { var s = hs[i]; if (s instanceof HTMLTag) { if (s.name in BBCode.maps) { var fnewline = 0; var sec = BBCode.maps[s.name]; if (sec.ignore) { continue; } if ('newline' in sec) { fnewline = sec.newline; bbcode.newline(sec.newline); } if (!s.content && !sec.empty) { // drop this continue; } var bbs = that.convertStyle(s, sec); bbcode.open(bbs); if (s.content) { recursive(s.content.stack, fnewline); } for (var j = bbs.length - 1; j >= 0; j--) { bbcode.close(bbs[j].section); } if (fnewline) { // weak new line bbcode.newline(); } } else if (s.content) { // drop section recursive(s.content.stack); } } else if (typeof s === 'string') { // force space //s = s.replace(/ /gi, ' '); bbcode.append(s); } } }; recursive(hstack.stack); return bbcode; }; HTML2BBCode.prototype.parse = function (html) { return new HTMLStack().parse(html) .strip().dedup().decode(); }; HTML2BBCode.prototype.feed = function (html) { var hstack = this.parse(html); if (this.opts.debug) { hstack.showtree(); } var bbcode = this.convert(hstack); return bbcode; }; return { HTMLTag: HTMLTag, HTMLStack: HTMLStack, BBCode: BBCode, HTML2BBCode: HTML2BBCode }; });