149 lines
2.8 KiB
JavaScript
149 lines
2.8 KiB
JavaScript
|
/*
|
||
|
Module dependencies
|
||
|
*/
|
||
|
var ElementType = require('domelementtype');
|
||
|
var entities = require('entities');
|
||
|
|
||
|
var unencodedElements = {
|
||
|
__proto__: null,
|
||
|
style: true,
|
||
|
script: true,
|
||
|
xmp: true,
|
||
|
iframe: true,
|
||
|
noembed: true,
|
||
|
noframes: true,
|
||
|
plaintext: true,
|
||
|
noscript: true
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
Format attributes
|
||
|
*/
|
||
|
function formatAttrs(attributes, opts) {
|
||
|
if (!attributes) return;
|
||
|
|
||
|
var output = '',
|
||
|
value;
|
||
|
|
||
|
// Loop through the attributes
|
||
|
for (var key in attributes) {
|
||
|
value = attributes[key];
|
||
|
if (output) {
|
||
|
output += ' ';
|
||
|
}
|
||
|
|
||
|
output += key;
|
||
|
if ((value !== null && value !== '') || opts.xmlMode) {
|
||
|
output += '="' + (opts.decodeEntities ? entities.encodeXML(value) : value) + '"';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return output;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
Self-enclosing tags (stolen from node-htmlparser)
|
||
|
*/
|
||
|
var singleTag = {
|
||
|
__proto__: null,
|
||
|
area: true,
|
||
|
base: true,
|
||
|
basefont: true,
|
||
|
br: true,
|
||
|
col: true,
|
||
|
command: true,
|
||
|
embed: true,
|
||
|
frame: true,
|
||
|
hr: true,
|
||
|
img: true,
|
||
|
input: true,
|
||
|
isindex: true,
|
||
|
keygen: true,
|
||
|
link: true,
|
||
|
meta: true,
|
||
|
param: true,
|
||
|
source: true,
|
||
|
track: true,
|
||
|
wbr: true,
|
||
|
};
|
||
|
|
||
|
|
||
|
var render = module.exports = function(dom, opts) {
|
||
|
if (!Array.isArray(dom) && !dom.cheerio) dom = [dom];
|
||
|
opts = opts || {};
|
||
|
|
||
|
var output = '';
|
||
|
|
||
|
for(var i = 0; i < dom.length; i++){
|
||
|
var elem = dom[i];
|
||
|
|
||
|
if (elem.type === 'root')
|
||
|
output += render(elem.children, opts);
|
||
|
else if (ElementType.isTag(elem))
|
||
|
output += renderTag(elem, opts);
|
||
|
else if (elem.type === ElementType.Directive)
|
||
|
output += renderDirective(elem);
|
||
|
else if (elem.type === ElementType.Comment)
|
||
|
output += renderComment(elem);
|
||
|
else if (elem.type === ElementType.CDATA)
|
||
|
output += renderCdata(elem);
|
||
|
else
|
||
|
output += renderText(elem, opts);
|
||
|
}
|
||
|
|
||
|
return output;
|
||
|
};
|
||
|
|
||
|
function renderTag(elem, opts) {
|
||
|
// Handle SVG
|
||
|
if (elem.name === "svg") opts = {decodeEntities: opts.decodeEntities, xmlMode: true};
|
||
|
|
||
|
var tag = '<' + elem.name,
|
||
|
attribs = formatAttrs(elem.attribs, opts);
|
||
|
|
||
|
if (attribs) {
|
||
|
tag += ' ' + attribs;
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
opts.xmlMode
|
||
|
&& (!elem.children || elem.children.length === 0)
|
||
|
) {
|
||
|
tag += '/>';
|
||
|
} else {
|
||
|
tag += '>';
|
||
|
if (elem.children) {
|
||
|
tag += render(elem.children, opts);
|
||
|
}
|
||
|
|
||
|
if (!singleTag[elem.name] || opts.xmlMode) {
|
||
|
tag += '</' + elem.name + '>';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return tag;
|
||
|
}
|
||
|
|
||
|
function renderDirective(elem) {
|
||
|
return '<' + elem.data + '>';
|
||
|
}
|
||
|
|
||
|
function renderText(elem, opts) {
|
||
|
var data = elem.data || '';
|
||
|
|
||
|
// if entities weren't decoded, no need to encode them back
|
||
|
if (opts.decodeEntities && !(elem.parent && elem.parent.name in unencodedElements)) {
|
||
|
data = entities.encodeXML(data);
|
||
|
}
|
||
|
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
function renderCdata(elem) {
|
||
|
return '<![CDATA[' + elem.children[0].data + ']]>';
|
||
|
}
|
||
|
|
||
|
function renderComment(elem) {
|
||
|
return '<!--' + elem.data + '-->';
|
||
|
}
|