mirror of https://github.com/go-gitea/gitea.git
116 lines
2.9 KiB
Go
116 lines
2.9 KiB
Go
|
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||
|
// SPDX-License-Identifier: MIT
|
||
|
|
||
|
package markup
|
||
|
|
||
|
import (
|
||
|
"strings"
|
||
|
|
||
|
"code.gitea.io/gitea/modules/emoji"
|
||
|
"code.gitea.io/gitea/modules/setting"
|
||
|
|
||
|
"golang.org/x/net/html"
|
||
|
"golang.org/x/net/html/atom"
|
||
|
)
|
||
|
|
||
|
func createEmoji(content, class, name string) *html.Node {
|
||
|
span := &html.Node{
|
||
|
Type: html.ElementNode,
|
||
|
Data: atom.Span.String(),
|
||
|
Attr: []html.Attribute{},
|
||
|
}
|
||
|
if class != "" {
|
||
|
span.Attr = append(span.Attr, html.Attribute{Key: "class", Val: class})
|
||
|
}
|
||
|
if name != "" {
|
||
|
span.Attr = append(span.Attr, html.Attribute{Key: "aria-label", Val: name})
|
||
|
}
|
||
|
|
||
|
text := &html.Node{
|
||
|
Type: html.TextNode,
|
||
|
Data: content,
|
||
|
}
|
||
|
|
||
|
span.AppendChild(text)
|
||
|
return span
|
||
|
}
|
||
|
|
||
|
func createCustomEmoji(alias string) *html.Node {
|
||
|
span := &html.Node{
|
||
|
Type: html.ElementNode,
|
||
|
Data: atom.Span.String(),
|
||
|
Attr: []html.Attribute{},
|
||
|
}
|
||
|
span.Attr = append(span.Attr, html.Attribute{Key: "class", Val: "emoji"})
|
||
|
span.Attr = append(span.Attr, html.Attribute{Key: "aria-label", Val: alias})
|
||
|
|
||
|
img := &html.Node{
|
||
|
Type: html.ElementNode,
|
||
|
DataAtom: atom.Img,
|
||
|
Data: "img",
|
||
|
Attr: []html.Attribute{},
|
||
|
}
|
||
|
img.Attr = append(img.Attr, html.Attribute{Key: "alt", Val: ":" + alias + ":"})
|
||
|
img.Attr = append(img.Attr, html.Attribute{Key: "src", Val: setting.StaticURLPrefix + "/assets/img/emoji/" + alias + ".png"})
|
||
|
|
||
|
span.AppendChild(img)
|
||
|
return span
|
||
|
}
|
||
|
|
||
|
// emojiShortCodeProcessor for rendering text like :smile: into emoji
|
||
|
func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) {
|
||
|
start := 0
|
||
|
next := node.NextSibling
|
||
|
for node != nil && node != next && start < len(node.Data) {
|
||
|
m := emojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:])
|
||
|
if m == nil {
|
||
|
return
|
||
|
}
|
||
|
m[0] += start
|
||
|
m[1] += start
|
||
|
|
||
|
start = m[1]
|
||
|
|
||
|
alias := node.Data[m[0]:m[1]]
|
||
|
alias = strings.ReplaceAll(alias, ":", "")
|
||
|
converted := emoji.FromAlias(alias)
|
||
|
if converted == nil {
|
||
|
// check if this is a custom reaction
|
||
|
if _, exist := setting.UI.CustomEmojisMap[alias]; exist {
|
||
|
replaceContent(node, m[0], m[1], createCustomEmoji(alias))
|
||
|
node = node.NextSibling.NextSibling
|
||
|
start = 0
|
||
|
continue
|
||
|
}
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
replaceContent(node, m[0], m[1], createEmoji(converted.Emoji, "emoji", converted.Description))
|
||
|
node = node.NextSibling.NextSibling
|
||
|
start = 0
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// emoji processor to match emoji and add emoji class
|
||
|
func emojiProcessor(ctx *RenderContext, node *html.Node) {
|
||
|
start := 0
|
||
|
next := node.NextSibling
|
||
|
for node != nil && node != next && start < len(node.Data) {
|
||
|
m := emoji.FindEmojiSubmatchIndex(node.Data[start:])
|
||
|
if m == nil {
|
||
|
return
|
||
|
}
|
||
|
m[0] += start
|
||
|
m[1] += start
|
||
|
|
||
|
codepoint := node.Data[m[0]:m[1]]
|
||
|
start = m[1]
|
||
|
val := emoji.FromCode(codepoint)
|
||
|
if val != nil {
|
||
|
replaceContent(node, m[0], m[1], createEmoji(codepoint, "emoji", val.Description))
|
||
|
node = node.NextSibling.NextSibling
|
||
|
start = 0
|
||
|
}
|
||
|
}
|
||
|
}
|