You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
			
				
					184 lines
				
				3.9 KiB
			
		
		
			
		
	
	
					184 lines
				
				3.9 KiB
			| 
								 
											4 years ago
										 
									 | 
							
								/*
							 | 
						||
| 
								 | 
							
								  Module dependencies
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								var ElementType = require('domelementtype');
							 | 
						||
| 
								 | 
							
								var entities = require('entities');
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* mixed-case SVG and MathML tags & attributes
							 | 
						||
| 
								 | 
							
								   recognized by the HTML parser, see
							 | 
						||
| 
								 | 
							
								   https://html.spec.whatwg.org/multipage/parsing.html#parsing-main-inforeign
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								var foreignNames = require('./foreignNames.json');
							 | 
						||
| 
								 | 
							
								foreignNames.elementNames.__proto__ = null; /* use as a simple dictionary */
							 | 
						||
| 
								 | 
							
								foreignNames.attributeNames.__proto__ = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								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 = '';
							 | 
						||
| 
								 | 
							
								  var value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // Loop through the attributes
							 | 
						||
| 
								 | 
							
								  for (var key in attributes) {
							 | 
						||
| 
								 | 
							
								    value = attributes[key];
							 | 
						||
| 
								 | 
							
								    if (output) {
							 | 
						||
| 
								 | 
							
								      output += ' ';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (opts.xmlMode === 'foreign') {
							 | 
						||
| 
								 | 
							
								      /* fix up mixed-case attribute names */
							 | 
						||
| 
								 | 
							
								      key = foreignNames.attributeNames[key] || key;
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    output += key;
							 | 
						||
| 
								 | 
							
								    if ((value !== null && value !== '') || opts.xmlMode) {
							 | 
						||
| 
								 | 
							
								      output +=
							 | 
						||
| 
								 | 
							
								        '="' +
							 | 
						||
| 
								 | 
							
								        (opts.decodeEntities
							 | 
						||
| 
								 | 
							
								          ? entities.encodeXML(value)
							 | 
						||
| 
								 | 
							
								          : value.replace(/\"/g, '"')) +
							 | 
						||
| 
								 | 
							
								        '"';
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  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;
							 | 
						||
| 
								 | 
							
								});
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								var foreignModeIntegrationPoints = [
							 | 
						||
| 
								 | 
							
								  'mi',
							 | 
						||
| 
								 | 
							
								  'mo',
							 | 
						||
| 
								 | 
							
								  'mn',
							 | 
						||
| 
								 | 
							
								  'ms',
							 | 
						||
| 
								 | 
							
								  'mtext',
							 | 
						||
| 
								 | 
							
								  'annotation-xml',
							 | 
						||
| 
								 | 
							
								  'foreignObject',
							 | 
						||
| 
								 | 
							
								  'desc',
							 | 
						||
| 
								 | 
							
								  'title'
							 | 
						||
| 
								 | 
							
								];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								function renderTag(elem, opts) {
							 | 
						||
| 
								 | 
							
								  // Handle SVG / MathML in HTML
							 | 
						||
| 
								 | 
							
								  if (opts.xmlMode === 'foreign') {
							 | 
						||
| 
								 | 
							
								    /* fix up mixed-case element names */
							 | 
						||
| 
								 | 
							
								    elem.name = foreignNames.elementNames[elem.name] || elem.name;
							 | 
						||
| 
								 | 
							
								    /* exit foreign mode at integration points */
							 | 
						||
| 
								 | 
							
								    if (
							 | 
						||
| 
								 | 
							
								      elem.parent &&
							 | 
						||
| 
								 | 
							
								      foreignModeIntegrationPoints.indexOf(elem.parent.name) >= 0
							 | 
						||
| 
								 | 
							
								    )
							 | 
						||
| 
								 | 
							
								      opts = Object.assign({}, opts, { xmlMode: false });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  if (!opts.xmlMode && ['svg', 'math'].indexOf(elem.name) >= 0) {
							 | 
						||
| 
								 | 
							
								    opts = Object.assign({}, opts, { xmlMode: 'foreign' });
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  var tag = '<' + elem.name;
							 | 
						||
| 
								 | 
							
								  var 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 + '-->';
							 | 
						||
| 
								 | 
							
								}
							 |