var SINGLE_TAGS = [ 'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr' ] var ATTRIBUTE_QUOTES_REQUIRED = /[\t\n\f\r " '`=<>]/ /** Render PostHTML Tree to HTML * * @param {Array|Object} tree PostHTML Tree @param {Object} options Options * * @return {String} HTML */ function render (tree, options) { /** Options * * @type {Object} * * @prop {Array<String|RegExp>} singleTags Custom single tags (selfClosing) * @prop {String} closingSingleTag Closing format for single tag @prop * @prop {Boolean} quoteAllAttributes If all attributes should be quoted. * Otherwise attributes will be unquoted when allowed. * * Formats: * * ``` tag: `<br></br>` ```, slash: `<br />` ```, ```default: `<br>` ``` */ options = options || {} var singleTags = options.singleTags ? SINGLE_TAGS.concat(options.singleTags) : SINGLE_TAGS var singleRegExp = singleTags.filter(function (tag) { return tag instanceof RegExp }) var closingSingleTag = options.closingSingleTag var quoteAllAttributes = options.quoteAllAttributes if (typeof quoteAllAttributes === 'undefined') { quoteAllAttributes = true } return html(tree) /** @private */ function isSingleTag (tag) { if (singleRegExp.length !== 0) { for (var i = 0; i < singleRegExp.length; i++) { return singleRegExp[i].test(tag) } } if (singleTags.indexOf(tag) === -1) { return false } return true } /** @private */ function attrs (obj) { var attr = '' for (var key in obj) { if (typeof obj[key] === 'string') { if (quoteAllAttributes || obj[key].match(ATTRIBUTE_QUOTES_REQUIRED)) { attr += ' ' + key + '="' + obj[key].replace(/"/g, '"') + '"' } else if (obj[key] === '') { attr += ' ' + key } else { attr += ' ' + key + '=' + obj[key] } } else if (obj[key] === true) { attr += ' ' + key } else if (typeof obj[key] === 'number') { attr += ' ' + key + '="' + obj[key] + '"' } } return attr } /** @private */ function traverse (tree, cb) { if (tree !== undefined) { for (var i = 0, length = tree.length; i < length; i++) { traverse(cb(tree[i]), cb) } } } /** * HTML Stringifier * * @param {Array|Object} tree PostHTML Tree * * @return {String} result HTML */ function html (tree) { var result = '' if (!Array.isArray(tree)) { tree = [tree] } traverse(tree, function (node) { // undefined, null, '', [], NaN if (node === undefined || node === null || node === false || node.length === 0 || Number.isNaN(node)) { return } // treat as new root tree if node is an array if (Array.isArray(node)) { result += html(node) return } if (typeof node === 'string' || typeof node === 'number') { result += node return } // skip node if (node.tag === false) { result += html(node.content) return } var tag = node.tag || 'div' result += '<' + tag if (node.attrs) { result += attrs(node.attrs) } if (isSingleTag(tag)) { switch (closingSingleTag) { case 'tag': result += '></' + tag + '>' break case 'slash': result += ' />' break default: result += '>' } result += html(node.content) } else { result += '>' + html(node.content) + '</' + tag + '>' } }) return result } } /** * @module posthtml-render * * @version 1.1.5 * @license MIT */ module.exports = render