Don't require !embed, use placeholders in previews

This commit is contained in:
Ben Visness 2021-06-20 13:01:40 -05:00
parent f1ccbc53d7
commit 4d9ef5917e
5 changed files with 78 additions and 31 deletions

Binary file not shown.

View File

@ -14,55 +14,55 @@ import (
) )
var ( var (
REEmbedTag = regexp.MustCompile(`^!embed\((?P<url>.+?)\)`)
// TODO: Timestamped youtube embeds // TODO: Timestamped youtube embeds
REYoutubeLong = regexp.MustCompile(`^https://www\.youtube\.com/watch?.*v=(?P<vid>[a-zA-Z0-9_-]{11})`) REYoutubeLong = regexp.MustCompile(`^https://www\.youtube\.com/watch?.*v=(?P<vid>[a-zA-Z0-9_-]{11})`)
REYoutubeShort = regexp.MustCompile(`^https://youtu\.be/(?P<vid>[a-zA-Z0-9_-]{11})`) REYoutubeShort = regexp.MustCompile(`^https://youtu\.be/(?P<vid>[a-zA-Z0-9_-]{11})`)
REVimeo = regexp.MustCompile(`^https://vimeo\.com/(?P<vid>\d+)`) REVimeo = regexp.MustCompile(`^https://vimeo\.com/(?P<vid>\d+)`)
// TODO: Twitch VODs / clips
// TODO: Desmos
// TODO: Tweets
) )
// ---------------------- // ----------------------
// Parser and delimiters // Parser and delimiters
// ---------------------- // ----------------------
type embedParser struct{} type embedParser struct {
Preview bool
func NewEmbedParser() parser.BlockParser {
return embedParser{}
} }
var _ parser.BlockParser = embedParser{}
func (s embedParser) Trigger() []byte { func (s embedParser) Trigger() []byte {
return []byte{'!'} return nil
} }
func (s embedParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) { func (s embedParser) Open(parent ast.Node, reader text.Reader, pc parser.Context) (ast.Node, parser.State) {
restOfLine, _ := reader.PeekLine() restOfLine, _ := reader.PeekLine()
urlMatch := REEmbedTag.FindSubmatch(restOfLine)
if urlMatch == nil {
return nil, parser.NoChildren
}
url := urlMatch[REEmbedTag.SubexpIndex("url")]
html := "" html := ""
if ytLongMatch := extract(REYoutubeLong, url, "vid"); ytLongMatch != nil { var match []byte
html = makeYoutubeEmbed(string(ytLongMatch)) if ytLongMatch := extract(REYoutubeLong, restOfLine, "vid"); ytLongMatch != nil {
} else if ytShortMatch := extract(REYoutubeShort, url, "vid"); ytShortMatch != nil { match = ytLongMatch
html = makeYoutubeEmbed(string(ytShortMatch)) html = makeYoutubeEmbed(string(ytLongMatch), s.Preview)
} else if vimeoMatch := extract(REVimeo, url, "vid"); vimeoMatch != nil { } else if ytShortMatch := extract(REYoutubeShort, restOfLine, "vid"); ytShortMatch != nil {
html = ` match = ytShortMatch
html = makeYoutubeEmbed(string(ytShortMatch), s.Preview)
} else if vimeoMatch := extract(REVimeo, restOfLine, "vid"); vimeoMatch != nil {
match = vimeoMatch
html = s.previewOrLegitEmbed("Vimeo", `
<div class="mw6"> <div class="mw6">
<div class="aspect-ratio aspect-ratio--16x9"> <div class="aspect-ratio aspect-ratio--16x9">
<iframe class="aspect-ratio--object" src="https://player.vimeo.com/video/` + string(vimeoMatch) + `" frameborder="0" allow="fullscreen; picture-in-picture" allowfullscreen></iframe> <iframe class="aspect-ratio--object" src="https://player.vimeo.com/video/`+string(vimeoMatch)+`" frameborder="0" allow="fullscreen; picture-in-picture" allowfullscreen></iframe>
</div> </div>
</div>` </div>`)
} }
if html == "" { if html == "" {
return nil, parser.NoChildren return nil, parser.NoChildren
} }
reader.Advance(len(urlMatch[0])) reader.Advance(len(match))
return NewEmbed(html), parser.NoChildren return NewEmbed(html), parser.NoChildren
} }
@ -80,6 +80,22 @@ func (s embedParser) CanAcceptIndentedLine() bool {
return false return false
} }
func (s embedParser) previewOrLegitEmbed(name string, legitHtml string) string {
if s.Preview {
return `
<div class="mw6">
<div class="aspect-ratio aspect-ratio--16x9">
<div class="aspect-ratio--object ba b--dimmest bg-light-gray i black flex items-center justify-center">
` + name + ` embed
</div>
</div>
</div>
`
}
return legitHtml
}
func extract(re *regexp.Regexp, src []byte, subexpName string) []byte { func extract(re *regexp.Regexp, src []byte, subexpName string) []byte {
m := re.FindSubmatch(src) m := re.FindSubmatch(src)
if m == nil { if m == nil {
@ -88,13 +104,22 @@ func extract(re *regexp.Regexp, src []byte, subexpName string) []byte {
return m[re.SubexpIndex(subexpName)] return m[re.SubexpIndex(subexpName)]
} }
func makeYoutubeEmbed(vid string) string { func makeYoutubeEmbed(vid string, preview bool) string {
return ` if preview {
return `
<div class="mw6">
<img src="https://img.youtube.com/vi/` + vid + `/hqdefault.jpg">
</div>
`
} else {
return `
<div class="mw6"> <div class="mw6">
<div class="aspect-ratio aspect-ratio--16x9"> <div class="aspect-ratio aspect-ratio--16x9">
<iframe class="aspect-ratio--object" src="https://www.youtube-nocookie.com/embed/` + vid + `" frameborder="0" allowfullscreen></iframe> <iframe class="aspect-ratio--object" src="https://www.youtube-nocookie.com/embed/` + vid + `" frameborder="0" allowfullscreen></iframe>
</div> </div>
</div>` </div>
`
}
} }
// ---------------------- // ----------------------
@ -155,11 +180,13 @@ func (r *EmbedHTMLRenderer) renderEmbed(w util.BufWriter, source []byte, n gast.
// Extension // Extension
// ---------------------- // ----------------------
type EmbedExtension struct{} type EmbedExtension struct {
Preview bool
}
func (e EmbedExtension) Extend(m goldmark.Markdown) { func (e EmbedExtension) Extend(m goldmark.Markdown) {
m.Parser().AddOptions(parser.WithBlockParsers( m.Parser().AddOptions(parser.WithBlockParsers(
util.Prioritized(NewEmbedParser(), 500), util.Prioritized(embedParser{Preview: e.Preview}, 500),
)) ))
m.Renderer().AddOptions(renderer.WithNodeRenderers( m.Renderer().AddOptions(renderer.WithNodeRenderers(
util.Prioritized(NewEmbedHTMLRenderer(), 500), util.Prioritized(NewEmbedHTMLRenderer(), 500),

View File

@ -90,11 +90,31 @@ func tokenizeBBCode(input string) []token {
return tokens return tokens
} }
var md = goldmark.New( var previewMarkdown = goldmark.New(
goldmark.WithExtensions(extension.GFM, SpoilerExtension{}, EmbedExtension{}, bTag{}), goldmark.WithExtensions(
extension.GFM,
SpoilerExtension{},
EmbedExtension{
Preview: true,
},
bTag{},
),
)
var realMarkdown = goldmark.New(
goldmark.WithExtensions(
extension.GFM,
SpoilerExtension{},
EmbedExtension{},
bTag{},
),
) )
func ParsePostInput(source string) string { func ParsePostInput(source string, preview bool) string {
md := realMarkdown
if preview {
md = previewMarkdown
}
var buf bytes.Buffer var buf bytes.Buffer
if err := md.Convert([]byte(source), &buf); err != nil { if err := md.Convert([]byte(source), &buf); err != nil {
panic(err) panic(err)

View File

@ -47,7 +47,7 @@ Mix 'em: [u][/i]wow.[/i][/u]
// } // }
func TestBBCodeParsing(t *testing.T) { func TestBBCodeParsing(t *testing.T) {
res := ParsePostInput(`[b]ONE[/b] [i]TWO[/i]`) res := ParsePostInput(`[b]ONE[/b] [i]TWO[/i]`, false)
fmt.Println(res) fmt.Println(res)
t.Fail() t.Fail()
} }

View File

@ -8,7 +8,7 @@ import (
func main() { func main() {
js.Global().Set("parseMarkdown", js.FuncOf(func(this js.Value, args []js.Value) interface{} { js.Global().Set("parseMarkdown", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
return parsing.ParsePostInput(args[0].String()) return parsing.ParsePostInput(args[0].String(), true)
})) }))
var done chan bool var done chan bool