mirror of
https://github.com/abhinavxd/libredesk.git
synced 2026-09-10 22:25:39 +00:00
141 lines
3.1 KiB
Go
141 lines
3.1 KiB
Go
package stringutil
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"golang.org/x/net/html"
|
|
)
|
|
|
|
// prepareHTMLForEmbedding rewrites structural markup (tables, link hrefs) into text that survives flattening.
|
|
func prepareHTMLForEmbedding(htmlContent string) string {
|
|
doc, err := html.Parse(strings.NewReader(htmlContent))
|
|
if err != nil {
|
|
return htmlContent
|
|
}
|
|
inlineAnchorMarkdown(doc)
|
|
flattenTables(doc)
|
|
|
|
var b strings.Builder
|
|
if err := html.Render(&b, doc); err != nil {
|
|
return htmlContent
|
|
}
|
|
return b.String()
|
|
}
|
|
|
|
// flattenTables turns each table into a <pre> of one header-labelled line per row, which also keeps it unsplittable.
|
|
func flattenTables(n *html.Node) {
|
|
var tables []*html.Node
|
|
var collect func(*html.Node)
|
|
collect = func(node *html.Node) {
|
|
if node.Type == html.ElementNode && node.Data == "table" {
|
|
tables = append(tables, node)
|
|
return
|
|
}
|
|
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
|
collect(c)
|
|
}
|
|
}
|
|
collect(n)
|
|
|
|
for _, table := range tables {
|
|
text := tableToText(table)
|
|
if text == "" {
|
|
continue
|
|
}
|
|
pre := &html.Node{Type: html.ElementNode, Data: "pre"}
|
|
pre.AppendChild(&html.Node{Type: html.TextNode, Data: text})
|
|
table.Parent.InsertBefore(pre, table)
|
|
table.Parent.RemoveChild(table)
|
|
}
|
|
}
|
|
|
|
func tableToText(table *html.Node) string {
|
|
rows := collectRows(table)
|
|
if len(rows) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var headers []string
|
|
start := 0
|
|
if isHeaderRow(rows[0]) {
|
|
headers = rowCells(rows[0])
|
|
start = 1
|
|
}
|
|
|
|
var lines []string
|
|
for _, row := range rows[start:] {
|
|
cells := rowCells(row)
|
|
if len(cells) == 0 {
|
|
continue
|
|
}
|
|
parts := make([]string, 0, len(cells))
|
|
for i, cell := range cells {
|
|
if i < len(headers) && headers[i] != "" {
|
|
parts = append(parts, headers[i]+": "+cell)
|
|
continue
|
|
}
|
|
parts = append(parts, cell)
|
|
}
|
|
lines = append(lines, strings.Join(parts, " | "))
|
|
}
|
|
if len(headers) > 0 && len(lines) == 0 {
|
|
lines = append(lines, strings.Join(headers, " | "))
|
|
}
|
|
if caption := strings.Join(strings.Fields(captionText(table)), " "); caption != "" {
|
|
lines = append([]string{caption}, lines...)
|
|
}
|
|
return strings.Join(lines, "\n")
|
|
}
|
|
|
|
func collectRows(table *html.Node) []*html.Node {
|
|
var rows []*html.Node
|
|
var walk func(*html.Node)
|
|
walk = func(node *html.Node) {
|
|
if node.Type == html.ElementNode && node.Data == "tr" {
|
|
rows = append(rows, node)
|
|
return
|
|
}
|
|
for c := node.FirstChild; c != nil; c = c.NextSibling {
|
|
walk(c)
|
|
}
|
|
}
|
|
walk(table)
|
|
return rows
|
|
}
|
|
|
|
func rowCells(row *html.Node) []string {
|
|
var cells []string
|
|
for c := row.FirstChild; c != nil; c = c.NextSibling {
|
|
if c.Type != html.ElementNode || (c.Data != "td" && c.Data != "th") {
|
|
continue
|
|
}
|
|
cells = append(cells, strings.Join(strings.Fields(nodeText(c)), " "))
|
|
}
|
|
return cells
|
|
}
|
|
|
|
func captionText(table *html.Node) string {
|
|
for c := table.FirstChild; c != nil; c = c.NextSibling {
|
|
if c.Type == html.ElementNode && c.Data == "caption" {
|
|
return nodeText(c)
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
func isHeaderRow(row *html.Node) bool {
|
|
seen := false
|
|
for c := row.FirstChild; c != nil; c = c.NextSibling {
|
|
if c.Type != html.ElementNode {
|
|
continue
|
|
}
|
|
switch c.Data {
|
|
case "th":
|
|
seen = true
|
|
case "td":
|
|
return false
|
|
}
|
|
}
|
|
return seen
|
|
}
|