- POST /_journal/api/v1/images/alt {dry_run, changes} sets alt on the
matching img of default or translated content through the tokenizer,
all or nothing (422), with a RecordSaved per changed post
- MCP journal_get_settings, journal_update_settings, journal_fix_headings
(manage_settings) and journal_set_image_alt (access_posts, destructive)
- journal:fix-headings help names its output and the refusal
93 lines
2.2 KiB
Go
93 lines
2.2 KiB
Go
package classes
|
|
|
|
import (
|
|
"bytes"
|
|
"strings"
|
|
|
|
xhtml "golang.org/x/net/html"
|
|
)
|
|
|
|
// SetImageAlt sets the alt attribute of every img in html whose src
|
|
// attribute (unescaped) equals src, adding it when missing. It returns the
|
|
// new HTML, the number of matching images and the alt the first match had
|
|
// (nil when it had none). It works on the x/net/html tokenizer: a matching
|
|
// img tag is written again from its token, so every attribute value,
|
|
// alt included, is escaped by the tokenizer and never concatenated; every
|
|
// other byte of html is copied as it is. An empty alt marks a decorative
|
|
// image (alt="").
|
|
func SetImageAlt(html, src, alt string) (string, int, *string) {
|
|
if src == "" || !containsFold(html, "<img") {
|
|
return html, 0, nil
|
|
}
|
|
z := xhtml.NewTokenizer(strings.NewReader(html))
|
|
var b bytes.Buffer
|
|
b.Grow(len(html) + len(alt))
|
|
matches := 0
|
|
var old *string
|
|
for {
|
|
tt := z.Next()
|
|
if tt == xhtml.ErrorToken {
|
|
break
|
|
}
|
|
raw := z.Raw()
|
|
if tt == xhtml.StartTagToken || tt == xhtml.SelfClosingTagToken {
|
|
// Raw is copied before Token, which reads the tag; Token is
|
|
// the only reader of the tag (TagName would consume it).
|
|
kept := append([]byte(nil), raw...)
|
|
tok := z.Token()
|
|
if tok.Data == "img" && imgSrc(tok) == src {
|
|
prev := setAlt(&tok, alt)
|
|
if matches == 0 {
|
|
old = prev
|
|
}
|
|
matches++
|
|
b.WriteString(tok.String())
|
|
continue
|
|
}
|
|
b.Write(kept)
|
|
continue
|
|
}
|
|
b.Write(raw)
|
|
}
|
|
if matches == 0 {
|
|
return html, 0, nil
|
|
}
|
|
return b.String(), matches, old
|
|
}
|
|
|
|
func imgSrc(tok xhtml.Token) string {
|
|
for _, a := range tok.Attr {
|
|
if a.Namespace == "" && a.Key == "src" {
|
|
return a.Val
|
|
}
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// setAlt sets tok's alt to alt and returns the previous value (nil when
|
|
// there was none). Repeated alt attributes collapse to one.
|
|
func setAlt(tok *xhtml.Token, alt string) *string {
|
|
var prev *string
|
|
attrs := tok.Attr[:0]
|
|
set := false
|
|
for _, a := range tok.Attr {
|
|
if a.Namespace == "" && a.Key == "alt" {
|
|
if prev == nil {
|
|
v := a.Val
|
|
prev = &v
|
|
}
|
|
if set {
|
|
continue
|
|
}
|
|
a.Val = alt
|
|
set = true
|
|
}
|
|
attrs = append(attrs, a)
|
|
}
|
|
if !set {
|
|
attrs = append(attrs, xhtml.Attribute{Key: "alt", Val: alt})
|
|
}
|
|
tok.Attr = attrs
|
|
return prev
|
|
}
|