瀏覽代碼

markdown: fix links for image nested inside a link (#2636)

Unknwon 7 年之前
父節點
當前提交
d7954014a4
共有 2 個文件被更改,包括 72 次插入48 次删除
  1. 68 44
      modules/markdown/markdown.go
  2. 4 4
      templates/repo/settings/options.tmpl

+ 68 - 44
modules/markdown/markdown.go

@@ -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.

+ 4 - 4
templates/repo/settings/options.tmpl

@@ -306,11 +306,11 @@
 				</div>
 				<div class="required field">
 					<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
-					<input id="repo_name" name="repo_name" required>
+					<input id="repo_name" name="repo_name" autocomplete="off" required>
 				</div>
 				<div class="required field">
 					<label for="new_owner_name">{{.i18n.Tr "repo.settings.transfer_owner"}}</label>
-					<input id="new_owner_name" name="new_owner_name" required>
+					<input id="new_owner_name" name="new_owner_name" autocomplete="off" required>
 				</div>
 
 				<div class="text right actions">
@@ -344,7 +344,7 @@
 				</div>
 				<div class="required field">
 					<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
-					<input id="repo_name" name="repo_name" required>
+					<input id="repo_name" name="repo_name" autocomplete="off" required>
 				</div>
 
 				<div class="text right actions">
@@ -376,7 +376,7 @@
 				</div>
 				<div class="required field">
 					<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label>
-					<input id="repo_name" name="repo_name" required>
+					<input id="repo_name" name="repo_name" autocomplete="off" required>
 				</div>
 
 				<div class="text right actions">