/** * WordPress dependencies */ import { escapeEditableHTML, escapeAttribute, isValidAttributeName, } from '@wordpress/escape-html'; /** * Internal dependencies */ import { toTree } from './to-tree'; /** @typedef {import('./types').RichTextValue} RichTextValue */ /** * Create an HTML string from a Rich Text value. * * @param {Object} $1 Named arguments. * @param {RichTextValue} $1.value Rich text value. * @param {boolean} [$1.preserveWhiteSpace] Preserves newlines if true. * * @return {string} HTML string. */ export function toHTMLString( { value, preserveWhiteSpace }: { value: RichTextValue, preserveWhiteSpace?: boolean } ): string { const tree = toTree({ value, preserveWhiteSpace, createEmpty, append, getLastChild, getParent, isText, getText, remove, appendText, }); return createChildrenHTML( tree.children ); } function createEmpty() { return {}; } function getLastChild( { children } ) { return children && children[ children.length - 1 ]; } function append( parent, object ) { if ( typeof object === 'string' ) { object = { text: object }; } object.parent = parent; parent.children = parent.children || []; parent.children.push( object ); return object; } function appendText( object, text ) { object.text += text; } function getParent( { parent } ) { return parent; } function isText( { text } ) { return typeof text === 'string'; } function getText( { text } ) { return text; } function remove( object ) { const index = object.parent.children.indexOf( object ); if ( index !== -1 ) { object.parent.children.splice( index, 1 ); } return object; } function createElementHTML( { type, attributes, object, children } ) { let attributeString = ''; for ( const key in attributes ) { if ( ! isValidAttributeName( key ) ) { continue; } attributeString += ` ${ key }="${ escapeAttribute( attributes[ key ] ) }"`; } if ( object ) { return `<${ type }${ attributeString }>`; } return `<${ type }${ attributeString }>${ createChildrenHTML( children ) }`; } function createChildrenHTML( children = [] ) { return children .map( ( child ) => { if ( child.html !== undefined ) { return child.html; } return child.text === undefined ? createElementHTML( child ) : escapeEditableHTML( child.text ); } ) .join( '' ); }