1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2025-07-28 05:02:49 +00:00

Reimplement images

This commit is contained in:
bouncepaw 2020-11-26 23:41:26 +05:00
parent 84a9f6bbf8
commit c984790d3d
8 changed files with 236 additions and 105 deletions

View File

@ -32,6 +32,7 @@ func init() {
} }
return return
} }
markup.HyphaIterate = IterateHyphaNamesWith
} }
// GetHyphaData finds a hypha addressed by `hyphaName` and returns its `hyphaData`. `hyphaData` is set to a zero value if this hypha does not exist. `isOld` is false if this hypha does not exist. // GetHyphaData finds a hypha addressed by `hyphaName` and returns its `hyphaData`. `hyphaData` is set to a zero value if this hypha does not exist. `isOld` is false if this hypha does not exist.
@ -190,7 +191,7 @@ func binaryHtmlBlock(hyphaName string, hd *HyphaData) string {
case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico": case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico":
return fmt.Sprintf(` return fmt.Sprintf(`
<div class="binary-container binary-container_with-img"> <div class="binary-container binary-container_with-img">
<a href="/page/%[1]s"><img src="/binary/%[1]s"/></a> <a href="/binary/%[1]s"><img src="/binary/%[1]s"/></a>
</div>`, hyphaName) </div>`, hyphaName)
case ".ogg", ".webm", ".mp4": case ".ogg", ".webm", ".mp4":
return fmt.Sprintf(` return fmt.Sprintf(`

View File

@ -23,7 +23,7 @@ import (
var WikiDir string var WikiDir string
// HyphaPattern is a pattern which all hyphae must match. // HyphaPattern is a pattern which all hyphae must match.
var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%]+`) var HyphaPattern = regexp.MustCompile(`[^?!:#@><*|"\'&%{}]+`)
// HyphaStorage is a mapping between canonical hypha names and their meta information. // HyphaStorage is a mapping between canonical hypha names and their meta information.
var HyphaStorage = make(map[string]*HyphaData) var HyphaStorage = make(map[string]*HyphaData)

View File

@ -13,53 +13,169 @@ func MatchesImg(line string) bool {
} }
type imgEntry struct { type imgEntry struct {
path string trimmedPath string
sizeH string path strings.Builder
sizeV string sizeW strings.Builder
desc string sizeH strings.Builder
desc strings.Builder
} }
func (entry *imgEntry) descriptionAsHtml(hyphaName string) (html string) {
if entry.desc.Len() == 0 {
return ""
}
lines := strings.Split(entry.desc.String(), "\n")
for _, line := range lines {
if line = strings.TrimSpace(line); line != "" {
if html != "" {
html += `<br>`
}
html += ParagraphToHtml(hyphaName, line)
}
}
return `<figcaption>` + html + `</figcaption>`
}
func (entry *imgEntry) sizeWAsAttr() string {
if entry.sizeW.Len() == 0 {
return ""
}
return ` width="` + entry.sizeW.String() + `"`
}
func (entry *imgEntry) sizeHAsAttr() string {
if entry.sizeH.Len() == 0 {
return ""
}
return ` height="` + entry.sizeH.String() + `"`
}
type imgState int
const (
inRoot imgState = iota
inName
inDimensionsW
inDimensionsH
inDescription
)
type Img struct { type Img struct {
entries []imgEntry entries []imgEntry
inDesc bool currEntry imgEntry
hyphaName string hyphaName string
state imgState
}
func (img *Img) pushEntry() {
if strings.TrimSpace(img.currEntry.path.String()) != "" {
img.entries = append(img.entries, img.currEntry)
img.currEntry = imgEntry{}
img.currEntry.path.Reset()
}
} }
func (img *Img) Process(line string) (shouldGoBackToNormal bool) { func (img *Img) Process(line string) (shouldGoBackToNormal bool) {
if img.inDesc { stateToProcessor := map[imgState]func(rune) bool{
rightBraceIndex := strings.IndexRune(line, '}') inRoot: img.processInRoot,
if cnt := len(img.entries); rightBraceIndex == -1 && cnt != 0 { inName: img.processInName,
img.entries[cnt-1].desc += "\n" + line inDimensionsW: img.processInDimensionsW,
} else if rightBraceIndex != -1 && cnt != 0 { inDimensionsH: img.processInDimensionsH,
img.entries[cnt-1].desc += "\n" + line[:rightBraceIndex] inDescription: img.processInDescription,
img.inDesc = false
} }
if strings.Count(line, "}") > 1 { for _, r := range line {
if shouldReturnTrue := stateToProcessor[img.state](r); shouldReturnTrue {
return true return true
} }
} else if s := strings.TrimSpace(line); s != "" {
if s[0] == '}' {
return true
}
img.parseStartOfEntry(line)
} }
return false return false
} }
func ImgFromFirstLine(line, hyphaName string) Img { func (img *Img) processInDescription(r rune) (shouldReturnTrue bool) {
img := Img{ switch r {
case '}':
img.state = inName
default:
img.currEntry.desc.WriteRune(r)
}
return false
}
func (img *Img) processInRoot(r rune) (shouldReturnTrue bool) {
switch r {
case '}':
img.pushEntry()
return true
case '\n', '\r':
img.pushEntry()
case ' ', '\t':
default:
img.state = inName
img.currEntry = imgEntry{}
img.currEntry.path.Reset()
img.currEntry.path.WriteRune(r)
}
return false
}
func (img *Img) processInName(r rune) (shouldReturnTrue bool) {
switch r {
case '}':
img.pushEntry()
return true
case '|':
img.state = inDimensionsW
case '{':
img.state = inDescription
case '\n', '\r':
img.pushEntry()
img.state = inRoot
default:
img.currEntry.path.WriteRune(r)
}
return false
}
func (img *Img) processInDimensionsW(r rune) (shouldReturnTrue bool) {
switch r {
case '}':
img.pushEntry()
return true
case '*':
img.state = inDimensionsH
case ' ', '\t', '\n':
case '{':
img.state = inDescription
default:
img.currEntry.sizeW.WriteRune(r)
}
return false
}
func (img *Img) processInDimensionsH(r rune) (shouldGoBackToNormal bool) {
switch r {
case '}':
img.pushEntry()
return true
case ' ', '\t', '\n':
case '{':
img.state = inDescription
default:
img.currEntry.sizeH.WriteRune(r)
}
return false
}
func ImgFromFirstLine(line, hyphaName string) (img *Img, shouldGoBackToNormal bool) {
img = &Img{
hyphaName: hyphaName, hyphaName: hyphaName,
entries: make([]imgEntry, 0), entries: make([]imgEntry, 0),
} }
line = line[strings.IndexRune(line, '{'):] line = line[strings.IndexRune(line, '{')+1:]
if len(line) == 1 { // if { only return img, img.Process(line)
} else {
line = line[1:] // Drop the {
}
return img
} }
func (img *Img) canonicalPathFor(path string) string { func (img *Img) binaryPathFor(path string) string {
path = strings.TrimSpace(path) path = strings.TrimSpace(path)
if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 { if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
return path return path
@ -68,73 +184,71 @@ func (img *Img) canonicalPathFor(path string) string {
} }
} }
func (img *Img) parseStartOfEntry(line string) (entry imgEntry, followedByDesc bool) { func (img *Img) pagePathFor(path string) string {
pipeIndex := strings.IndexRune(line, '|') path = strings.TrimSpace(path)
if pipeIndex == -1 { // If no : in string if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
entry.path = img.canonicalPathFor(line) return path
} else { } else {
entry.path = img.canonicalPathFor(line[:pipeIndex]) return "/page/" + xclCanonicalName(img.hyphaName, path)
line = strings.TrimPrefix(line, line[:pipeIndex+1])
var (
leftBraceIndex = strings.IndexRune(line, '{')
rightBraceIndex = strings.IndexRune(line, '}')
dimensions string
)
if leftBraceIndex == -1 {
dimensions = line
} else {
dimensions = line[:leftBraceIndex]
}
sizeH, sizeV := parseDimensions(dimensions)
entry.sizeH = sizeH
entry.sizeV = sizeV
if leftBraceIndex != -1 && rightBraceIndex == -1 {
img.inDesc = true
followedByDesc = true
entry.desc = strings.TrimPrefix(line, line[:leftBraceIndex+1])
} else if leftBraceIndex != -1 && rightBraceIndex != -1 {
entry.desc = line[leftBraceIndex+1 : rightBraceIndex]
} }
} }
img.entries = append(img.entries, entry)
return
}
func parseDimensions(dimensions string) (sizeH, sizeV string) { func parseDimensions(dimensions string) (sizeW, sizeH string) {
xIndex := strings.IndexRune(dimensions, '*') xIndex := strings.IndexRune(dimensions, '*')
if xIndex == -1 { // If no x in dimensions if xIndex == -1 { // If no x in dimensions
sizeH = strings.TrimSpace(dimensions) sizeW = strings.TrimSpace(dimensions)
} else { } else {
sizeH = strings.TrimSpace(dimensions[:xIndex]) sizeW = strings.TrimSpace(dimensions[:xIndex])
sizeV = strings.TrimSpace(strings.TrimPrefix(dimensions, dimensions[:xIndex+1])) sizeH = strings.TrimSpace(strings.TrimPrefix(dimensions, dimensions[:xIndex+1]))
} }
return return
} }
func (img Img) ToHtml() (html string) { func (img *Img) checkLinks() map[string]bool {
m := make(map[string]bool)
for i, entry := range img.entries {
// Also trim them for later use
entry.trimmedPath = strings.TrimSpace(entry.path.String())
isAbsoluteUrl := strings.ContainsRune(entry.trimmedPath, ':')
if !isAbsoluteUrl {
entry.trimmedPath = canonicalName(entry.trimmedPath)
}
img.entries[i] = entry
m[entry.trimmedPath] = isAbsoluteUrl
}
HyphaIterate(func(hyphaName string) {
for _, entry := range img.entries { for _, entry := range img.entries {
html += fmt.Sprintf(`<figure> if hyphaName == entry.trimmedPath {
<img src="%s" width="%s" height="%s"> m[entry.trimmedPath] = true
`, entry.path, entry.sizeH, entry.sizeV)
if entry.desc != "" {
html += ` <figcaption>`
for i, line := range strings.Split(entry.desc, "\n") {
if line != "" {
if i > 0 {
html += `<br>`
}
html += ParagraphToHtml(img.hyphaName, line)
} }
} }
html += `</figcaption>` })
return m
} }
func (img *Img) ToHtml() (html string) {
linkAvailabilityMap := img.checkLinks()
isOneImageOnly := len(img.entries) == 1 && img.entries[0].desc.Len() == 0
if isOneImageOnly {
html += `<section class="img-gallery img-gallery_one-image">`
} else {
html += `<section class="img-gallery img-gallery_many-images">`
}
for _, entry := range img.entries {
html += `<figure>`
// If is existing hypha or an external path
if linkAvailabilityMap[entry.trimmedPath] {
html += fmt.Sprintf(
`<a href="%s"><img src="%s" %s %s></a>`,
img.pagePathFor(entry.trimmedPath),
img.binaryPathFor(entry.trimmedPath),
entry.sizeWAsAttr(), entry.sizeHAsAttr())
} else { // If is a non-existent hypha
html += fmt.Sprintf(`<a class="wikilink_new" href="%s">Hypha <em>%s</em> does not exist</a>`, img.pagePathFor(entry.trimmedPath), entry.trimmedPath)
}
html += entry.descriptionAsHtml(img.hyphaName)
html += `</figure>` html += `</figure>`
} }
return `<section class="img-gallery"> return html + `</section>`
` + html + `
</section>`
} }

