/**
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 += ""+e.name+">";//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 += ""+e.name+">";
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;
}