Skip to content
quarry Artifact [b0b71034fd]
Login

Artifact [b0b71034fd]

Artifact b0b71034fd294217a362326e6ff97c5b025bc502543c3f20a7ecd8f1eb09ef0a:


package markup

import (
	"fmt"
	"regexp"
	"strings"

	"golang.org/x/net/html"
	"golang.org/x/net/html/atom"
)

// LandingRef is what a LandingDescriber knows about the landing a !N
// names: its number and where it stands.
type LandingRef struct {
	Number int    // the number the reader sees
	State  string // "open", "accepted" or "rejected"
}

// LandingDescriber is an OPTIONAL upgrade on Resolver: a !N that names a
// landing reads as that landing's state icon and number.
type LandingDescriber interface {
	DescribeLanding(t RefToken) (LandingRef, bool)
}

// refIcon is the glyph a resolved reference wears in front of its
// number: the state icon the ticket and landing lists already draw,
// carried as a sprite reference for the pages and as a key (data-ic) for
// the email renderer, which swaps it for a PNG because mail drops SVG.
type refIcon struct {
	Key    string // "ticket-closed", "landing-open", ...
	Symbol string // the sprite symbol the lists use
	Tone   string // the list's colour class for that state
	Label  string // "ticket 305, closed"
}

func ticketIcon(tr TicketRef, number string) refIcon {
	word := tr.State
	if word == "" {
		word = "open"
	}
	ic := refIcon{Key: "ticket-" + word, Symbol: "i-tkt-open", Tone: "g-open"}
	switch word {
	case "review":
		ic.Tone = "g-review"
	case "closed":
		ic.Symbol, ic.Tone = "i-tkt-done", "g-done"
	}
	ic.Label = "ticket, " + word
	if number != "" && !hexID.MatchString(number) {
		ic.Label = "ticket " + number + ", " + word
	}
	return ic
}

func landingIcon(lr LandingRef) refIcon {
	ic := refIcon{Key: "landing-" + lr.State, Symbol: "i-tkt-open", Tone: "g-open"}
	if lr.State != "open" {
		ic.Symbol, ic.Tone = "i-tkt-done", "g-done"
	}
	ic.Label = fmt.Sprintf("landing %d, %s", lr.Number, lr.State)
	return ic
}

// wear puts the icon inside the anchor, ahead of its number, and names
// the reference for a reader who cannot see the glyph.
func (ic refIcon) wear(a *html.Node) {
	svg := &html.Node{Type: html.ElementNode, Data: "svg", DataAtom: atom.Svg, Attr: []html.Attribute{
		{Key: "class", Val: "ic ref-ic " + ic.Tone}, {Key: "aria-hidden", Val: "true"}}}
	svg.AppendChild(&html.Node{Type: html.ElementNode, Data: "use", Attr: []html.Attribute{{Key: "href", Val: "#" + ic.Symbol}}})
	a.InsertBefore(svg, a.FirstChild)
	a.Attr = append(a.Attr,
		html.Attribute{Key: "data-ic", Val: ic.Key},
		html.Attribute{Key: "aria-label", Val: ic.Label},
		html.Attribute{Key: "title", Val: ic.Label})
}

var refNumber = regexp.MustCompile(`(?i)^(?:ticket\s+)?(?:([a-zA-Z0-9][a-zA-Z0-9._-]*):)?[#!]([1-9][0-9]*)$`)

// numberLabel is a typed reference reduced to what follows its sigil:
// the icon says what kind of thing it is, so "#305" and "!768" show
// 305 and 768 (a repo prefix stays: "demo:768"). Anything else the
// author wrote is left as it is.
func numberLabel(label string) string {
	m := refNumber.FindStringSubmatch(strings.TrimSpace(label))
	if m == nil {
		return label
	}
	if m[1] != "" {
		return m[1] + ":" + m[2]
	}
	return m[2]
}