View File

@ -12,6 +12,9 @@ var HyphaExists func(string) bool
// HyphaAccess holds function that accesses a hypha by its name. // HyphaAccess holds function that accesses a hypha by its name.
var HyphaAccess func(string) (rawText, binaryHtml string, err error) var HyphaAccess func(string) (rawText, binaryHtml string, err error)
// HyphaIterate is a function that iterates all hypha names existing.
var HyphaIterate func(func(string))
// GemLexerState is used by markup parser to remember what is going on. // GemLexerState is used by markup parser to remember what is going on.
type GemLexerState struct { type GemLexerState struct {
// Name of hypha being parsed // Name of hypha being parsed
@ -21,7 +24,7 @@ type GemLexerState struct {
id int id int
buf string buf string
// Temporaries // Temporaries
img Img img *Img
} }
type Line struct { type Line struct {
@ -84,7 +87,7 @@ func geminiLineToAST(line string, state *GemLexerState, ast *[]Line) {
imgState: imgState:
if shouldGoBackToNormal := state.img.Process(line); shouldGoBackToNormal { if shouldGoBackToNormal := state.img.Process(line); shouldGoBackToNormal {
state.where = "" state.where = ""
addLine(state.img) addLine(*state.img)
} }
return return
@ -174,8 +177,13 @@ normalState:
case line == "----": case line == "----":
*ast = append(*ast, Line{id: -1, contents: "<hr/>"}) *ast = append(*ast, Line{id: -1, contents: "<hr/>"})
case MatchesImg(line): case MatchesImg(line):
img, shouldGoBackToNormal := ImgFromFirstLine(line, state.name)
if shouldGoBackToNormal {
addLine(*img)
} else {
state.where = "img" state.where = "img"
state.img = ImgFromFirstLine(line, state.name) state.img = img
}
default: default:
addLine(fmt.Sprintf("<p id='%d'>%s</p>", state.id, ParagraphToHtml(state.name, line))) addLine(fmt.Sprintf("<p id='%d'>%s</p>", state.id, ParagraphToHtml(state.name, line)))
} }

View File

@ -21,6 +21,8 @@ func Parse(ast []Line, from, to int, state GemParserState) (html string) {
html += v.ToHtml() html += v.ToHtml()
case string: case string:
html += v html += v
default:
html += "Unknown"
} }
} }
} }

