summaryrefslogtreecommitdiff
path: root/app/parsers.go
diff options
context:
space:
mode:
Diffstat (limited to 'app/parsers.go')
-rwxr-xr-xapp/parsers.go110
1 files changed, 65 insertions, 45 deletions
diff --git a/app/parsers.go b/app/parsers.go
index 976c1b5..5f92658 100755
--- a/app/parsers.go
+++ b/app/parsers.go
@@ -9,6 +9,9 @@ import (
"golang.org/x/net/html"
)
+// ParseComments renders a comment thread, nesting replies under the comment they
+// answer. It returns a placeholder message rather than failing if the upstream
+// fetch errored.
func (s skunkyart) ParseComments(c devianter.Comments, daError devianter.Error) string {
if daError.RAW != nil {
return "Failed to fetch comments :("
@@ -29,9 +32,9 @@ func (s skunkyart) ParseComments(c devianter.Comments, daError devianter.Error)
cmmts.WriteString(`"><p id="`)
cmmts.WriteString(strconv.Itoa(x.ID))
cmmts.WriteString(`"><img src="`)
- cmmts.WriteString(UrlBuilder("media", "emojitar", x.User.Username, "?type=a"))
+ cmmts.WriteString(URLBuilder("media", "emojitar", x.User.Username, "?type=a"))
cmmts.WriteString(`" width="30px" height="30px"><a href="`)
- cmmts.WriteString(UrlBuilder("group_user", "?q=", x.User.Username, "&type=a"))
+ cmmts.WriteString(URLBuilder("group_user", "?q=", x.User.Username, "&type=a"))
cmmts.WriteString(`"><b`)
cmmts.WriteString(` class="`)
if x.User.Banned {
@@ -76,6 +79,9 @@ func (s skunkyart) ParseComments(c devianter.Comments, daError devianter.Error)
return cmmts.String()
}
+// DeviationList renders devs as an HTML grid, or as an Atom feed when the
+// request asked for one and allowAtom permits it. NSFW entries are dropped
+// unless the instance allows them. Passing content adds a navigation bar.
func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, content ...DeviationList) string {
if s.Atom && s.Page > 1 {
s.ReturnHTTPError(400)
@@ -86,16 +92,16 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con
for i, l := 0, len(devs); i < l; i++ {
data := &devs[i]
- if preview, fullview := ParseMedia(data.Media, 320), ParseMedia(data.Media); !(data.NSFW && !CFG.Nsfw) {
+ if preview, fullview := ParseMedia(data.Media, 320), ParseMedia(data.Media); !data.NSFW || CFG.Nsfw {
if allowAtom && s.Atom {
- s.Writer.Header().Add("Content-type", "application/atom+xml")
+ s.Writer.Header().Add("Content-Type", "application/atom+xml")
id := strconv.Itoa(data.ID)
listContent.WriteString(`<entry><author><name>`)
listContent.WriteString(data.Author.Username)
listContent.WriteString(`</name></author><title>`)
listContent.WriteString(data.Title)
listContent.WriteString(`</title><link rel="alternate" type="text/html" href="`)
- listContent.WriteString(UrlBuilder("post", data.Author.Username, "atom-"+id))
+ listContent.WriteString(URLBuilder("post", data.Author.Username, "atom-"+id))
listContent.WriteString(`"/><id>`)
listContent.WriteString(id)
listContent.WriteString(`</id><published>`)
@@ -106,7 +112,7 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con
listContent.WriteString(`</media:title><media:thumbinal url="`)
listContent.WriteString(preview)
listContent.WriteString(`"/></media:group><content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><a href="`)
- listContent.WriteString(ConvertDeviantArtUrlToSkunkyArt(data.Url))
+ listContent.WriteString(ConvertDeviantArtURLToSkunkyArt(data.Url))
listContent.WriteString(`"><img src="`)
listContent.WriteString(fullview)
listContent.WriteString(`"/></a><p>`)
@@ -124,7 +130,7 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con
listContent.WriteString(`<h1>[ TEXT ]</h1>`)
}
listContent.WriteString(`<br><a href="`)
- listContent.WriteString(ConvertDeviantArtUrlToSkunkyArt(data.Url))
+ listContent.WriteString(ConvertDeviantArtURLToSkunkyArt(data.Url))
listContent.WriteString(`">`)
listContent.WriteString(data.Author.Username)
listContent.WriteString(" - ")
@@ -149,11 +155,12 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con
list.WriteString(`<?xml version="1.0" encoding="UTF-8"?><feed xmlns:media="http://search.yahoo.com/mrss/" xmlns="http://www.w3.org/2005/Atom">`)
list.WriteString(`<title>`)
- if s.Type == 0 {
+ switch {
+ case s.Type == 0:
list.WriteString("Daily Deviations")
- } else if s.Type == 'g' && len(devs) != 0 {
+ case s.Type == 'g' && len(devs) != 0:
list.WriteString(devs[0].Author.Username)
- } else {
+ default:
list.WriteString("SkunkyArt")
}
list.WriteString(`</title>`)
@@ -181,19 +188,26 @@ func (s skunkyart) DeviationList(devs []devianter.Deviation, allowAtom bool, con
}
/* DESCRIPTION/COMMENT PARSER */
+
+// text is one styled run within a description: the rendered HTML, the raw source
+// it came from, and the offsets it spans in the original block.
type text struct {
- TXT string
- TXT_RAW string
- From int
- To int
+ Txt string
+ TxtRaw string
+ From int
+ To int
}
-// TODO: rewrite this whole mess
+// ParseDescription renders a DeviantArt description into HTML, handling both the
+// Draft.js-style JSON payload and the plain HTML markup DeviantArt returns, and
+// rewriting embedded links and artwork references to point at this instance.
+//
+// TODO: rewrite this whole mess.
func ParseDescription(dscr devianter.Text) string {
var parsedDescription strings.Builder
TagBuilder := func(content string, tags ...string) string {
l := len(tags)
- for x := 0; x < l; x++ {
+ for x := range l {
var htm strings.Builder
htm.WriteString("<")
htm.WriteString(tags[x])
@@ -208,7 +222,7 @@ func ParseDescription(dscr devianter.Text) string {
}
return content
}
- DeleteTrackingFromUrl := func(url string) string {
+ DeleteTrackingFromURL := func(url string) string {
if len(url) > 42 && url[:42] == "https://www.deviantart.com/users/outgoing?" {
url = url[42:]
}
@@ -220,30 +234,34 @@ func ParseDescription(dscr devianter.Text) string {
description[dl-1] == '}' {
var descr struct {
Blocks []struct {
- Text, Type string
+ Text string `json:"text"`
+ Type string `json:"type"`
InlineStyleRanges []struct {
- Offset, Length int
- Style string
- }
+ Offset int `json:"offset"`
+ Length int `json:"length"`
+ Style string `json:"style"`
+ } `json:"inlineStyleRanges"`
EntityRanges []struct {
- Offset, Length int
- Key int
- }
+ Offset int `json:"offset"`
+ Length int `json:"length"`
+ Key int `json:"key"`
+ } `json:"entityRanges"`
Data struct {
- TextAlignment string
- }
- }
+ TextAlignment string `json:"textAlignment"`
+ } `json:"data"`
+ } `json:"blocks"`
EntityMap map[string]struct {
- Type string
+ Type string `json:"type"`
Data struct {
- Url string
+ URL string `json:"url"`
Config struct {
- Aligment string
- Width int
- }
- Data devianter.Deviation
- }
- }
+ // "aligment" is DeviantArt's own spelling; do not correct it.
+ Aligment string `json:"aligment"`
+ Width int `json:"width"`
+ } `json:"config"`
+ Data devianter.Deviation `json:"data"`
+ } `json:"data"`
+ } `json:"entityMap"`
}
e := json.Unmarshal([]byte(description), &descr)
try(e)
@@ -252,8 +270,8 @@ func ParseDescription(dscr devianter.Text) string {
urls := make(map[int]string)
for n, x := range descr.EntityMap {
num, _ := strconv.Atoi(n)
- if x.Data.Url != "" {
- urls[num] = DeleteTrackingFromUrl(x.Data.Url)
+ if x.Data.URL != "" {
+ urls[num] = DeleteTrackingFromURL(x.Data.URL)
}
entities[num] = x.Data.Data
}
@@ -278,10 +296,10 @@ func ParseDescription(dscr devianter.Text) string {
FT := Styles.From * Styles.To
tags[FT] = append(tags[FT], rngs.Style)
}
- for n := 0; n < len(Styles); n++ {
+ for n := range Styles {
Styles := &Styles[n]
- Styles.TXT_RAW = x.Text[Styles.From:Styles.To]
- Styles.TXT = TagBuilder(Styles.TXT_RAW, tags[Styles.From*Styles.To]...)
+ Styles.TxtRaw = x.Text[Styles.From:Styles.To]
+ Styles.Txt = TagBuilder(Styles.TxtRaw, tags[Styles.From*Styles.To]...)
}
}
@@ -290,7 +308,7 @@ func ParseDescription(dscr devianter.Text) string {
if len(x.EntityRanges) != 0 {
d := entities[x.EntityRanges[0].Key]
parsedDescription.WriteString(`<a href="`)
- parsedDescription.WriteString(ConvertDeviantArtUrlToSkunkyArt(d.Url))
+ parsedDescription.WriteString(ConvertDeviantArtURLToSkunkyArt(d.Url))
parsedDescription.WriteString(`"><img width="50%" src="`)
parsedDescription.WriteString(ParseMedia(d.Media))
parsedDescription.WriteString(`" title="`)
@@ -314,10 +332,10 @@ func ParseDescription(dscr devianter.Text) string {
parsedDescription.WriteString(`<a target="_blank" href="`)
parsedDescription.WriteString(urls[ra.Key])
parsedDescription.WriteString(`">`)
- parsedDescription.WriteString(r.TXT)
+ parsedDescription.WriteString(r.Txt)
parsedDescription.WriteString(`</a>`)
} else if l > n+1 {
- parsedDescription.WriteString(r.TXT)
+ parsedDescription.WriteString(r.Txt)
}
parsedDescription.WriteString(TagBuilder(tag, x.Text[r.To:]))
}
@@ -332,13 +350,15 @@ func ParseDescription(dscr devianter.Text) string {
switch tt.Next() {
case html.ErrorToken:
return parsedDescription.String()
+ case html.CommentToken, html.DoctypeToken:
+ // No renderable content; skip.
case html.StartTagToken, html.EndTagToken, html.SelfClosingTagToken:
token := tt.Token()
switch token.Data {
case "a":
for _, a := range token.Attr {
if a.Key == "href" {
- url := DeleteTrackingFromUrl(a.Val)
+ url := DeleteTrackingFromURL(a.Val)
parsedDescription.WriteString(`<a target="_blank" href="`)
parsedDescription.WriteString(url)
parsedDescription.WriteString(`">`)
@@ -352,7 +372,7 @@ func ParseDescription(dscr devianter.Text) string {
switch a.Key {
case "src":
if len(a.Val) > 9 && a.Val[8:9] == "e" {
- uri = UrlBuilder("media", "emojitar", a.Val[37:len(a.Val)-4], "?type=e")
+ uri = URLBuilder("media", "emojitar", a.Val[37:len(a.Val)-4], "?type=e")
}
case "title":
title = a.Val