markdown.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package base
  5. import (
  6. "bytes"
  7. "net/http"
  8. "path"
  9. "path/filepath"
  10. "strings"
  11. "github.com/gogits/gfm"
  12. )
  13. func isletter(c byte) bool {
  14. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
  15. }
  16. func isalnum(c byte) bool {
  17. return (c >= '0' && c <= '9') || isletter(c)
  18. }
  19. var validLinks = [][]byte{[]byte("http://"), []byte("https://"), []byte("ftp://"), []byte("mailto://")}
  20. func isLink(link []byte) bool {
  21. for _, prefix := range validLinks {
  22. if len(link) > len(prefix) && bytes.Equal(bytes.ToLower(link[:len(prefix)]), prefix) && isalnum(link[len(prefix)]) {
  23. return true
  24. }
  25. }
  26. return false
  27. }
  28. func IsMarkdownFile(name string) bool {
  29. name = strings.ToLower(name)
  30. switch filepath.Ext(name) {
  31. case ".md", ".markdown", ".mdown":
  32. return true
  33. }
  34. return false
  35. }
  36. func IsTextFile(data []byte) (string, bool) {
  37. contentType := http.DetectContentType(data)
  38. if strings.Index(contentType, "text/") != -1 {
  39. return contentType, true
  40. }
  41. return contentType, false
  42. }
  43. func IsReadmeFile(name string) bool {
  44. name = strings.ToLower(name)
  45. if len(name) < 6 {
  46. return false
  47. }
  48. if name[:6] == "readme" {
  49. return true
  50. }
  51. return false
  52. }
  53. type CustomRender struct {
  54. gfm.Renderer
  55. urlPrefix string
  56. }
  57. func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
  58. if len(link) > 0 && !isLink(link) {
  59. if link[0] == '#' {
  60. // link = append([]byte(options.urlPrefix), link...)
  61. } else {
  62. link = []byte(path.Join(options.urlPrefix, string(link)))
  63. }
  64. }
  65. options.Renderer.Link(out, link, title, content)
  66. }
  67. func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte {
  68. htmlFlags := 0
  69. // htmlFlags |= gfm.HTML_USE_XHTML
  70. // htmlFlags |= gfm.HTML_USE_SMARTYPANTS
  71. // htmlFlags |= gfm.HTML_SMARTYPANTS_FRACTIONS
  72. // htmlFlags |= gfm.HTML_SMARTYPANTS_LATEX_DASHES
  73. htmlFlags |= gfm.HTML_SKIP_HTML
  74. htmlFlags |= gfm.HTML_SKIP_STYLE
  75. htmlFlags |= gfm.HTML_SKIP_SCRIPT
  76. htmlFlags |= gfm.HTML_GITHUB_BLOCKCODE
  77. htmlFlags |= gfm.HTML_OMIT_CONTENTS
  78. // htmlFlags |= gfm.HTML_COMPLETE_PAGE
  79. renderer := &CustomRender{
  80. Renderer: gfm.HtmlRenderer(htmlFlags, "", ""),
  81. urlPrefix: urlPrefix,
  82. }
  83. // set up the parser
  84. extensions := 0
  85. extensions |= gfm.EXTENSION_NO_INTRA_EMPHASIS
  86. extensions |= gfm.EXTENSION_TABLES
  87. extensions |= gfm.EXTENSION_FENCED_CODE
  88. extensions |= gfm.EXTENSION_AUTOLINK
  89. extensions |= gfm.EXTENSION_STRIKETHROUGH
  90. extensions |= gfm.EXTENSION_HARD_LINE_BREAK
  91. extensions |= gfm.EXTENSION_SPACE_HEADERS
  92. extensions |= gfm.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
  93. body := gfm.Markdown(rawBytes, renderer, extensions)
  94. return body
  95. }