The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
var ElementType = require("domelementtype"),
    isTag = ElementType.isTag;

module.exports = {
	getInnerHTML: getInnerHTML,
	getOuterHTML: getOuterHTML,
	getText: getText
};

function getInnerHTML(elem){
	return elem.children ? elem.children.map(getOuterHTML).join("") : "";
}

//boolean attributes without a value (taken from MatthewMueller/cheerio)
var booleanAttribs = {
	__proto__: null,
	async: true,
	autofocus: true,
	autoplay: true,
	checked: true,
	controls: true,
	defer: true,
	disabled: true,
	hidden: true,
	loop: true,
	multiple: true,
	open: true,
	readonly: true,
	required: true,
	scoped: true,
	selected: true
};

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 getOuterHTML(elem){
	switch(elem.type){
	case ElementType.Text:
		return elem.data;
	case ElementType.Comment:
		return "<!--" + elem.data + "-->";
	case ElementType.Directive:
		return "<" + elem.data + ">";
	case ElementType.CDATA:
		return "<!CDATA " + getInnerHTML(elem) + "]]>";
	}

	var ret = "<" + elem.name;
	if("attribs" in elem){
		for(var attr in elem.attribs){
			if(elem.attribs.hasOwnProperty(attr)){
				ret += " " + attr;
				var value = elem.attribs[attr];
				if(value == null){
					if( !(attr in booleanAttribs) ){
						ret += "=\"\"";
					}
				} else {
					ret += "=\"" + value + "\"";
				}
			}
		}
	}

	if (elem.name in emptyTags && elem.children.length === 0) {
		return ret + " />";
	} else {
		return ret + ">" + getInnerHTML(elem) + "</" + elem.name + ">";
	}
}

function getText(elem){
	if(Array.isArray(elem)) return elem.map(getText).join("");
	if(isTag(elem) || elem.type === ElementType.CDATA) return getText(elem.children);
	if(elem.type === ElementType.Text) return elem.data;
	return "";
}