markdown.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121
  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 IsImageFile(data []byte) (string, bool) {
  44. contentType := http.DetectContentType(data)
  45. if strings.Index(contentType, "image/") != -1 {
  46. return contentType, true
  47. }
  48. return contentType, false
  49. }
  50. func IsReadmeFile(name string) bool {
  51. name = strings.ToLower(name)
  52. if len(name) < 6 {
  53. return false
  54. }
  55. if name[:6] == "readme" {
  56. return true
  57. }
  58. return false
  59. }
  60. type CustomRender struct {
  61. gfm.Renderer
  62. urlPrefix string
  63. }
  64. func (options *CustomRender) Link(out *bytes.Buffer, link []byte, title []byte, content []byte) {
  65. if len(link) > 0 && !isLink(link) {
  66. if link[0] == '#' {
  67. // link = append([]byte(options.urlPrefix), link...)
  68. } else {
  69. link = []byte(path.Join(options.urlPrefix, string(link)))
  70. }
  71. }
  72. options.Renderer.Link(out, link, title, content)
  73. }
  74. func RenderMarkdown(rawBytes []byte, urlPrefix string) []byte {
  75. htmlFlags := 0
  76. // htmlFlags |= gfm.HTML_USE_XHTML
  77. // htmlFlags |= gfm.HTML_USE_SMARTYPANTS
  78. // htmlFlags |= gfm.HTML_SMARTYPANTS_FRACTIONS
  79. // htmlFlags |= gfm.HTML_SMARTYPANTS_LATEX_DASHES
  80. htmlFlags |= gfm.HTML_SKIP_HTML
  81. htmlFlags |= gfm.HTML_SKIP_STYLE
  82. htmlFlags |= gfm.HTML_SKIP_SCRIPT
  83. htmlFlags |= gfm.HTML_GITHUB_BLOCKCODE
  84. htmlFlags |= gfm.HTML_OMIT_CONTENTS
  85. // htmlFlags |= gfm.HTML_COMPLETE_PAGE
  86. renderer := &CustomRender{
  87. Renderer: gfm.HtmlRenderer(htmlFlags, "", ""),
  88. urlPrefix: urlPrefix,
  89. }
  90. // set up the parser
  91. extensions := 0
  92. extensions |= gfm.EXTENSION_NO_INTRA_EMPHASIS
  93. extensions |= gfm.EXTENSION_TABLES
  94. extensions |= gfm.EXTENSION_FENCED_CODE
  95. extensions |= gfm.EXTENSION_AUTOLINK
  96. extensions |= gfm.EXTENSION_STRIKETHROUGH
  97. extensions |= gfm.EXTENSION_HARD_LINE_BREAK
  98. extensions |= gfm.EXTENSION_SPACE_HEADERS
  99. extensions |= gfm.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
  100. body := gfm.Markdown(rawBytes, renderer, extensions)
  101. return body
  102. }