2023-05-27 17:27:56 +00:00
|
|
|
package converters
|
|
|
|
|
|
|
|
import (
|
2023-05-28 09:10:23 +00:00
|
|
|
"fmt"
|
2023-05-28 14:26:48 +00:00
|
|
|
"html"
|
2023-05-27 17:27:56 +00:00
|
|
|
"sort"
|
2023-05-28 09:10:23 +00:00
|
|
|
"strings"
|
2023-05-28 11:39:37 +00:00
|
|
|
"unicode/utf16"
|
2023-05-27 17:27:56 +00:00
|
|
|
|
|
|
|
"github.com/medium.rip/pkg/entities"
|
|
|
|
)
|
|
|
|
|
|
|
|
type RangeWithMarkup struct {
|
2023-05-28 10:43:44 +00:00
|
|
|
Range []int
|
2023-05-27 17:27:56 +00:00
|
|
|
Markups []entities.Markup
|
|
|
|
}
|
|
|
|
|
|
|
|
func unique(intSlice []int) []int {
|
2023-05-28 10:43:44 +00:00
|
|
|
keys := make(map[int]bool)
|
|
|
|
list := []int{}
|
|
|
|
for _, entry := range intSlice {
|
|
|
|
if _, value := keys[entry]; !value {
|
|
|
|
keys[entry] = true
|
|
|
|
list = append(list, entry)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list
|
2023-05-27 17:27:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func ranges(text string, markups []entities.Markup) []RangeWithMarkup {
|
|
|
|
ranges := make([]RangeWithMarkup, 0)
|
|
|
|
|
|
|
|
// first, get all the borders of the markups
|
|
|
|
markupBoundaries := make([]int, 0)
|
|
|
|
for _, m := range markups {
|
|
|
|
markupBoundaries = append(markupBoundaries, []int{int(m.Start), int(m.End)}...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// include the start and end indexes of the text
|
|
|
|
markupBoundaries = append([]int{0}, markupBoundaries...)
|
2023-05-28 11:39:37 +00:00
|
|
|
markupBoundaries = append(markupBoundaries, len(utf16.Encode([]rune(text))))
|
2023-05-27 17:27:56 +00:00
|
|
|
|
|
|
|
// remove duplicates
|
|
|
|
markupBoundaries = unique(markupBoundaries)
|
|
|
|
|
|
|
|
// sort slice
|
|
|
|
sort.Slice(markupBoundaries, func(i, j int) bool {
|
|
|
|
return markupBoundaries[i] < markupBoundaries[j]
|
|
|
|
})
|
|
|
|
|
|
|
|
// attach markup to every range
|
|
|
|
for i := 0; i < len(markupBoundaries)-1; i++ {
|
|
|
|
start := markupBoundaries[i]
|
|
|
|
end := markupBoundaries[i+1]
|
|
|
|
|
|
|
|
// check if this markup is covered by the range
|
|
|
|
coveredMarkups := make([]entities.Markup, 0)
|
|
|
|
for _, m := range markups {
|
2023-05-28 10:43:44 +00:00
|
|
|
if (int(m.Start) >= start && int(m.Start) < end) || (int(m.End-1) >= start && int(m.End-1) < end) {
|
2023-05-27 17:27:56 +00:00
|
|
|
coveredMarkups = append(coveredMarkups, m)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// append the range
|
|
|
|
ranges = append(ranges, RangeWithMarkup{
|
2023-05-28 10:43:44 +00:00
|
|
|
Range: []int{start, end},
|
2023-05-27 17:27:56 +00:00
|
|
|
Markups: coveredMarkups,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return ranges
|
|
|
|
}
|
|
|
|
|
2023-05-28 10:43:44 +00:00
|
|
|
func ConvertMarkup(text string, markups []entities.Markup) string {
|
2023-05-28 14:26:48 +00:00
|
|
|
if len(markups) == 0 {
|
|
|
|
return html.EscapeString(text)
|
|
|
|
}
|
|
|
|
|
2023-05-28 09:10:23 +00:00
|
|
|
var markedUp strings.Builder
|
|
|
|
for _, r := range ranges(text, markups) {
|
2023-05-28 11:39:37 +00:00
|
|
|
// handle utf-16
|
|
|
|
utf16Text := utf16.Encode([]rune(text))
|
|
|
|
ranged := utf16Text[r.Range[0]:r.Range[1]]
|
|
|
|
textToWrap := string(utf16.Decode(ranged))
|
2023-05-28 10:43:44 +00:00
|
|
|
markedUp.WriteString(wrapInMarkups(textToWrap, r.Markups))
|
2023-05-28 09:10:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return markedUp.String()
|
|
|
|
}
|
|
|
|
|
2023-05-28 10:43:44 +00:00
|
|
|
func wrapInMarkups(child string, markups []entities.Markup) string {
|
2023-05-28 09:10:23 +00:00
|
|
|
if len(markups) == 0 {
|
|
|
|
return child
|
|
|
|
}
|
|
|
|
markedUp := markupNodeInContainer(child, markups[0])
|
2023-05-28 10:43:44 +00:00
|
|
|
return wrapInMarkups(markedUp, markups[1:])
|
2023-05-28 09:10:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func markupNodeInContainer(child string, markup entities.Markup) string {
|
|
|
|
switch markup.Type {
|
|
|
|
case "A":
|
|
|
|
if markup.Href != nil {
|
2023-05-28 14:26:48 +00:00
|
|
|
return fmt.Sprintf(`<a href="%s">%s</a>`, *markup.Href, html.EscapeString(child))
|
2023-05-28 09:10:23 +00:00
|
|
|
} else if markup.UserID != nil {
|
2023-05-28 14:26:48 +00:00
|
|
|
return fmt.Sprintf(`<a href="https://medium.com/u/%s">%s</a>`, markup.UserID, html.EscapeString(child))
|
2023-05-28 09:10:23 +00:00
|
|
|
}
|
|
|
|
case "CODE":
|
2023-05-28 14:26:48 +00:00
|
|
|
return fmt.Sprintf(`<code>%s</code>`, html.EscapeString(child))
|
2023-05-28 09:10:23 +00:00
|
|
|
case "EM":
|
2023-05-28 14:26:48 +00:00
|
|
|
return fmt.Sprintf(`<em>%s</em>`, html.EscapeString(child))
|
2023-05-28 09:10:23 +00:00
|
|
|
case "STRONG":
|
2023-05-28 14:26:48 +00:00
|
|
|
return fmt.Sprintf(`<strong>%s</strong>`, html.EscapeString(child))
|
2023-05-28 09:10:23 +00:00
|
|
|
default:
|
2023-05-28 14:26:48 +00:00
|
|
|
return fmt.Sprintf(`<code>%s</code>`, html.EscapeString(child))
|
2023-05-28 09:10:23 +00:00
|
|
|
}
|
2023-05-28 14:26:48 +00:00
|
|
|
return html.EscapeString(child)
|
2023-05-28 10:43:44 +00:00
|
|
|
}
|