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.
		
		
		
		
		
			
		
			
				
					
					
						
							183 lines
						
					
					
						
							3.9 KiB
						
					
					
				
			
		
		
	
	
							183 lines
						
					
					
						
							3.9 KiB
						
					
					
				/* | 
						|
  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 + '-->'; | 
						|
}
 | 
						|
 |