license import * as ml from "../ast/ast" ; export interface IVisitor { visitTag ( tag ) : any ; visitElement ( element ) : any ; visitText ( text ) : any ; visitDeclaration ( decl ) : any ; visitDoctype ( doctype ) : any ; } class Visitor implements IVisitor { visitTag ( tag ) { const strAttrs = this . _serializeAttributes ( tag . attrs ) ; if ( tag . children . length === 0 ) { return `<${ tag . name }${ strAttrs }/>` ; } const strChildren = tag . children . map ( node => node . visit ( this ) ) ; return `<${ tag . name }${ strAttrs }>${ strChildren . join ( "" ) }</${ tag . name }>` ; } visitText ( text ) { return _escapeXml ( text . value ) ; } visitElement ( element ) { const attrs = { } ; element . attrs . forEach ( ( attr ) => { attrs [ attr . name ] = attr . value ; } ) ; const tag = new Tag ( element . name , attrs , element . children as any ) ; return this . visitTag ( tag ) ; } visitDeclaration ( decl ) { return `<?xml${ this . _serializeAttributes ( decl . attrs ) } ?>` ; } private _serializeAttributes ( attrs ) { const strAttrs = Object . keys ( attrs ) . map ( ( name ) => `${ name }="${ _escapeXml ( attrs [ name ] ) }"` ) . join ( " " ) ; return strAttrs . length > 0 ? " " + strAttrs : "" ; } visitDoctype ( doctype ) { return `<!DOCTYPE ${ doctype . rootTag } [\n${ doctype . dtd }\n]>` ; } } const _visitor = new Visitor ( ) ; export function serialize ( nodes ) { return nodes . map ( ( node ) : string => node . visit ( _visitor ) ) . join ( "" ) ; } export interface Node { visit ( visitor ) : any ; } export class Declaration implements Node { constructor ( public attrs ) { } visit ( visitor ) { return visitor . visitDeclaration ( this ) ; } } export class Doctype implements Node { constructor ( public rootTag , public dtd ) { } visit ( visitor ) { return visitor . visitDoctype ( this ) ; } } export class Tag implements Node { constructor ( public name , public attrs = { } , public children = [ ] ) { } visit ( visitor ) { return visitor . visitTag ( this ) ; } } export class Text implements Node { constructor ( public value ) { } visit ( visitor ) { return visitor . visitText ( this ) ; } } export class CR extends Text { constructor ( ws = 0 ) { super ( `\n${ new Array ( ws + 1 ) . join ( " " ) }` ) ; } } const _ESCAPED_CHARS = [ [ /&/g , "&amp;" ] , [ /"/g , "&quot;" ] , [ /'/g , "&apos;" ] , [ /</g , "&lt;" ] , [ />/g , "&gt;" ] ] ; function _escapeXml ( text ) { return _ESCAPED_CHARS . reduce ( ( str , entry ) => str . replace ( entry [ 0 ] , entry [ 1 ] ) , text ) ; }