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