gitea源码

html_emoji.go 2.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. // Copyright 2024 The Gitea Authors. All rights reserved.
  2. // SPDX-License-Identifier: MIT
  3. package markup
  4. import (
  5. "strings"
  6. "code.gitea.io/gitea/modules/emoji"
  7. "code.gitea.io/gitea/modules/setting"
  8. "golang.org/x/net/html"
  9. "golang.org/x/net/html/atom"
  10. )
  11. func createEmoji(ctx *RenderContext, content, name string) *html.Node {
  12. span := &html.Node{
  13. Type: html.ElementNode,
  14. Data: atom.Span.String(),
  15. Attr: []html.Attribute{},
  16. }
  17. span.Attr = append(span.Attr, ctx.RenderInternal.NodeSafeAttr("class", "emoji"))
  18. if name != "" {
  19. span.Attr = append(span.Attr, html.Attribute{Key: "aria-label", Val: name})
  20. }
  21. text := &html.Node{
  22. Type: html.TextNode,
  23. Data: content,
  24. }
  25. span.AppendChild(text)
  26. return span
  27. }
  28. func createCustomEmoji(ctx *RenderContext, alias string) *html.Node {
  29. span := &html.Node{
  30. Type: html.ElementNode,
  31. Data: atom.Span.String(),
  32. Attr: []html.Attribute{},
  33. }
  34. span.Attr = append(span.Attr, ctx.RenderInternal.NodeSafeAttr("class", "emoji"))
  35. span.Attr = append(span.Attr, html.Attribute{Key: "aria-label", Val: alias})
  36. img := &html.Node{
  37. Type: html.ElementNode,
  38. DataAtom: atom.Img,
  39. Data: "img",
  40. Attr: []html.Attribute{},
  41. }
  42. img.Attr = append(img.Attr, html.Attribute{Key: "alt", Val: ":" + alias + ":"})
  43. img.Attr = append(img.Attr, html.Attribute{Key: "src", Val: setting.StaticURLPrefix + "/assets/img/emoji/" + alias + ".png"})
  44. span.AppendChild(img)
  45. return span
  46. }
  47. // emojiShortCodeProcessor for rendering text like :smile: into emoji
  48. func emojiShortCodeProcessor(ctx *RenderContext, node *html.Node) {
  49. start := 0
  50. next := node.NextSibling
  51. for node != nil && node != next && start < len(node.Data) {
  52. m := globalVars().emojiShortCodeRegex.FindStringSubmatchIndex(node.Data[start:])
  53. if m == nil {
  54. return
  55. }
  56. m[0] += start
  57. m[1] += start
  58. start = m[1]
  59. alias := node.Data[m[0]:m[1]]
  60. alias = strings.ReplaceAll(alias, ":", "")
  61. converted := emoji.FromAlias(alias)
  62. if converted == nil {
  63. // check if this is a custom reaction
  64. if _, exist := setting.UI.CustomEmojisMap[alias]; exist {
  65. replaceContent(node, m[0], m[1], createCustomEmoji(ctx, alias))
  66. node = node.NextSibling.NextSibling
  67. start = 0
  68. continue
  69. }
  70. continue
  71. }
  72. replaceContent(node, m[0], m[1], createEmoji(ctx, converted.Emoji, converted.Description))
  73. node = node.NextSibling.NextSibling
  74. start = 0
  75. }
  76. }
  77. // emoji processor to match emoji and add emoji class
  78. func emojiProcessor(ctx *RenderContext, node *html.Node) {
  79. start := 0
  80. next := node.NextSibling
  81. for node != nil && node != next && start < len(node.Data) {
  82. m := emoji.FindEmojiSubmatchIndex(node.Data[start:])
  83. if m == nil {
  84. return
  85. }
  86. m[0] += start
  87. m[1] += start
  88. codepoint := node.Data[m[0]:m[1]]
  89. start = m[1]
  90. val := emoji.FromCode(codepoint)
  91. if val != nil {
  92. replaceContent(node, m[0], m[1], createEmoji(ctx, codepoint, val.Description))
  93. node = node.NextSibling.NextSibling
  94. start = 0
  95. }
  96. }
  97. }