/* Original C version https://github.com/jgm/peg-markdown/ * Copyright 2008 John MacFarlane (jgm at berkeley dot edu). * * Modifications and translation from C into Go * based on markdown_output.c * Copyright 2010 Michael Teichgräber (mt at wmipf dot de) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License or the MIT * license. See LICENSE for details. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ package markdown // HTML output functions import ( "os" "fmt" "log" "rand" "strings" ) type Writer interface { WriteString(string) (int, os.Error) WriteRune(int) (int, os.Error) WriteByte(byte) os.Error } type htmlOut struct { Writer padded int notenum int endNotes []*element /* List of endnotes to print after main content. */ } // WriteHtml prints a document tree in HTML format using the specified Writer. // func (d *Doc) WriteHtml(w Writer) int { out := new(htmlOut) out.Writer = w out.padded = 2 out.elist(d.tree, false) if len(out.endNotes) != 0 { out.pad(2) out.printEndnotes() } out.WriteByte('\n') return 0 } // pad - add newlines if needed func (h *htmlOut) pad(n int) *htmlOut { for ; n > h.padded; n-- { h.WriteByte('\n') } h.padded = n return h } func (h *htmlOut) pset(n int) *htmlOut { h.padded = n return h } // print a string func (w *htmlOut) s(s string) *htmlOut { w.WriteString(s) return w } /* print string, escaping for HTML * If obfuscate selected, convert characters to hex or decimal entities at random */ func (w *htmlOut) str(s string, obfuscate bool) *htmlOut { var ws string var i0 = 0 for i, r := range s { switch r { case '&': ws = "&" case '<': ws = "<" case '>': ws = ">" case '"': ws = """ default: if obfuscate { if rand.Intn(1) == 0 { ws = fmt.Sprintf("&#%d;", r) } else { ws = fmt.Sprintf("&#%x;", r) } } else { if i0 == -1 { i0 = i } continue } } if i0 != -1 { w.WriteString(s[i0:i]) i0 = -1 } w.WriteString(ws) } if i0 != -1 { w.WriteString(s[i0:]) } return w } /* print a list of elements */ func (w *htmlOut) elist(list *element, obfuscate bool) *htmlOut { for list != nil { w.elem(list, obfuscate) list = list.next } return w } // print an element func (w *htmlOut) elem(elt *element, obfuscate bool) *htmlOut { var s string switch elt.key { case SPACE: s = elt.contents.str case LINEBREAK: s = "
\n" case STR: w.str(elt.contents.str, obfuscate) case ELLIPSIS: s = "…" case EMDASH: s = "—" case ENDASH: s = "–" case APOSTROPHE: s = "’" case SINGLEQUOTED: w.s("‘").elist(elt.children, obfuscate).s("’") case DOUBLEQUOTED: w.s("“").elist(elt.children, obfuscate).s("”") case CODE: w.s("").str(elt.contents.str, obfuscate).s("") case HTML: s = elt.contents.str case LINK: if strings.Index(elt.contents.link.url, "mailto:") == 0 { obfuscate = true /* obfuscate mailto: links */ } w.s(` 0 { w.s(` title="`).str(elt.contents.link.title, obfuscate).s(`"`) } w.s(">").elist(elt.contents.link.label, obfuscate).s("") case IMAGE: w.s(``)
		w.elist(elt.contents.link.label, obfuscate).s(` 0 { w.s(` title="`).str(elt.contents.link.title, obfuscate).s(`"`) } w.s(" />") case EMPH: w.s("").elist(elt.children, obfuscate).s("") case STRONG: w.s("").elist(elt.children, obfuscate).s("") case LIST: w.elist(elt.children, obfuscate) case RAW: /* Shouldn't occur - these are handled by process_raw_blocks() */ log.Exitf("RAW") case H1, H2, H3, H4, H5, H6: h := "h" + string('1'+elt.key-H1) + ">" /* assumes H1 ... H6 are in order */ w.pad(2).s("<").s(h).elist(elt.children, obfuscate).s("").elist(elt.children, obfuscate).s("

").pset(0) case HRULE: w.pad(2).s("
").pset(0) case HTMLBLOCK: w.pad(2).s(elt.contents.str).pset(0) case VERBATIM: w.pad(2).s("
").str(elt.contents.str, obfuscate).s("
").pset(0) case BULLETLIST: w.pad(2).s("").pset(0) case ORDEREDLIST: w.pad(2).s("
    ").pset(0).elist(elt.children, obfuscate).pad(1).s("
").pset(0) case DEFINITIONLIST: w.pad(2).s("
").pset(0).elist(elt.children, obfuscate).pad(1).s("
").pset(0) case DEFTITLE: w.pad(1).s("
").pset(2).elist(elt.children, obfuscate).s("
").pset(0) case DEFDATA: w.pad(1).s("
").pset(2).elist(elt.children, obfuscate).s("
").pset(0) case LISTITEM: w.pad(1).s("
  • ").pset(2).elist(elt.children, obfuscate).s("
  • ").pset(0) case BLOCKQUOTE: w.pad(2).s("
    \n").pset(2).elist(elt.children, obfuscate).pad(1).s("
    ").pset(0) case REFERENCE: /* Nonprinting */ case NOTE: /* if contents.str == 0, then print note; else ignore, since this * is a note block that has been incorporated into the notes list */ if elt.contents.str == "" { w.endNotes = append(w.endNotes, elt) /* add an endnote to global endnotes list */ w.notenum++ nn := w.notenum s = fmt.Sprintf(`[%d]`, nn, nn, nn, nn) } default: log.Exitf("htmlOut.elem encountered unknown element key = %d\n", elt.key) } if s != "" { w.s(s) } return w } func (w *htmlOut) printEndnotes() { counter := 0 w.s("
    \n
      ") for _, elt := range w.endNotes { counter++ w.pad(1).s(fmt.Sprintf("
    1. \n", counter)).pset(2) w.elist(elt.children, false) w.s(fmt.Sprintf(" [back]", counter)) w.pad(1).s("
    2. ") } w.pad(1).s("
    ") }