|
|
|
@ -192,42 +192,11 @@ func (options *Renderer) ListItem(out *bytes.Buffer, text []byte, flags int) {
|
|
|
|
|
// Note: this section is for purpose of increase performance and
|
|
|
|
|
// reduce memory allocation at runtime since they are constant literals.
|
|
|
|
|
var ( |
|
|
|
|
svgSuffix = []byte(".svg") |
|
|
|
|
svgSuffixWithMark = []byte(".svg?") |
|
|
|
|
spaceBytes = []byte(" ") |
|
|
|
|
spaceEncodedBytes = []byte("%20") |
|
|
|
|
pound = []byte("#") |
|
|
|
|
space = " " |
|
|
|
|
spaceEncoded = "%20" |
|
|
|
|
pound = []byte("#") |
|
|
|
|
space = " " |
|
|
|
|
spaceEncoded = "%20" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
// Image defines how images should be processed to produce corresponding HTML elements.
|
|
|
|
|
func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byte) { |
|
|
|
|
prefix := strings.Replace(r.urlPrefix, "/src/", "/raw/", 1) |
|
|
|
|
if len(link) > 0 { |
|
|
|
|
if isLink(link) { |
|
|
|
|
// External link with .svg suffix usually means CI status.
|
|
|
|
|
// TODO: define a keyword to allow non-svg images render as external link.
|
|
|
|
|
if bytes.HasSuffix(link, svgSuffix) || bytes.Contains(link, svgSuffixWithMark) { |
|
|
|
|
r.Renderer.Image(out, link, title, alt) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
if link[0] != '/' { |
|
|
|
|
prefix += "/" |
|
|
|
|
} |
|
|
|
|
link = bytes.Replace([]byte((prefix + string(link))), spaceBytes, spaceEncodedBytes, -1) |
|
|
|
|
fmt.Println(333, string(link)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
out.WriteString(`<a href="`) |
|
|
|
|
out.Write(link) |
|
|
|
|
out.WriteString(`">`) |
|
|
|
|
r.Renderer.Image(out, link, title, alt) |
|
|
|
|
out.WriteString("</a>") |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// cutoutVerbosePrefix cutouts URL prefix including sub-path to
|
|
|
|
|
// return a clean unified string of request URL path.
|
|
|
|
|
func cutoutVerbosePrefix(prefix string) string { |
|
|
|
@ -353,14 +322,63 @@ var (
|
|
|
|
|
rightAngleBracket = []byte(">") |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
var noEndTags = []string{"img", "input", "br", "hr"} |
|
|
|
|
var noEndTags = []string{"input", "br", "hr", "img"} |
|
|
|
|
|
|
|
|
|
// wrapImgWithLink warps link to standalone <img> tags.
|
|
|
|
|
func wrapImgWithLink(urlPrefix string, buf *bytes.Buffer, token html.Token) { |
|
|
|
|
var src, alt string |
|
|
|
|
// Extract "src" and "alt" attributes
|
|
|
|
|
for i := range token.Attr { |
|
|
|
|
switch token.Attr[i].Key { |
|
|
|
|
case "src": |
|
|
|
|
src = token.Attr[i].Val |
|
|
|
|
case "alt": |
|
|
|
|
alt = token.Attr[i].Val |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Skip in case the "src" is empty
|
|
|
|
|
if len(src) == 0 { |
|
|
|
|
buf.WriteString(token.String()) |
|
|
|
|
return |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
buf.WriteString(`<a href="`) |
|
|
|
|
buf.WriteString(src) |
|
|
|
|
buf.WriteString(`">`) |
|
|
|
|
|
|
|
|
|
// Prepend repository base URL for internal links
|
|
|
|
|
if !isLink([]byte(src)) { |
|
|
|
|
urlPrefix = strings.Replace(urlPrefix, "/src/", "/raw/", 1) |
|
|
|
|
if src[0] != '/' { |
|
|
|
|
urlPrefix += "/" |
|
|
|
|
} |
|
|
|
|
src = strings.Replace(urlPrefix+string(src), " ", "%20", -1) |
|
|
|
|
buf.WriteString(`<img src="`) |
|
|
|
|
buf.WriteString(src) |
|
|
|
|
buf.WriteString(`"`) |
|
|
|
|
|
|
|
|
|
if len(alt) > 0 { |
|
|
|
|
buf.WriteString(` alt="`) |
|
|
|
|
buf.WriteString(alt) |
|
|
|
|
buf.WriteString(`"`) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
buf.WriteString(`>`) |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
buf.WriteString(token.String()) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
buf.WriteString(`</a>`) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// PostProcess treats different types of HTML differently,
|
|
|
|
|
// and only renders special links for plain text blocks.
|
|
|
|
|
func PostProcess(rawHtml []byte, urlPrefix string, metas map[string]string) []byte { |
|
|
|
|
func PostProcess(rawHTML []byte, urlPrefix string, metas map[string]string) []byte { |
|
|
|
|
startTags := make([]string, 0, 5) |
|
|
|
|
var buf bytes.Buffer |
|
|
|
|
tokenizer := html.NewTokenizer(bytes.NewReader(rawHtml)) |
|
|
|
|
buf := bytes.NewBuffer(nil) |
|
|
|
|
tokenizer := html.NewTokenizer(bytes.NewReader(rawHTML)) |
|
|
|
|
|
|
|
|
|
OUTER_LOOP: |
|
|
|
|
for html.ErrorToken != tokenizer.Next() { |
|
|
|
@ -370,8 +388,14 @@ OUTER_LOOP:
|
|
|
|
|
buf.Write(RenderSpecialLink([]byte(token.String()), urlPrefix, metas)) |
|
|
|
|
|
|
|
|
|
case html.StartTagToken: |
|
|
|
|
buf.WriteString(token.String()) |
|
|
|
|
tagName := token.Data |
|
|
|
|
|
|
|
|
|
if tagName == "img" { |
|
|
|
|
wrapImgWithLink(urlPrefix, buf, token) |
|
|
|
|
continue OUTER_LOOP |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
buf.WriteString(token.String()) |
|
|
|
|
// If this is an excluded tag, we skip processing all output until a close tag is encountered.
|
|
|
|
|
if strings.EqualFold("a", tagName) || strings.EqualFold("code", tagName) || strings.EqualFold("pre", tagName) { |
|
|
|
|
stackNum := 1 |
|
|
|
@ -381,14 +405,14 @@ OUTER_LOOP:
|
|
|
|
|
// Copy the token to the output verbatim
|
|
|
|
|
buf.WriteString(token.String()) |
|
|
|
|
|
|
|
|
|
if token.Type == html.StartTagToken { |
|
|
|
|
// Stack number doesn't increate for tags without end tags.
|
|
|
|
|
if token.Type == html.StartTagToken && !com.IsSliceContainsStr(noEndTags, token.Data) { |
|
|
|
|
stackNum++ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// If this is the close tag to the outer-most, we are done
|
|
|
|
|
if token.Type == html.EndTagToken { |
|
|
|
|
stackNum-- |
|
|
|
|
|
|
|
|
|
if stackNum <= 0 && strings.EqualFold(tagName, token.Data) { |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
@ -397,8 +421,8 @@ OUTER_LOOP:
|
|
|
|
|
continue OUTER_LOOP |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if !com.IsSliceContainsStr(noEndTags, token.Data) { |
|
|
|
|
startTags = append(startTags, token.Data) |
|
|
|
|
if !com.IsSliceContainsStr(noEndTags, tagName) { |
|
|
|
|
startTags = append(startTags, tagName) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
case html.EndTagToken: |
|
|
|
@ -422,7 +446,7 @@ OUTER_LOOP:
|
|
|
|
|
|
|
|
|
|
// If we are not at the end of the input, then some other parsing error has occurred,
|
|
|
|
|
// so return the input verbatim.
|
|
|
|
|
return rawHtml |
|
|
|
|
return rawHTML |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// Render renders Markdown to HTML with special links.
|
|
|
|
|