@ -1 +1 @@
Subproject commit 41b572a78082e8eaa62a04937f95dad295039e8a Subproject commit 7828352598c19afe5f2e13df0219656ac7b44c9c

View File

@ -34,8 +34,11 @@ article pre.codeblock {background-color:#eee; padding:.5rem; white-space: pre-wr
.binary-container_with-video video, .binary-container_with-video video,
.binary-container_with-audio audio {width: 100%} .binary-container_with-audio audio {width: 100%}
.navi-title a {text-decoration:none;} .navi-title a {text-decoration:none;}
.img-gallery img { max-width: 100%; } .img-gallery { text-align: center; margin-top: .25rem; }
.img-gallery_many-images { background-color: #eee; border-radius: .25rem; padding: .5rem; }
.img-gallery img { max-width: 100%; max-height: 50vh; }
figure { margin: 0; } figure { margin: 0; }
figcaption { padding-bottom: .5rem; }
nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;} nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;}
nav ul li {list-style-type:none;margin-right:1rem;} nav ul li {list-style-type:none;margin-right:1rem;}

View File

@ -56,8 +56,11 @@ article pre.codeblock {background-color:#eee; padding:.5rem; white-space: pre-wr
.binary-container_with-video video, .binary-container_with-video video,
.binary-container_with-audio audio {width: 100%} .binary-container_with-audio audio {width: 100%}
.navi-title a {text-decoration:none;} .navi-title a {text-decoration:none;}
.img-gallery img { max-width: 100%; } .img-gallery { text-align: center; margin-top: .25rem; }
.img-gallery_many-images { background-color: #eee; border-radius: .25rem; padding: .5rem; }
.img-gallery img { max-width: 100%; max-height: 50vh; }
figure { margin: 0; } figure { margin: 0; }
figcaption { padding-bottom: .5rem; }
nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;} nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;}
nav ul li {list-style-type:none;margin-right:1rem;} nav ul li {list-style-type:none;margin-right:1rem;}
@ -71,31 +74,31 @@ nav ul li {list-style-type:none;margin-right:1rem;}
.rc-entry__links { grid-column: 1 / span 2; } .rc-entry__links { grid-column: 1 / span 2; }
.rc-entry__author { font-style: italic; } .rc-entry__author { font-style: italic; }
`) `)
//line templates/css.qtpl:51 //line templates/css.qtpl:54
} }
//line templates/css.qtpl:51 //line templates/css.qtpl:54
func WriteDefaultCSS(qq422016 qtio422016.Writer) { func WriteDefaultCSS(qq422016 qtio422016.Writer) {
//line templates/css.qtpl:51 //line templates/css.qtpl:54
qw422016 := qt422016.AcquireWriter(qq422016) qw422016 := qt422016.AcquireWriter(qq422016)
//line templates/css.qtpl:51 //line templates/css.qtpl:54
StreamDefaultCSS(qw422016) StreamDefaultCSS(qw422016)
//line templates/css.qtpl:51 //line templates/css.qtpl:54
qt422016.ReleaseWriter(qw422016) qt422016.ReleaseWriter(qw422016)
//line templates/css.qtpl:51 //line templates/css.qtpl:54
} }
//line templates/css.qtpl:51 //line templates/css.qtpl:54
func DefaultCSS() string { func DefaultCSS() string {
//line templates/css.qtpl:51 //line templates/css.qtpl:54
qb422016 := qt422016.AcquireByteBuffer() qb422016 := qt422016.AcquireByteBuffer()
//line templates/css.qtpl:51 //line templates/css.qtpl:54
WriteDefaultCSS(qb422016) WriteDefaultCSS(qb422016)
//line templates/css.qtpl:51 //line templates/css.qtpl:54
qs422016 := string(qb422016.B) qs422016 := string(qb422016.B)
//line templates/css.qtpl:51 //line templates/css.qtpl:54
qt422016.ReleaseByteBuffer(qb422016) qt422016.ReleaseByteBuffer(qb422016)
//line templates/css.qtpl:51 //line templates/css.qtpl:54
return qs422016 return qs422016
//line templates/css.qtpl:51 //line templates/css.qtpl:54
} }