mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-11-27 21:03:56 +01:00
Split filePreviewPatternProcessor into a new type FilePreview and some functions to make code more maintainable
This commit is contained in:
parent
562e5cdf32
commit
d789d33229
2 changed files with 276 additions and 238 deletions
269
modules/markup/file_preview.go
Normal file
269
modules/markup/file_preview.go
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
package markup
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"html/template"
|
||||||
|
"regexp"
|
||||||
|
"slices"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/modules/charset"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
|
"code.gitea.io/gitea/modules/translation"
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
"golang.org/x/net/html/atom"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2"
|
||||||
|
filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`)
|
||||||
|
)
|
||||||
|
|
||||||
|
type FilePreview struct {
|
||||||
|
fileContent []template.HTML
|
||||||
|
subTitle template.HTML
|
||||||
|
lineOffset int
|
||||||
|
urlFull string
|
||||||
|
filePath string
|
||||||
|
start int
|
||||||
|
end int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Locale) *FilePreview {
|
||||||
|
preview := &FilePreview{}
|
||||||
|
|
||||||
|
m := filePreviewPattern.FindStringSubmatchIndex(node.Data)
|
||||||
|
if m == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that every group has a match
|
||||||
|
if slices.Contains(m, -1) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
preview.urlFull = node.Data[m[0]:m[1]]
|
||||||
|
|
||||||
|
// Ensure that we only use links to local repositories
|
||||||
|
if !strings.HasPrefix(preview.urlFull, setting.AppURL+setting.AppSubURL) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/")
|
||||||
|
|
||||||
|
commitSha := node.Data[m[4]:m[5]]
|
||||||
|
preview.filePath = node.Data[m[6]:m[7]]
|
||||||
|
hash := node.Data[m[8]:m[9]]
|
||||||
|
|
||||||
|
preview.start = m[0]
|
||||||
|
preview.end = m[1]
|
||||||
|
|
||||||
|
// If url ends in '.', it's very likely that it is not part of the
|
||||||
|
// actual url but used to finish a sentence.
|
||||||
|
if strings.HasSuffix(preview.urlFull, ".") {
|
||||||
|
preview.end--
|
||||||
|
preview.urlFull = preview.urlFull[:len(preview.urlFull)-1]
|
||||||
|
hash = hash[:len(hash)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
projPathSegments := strings.Split(projPath, "/")
|
||||||
|
fileContent, err := DefaultProcessorHelper.GetRepoFileContent(
|
||||||
|
ctx.Ctx,
|
||||||
|
projPathSegments[len(projPathSegments)-2],
|
||||||
|
projPathSegments[len(projPathSegments)-1],
|
||||||
|
commitSha, preview.filePath,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lineSpecs := strings.Split(hash, "-")
|
||||||
|
lineCount := len(fileContent)
|
||||||
|
|
||||||
|
commitLinkBuffer := new(bytes.Buffer)
|
||||||
|
html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black"))
|
||||||
|
|
||||||
|
if len(lineSpecs) == 1 {
|
||||||
|
line, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L"))
|
||||||
|
if line < 1 || line > lineCount {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
preview.fileContent = fileContent[line-1 : line]
|
||||||
|
preview.subTitle = locale.Tr(
|
||||||
|
"markup.filepreview.line", line,
|
||||||
|
template.HTML(commitLinkBuffer.String()),
|
||||||
|
)
|
||||||
|
|
||||||
|
preview.lineOffset = line - 1
|
||||||
|
} else {
|
||||||
|
startLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L"))
|
||||||
|
endLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[1], "L"))
|
||||||
|
|
||||||
|
if startLine < 1 || endLine < 1 || startLine > lineCount || endLine > lineCount || endLine < startLine {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
preview.fileContent = fileContent[startLine-1 : endLine]
|
||||||
|
preview.subTitle = locale.Tr(
|
||||||
|
"markup.filepreview.lines", startLine, endLine,
|
||||||
|
template.HTML(commitLinkBuffer.String()),
|
||||||
|
)
|
||||||
|
|
||||||
|
preview.lineOffset = startLine - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return preview
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *FilePreview) CreateHtml(locale translation.Locale) *html.Node {
|
||||||
|
table := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Table.String(),
|
||||||
|
Attr: []html.Attribute{{Key: "class", Val: "file-preview"}},
|
||||||
|
}
|
||||||
|
tbody := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Tbody.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
status := &charset.EscapeStatus{}
|
||||||
|
statuses := make([]*charset.EscapeStatus, len(p.fileContent))
|
||||||
|
for i, line := range p.fileContent {
|
||||||
|
statuses[i], p.fileContent[i] = charset.EscapeControlHTML(line, locale, charset.FileviewContext)
|
||||||
|
status = status.Or(statuses[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
for idx, code := range p.fileContent {
|
||||||
|
tr := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Tr.String(),
|
||||||
|
}
|
||||||
|
|
||||||
|
lineNum := strconv.Itoa(p.lineOffset + idx + 1)
|
||||||
|
|
||||||
|
tdLinesnum := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Td.String(),
|
||||||
|
Attr: []html.Attribute{
|
||||||
|
{Key: "id", Val: "L" + lineNum},
|
||||||
|
{Key: "class", Val: "lines-num"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spanLinesNum := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Span.String(),
|
||||||
|
Attr: []html.Attribute{
|
||||||
|
{Key: "id", Val: "L" + lineNum},
|
||||||
|
{Key: "data-line-number", Val: lineNum},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tdLinesnum.AppendChild(spanLinesNum)
|
||||||
|
tr.AppendChild(tdLinesnum)
|
||||||
|
|
||||||
|
if status.Escaped {
|
||||||
|
tdLinesEscape := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Td.String(),
|
||||||
|
Attr: []html.Attribute{
|
||||||
|
{Key: "class", Val: "lines-escape"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if statuses[idx].Escaped {
|
||||||
|
btnTitle := ""
|
||||||
|
if statuses[idx].HasInvisible {
|
||||||
|
btnTitle += locale.TrString("repo.invisible_runes_line") + " "
|
||||||
|
}
|
||||||
|
if statuses[idx].HasAmbiguous {
|
||||||
|
btnTitle += locale.TrString("repo.ambiguous_runes_line")
|
||||||
|
}
|
||||||
|
|
||||||
|
escapeBtn := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Button.String(),
|
||||||
|
Attr: []html.Attribute{
|
||||||
|
{Key: "class", Val: "toggle-escape-button btn interact-bg"},
|
||||||
|
{Key: "title", Val: btnTitle},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
tdLinesEscape.AppendChild(escapeBtn)
|
||||||
|
}
|
||||||
|
|
||||||
|
tr.AppendChild(tdLinesEscape)
|
||||||
|
}
|
||||||
|
|
||||||
|
tdCode := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Td.String(),
|
||||||
|
Attr: []html.Attribute{
|
||||||
|
{Key: "rel", Val: "L" + lineNum},
|
||||||
|
{Key: "class", Val: "lines-code chroma"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
codeInner := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Code.String(),
|
||||||
|
Attr: []html.Attribute{{Key: "class", Val: "code-inner"}},
|
||||||
|
}
|
||||||
|
codeText := &html.Node{
|
||||||
|
Type: html.RawNode,
|
||||||
|
Data: string(code),
|
||||||
|
}
|
||||||
|
codeInner.AppendChild(codeText)
|
||||||
|
tdCode.AppendChild(codeInner)
|
||||||
|
tr.AppendChild(tdCode)
|
||||||
|
|
||||||
|
tbody.AppendChild(tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
table.AppendChild(tbody)
|
||||||
|
|
||||||
|
twrapper := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Div.String(),
|
||||||
|
Attr: []html.Attribute{{Key: "class", Val: "ui table"}},
|
||||||
|
}
|
||||||
|
twrapper.AppendChild(table)
|
||||||
|
|
||||||
|
header := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Div.String(),
|
||||||
|
Attr: []html.Attribute{{Key: "class", Val: "header"}},
|
||||||
|
}
|
||||||
|
afilepath := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.A.String(),
|
||||||
|
Attr: []html.Attribute{
|
||||||
|
{Key: "href", Val: p.urlFull},
|
||||||
|
{Key: "class", Val: "muted"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
afilepath.AppendChild(&html.Node{
|
||||||
|
Type: html.TextNode,
|
||||||
|
Data: p.filePath,
|
||||||
|
})
|
||||||
|
header.AppendChild(afilepath)
|
||||||
|
|
||||||
|
psubtitle := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Span.String(),
|
||||||
|
Attr: []html.Attribute{{Key: "class", Val: "text small grey"}},
|
||||||
|
}
|
||||||
|
psubtitle.AppendChild(&html.Node{
|
||||||
|
Type: html.RawNode,
|
||||||
|
Data: string(p.subTitle),
|
||||||
|
})
|
||||||
|
header.AppendChild(psubtitle)
|
||||||
|
|
||||||
|
preview_node := &html.Node{
|
||||||
|
Type: html.ElementNode,
|
||||||
|
Data: atom.Div.String(),
|
||||||
|
Attr: []html.Attribute{{Key: "class", Val: "file-preview-box"}},
|
||||||
|
}
|
||||||
|
preview_node.AppendChild(header)
|
||||||
|
preview_node.AppendChild(twrapper)
|
||||||
|
|
||||||
|
return preview_node
|
||||||
|
}
|
|
@ -5,19 +5,15 @@ package markup
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"html/template"
|
|
||||||
"io"
|
"io"
|
||||||
"net/url"
|
"net/url"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"slices"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"code.gitea.io/gitea/modules/base"
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/charset"
|
|
||||||
"code.gitea.io/gitea/modules/emoji"
|
"code.gitea.io/gitea/modules/emoji"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
|
@ -65,9 +61,6 @@ var (
|
||||||
|
|
||||||
validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`)
|
validLinksPattern = regexp.MustCompile(`^[a-z][\w-]+://`)
|
||||||
|
|
||||||
// filePreviewPattern matches "http://domain/org/repo/src/commit/COMMIT/filepath#L1-L2"
|
|
||||||
filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([0-9a-f]{4,64})/(\S+)#(L\d+(?:-L\d+)?)`)
|
|
||||||
|
|
||||||
// While this email regex is definitely not perfect and I'm sure you can come up
|
// While this email regex is definitely not perfect and I'm sure you can come up
|
||||||
// with edge cases, it is still accepted by the CommonMark specification, as
|
// with edge cases, it is still accepted by the CommonMark specification, as
|
||||||
// well as the HTML5 spec:
|
// well as the HTML5 spec:
|
||||||
|
@ -1072,252 +1065,28 @@ func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) {
|
||||||
|
|
||||||
next := node.NextSibling
|
next := node.NextSibling
|
||||||
for node != nil && node != next {
|
for node != nil && node != next {
|
||||||
m := filePreviewPattern.FindStringSubmatchIndex(node.Data)
|
|
||||||
if m == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure that every group has a match
|
|
||||||
if slices.Contains(m, -1) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
urlFull := node.Data[m[0]:m[1]]
|
|
||||||
|
|
||||||
// Ensure that we only use links to local repositories
|
|
||||||
if !strings.HasPrefix(urlFull, setting.AppURL+setting.AppSubURL) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/")
|
|
||||||
|
|
||||||
commitSha := node.Data[m[4]:m[5]]
|
|
||||||
filePath := node.Data[m[6]:m[7]]
|
|
||||||
hash := node.Data[m[8]:m[9]]
|
|
||||||
|
|
||||||
start := m[0]
|
|
||||||
end := m[1]
|
|
||||||
|
|
||||||
// If url ends in '.', it's very likely that it is not part of the
|
|
||||||
// actual url but used to finish a sentence.
|
|
||||||
if strings.HasSuffix(urlFull, ".") {
|
|
||||||
end--
|
|
||||||
urlFull = urlFull[:len(urlFull)-1]
|
|
||||||
hash = hash[:len(hash)-1]
|
|
||||||
}
|
|
||||||
|
|
||||||
projPathSegments := strings.Split(projPath, "/")
|
|
||||||
fileContent, err := DefaultProcessorHelper.GetRepoFileContent(
|
|
||||||
ctx.Ctx,
|
|
||||||
projPathSegments[len(projPathSegments)-2],
|
|
||||||
projPathSegments[len(projPathSegments)-1],
|
|
||||||
commitSha, filePath,
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
lineSpecs := strings.Split(hash, "-")
|
|
||||||
lineCount := len(fileContent)
|
|
||||||
|
|
||||||
commitLinkBuffer := new(bytes.Buffer)
|
|
||||||
html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black"))
|
|
||||||
|
|
||||||
var subTitle template.HTML
|
|
||||||
var lineOffset int
|
|
||||||
|
|
||||||
locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale)
|
locale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale)
|
||||||
if !ok {
|
if !ok {
|
||||||
locale = translation.NewLocale("en-US")
|
locale = translation.NewLocale("en-US")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(lineSpecs) == 1 {
|
preview := NewFilePreview(ctx, node, locale)
|
||||||
line, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L"))
|
if preview == nil {
|
||||||
if line < 1 || line > lineCount {
|
return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fileContent = fileContent[line-1 : line]
|
|
||||||
subTitle = locale.Tr(
|
|
||||||
"markup.filepreview.line", line,
|
|
||||||
template.HTML(commitLinkBuffer.String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
lineOffset = line - 1
|
|
||||||
} else {
|
|
||||||
startLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[0], "L"))
|
|
||||||
endLine, _ := strconv.Atoi(strings.TrimPrefix(lineSpecs[1], "L"))
|
|
||||||
|
|
||||||
if startLine < 1 || endLine < 1 || startLine > lineCount || endLine > lineCount || endLine < startLine {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fileContent = fileContent[startLine-1 : endLine]
|
|
||||||
subTitle = locale.Tr(
|
|
||||||
"markup.filepreview.lines", startLine, endLine,
|
|
||||||
template.HTML(commitLinkBuffer.String()),
|
|
||||||
)
|
|
||||||
|
|
||||||
lineOffset = startLine - 1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table := &html.Node{
|
preview_node := preview.CreateHtml(locale)
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Table.String(),
|
|
||||||
Attr: []html.Attribute{{Key: "class", Val: "file-preview"}},
|
|
||||||
}
|
|
||||||
tbody := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Tbody.String(),
|
|
||||||
}
|
|
||||||
|
|
||||||
status := &charset.EscapeStatus{}
|
|
||||||
statuses := make([]*charset.EscapeStatus, len(fileContent))
|
|
||||||
for i, line := range fileContent {
|
|
||||||
statuses[i], fileContent[i] = charset.EscapeControlHTML(line, locale, charset.FileviewContext)
|
|
||||||
status = status.Or(statuses[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
for idx, code := range fileContent {
|
|
||||||
tr := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Tr.String(),
|
|
||||||
}
|
|
||||||
|
|
||||||
lineNum := strconv.Itoa(lineOffset + idx + 1)
|
|
||||||
|
|
||||||
tdLinesnum := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Td.String(),
|
|
||||||
Attr: []html.Attribute{
|
|
||||||
{Key: "id", Val: "L" + lineNum},
|
|
||||||
{Key: "class", Val: "lines-num"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
spanLinesNum := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Span.String(),
|
|
||||||
Attr: []html.Attribute{
|
|
||||||
{Key: "id", Val: "L" + lineNum},
|
|
||||||
{Key: "data-line-number", Val: lineNum},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tdLinesnum.AppendChild(spanLinesNum)
|
|
||||||
tr.AppendChild(tdLinesnum)
|
|
||||||
|
|
||||||
if status.Escaped {
|
|
||||||
tdLinesEscape := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Td.String(),
|
|
||||||
Attr: []html.Attribute{
|
|
||||||
{Key: "class", Val: "lines-escape"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if statuses[idx].Escaped {
|
|
||||||
btnTitle := ""
|
|
||||||
if statuses[idx].HasInvisible {
|
|
||||||
btnTitle += locale.TrString("repo.invisible_runes_line") + " "
|
|
||||||
}
|
|
||||||
if statuses[idx].HasAmbiguous {
|
|
||||||
btnTitle += locale.TrString("repo.ambiguous_runes_line")
|
|
||||||
}
|
|
||||||
|
|
||||||
escapeBtn := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Button.String(),
|
|
||||||
Attr: []html.Attribute{
|
|
||||||
{Key: "class", Val: "toggle-escape-button btn interact-bg"},
|
|
||||||
{Key: "title", Val: btnTitle},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
tdLinesEscape.AppendChild(escapeBtn)
|
|
||||||
}
|
|
||||||
|
|
||||||
tr.AppendChild(tdLinesEscape)
|
|
||||||
}
|
|
||||||
|
|
||||||
tdCode := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Td.String(),
|
|
||||||
Attr: []html.Attribute{
|
|
||||||
{Key: "rel", Val: "L" + lineNum},
|
|
||||||
{Key: "class", Val: "lines-code chroma"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
codeInner := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Code.String(),
|
|
||||||
Attr: []html.Attribute{{Key: "class", Val: "code-inner"}},
|
|
||||||
}
|
|
||||||
codeText := &html.Node{
|
|
||||||
Type: html.RawNode,
|
|
||||||
Data: string(code),
|
|
||||||
}
|
|
||||||
codeInner.AppendChild(codeText)
|
|
||||||
tdCode.AppendChild(codeInner)
|
|
||||||
tr.AppendChild(tdCode)
|
|
||||||
|
|
||||||
tbody.AppendChild(tr)
|
|
||||||
}
|
|
||||||
|
|
||||||
table.AppendChild(tbody)
|
|
||||||
|
|
||||||
twrapper := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Div.String(),
|
|
||||||
Attr: []html.Attribute{{Key: "class", Val: "ui table"}},
|
|
||||||
}
|
|
||||||
twrapper.AppendChild(table)
|
|
||||||
|
|
||||||
header := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Div.String(),
|
|
||||||
Attr: []html.Attribute{{Key: "class", Val: "header"}},
|
|
||||||
}
|
|
||||||
afilepath := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.A.String(),
|
|
||||||
Attr: []html.Attribute{
|
|
||||||
{Key: "href", Val: urlFull},
|
|
||||||
{Key: "class", Val: "muted"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
afilepath.AppendChild(&html.Node{
|
|
||||||
Type: html.TextNode,
|
|
||||||
Data: filePath,
|
|
||||||
})
|
|
||||||
header.AppendChild(afilepath)
|
|
||||||
|
|
||||||
psubtitle := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Span.String(),
|
|
||||||
Attr: []html.Attribute{{Key: "class", Val: "text small grey"}},
|
|
||||||
}
|
|
||||||
psubtitle.AppendChild(&html.Node{
|
|
||||||
Type: html.RawNode,
|
|
||||||
Data: string(subTitle),
|
|
||||||
})
|
|
||||||
header.AppendChild(psubtitle)
|
|
||||||
|
|
||||||
preview := &html.Node{
|
|
||||||
Type: html.ElementNode,
|
|
||||||
Data: atom.Div.String(),
|
|
||||||
Attr: []html.Attribute{{Key: "class", Val: "file-preview-box"}},
|
|
||||||
}
|
|
||||||
preview.AppendChild(header)
|
|
||||||
preview.AppendChild(twrapper)
|
|
||||||
|
|
||||||
// Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div
|
// Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div
|
||||||
before := node.Data[:start]
|
before := node.Data[:preview.start]
|
||||||
after := node.Data[end:]
|
after := node.Data[preview.end:]
|
||||||
node.Data = before
|
node.Data = before
|
||||||
nextSibling := node.NextSibling
|
nextSibling := node.NextSibling
|
||||||
node.Parent.InsertBefore(&html.Node{
|
node.Parent.InsertBefore(&html.Node{
|
||||||
Type: html.RawNode,
|
Type: html.RawNode,
|
||||||
Data: "</p>",
|
Data: "</p>",
|
||||||
}, nextSibling)
|
}, nextSibling)
|
||||||
node.Parent.InsertBefore(preview, nextSibling)
|
node.Parent.InsertBefore(preview_node, nextSibling)
|
||||||
node.Parent.InsertBefore(&html.Node{
|
node.Parent.InsertBefore(&html.Node{
|
||||||
Type: html.RawNode,
|
Type: html.RawNode,
|
||||||
Data: "<p>" + after,
|
Data: "<p>" + after,
|
||||||
|
|
Loading…
Reference in a new issue