enum TagType { Paragraph , Header1 , Header2 , Header3 , HorizontalRule } class TagTypeToHtml { private readonly tagType : Map < TagType , string > = new Map < TagType , string > ( ) ; constructor ( ) { this . tagType . set ( TagType . Header1 , "h1" ) ; this . tagType . set ( TagType . Header2 , "h2" ) ; this . tagType . set ( TagType . Header3 , "h3" ) ; this . tagType . set ( TagType . Paragraph , "p" ) ; this . tagType . set ( TagType . HorizontalRule , "hr" ) ; } public OpeningTag ( tagType ) { return this . GetTag ( tagType , `<` ) ; } public ClosingTag ( tagType ) { return this . GetTag ( tagType , `</` ) ; } private GetTag ( tagType , openingTagPattern ) { let tag = this . tagType . get ( tagType ) ; if ( tag !== null ) { return `${ openingTagPattern }${ tag }>` ; } return `${ openingTagPattern }p>` ; } } interface IMarkdownDocument { Add ( ... content ) : void ; Get ( ) : string ; } class MarkdownDocument implements IMarkdownDocument { private content : string = "" ; Add ( ... content ) { content . forEach ( element => { this . content += element ; } ) ; } Get ( ) { return this . content ; } } class LineParser { public Parse ( value , tag ) { let output = [ false , "" ] ; output [ 1 ] = value ; if ( value === "" ) { return output ; } let split = value . startsWith ( `${ tag }` ) ; if ( split ) { output [ 0 ] = true ; output [ 1 ] = value . substr ( tag . length ) ; } return output ; } } class ParseElement { CurrentLine : string = "" ; } interface IVisitor { Visit ( token , markdownDocument ) : void ; } interface IVisitable { Accept ( visitor , token , markdownDocument ) : void ; } class Visitable implements IVisitable { Accept ( visitor , token , markdownDocument ) { visitor . Visit ( token , markdownDocument ) ; } } abstract class VisitorBase implements IVisitor { constructor ( private readonly tagType , private readonly TagTypeToHtml ) { } Visit ( token , markdownDocument ) { markdownDocument . Add ( this . TagTypeToHtml . OpeningTag ( this . tagType ) , token . CurrentLine , this . TagTypeToHtml . ClosingTag ( this . tagType ) ) ; } } abstract class Handler < T > { protected next : Handler < T > | null = null ; public SetNext ( next ) { this . next = next ; } public HandleRequest ( request ) { if ( ! this . CanHandle ( request ) ) { if ( this . next != null ) { this . next . HandleRequest ( request ) ; } return ; } } protected abstract CanHandle ( request ) ; } class ParseChainHandler extends Handler < ParseElement > { private readonly visitable : IVisitable = new Visitable ( ) ; protected CanHandle ( request ) { let split = new LineParser ( ) . Parse ( request . CurrentLine , this . tagType ) ; if ( split [ 0 ] ) { request . CurrentLine = split [ 1 ] ; this . visitable . Accept ( this . visitor , request , this . document ) ; } return split [ 0 ] ; } constructor ( private readonly document , private readonly tagType , private readonly visitor ) { super ( ) ; } } class Header1Visitor extends VisitorBase { constructor ( ) { super ( TagType . Header1 , new TagTypeToHtml ( ) ) ; } } class Header2Visitor extends VisitorBase { constructor ( ) { super ( TagType . Header2 , new TagTypeToHtml ( ) ) ; } } class Header3Visitor extends VisitorBase { constructor ( ) { super ( TagType . Header3 , new TagTypeToHtml ( ) ) ; } } class ParagraphVisitor extends VisitorBase { constructor ( ) { super ( TagType . Paragraph , new TagTypeToHtml ( ) ) ; } } class HorizontalRuleVisitor extends VisitorBase { constructor ( ) { super ( TagType . HorizontalRule , new TagTypeToHtml ( ) ) ; } } class Header1ChainHandler extends ParseChainHandler { constructor ( document ) { super ( document , "# " , new Header1Visitor ( ) ) ; } } class Header2ChainHandler extends ParseChainHandler { constructor ( document ) { super ( document , "## " , new Header2Visitor ( ) ) ; } } class Header3ChainHandler extends ParseChainHandler { constructor ( document ) { super ( document , "### " , new Header3Visitor ( ) ) ; } } class HorizontalRuleHandler extends ParseChainHandler { constructor ( document ) { super ( document , "---" , new HorizontalRuleVisitor ( ) ) ; } } class ParagraphHandler extends Handler < ParseElement > { private readonly visitable : IVisitable = new Visitable ( ) ; private readonly visitor : IVisitor = new ParagraphVisitor ( ) ; protected CanHandle ( request ) { this . visitable . Accept ( this . visitor , request , this . document ) ; return true ; } constructor ( private readonly document ) { super ( ) ; } } class ChainOfResponsibilityFactory { Build ( document ) { let header1 = new Header1ChainHandler ( document ) ; let header2 = new Header2ChainHandler ( document ) ; let header3 = new Header3ChainHandler ( document ) ; let horizontalRule = new HorizontalRuleHandler ( document ) ; let paragraph = new ParagraphHandler ( document ) ; header1 . SetNext ( header2 ) ; header2 . SetNext ( header3 ) ; header3 . SetNext ( horizontalRule ) ; horizontalRule . SetNext ( paragraph ) ; return header1 ; } } class Markdown { public ToHtml ( text ) { let document = new MarkdownDocument ( ) ; let header1 = new ChainOfResponsibilityFactory ( ) . Build ( document ) ; let lines = text . split ( `\n` ) ; for ( let index = 0 ; index < lines . length ; index ++ ) { let parseElement = new ParseElement ( ) ; parseElement . CurrentLine = lines [ index ] ; header1 . HandleRequest ( parseElement ) ; } return document . Get ( ) ; } } class HtmlHandler { private markdownChange : Markdown = new Markdown ; public TextChangeHandler ( id , output ) { let markdown = < HTMLTextAreaElement > document . getElementById ( id ) ; let markdownOutput = < HTMLLabelElement > document . getElementById ( output ) ; if ( markdown !== null ) { markdown . onkeyup = ( e ) => { this . RenderHtmlContent ( markdown , markdownOutput ) ; } ; window . onload = ( e ) => { this . RenderHtmlContent ( markdown , markdownOutput ) ; } ; } } private RenderHtmlContent ( markdown , markdownOutput ) { if ( markdown . value ) { markdownOutput . innerHTML = this . markdownChange . ToHtml ( markdown . value ) ; } else markdownOutput . innerHTML = "<p></p>" ; } }