2024-11-04 18:59:50 +08:00
// 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"
)
2024-11-18 13:25:42 +08:00
func createEmoji ( ctx * RenderContext , content , name string ) * html . Node {
2024-11-04 18:59:50 +08:00
span := & html . Node {
Type : html . ElementNode ,
Data : atom . Span . String ( ) ,
Attr : [ ] html . Attribute { } ,
}
2024-11-18 13:25:42 +08:00
span . Attr = append ( span . Attr , ctx . RenderInternal . NodeSafeAttr ( "class" , "emoji" ) )
2024-11-04 18:59:50 +08:00
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
}
2024-11-18 13:25:42 +08:00
func createCustomEmoji ( ctx * RenderContext , alias string ) * html . Node {
2024-11-04 18:59:50 +08:00
span := & html . Node {
Type : html . ElementNode ,
Data : atom . Span . String ( ) ,
Attr : [ ] html . Attribute { } ,
}
2024-11-18 13:25:42 +08:00
span . Attr = append ( span . Attr , ctx . RenderInternal . NodeSafeAttr ( "class" , "emoji" ) )
2024-11-04 18:59:50 +08:00
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 ) {
2024-11-16 16:41:44 +08:00
m := globalVars ( ) . emojiShortCodeRegex . FindStringSubmatchIndex ( node . Data [ start : ] )
2024-11-04 18:59:50 +08:00
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 {
2024-11-18 13:25:42 +08:00
replaceContent ( node , m [ 0 ] , m [ 1 ] , createCustomEmoji ( ctx , alias ) )
2024-11-04 18:59:50 +08:00
node = node . NextSibling . NextSibling
start = 0
continue
}
continue
}
2024-11-18 13:25:42 +08:00
replaceContent ( node , m [ 0 ] , m [ 1 ] , createEmoji ( ctx , converted . Emoji , converted . Description ) )
2024-11-04 18:59:50 +08:00
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 {
2024-11-18 13:25:42 +08:00
replaceContent ( node , m [ 0 ] , m [ 1 ] , createEmoji ( ctx , codepoint , val . Description ) )
2024-11-04 18:59:50 +08:00
node = node . NextSibling . NextSibling
start = 0
}
}
}