You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

100 lines
2.5 KiB

  1. // Copyright 2012 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package present
  5. import (
  6. "fmt"
  7. "log"
  8. "net/url"
  9. "strings"
  10. )
  11. func init() {
  12. Register("link", parseLink)
  13. }
  14. type Link struct {
  15. URL *url.URL
  16. Label string
  17. }
  18. func (l Link) TemplateName() string { return "link" }
  19. func parseLink(ctx *Context, fileName string, lineno int, text string) (Elem, error) {
  20. args := strings.Fields(text)
  21. if len(args) < 2 {
  22. return nil, fmt.Errorf("link element must have at least 2 arguments")
  23. }
  24. url, err := url.Parse(args[1])
  25. if err != nil {
  26. return nil, err
  27. }
  28. label := ""
  29. if len(args) > 2 {
  30. label = strings.Join(args[2:], " ")
  31. } else {
  32. scheme := url.Scheme + "://"
  33. if url.Scheme == "mailto" {
  34. scheme = "mailto:"
  35. }
  36. label = strings.Replace(url.String(), scheme, "", 1)
  37. }
  38. return Link{url, label}, nil
  39. }
  40. func renderLink(href, text string) string {
  41. text = font(text)
  42. if text == "" {
  43. text = href
  44. }
  45. // Open links in new window only when their url is absolute.
  46. target := "_blank"
  47. if u, err := url.Parse(href); err != nil {
  48. log.Println("renderLink parsing url:", err)
  49. } else if !u.IsAbs() || u.Scheme == "javascript" {
  50. target = "_self"
  51. }
  52. return fmt.Sprintf(`<a href="%s" target="%s">%s</a>`, href, target, text)
  53. }
  54. // parseInlineLink parses an inline link at the start of s, and returns
  55. // a rendered HTML link and the total length of the raw inline link.
  56. // If no inline link is present, it returns all zeroes.
  57. func parseInlineLink(s string) (link string, length int) {
  58. if !strings.HasPrefix(s, "[[") {
  59. return
  60. }
  61. end := strings.Index(s, "]]")
  62. if end == -1 {
  63. return
  64. }
  65. urlEnd := strings.Index(s, "]")
  66. rawURL := s[2:urlEnd]
  67. const badURLChars = `<>"{}|\^[] ` + "`" // per RFC2396 section 2.4.3
  68. if strings.ContainsAny(rawURL, badURLChars) {
  69. return
  70. }
  71. if urlEnd == end {
  72. simpleUrl := ""
  73. url, err := url.Parse(rawURL)
  74. if err == nil {
  75. // If the URL is http://foo.com, drop the http://
  76. // In other words, render [[http://golang.org]] as:
  77. // <a href="http://golang.org">golang.org</a>
  78. if strings.HasPrefix(rawURL, url.Scheme+"://") {
  79. simpleUrl = strings.TrimPrefix(rawURL, url.Scheme+"://")
  80. } else if strings.HasPrefix(rawURL, url.Scheme+":") {
  81. simpleUrl = strings.TrimPrefix(rawURL, url.Scheme+":")
  82. }
  83. }
  84. return renderLink(rawURL, simpleUrl), end + 2
  85. }
  86. if s[urlEnd:urlEnd+2] != "][" {
  87. return
  88. }
  89. text := s[urlEnd+2 : end]
  90. return renderLink(rawURL, text), end + 2
  91. }