Skip to content
This repository
Newer
Older
100644 860 lines (749 sloc) 21.029 kb
965748ad »
2011-05-28 refactored into a proper package
1 //
f8f70572 »
2011-06-27 simplified BSD license
2 // Blackfriday Markdown Processor
3 // Available at http://github.com/russross/blackfriday
4 //
5 // Copyright © 2011 Russ Ross <russ@russross.com>.
fde2c606 »
2011-06-28 version number, few more options for command-line tool
6 // Distributed under the Simplified BSD License.
f8f70572 »
2011-06-27 simplified BSD license
7 // See README.md for details.
965748ad »
2011-05-28 refactored into a proper package
8 //
9
10 //
11 //
12 // HTML rendering backend
13 //
14 //
15
16 package blackfriday
17
18 import (
19 "bytes"
20 "fmt"
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
21 "regexp"
965748ad »
2011-05-28 refactored into a proper package
22 "strconv"
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
23 "strings"
965748ad »
2011-05-28 refactored into a proper package
24 )
25
530123dd »
2011-07-07 additional doc comments
26 // Html renderer configuration options.
965748ad »
2011-05-28 refactored into a proper package
27 const (
530123dd »
2011-07-07 additional doc comments
28 HTML_SKIP_HTML = 1 << iota // skip preformatted HTML blocks
29 HTML_SKIP_STYLE // skip embedded <style> elements
30 HTML_SKIP_IMAGES // skip embedded images
31 HTML_SKIP_LINKS // skip all links
2f50a53f »
2014-01-22 Rename HTML_SKIP_SCRIPT to HTML_SANITIZE_OUTPUT
32 HTML_SANITIZE_OUTPUT // strip output of everything that's not known to be safe
530123dd »
2011-07-07 additional doc comments
33 HTML_SAFELINK // only link to trusted protocols
d71c7591 »
2014-02-25 add HTML_NOFOLLOW_LINKS
34 HTML_NOFOLLOW_LINKS // only link with rel="nofollow"
530123dd »
2011-07-07 additional doc comments
35 HTML_TOC // generate a table of contents
36 HTML_OMIT_CONTENTS // skip the main contents (for a standalone table of contents)
37 HTML_COMPLETE_PAGE // generate a complete HTML page
38 HTML_GITHUB_BLOCKCODE // use github fenced code rendering rules
39 HTML_USE_XHTML // generate XHTML output instead of HTML
40 HTML_USE_SMARTYPANTS // enable smart punctuation substitutions
41 HTML_SMARTYPANTS_FRACTIONS // enable smart fractions (with HTML_USE_SMARTYPANTS)
42 HTML_SMARTYPANTS_LATEX_DASHES // enable LaTeX-style dashes (with HTML_USE_SMARTYPANTS)
965748ad »
2011-05-28 refactored into a proper package
43 )
44
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
45 var (
31a96c6c »
2014-01-26 go fmt
46 tags = []string{
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
47 "b",
48 "blockquote",
49 "code",
50 "del",
51 "dd",
52 "dl",
53 "dt",
54 "em",
55 "h1",
56 "h2",
57 "h3",
58 "h4",
59 "h5",
60 "h6",
61 "i",
62 "kbd",
63 "li",
64 "ol",
65 "p",
66 "pre",
67 "s",
68 "sup",
69 "sub",
70 "strong",
71 "strike",
72 "ul",
73 }
31a96c6c »
2014-01-26 go fmt
74 urlRe = `((https?|ftp):\/\/|\/)[-A-Za-z0-9+&@#\/%?=~_|!:,.;\(\)]+`
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
75 tagWhitelist = regexp.MustCompile(`^(<\/?(` + strings.Join(tags, "|") + `)>|<(br|hr)\s?\/?>)$`)
31a96c6c »
2014-01-26 go fmt
76 anchorClean = regexp.MustCompile(`^(<a\shref="` + urlRe + `"(\stitle="[^"<>]+")?\s?>|<\/a>)$`)
77 imgClean = regexp.MustCompile(`^(<img\ssrc="` + urlRe + `"(\swidth="\d{1,3}")?(\sheight="\d{1,3}")?(\salt="[^"<>]*")?(\stitle="[^"<>]*")?\s?\/?>)$`)
b0bdfbec »
2014-01-26 Fix bug in autolink overescaping html entities
78 // TODO: improve this regexp to catch all possible entities:
79 htmlEntity = regexp.MustCompile(`&[a-z]{2,5};`)
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
80 )
81
bb8ee591 »
2011-07-07 doc improvements, commenting
82 // Html is a type that implements the Renderer interface for HTML output.
83 //
84 // Do not create this directly, instead use the HtmlRenderer function.
3c6f18af »
2011-06-29 Renderer is now an interface
85 type Html struct {
873a60ad »
2011-06-29 complete page rendering is now an option in the library
86 flags int // HTML_* options
ea3d80e2 »
2011-06-26 clean up main markdown function: split out first and second passes
87 closeTag string // how to end singleton tags: either " />\n" or ">\n"
873a60ad »
2011-06-29 complete page rendering is now an option in the library
88 title string // document title
89 css string // optional css file url (used with HTML_COMPLETE_PAGE)
90
91 // table of contents data
55697351 »
2011-06-29 table of contents support beefed up
92 tocMarker int
873a60ad »
2011-06-29 complete page rendering is now an option in the library
93 headerCount int
94 currentLevel int
95 toc *bytes.Buffer
96
bb8ee591 »
2011-07-07 doc improvements, commenting
97 smartypants *smartypantsRenderer
965748ad »
2011-05-28 refactored into a proper package
98 }
99
3c6f18af »
2011-06-29 Renderer is now an interface
100 const (
101 xhtmlClose = " />\n"
102 htmlClose = ">\n"
103 )
965748ad »
2011-05-28 refactored into a proper package
104
bb8ee591 »
2011-07-07 doc improvements, commenting
105 // HtmlRenderer creates and configures an Html object, which
106 // satisfies the Renderer interface.
107 //
108 // flags is a set of HTML_* options ORed together.
109 // title is the title of the document, and css is a URL for the document's
110 // stylesheet.
111 // title and css are only used when HTML_COMPLETE_PAGE is selected.
3c6f18af »
2011-06-29 Renderer is now an interface
112 func HtmlRenderer(flags int, title string, css string) Renderer {
965748ad »
2011-05-28 refactored into a proper package
113 // configure the rendering engine
9d23b68f »
2011-05-30 export all names from Renderer struct
114 closeTag := htmlClose
965748ad »
2011-05-28 refactored into a proper package
115 if flags&HTML_USE_XHTML != 0 {
9d23b68f »
2011-05-30 export all names from Renderer struct
116 closeTag = xhtmlClose
965748ad »
2011-05-28 refactored into a proper package
117 }
118
3c6f18af »
2011-06-29 Renderer is now an interface
119 return &Html{
873a60ad »
2011-06-29 complete page rendering is now an option in the library
120 flags: flags,
121 closeTag: closeTag,
122 title: title,
123 css: css,
965748ad »
2011-05-28 refactored into a proper package
124
873a60ad »
2011-06-29 complete page rendering is now an option in the library
125 headerCount: 0,
126 currentLevel: 0,
55697351 »
2011-06-29 table of contents support beefed up
127 toc: new(bytes.Buffer),
965748ad »
2011-05-28 refactored into a proper package
128
bb8ee591 »
2011-07-07 doc improvements, commenting
129 smartypants: smartypants(flags),
965748ad »
2011-05-28 refactored into a proper package
130 }
131 }
132
cc0d56d0 »
2014-01-26 Extract a chain of ifs into separate func
133 // Using if statements is a bit faster than a switch statement. As the compiler
134 // improves, this should be unnecessary this is only worthwhile because
135 // attrEscape is the single largest CPU user in normal use.
136 // Also tried using map, but that gave a ~3x slowdown.
137 func escapeSingleChar(char byte) (string, bool) {
138 if char == '"' {
139 return "&quot;", true
140 }
141 if char == '&' {
142 return "&amp;", true
143 }
144 if char == '<' {
145 return "&lt;", true
146 }
147 if char == '>' {
148 return "&gt;", true
149 }
150 return "", false
151 }
152
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
153 func attrEscape(out *bytes.Buffer, src []byte) {
44db7217 »
2011-06-24 rewrite of attrEscape: cleaner and faster
154 org := 0
155 for i, ch := range src {
cc0d56d0 »
2014-01-26 Extract a chain of ifs into separate func
156 if entity, ok := escapeSingleChar(ch); ok {
44db7217 »
2011-06-24 rewrite of attrEscape: cleaner and faster
157 if i > org {
158 // copy all the normal characters since the last escape
159 out.Write(src[org:i])
160 }
161 org = i + 1
cc0d56d0 »
2014-01-26 Extract a chain of ifs into separate func
162 out.WriteString(entity)
965748ad »
2011-05-28 refactored into a proper package
163 }
164 }
44db7217 »
2011-06-24 rewrite of attrEscape: cleaner and faster
165 if org < len(src) {
166 out.Write(src[org:])
167 }
965748ad »
2011-05-28 refactored into a proper package
168 }
169
b0bdfbec »
2014-01-26 Fix bug in autolink overescaping html entities
170 func entityEscapeWithSkip(out *bytes.Buffer, src []byte, skipRanges [][]int) {
171 end := 0
172 for _, rang := range skipRanges {
173 attrEscape(out, src[end:rang[0]])
174 out.Write(src[rang[0]:rang[1]])
175 end = rang[1]
176 }
177 attrEscape(out, src[end:])
178 }
179
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
180 func (options *Html) GetFlags() int {
181 return options.flags
182 }
183
2dff0864 »
2014-04-05 Add header ID support and tests: # Header {#myid}
184 func (options *Html) Header(out *bytes.Buffer, text func() bool, level int, id string) {
cf97fbd8 »
2011-06-25 experiment: render headers directly to output buffer to avoid a copy;…
185 marker := out.Len()
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
186 doubleSpace(out)
965748ad »
2011-05-28 refactored into a proper package
187
2dff0864 »
2014-04-05 Add header ID support and tests: # Header {#myid}
188 if id != "" {
189 out.WriteString(fmt.Sprintf("<h%d id=\"%s\">", level, id))
190 } else if options.flags&HTML_TOC != 0 {
55697351 »
2011-06-29 table of contents support beefed up
191 // headerCount is incremented in htmlTocHeader
873a60ad »
2011-06-29 complete page rendering is now an option in the library
192 out.WriteString(fmt.Sprintf("<h%d id=\"toc_%d\">", level, options.headerCount))
965748ad »
2011-05-28 refactored into a proper package
193 } else {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
194 out.WriteString(fmt.Sprintf("<h%d>", level))
965748ad »
2011-05-28 refactored into a proper package
195 }
196
55697351 »
2011-06-29 table of contents support beefed up
197 tocMarker := out.Len()
cf97fbd8 »
2011-06-25 experiment: render headers directly to output buffer to avoid a copy;…
198 if !text() {
199 out.Truncate(marker)
200 return
201 }
873a60ad »
2011-06-29 complete page rendering is now an option in the library
202
203 // are we building a table of contents?
204 if options.flags&HTML_TOC != 0 {
3c6f18af »
2011-06-29 Renderer is now an interface
205 options.TocHeader(out.Bytes()[tocMarker:], level)
873a60ad »
2011-06-29 complete page rendering is now an option in the library
206 }
207
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
208 out.WriteString(fmt.Sprintf("</h%d>\n", level))
965748ad »
2011-05-28 refactored into a proper package
209 }
210
3c6f18af »
2011-06-29 Renderer is now an interface
211 func (options *Html) BlockHtml(out *bytes.Buffer, text []byte) {
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
212 if options.flags&HTML_SKIP_HTML != 0 {
213 return
214 }
215
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
216 doubleSpace(out)
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
217 out.Write(text)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
218 out.WriteByte('\n')
965748ad »
2011-05-28 refactored into a proper package
219 }
220
3c6f18af »
2011-06-29 Renderer is now an interface
221 func (options *Html) HRule(out *bytes.Buffer) {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
222 doubleSpace(out)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
223 out.WriteString("<hr")
9d23b68f »
2011-05-30 export all names from Renderer struct
224 out.WriteString(options.closeTag)
965748ad »
2011-05-28 refactored into a proper package
225 }
226
3c6f18af »
2011-06-29 Renderer is now an interface
227 func (options *Html) BlockCode(out *bytes.Buffer, text []byte, lang string) {
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
228 if options.flags&HTML_GITHUB_BLOCKCODE != 0 {
3c6f18af »
2011-06-29 Renderer is now an interface
229 options.BlockCodeGithub(out, text, lang)
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
230 } else {
3c6f18af »
2011-06-29 Renderer is now an interface
231 options.BlockCodeNormal(out, text, lang)
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
232 }
233 }
234
3c6f18af »
2011-06-29 Renderer is now an interface
235 func (options *Html) BlockCodeNormal(out *bytes.Buffer, text []byte, lang string) {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
236 doubleSpace(out)
965748ad »
2011-05-28 refactored into a proper package
237
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
238 // parse out the language names/classes
239 count := 0
240 for _, elt := range strings.Fields(lang) {
241 if elt[0] == '.' {
242 elt = elt[1:]
965748ad »
2011-05-28 refactored into a proper package
243 }
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
244 if len(elt) == 0 {
245 continue
246 }
247 if count == 0 {
248 out.WriteString("<pre><code class=\"")
249 } else {
250 out.WriteByte(' ')
251 }
252 attrEscape(out, []byte(elt))
253 count++
965748ad »
2011-05-28 refactored into a proper package
254 }
255
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
256 if count == 0 {
257 out.WriteString("<pre><code>")
258 } else {
259 out.WriteString("\">")
965748ad »
2011-05-28 refactored into a proper package
260 }
261
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
262 attrEscape(out, text)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
263 out.WriteString("</code></pre>\n")
965748ad »
2011-05-28 refactored into a proper package
264 }
265
d0d85495 »
2012-11-22 Fix up method documentation formatting.
266 // GitHub style code block:
267 //
268 // <pre lang="LANG"><code>
269 // ...
a25d9a54 »
2012-11-22 Fix html tag ordering in doc string.
270 // </code></pre>
d0d85495 »
2012-11-22 Fix up method documentation formatting.
271 //
272 // Unlike other parsers, we store the language identifier in the <pre>,
273 // and don't let the user generate custom classes.
274 //
275 // The language identifier in the <pre> block gets postprocessed and all
276 // the code inside gets syntax highlighted with Pygments. This is much safer
277 // than letting the user specify a CSS class for highlighting.
278 //
279 // Note that we only generate HTML for the first specifier.
280 // E.g.
281 // ~~~~ {.python .numbered} => <pre lang="python"><code>
3c6f18af »
2011-06-29 Renderer is now an interface
282 func (options *Html) BlockCodeGithub(out *bytes.Buffer, text []byte, lang string) {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
283 doubleSpace(out)
965748ad »
2011-05-28 refactored into a proper package
284
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
285 // parse out the language name
286 count := 0
287 for _, elt := range strings.Fields(lang) {
288 if elt[0] == '.' {
289 elt = elt[1:]
965748ad »
2011-05-28 refactored into a proper package
290 }
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
291 if len(elt) == 0 {
292 continue
965748ad »
2011-05-28 refactored into a proper package
293 }
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
294 out.WriteString("<pre lang=\"")
295 attrEscape(out, []byte(elt))
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
296 out.WriteString("\"><code>")
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
297 count++
298 break
965748ad »
2011-05-28 refactored into a proper package
299 }
300
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
301 if count == 0 {
302 out.WriteString("<pre><code>")
965748ad »
2011-05-28 refactored into a proper package
303 }
304
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
305 attrEscape(out, text)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
306 out.WriteString("</code></pre>\n")
965748ad »
2011-05-28 refactored into a proper package
307 }
308
3c6f18af »
2011-06-29 Renderer is now an interface
309 func (options *Html) BlockQuote(out *bytes.Buffer, text []byte) {
689f6cb7 »
2011-07-01 more consistent spacing of block-level elements
310 doubleSpace(out)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
311 out.WriteString("<blockquote>\n")
312 out.Write(text)
689f6cb7 »
2011-07-01 more consistent spacing of block-level elements
313 out.WriteString("</blockquote>\n")
965748ad »
2011-05-28 refactored into a proper package
314 }
315
3c6f18af »
2011-06-29 Renderer is now an interface
316 func (options *Html) Table(out *bytes.Buffer, header []byte, body []byte, columnData []int) {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
317 doubleSpace(out)
318 out.WriteString("<table>\n<thead>\n")
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
319 out.Write(header)
bd60e369 »
2011-07-01 removing more redundant checks, additional cleanup of block parsing
320 out.WriteString("</thead>\n\n<tbody>\n")
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
321 out.Write(body)
bd60e369 »
2011-07-01 removing more redundant checks, additional cleanup of block parsing
322 out.WriteString("</tbody>\n</table>\n")
965748ad »
2011-05-28 refactored into a proper package
323 }
324
3c6f18af »
2011-06-29 Renderer is now an interface
325 func (options *Html) TableRow(out *bytes.Buffer, text []byte) {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
326 doubleSpace(out)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
327 out.WriteString("<tr>\n")
328 out.Write(text)
bd60e369 »
2011-07-01 removing more redundant checks, additional cleanup of block parsing
329 out.WriteString("\n</tr>\n")
965748ad »
2011-05-28 refactored into a proper package
330 }
331
6e6572e9 »
2013-10-16 Added th to table headers so that styling with things like Twitter Bo…
332 func (options *Html) TableHeaderCell(out *bytes.Buffer, text []byte, align int) {
333 doubleSpace(out)
334 switch align {
335 case TABLE_ALIGNMENT_LEFT:
336 out.WriteString("<th align=\"left\">")
337 case TABLE_ALIGNMENT_RIGHT:
338 out.WriteString("<th align=\"right\">")
339 case TABLE_ALIGNMENT_CENTER:
340 out.WriteString("<th align=\"center\">")
341 default:
342 out.WriteString("<th>")
343 }
344
345 out.Write(text)
346 out.WriteString("</th>")
347 }
348
3c6f18af »
2011-06-29 Renderer is now an interface
349 func (options *Html) TableCell(out *bytes.Buffer, text []byte, align int) {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
350 doubleSpace(out)
965748ad »
2011-05-28 refactored into a proper package
351 switch align {
352 case TABLE_ALIGNMENT_LEFT:
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
353 out.WriteString("<td align=\"left\">")
965748ad »
2011-05-28 refactored into a proper package
354 case TABLE_ALIGNMENT_RIGHT:
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
355 out.WriteString("<td align=\"right\">")
965748ad »
2011-05-28 refactored into a proper package
356 case TABLE_ALIGNMENT_CENTER:
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
357 out.WriteString("<td align=\"center\">")
965748ad »
2011-05-28 refactored into a proper package
358 default:
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
359 out.WriteString("<td>")
965748ad »
2011-05-28 refactored into a proper package
360 }
361
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
362 out.Write(text)
363 out.WriteString("</td>")
965748ad »
2011-05-28 refactored into a proper package
364 }
365
7bdb82c5 »
2013-06-26 new tests pass but old tests now fail...
366 func (options *Html) Footnotes(out *bytes.Buffer, text func() bool) {
be082a1e »
2013-06-25 First attempt at supporting Pandoc-style footnotes. The existing test…
367 out.WriteString("<div class=\"footnotes\">\n")
368 options.HRule(out)
7bdb82c5 »
2013-06-26 new tests pass but old tests now fail...
369 options.List(out, text, LIST_TYPE_ORDERED)
be082a1e »
2013-06-25 First attempt at supporting Pandoc-style footnotes. The existing test…
370 out.WriteString("</div>\n")
371 }
372
7bdb82c5 »
2013-06-26 new tests pass but old tests now fail...
373 func (options *Html) FootnoteItem(out *bytes.Buffer, name, text []byte, flags int) {
374 if flags&LIST_ITEM_CONTAINS_BLOCK != 0 || flags&LIST_ITEM_BEGINNING_OF_LIST != 0 {
375 doubleSpace(out)
376 }
c23099e5 »
2013-07-01 Implementation and some tests for inline footnotes. Also I noticed th…
377 out.WriteString(`<li id="fn:`)
7bdb82c5 »
2013-06-26 new tests pass but old tests now fail...
378 out.Write(slugify(name))
379 out.WriteString(`">`)
380 out.Write(text)
381 out.WriteString("</li>\n")
382 }
383
3c6f18af »
2011-06-29 Renderer is now an interface
384 func (options *Html) List(out *bytes.Buffer, text func() bool, flags int) {
eff64c56 »
2011-06-25 reduce copying for lists
385 marker := out.Len()
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
386 doubleSpace(out)
eff64c56 »
2011-06-25 reduce copying for lists
387
965748ad »
2011-05-28 refactored into a proper package
388 if flags&LIST_TYPE_ORDERED != 0 {
689f6cb7 »
2011-07-01 more consistent spacing of block-level elements
389 out.WriteString("<ol>")
965748ad »
2011-05-28 refactored into a proper package
390 } else {
689f6cb7 »
2011-07-01 more consistent spacing of block-level elements
391 out.WriteString("<ul>")
965748ad »
2011-05-28 refactored into a proper package
392 }
eff64c56 »
2011-06-25 reduce copying for lists
393 if !text() {
394 out.Truncate(marker)
395 return
396 }
965748ad »
2011-05-28 refactored into a proper package
397 if flags&LIST_TYPE_ORDERED != 0 {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
398 out.WriteString("</ol>\n")
965748ad »
2011-05-28 refactored into a proper package
399 } else {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
400 out.WriteString("</ul>\n")
965748ad »
2011-05-28 refactored into a proper package
401 }
402 }
403
3c6f18af »
2011-06-29 Renderer is now an interface
404 func (options *Html) ListItem(out *bytes.Buffer, text []byte, flags int) {
689f6cb7 »
2011-07-01 more consistent spacing of block-level elements
405 if flags&LIST_ITEM_CONTAINS_BLOCK != 0 || flags&LIST_ITEM_BEGINNING_OF_LIST != 0 {
406 doubleSpace(out)
407 }
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
408 out.WriteString("<li>")
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
409 out.Write(text)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
410 out.WriteString("</li>\n")
965748ad »
2011-05-28 refactored into a proper package
411 }
412
3c6f18af »
2011-06-29 Renderer is now an interface
413 func (options *Html) Paragraph(out *bytes.Buffer, text func() bool) {
e22e43bf »
2011-06-26 eliminate a buffering level for paragraphs
414 marker := out.Len()
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
415 doubleSpace(out)
965748ad »
2011-05-28 refactored into a proper package
416
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
417 out.WriteString("<p>")
e22e43bf »
2011-06-26 eliminate a buffering level for paragraphs
418 if !text() {
419 out.Truncate(marker)
420 return
421 }
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
422 out.WriteString("</p>\n")
965748ad »
2011-05-28 refactored into a proper package
423 }
424
2aca6670 »
2011-06-29 simplify inline callback interface
425 func (options *Html) AutoLink(out *bytes.Buffer, link []byte, kind int) {
b0bdfbec »
2014-01-26 Fix bug in autolink overescaping html entities
426 skipRanges := htmlEntity.FindAllIndex(link, -1)
81cefb5e »
2011-05-29 split parser into multiple files, clean up naming
427 if options.flags&HTML_SAFELINK != 0 && !isSafeLink(link) && kind != LINK_TYPE_EMAIL {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
428 // mark it but don't link it if it is not a safe link: no smartypants
429 out.WriteString("<tt>")
b0bdfbec »
2014-01-26 Fix bug in autolink overescaping html entities
430 entityEscapeWithSkip(out, link, skipRanges)
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
431 out.WriteString("</tt>")
2aca6670 »
2011-06-29 simplify inline callback interface
432 return
965748ad »
2011-05-28 refactored into a proper package
433 }
434
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
435 out.WriteString("<a href=\"")
965748ad »
2011-05-28 refactored into a proper package
436 if kind == LINK_TYPE_EMAIL {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
437 out.WriteString("mailto:")
965748ad »
2011-05-28 refactored into a proper package
438 }
b0bdfbec »
2014-01-26 Fix bug in autolink overescaping html entities
439 entityEscapeWithSkip(out, link, skipRanges)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
440 out.WriteString("\">")
965748ad »
2011-05-28 refactored into a proper package
441
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
442 // Pretty print: if we get an email address as
443 // an actual URI, e.g. `mailto:foo@bar.com`, we don't
444 // want to print the `mailto:` prefix
f3386eb8 »
2011-05-31 gofmt
445 switch {
679e1686 »
2011-05-30 performance fix: with autolinking on, it is almost twice as fast now
446 case bytes.HasPrefix(link, []byte("mailto://")):
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
447 attrEscape(out, link[len("mailto://"):])
f3386eb8 »
2011-05-31 gofmt
448 case bytes.HasPrefix(link, []byte("mailto:")):
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
449 attrEscape(out, link[len("mailto:"):])
679e1686 »
2011-05-30 performance fix: with autolinking on, it is almost twice as fast now
450 default:
b0bdfbec »
2014-01-26 Fix bug in autolink overescaping html entities
451 entityEscapeWithSkip(out, link, skipRanges)
965748ad »
2011-05-28 refactored into a proper package
452 }
453
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
454 out.WriteString("</a>")
965748ad »
2011-05-28 refactored into a proper package
455 }
456
2aca6670 »
2011-06-29 simplify inline callback interface
457 func (options *Html) CodeSpan(out *bytes.Buffer, text []byte) {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
458 out.WriteString("<code>")
459 attrEscape(out, text)
460 out.WriteString("</code>")
965748ad »
2011-05-28 refactored into a proper package
461 }
462
2aca6670 »
2011-06-29 simplify inline callback interface
463 func (options *Html) DoubleEmphasis(out *bytes.Buffer, text []byte) {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
464 out.WriteString("<strong>")
465 out.Write(text)
466 out.WriteString("</strong>")
965748ad »
2011-05-28 refactored into a proper package
467 }
468
2aca6670 »
2011-06-29 simplify inline callback interface
469 func (options *Html) Emphasis(out *bytes.Buffer, text []byte) {
965748ad »
2011-05-28 refactored into a proper package
470 if len(text) == 0 {
2aca6670 »
2011-06-29 simplify inline callback interface
471 return
965748ad »
2011-05-28 refactored into a proper package
472 }
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
473 out.WriteString("<em>")
474 out.Write(text)
475 out.WriteString("</em>")
965748ad »
2011-05-28 refactored into a proper package
476 }
477
2aca6670 »
2011-06-29 simplify inline callback interface
478 func (options *Html) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) {
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
479 if options.flags&HTML_SKIP_IMAGES != 0 {
2aca6670 »
2011-06-29 simplify inline callback interface
480 return
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
481 }
482
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
483 out.WriteString("<img src=\"")
484 attrEscape(out, link)
485 out.WriteString("\" alt=\"")
965748ad »
2011-05-28 refactored into a proper package
486 if len(alt) > 0 {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
487 attrEscape(out, alt)
965748ad »
2011-05-28 refactored into a proper package
488 }
489 if len(title) > 0 {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
490 out.WriteString("\" title=\"")
491 attrEscape(out, title)
965748ad »
2011-05-28 refactored into a proper package
492 }
493
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
494 out.WriteByte('"')
9d23b68f »
2011-05-30 export all names from Renderer struct
495 out.WriteString(options.closeTag)
2aca6670 »
2011-06-29 simplify inline callback interface
496 return
965748ad »
2011-05-28 refactored into a proper package
497 }
498
2aca6670 »
2011-06-29 simplify inline callback interface
499 func (options *Html) LineBreak(out *bytes.Buffer) {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
500 out.WriteString("<br")
9d23b68f »
2011-05-30 export all names from Renderer struct
501 out.WriteString(options.closeTag)
965748ad »
2011-05-28 refactored into a proper package
502 }
503
2aca6670 »
2011-06-29 simplify inline callback interface
504 func (options *Html) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
505 if options.flags&HTML_SKIP_LINKS != 0 {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
506 // write the link text out but don't link it, just mark it with typewriter font
507 out.WriteString("<tt>")
508 attrEscape(out, content)
509 out.WriteString("</tt>")
2aca6670 »
2011-06-29 simplify inline callback interface
510 return
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
511 }
965748ad »
2011-05-28 refactored into a proper package
512
81cefb5e »
2011-05-29 split parser into multiple files, clean up naming
513 if options.flags&HTML_SAFELINK != 0 && !isSafeLink(link) {
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
514 // write the link text out but don't link it, just mark it with typewriter font
515 out.WriteString("<tt>")
516 attrEscape(out, content)
517 out.WriteString("</tt>")
2aca6670 »
2011-06-29 simplify inline callback interface
518 return
965748ad »
2011-05-28 refactored into a proper package
519 }
520
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
521 out.WriteString("<a href=\"")
f9b03f67 »
2011-06-24 output validates, command-line tool has useful options
522 attrEscape(out, link)
965748ad »
2011-05-28 refactored into a proper package
523 if len(title) > 0 {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
524 out.WriteString("\" title=\"")
525 attrEscape(out, title)
965748ad »
2011-05-28 refactored into a proper package
526 }
d71c7591 »
2014-02-25 add HTML_NOFOLLOW_LINKS
527 if options.flags&HTML_NOFOLLOW_LINKS != 0 {
528 out.WriteString("\" rel=\"nofollow")
529 }
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
530 out.WriteString("\">")
f9b03f67 »
2011-06-24 output validates, command-line tool has useful options
531 out.Write(content)
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
532 out.WriteString("</a>")
2aca6670 »
2011-06-29 simplify inline callback interface
533 return
965748ad »
2011-05-28 refactored into a proper package
534 }
535
2aca6670 »
2011-06-29 simplify inline callback interface
536 func (options *Html) RawHtmlTag(out *bytes.Buffer, text []byte) {
81cefb5e »
2011-05-29 split parser into multiple files, clean up naming
537 if options.flags&HTML_SKIP_HTML != 0 {
2aca6670 »
2011-06-29 simplify inline callback interface
538 return
965748ad »
2011-05-28 refactored into a proper package
539 }
81cefb5e »
2011-05-29 split parser into multiple files, clean up naming
540 if options.flags&HTML_SKIP_STYLE != 0 && isHtmlTag(text, "style") {
2aca6670 »
2011-06-29 simplify inline callback interface
541 return
965748ad »
2011-05-28 refactored into a proper package
542 }
81cefb5e »
2011-05-29 split parser into multiple files, clean up naming
543 if options.flags&HTML_SKIP_LINKS != 0 && isHtmlTag(text, "a") {
2aca6670 »
2011-06-29 simplify inline callback interface
544 return
965748ad »
2011-05-28 refactored into a proper package
545 }
81cefb5e »
2011-05-29 split parser into multiple files, clean up naming
546 if options.flags&HTML_SKIP_IMAGES != 0 && isHtmlTag(text, "img") {
2aca6670 »
2011-06-29 simplify inline callback interface
547 return
965748ad »
2011-05-28 refactored into a proper package
548 }
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
549 out.Write(text)
965748ad »
2011-05-28 refactored into a proper package
550 }
551
2aca6670 »
2011-06-29 simplify inline callback interface
552 func (options *Html) TripleEmphasis(out *bytes.Buffer, text []byte) {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
553 out.WriteString("<strong><em>")
554 out.Write(text)
555 out.WriteString("</em></strong>")
965748ad »
2011-05-28 refactored into a proper package
556 }
557
2aca6670 »
2011-06-29 simplify inline callback interface
558 func (options *Html) StrikeThrough(out *bytes.Buffer, text []byte) {
ee3fe992 »
2011-05-30 rudimentary latex backend, additional cleanup
559 out.WriteString("<del>")
560 out.Write(text)
561 out.WriteString("</del>")
965748ad »
2011-05-28 refactored into a proper package
562 }
563
be082a1e »
2013-06-25 First attempt at supporting Pandoc-style footnotes. The existing test…
564 func (options *Html) FootnoteRef(out *bytes.Buffer, ref []byte, id int) {
565 slug := slugify(ref)
566 out.WriteString(`<sup class="footnote-ref" id="fnref:`)
567 out.Write(slug)
568 out.WriteString(`"><a rel="footnote" href="#fn:`)
569 out.Write(slug)
570 out.WriteString(`">`)
571 out.WriteString(strconv.Itoa(id))
572 out.WriteString(`</a></sup>`)
573 }
574
3c6f18af »
2011-06-29 Renderer is now an interface
575 func (options *Html) Entity(out *bytes.Buffer, entity []byte) {
873a60ad »
2011-06-29 complete page rendering is now an option in the library
576 out.Write(entity)
577 }
578
3c6f18af »
2011-06-29 Renderer is now an interface
579 func (options *Html) NormalText(out *bytes.Buffer, text []byte) {
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
580 if options.flags&HTML_USE_SMARTYPANTS != 0 {
3c6f18af »
2011-06-29 Renderer is now an interface
581 options.Smartypants(out, text)
b1a03182 »
2011-06-28 refactoring: inline renderers return bools, preparing rendering struc…
582 } else {
583 attrEscape(out, text)
584 }
965748ad »
2011-05-28 refactored into a proper package
585 }
586
3c6f18af »
2011-06-29 Renderer is now an interface
587 func (options *Html) Smartypants(out *bytes.Buffer, text []byte) {
588 smrt := smartypantsData{false, false}
589
590 // first do normal entity escaping
591 var escaped bytes.Buffer
592 attrEscape(&escaped, text)
593 text = escaped.Bytes()
594
595 mark := 0
596 for i := 0; i < len(text); i++ {
597 if action := options.smartypants[text[i]]; action != nil {
598 if i > mark {
599 out.Write(text[mark:i])
600 }
601
602 previousChar := byte(0)
603 if i > 0 {
604 previousChar = text[i-1]
605 }
606 i += action(out, &smrt, previousChar, text[i:])
607 mark = i + 1
608 }
609 }
610
611 if mark < len(text) {
612 out.Write(text[mark:])
613 }
614 }
615
616 func (options *Html) DocumentHeader(out *bytes.Buffer) {
873a60ad »
2011-06-29 complete page rendering is now an option in the library
617 if options.flags&HTML_COMPLETE_PAGE == 0 {
618 return
619 }
620
621 ending := ""
622 if options.flags&HTML_USE_XHTML != 0 {
623 out.WriteString("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" ")
624 out.WriteString("\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n")
625 out.WriteString("<html xmlns=\"http://www.w3.org/1999/xhtml\">\n")
626 ending = " /"
627 } else {
8a86b6d6 »
2012-10-21 HTML5 doctype, Wrap TOC with <nav>
628 out.WriteString("<!DOCTYPE html>\n")
873a60ad »
2011-06-29 complete page rendering is now an option in the library
629 out.WriteString("<html>\n")
630 }
631 out.WriteString("<head>\n")
632 out.WriteString(" <title>")
3c6f18af »
2011-06-29 Renderer is now an interface
633 options.NormalText(out, []byte(options.title))
873a60ad »
2011-06-29 complete page rendering is now an option in the library
634 out.WriteString("</title>\n")
635 out.WriteString(" <meta name=\"GENERATOR\" content=\"Blackfriday Markdown Processor v")
636 out.WriteString(VERSION)
637 out.WriteString("\"")
638 out.WriteString(ending)
639 out.WriteString(">\n")
8a86b6d6 »
2012-10-21 HTML5 doctype, Wrap TOC with <nav>
640 out.WriteString(" <meta charset=\"utf-8\"")
873a60ad »
2011-06-29 complete page rendering is now an option in the library
641 out.WriteString(ending)
642 out.WriteString(">\n")
643 if options.css != "" {
644 out.WriteString(" <link rel=\"stylesheet\" type=\"text/css\" href=\"")
645 attrEscape(out, []byte(options.css))
646 out.WriteString("\"")
647 out.WriteString(ending)
648 out.WriteString(">\n")
649 }
650 out.WriteString("</head>\n")
651 out.WriteString("<body>\n")
55697351 »
2011-06-29 table of contents support beefed up
652
653 options.tocMarker = out.Len()
873a60ad »
2011-06-29 complete page rendering is now an option in the library
654 }
655
3c6f18af »
2011-06-29 Renderer is now an interface
656 func (options *Html) DocumentFooter(out *bytes.Buffer) {
55697351 »
2011-06-29 table of contents support beefed up
657 // finalize and insert the table of contents
658 if options.flags&HTML_TOC != 0 {
3c6f18af »
2011-06-29 Renderer is now an interface
659 options.TocFinalize()
55697351 »
2011-06-29 table of contents support beefed up
660
661 // now we have to insert the table of contents into the document
662 var temp bytes.Buffer
663
664 // start by making a copy of everything after the document header
665 temp.Write(out.Bytes()[options.tocMarker:])
666
667 // now clear the copied material from the main output buffer
668 out.Truncate(options.tocMarker)
669
d3c82250 »
2011-06-29 corner case spacing issue with table of contents
670 // corner case spacing issue
671 if options.flags&HTML_COMPLETE_PAGE != 0 {
672 out.WriteByte('\n')
673 }
674
55697351 »
2011-06-29 table of contents support beefed up
675 // insert the table of contents
8a86b6d6 »
2012-10-21 HTML5 doctype, Wrap TOC with <nav>
676 out.WriteString("<nav>\n")
55697351 »
2011-06-29 table of contents support beefed up
677 out.Write(options.toc.Bytes())
8a86b6d6 »
2012-10-21 HTML5 doctype, Wrap TOC with <nav>
678 out.WriteString("</nav>\n")
55697351 »
2011-06-29 table of contents support beefed up
679
d3c82250 »
2011-06-29 corner case spacing issue with table of contents
680 // corner case spacing issue
681 if options.flags&HTML_COMPLETE_PAGE == 0 && options.flags&HTML_OMIT_CONTENTS == 0 {
682 out.WriteByte('\n')
683 }
684
55697351 »
2011-06-29 table of contents support beefed up
685 // write out everything that came after it
686 if options.flags&HTML_OMIT_CONTENTS == 0 {
687 out.Write(temp.Bytes())
688 }
689 }
690
691 if options.flags&HTML_COMPLETE_PAGE != 0 {
692 out.WriteString("\n</body>\n")
693 out.WriteString("</html>\n")
965748ad »
2011-05-28 refactored into a proper package
694 }
873a60ad »
2011-06-29 complete page rendering is now an option in the library
695
965748ad »
2011-05-28 refactored into a proper package
696 }
697
3c6f18af »
2011-06-29 Renderer is now an interface
698 func (options *Html) TocHeader(text []byte, level int) {
55697351 »
2011-06-29 table of contents support beefed up
699 for level > options.currentLevel {
700 switch {
701 case bytes.HasSuffix(options.toc.Bytes(), []byte("</li>\n")):
d3c82250 »
2011-06-29 corner case spacing issue with table of contents
702 // this sublist can nest underneath a header
55697351 »
2011-06-29 table of contents support beefed up
703 size := options.toc.Len()
704 options.toc.Truncate(size - len("</li>\n"))
705
706 case options.currentLevel > 0:
707 options.toc.WriteString("<li>")
708 }
d3c82250 »
2011-06-29 corner case spacing issue with table of contents
709 if options.toc.Len() > 0 {
710 options.toc.WriteByte('\n')
711 }
712 options.toc.WriteString("<ul>\n")
55697351 »
2011-06-29 table of contents support beefed up
713 options.currentLevel++
714 }
715
716 for level < options.currentLevel {
717 options.toc.WriteString("</ul>")
718 if options.currentLevel > 1 {
719 options.toc.WriteString("</li>\n")
720 }
721 options.currentLevel--
722 }
723
724 options.toc.WriteString("<li><a href=\"#toc_")
725 options.toc.WriteString(strconv.Itoa(options.headerCount))
726 options.toc.WriteString("\">")
727 options.headerCount++
728
729 options.toc.Write(text)
730
731 options.toc.WriteString("</a></li>\n")
732 }
733
3c6f18af »
2011-06-29 Renderer is now an interface
734 func (options *Html) TocFinalize() {
873a60ad »
2011-06-29 complete page rendering is now an option in the library
735 for options.currentLevel > 1 {
55697351 »
2011-06-29 table of contents support beefed up
736 options.toc.WriteString("</ul></li>\n")
873a60ad »
2011-06-29 complete page rendering is now an option in the library
737 options.currentLevel--
965748ad »
2011-05-28 refactored into a proper package
738 }
739
873a60ad »
2011-06-29 complete page rendering is now an option in the library
740 if options.currentLevel > 0 {
55697351 »
2011-06-29 table of contents support beefed up
741 options.toc.WriteString("</ul>\n")
965748ad »
2011-05-28 refactored into a proper package
742 }
743 }
744
81cefb5e »
2011-05-29 split parser into multiple files, clean up naming
745 func isHtmlTag(tag []byte, tagname string) bool {
82262382 »
2013-04-18 Improve html element stripping code
746 found, _ := findHtmlTagPos(tag, tagname)
747 return found
748 }
749
e02c392d »
2014-01-22 Extract useful code to separate func
750 // Look for a character, but ignore it when it's in any kind of quotes, it
751 // might be JavaScript
752 func skipUntilCharIgnoreQuotes(html []byte, start int, char byte) int {
753 inSingleQuote := false
754 inDoubleQuote := false
755 inGraveQuote := false
756 i := start
757 for i < len(html) {
758 switch {
759 case html[i] == char && !inSingleQuote && !inDoubleQuote && !inGraveQuote:
760 return i
761 case html[i] == '\'':
762 inSingleQuote = !inSingleQuote
763 case html[i] == '"':
764 inDoubleQuote = !inDoubleQuote
765 case html[i] == '`':
766 inGraveQuote = !inGraveQuote
767 }
768 i++
769 }
770 return start
771 }
772
82262382 »
2013-04-18 Improve html element stripping code
773 func findHtmlTagPos(tag []byte, tagname string) (bool, int) {
965748ad »
2011-05-28 refactored into a proper package
774 i := 0
775 if i < len(tag) && tag[0] != '<' {
82262382 »
2013-04-18 Improve html element stripping code
776 return false, -1
965748ad »
2011-05-28 refactored into a proper package
777 }
778 i++
a2fda5e9 »
2013-04-13 Extract repetitive code to a func
779 i = skipSpace(tag, i)
965748ad »
2011-05-28 refactored into a proper package
780
781 if i < len(tag) && tag[i] == '/' {
782 i++
783 }
784
a2fda5e9 »
2013-04-13 Extract repetitive code to a func
785 i = skipSpace(tag, i)
d5a8df16 »
2013-04-13 Fix bug in isHtmlTag()
786 j := 0
55cde00c »
2011-06-28 camel case
787 for ; i < len(tag); i, j = i+1, j+1 {
788 if j >= len(tagname) {
965748ad »
2011-05-28 refactored into a proper package
789 break
790 }
791
b79e720a »
2013-04-13 Make isHtmlTag() case insensitive
792 if strings.ToLower(string(tag[i]))[0] != tagname[j] {
82262382 »
2013-04-18 Improve html element stripping code
793 return false, -1
965748ad »
2011-05-28 refactored into a proper package
794 }
795 }
796
797 if i == len(tag) {
82262382 »
2013-04-18 Improve html element stripping code
798 return false, -1
799 }
800
e02c392d »
2014-01-22 Extract useful code to separate func
801 rightAngle := skipUntilCharIgnoreQuotes(tag, i, '>')
802 if rightAngle > i {
803 return true, rightAngle
965748ad »
2011-05-28 refactored into a proper package
804 }
805
82262382 »
2013-04-18 Improve html element stripping code
806 return false, -1
965748ad »
2011-05-28 refactored into a proper package
807 }
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
808
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
809 func sanitizeHtml(html []byte) []byte {
810 var result []byte
811 for string(html) != "" {
812 skip, tag, rest := findHtmlTag(html)
813 html = rest
814 result = append(result, skip...)
815 result = append(result, sanitizeTag(tag)...)
816 }
817 return append(result, []byte("\n")...)
818 }
819
820 func sanitizeTag(tag []byte) []byte {
821 if tagWhitelist.Match(tag) || anchorClean.Match(tag) || imgClean.Match(tag) {
822 return tag
823 }
786aed62 »
2014-04-05 Explicit return byte array at end of function.
824 return []byte("")
55cd8200 »
2014-01-22 Rewrite protection against JavaScript injection
825 }
826
827 func skipUntilChar(text []byte, start int, char byte) int {
828 i := start
829 for i < len(text) && text[i] != char {
830 i++
831 }
832 return i
833 }
834
835 func findHtmlTag(html []byte) (skip, tag, rest []byte) {
836 start := skipUntilChar(html, 0, '<')
837 rightAngle := skipUntilCharIgnoreQuotes(html, start, '>')
838 if rightAngle > start {
839 skip = html[0:start]
840 tag = html[start : rightAngle+1]
841 rest = html[rightAngle+1:]
842 return
843 }
844
845 return []byte(""), []byte(""), []byte("")
846 }
847
a2fda5e9 »
2013-04-13 Extract repetitive code to a func
848 func skipSpace(tag []byte, i int) int {
849 for i < len(tag) && isspace(tag[i]) {
850 i++
851 }
852 return i
853 }
854
ae9562f6 »
2011-06-29 move whitespace stripping to parser, not renderers
855 func doubleSpace(out *bytes.Buffer) {
856 if out.Len() > 0 {
857 out.WriteByte('\n')
858 }
859 }
Something went wrong with that request. Please try again.