/** This is a quick and dirty script using "htmlparser2" to add some indentation to html outputs of JS mustache compiler. I haven't put any thought into it. It's only quick fixes and hacks for Ace html preview files only, so it's not supposed to be a general purpose solution and may not produce exactly the original html view. */ var htmlparser = require("htmlparser2") var keep_help = false; module.exports = function(html_input , keep_help_comments, is_ajax_content, callback) { keep_help = keep_help_comments || false; var handler = new htmlparser.DomHandler(function (error, dom) { if (error) { } else { var result = (!is_ajax_content ? "" : "")+pretty_print(dom , 0); callback.call(null, result) } }); var parser = new htmlparser.Parser(handler); parser.write(html_input); parser.done(); } var emptyTags = { __proto__: null, area: true, base: true, basefont: true, br: true, col: true, frame: true, hr: true, img: true, input: true, isindex: true, link: true, meta: true, param: true, embed: true }; function trim(str) { return str.replace(/^\s+/, '').replace(/\s+$/, ''); } var blockTags = { __proto__: null, div: true, p: true, ul: true, ol: true, blockquote:true }; function hasBlockElements(elements) { for(var e in elements) if(elements.hasOwnProperty(e)) { if(elements[e].name in blockTags) return true; } return false; } function pretty_print(dom , level) { var output = ""; for(var i = 0 ; i < dom.length ; i++) { var e = dom[i]; if(e.type == 'tag' || e.type == 'script' || e.type == 'style') { output += "\r\n"; for(var t = 0 ; t < level ; t++) output += "\t"; output += "<"+e.name; var pWithBlocks = e.name == "p" && hasBlockElements(e.children);//the p element cannot contain "block" levels, so don't add closing tag, to validate! if("attribs" in e) { for(var name in e.attribs) if(e.attribs.hasOwnProperty(name)) { output += " "+name+'="'+(trim(e.attribs[name]).replace(/\s/g, ' '))+'"'; } } if(e.name == "pre") { var text = e.children[0].data; var lines = text.split("\r\n"); var new_lines = ''; for(var l = 0 ; l < lines.length ; l++) new_lines += (lines[l].replace(/^\s+/, '')) + "\r\n"; e.children[0].data = new_lines; } else if( ((e.type == "script" && "attribs" in e && !("src" in e.attribs)) || e.type == "style") ) { var text = e.children[0].data; var lines = text.split("\r\n"); var new_lines = ''; var tabs = ""; for(var t = 0 ; t < level+1 ; t++) tabs += "\t"; for(var l = 0 ; l < lines.length ; l++) new_lines += tabs + lines[l] + "\r\n"; e.children[0].data = new_lines; } if(e.name in emptyTags) { output += " />"; if((i < dom.length - 1 && dom[i+1].type == 'tag') || (i < dom.length - 2 && dom[i+1].type == 'text' && dom[i+2].type == 'tag')); else output += "\r\n"; } else { output += ">"; if(pWithBlocks) output += "";//close the p tag here cause that's enough (only good for ACE!) var children_output = ''; if(e.children && e.children.length > 0){ if(e.children.length == 1 && (e.children[0].type == 'text' || e.children[0].type == 'comment')) { if(e.children[0].type == 'text') { var txt = e.children[0].data; if(e.name == "pre") output += trim(txt); else if(txt.length < 40) { if(txt.match(/^\s/)) output += ' '; output += trim(txt); if(txt.match(/\s$/)) output += ' '; } else { if(txt.match(/^\s/)) {//if the text already has spaces before it, then ok, add tabs, etc output += "\r\n"; for(var t = 0 ; t < level+1 ; t++) output += "\t"; } output += trim(txt); if(txt.match(/\s$/)) {//if the text already has spaces after it, then ok, add tabs, etc output += "\r\n"; for(var t = 0 ; t < level ; t++) output += "\t"; } } } else { if( keep_help || e.children[0].data.indexOf('section:') == -1 ) { output += ""; } } } else { children_output = pretty_print(e.children , !pWithBlocks ? level + 1 : level , e , i); output += children_output; for(var t = 0 ; t < level ; t++) output += "\t"; } } if(!pWithBlocks) output += ""; if(e.type == 'script' || e.type == 'style') { if( (i < dom.length - 1 && dom[i+1].type == e.type) || (i < dom.length - 2 && dom[i+1].type == 'text' && dom[i+2].type == e.type) ); else output += "\r\n"; } else { if( i < dom.length - 1 && (dom[i+1].type == 'comment') );//don't add new line if adjacent next is a comment, we add new line after comment else if( i < dom.length - 1 && dom[i+1].type == 'text' && dom[i+1].data.match(/^\S/)) { //console.log("here"); } else if( i < dom.length - 2 && dom[i+1].type == 'text' && dom[i+2].type == 'tag' && (!dom[i+2].children || (dom[i+2].children.length == 1 && dom[i+2].children[0].type == 'text')) ) { //console.log("here"); } else output += "\r\n"; } } } else if(e.type == 'text' || e.type == 'comment') { var text = trim(e.data); if(text.length > 0) { if(e.type == 'comment' && i > 0 && dom[i-1].type == 'tag') { if( keep_help || text.indexOf('section:') == -1 ) { output += "\r\n"; } } else { if(e.type == 'comment' && ( !(keep_help || text.indexOf('section:') == -1) )) { //ignore if #section comment and help not needed continue; } if(e.type == 'text' && i == dom.length - 1 && i >= 1); else output += "\r\n"; if(e.type == 'text' && e.data.match(/^\S/) && i > 0 && dom[i-1].type == 'tag');//don't add tabs if text doesn't start with "SPACE" else for(var t = 0 ; t < level ; t++) output += "\t"; if(e.type == 'text') output += text; else { if(text.indexOf('[if !IE]') >= 0) { output += ''; } else if(text.indexOf('[if ') >= 0) { output += ""; } else if(text.indexOf('= 0) { output += ''; } else { output += ""; } } if(e.type == 'text' && i < dom.length - 1 && dom[i+1].type == 'tag'); else if(e.type == 'comment' && i < dom.length - 2 && (dom[i+2].type == 'tag' || dom[i+2].type == 'script' || dom[i+2].type == 'style')); else output += "\r\n"; } } } } return output; }