1
0
mirror of https://github.com/osmarks/mycorrhiza.git synced 2024-12-12 05:20:26 +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
}
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.
@ -190,7 +191,7 @@ func binaryHtmlBlock(hyphaName string, hd *HyphaData) string {
case ".jpg", ".gif", ".png", ".webp", ".svg", ".ico":
return fmt.Sprintf(`
<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)
case ".ogg", ".webm", ".mp4":
return fmt.Sprintf(`

View File

@ -23,7 +23,7 @@ import (
var WikiDir string
// 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.
var HyphaStorage = make(map[string]*HyphaData)

View File

@ -13,53 +13,169 @@ func MatchesImg(line string) bool {
}
type imgEntry struct {
path string
sizeH string
sizeV string
desc string
trimmedPath string
path strings.Builder
sizeW strings.Builder
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 {
entries []imgEntry
inDesc bool
currEntry imgEntry
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) {
if img.inDesc {
rightBraceIndex := strings.IndexRune(line, '}')
if cnt := len(img.entries); rightBraceIndex == -1 && cnt != 0 {
img.entries[cnt-1].desc += "\n" + line
} else if rightBraceIndex != -1 && cnt != 0 {
img.entries[cnt-1].desc += "\n" + line[:rightBraceIndex]
img.inDesc = false
}
if strings.Count(line, "}") > 1 {
stateToProcessor := map[imgState]func(rune) bool{
inRoot: img.processInRoot,
inName: img.processInName,
inDimensionsW: img.processInDimensionsW,
inDimensionsH: img.processInDimensionsH,
inDescription: img.processInDescription,
}
for _, r := range line {
if shouldReturnTrue := stateToProcessor[img.state](r); shouldReturnTrue {
return true
}
} else if s := strings.TrimSpace(line); s != "" {
if s[0] == '}' {
return true
}
img.parseStartOfEntry(line)
}
return false
}
func ImgFromFirstLine(line, hyphaName string) Img {
img := Img{
func (img *Img) processInDescription(r rune) (shouldReturnTrue bool) {
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,
entries: make([]imgEntry, 0),
}
line = line[strings.IndexRune(line, '{'):]
if len(line) == 1 { // if { only
} else {
line = line[1:] // Drop the {
}
return img
line = line[strings.IndexRune(line, '{')+1:]
return img, img.Process(line)
}
func (img *Img) canonicalPathFor(path string) string {
func (img *Img) binaryPathFor(path string) string {
path = strings.TrimSpace(path)
if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
return path
@ -68,73 +184,71 @@ func (img *Img) canonicalPathFor(path string) string {
}
}
func (img *Img) parseStartOfEntry(line string) (entry imgEntry, followedByDesc bool) {
pipeIndex := strings.IndexRune(line, '|')
if pipeIndex == -1 { // If no : in string
entry.path = img.canonicalPathFor(line)
func (img *Img) pagePathFor(path string) string {
path = strings.TrimSpace(path)
if strings.IndexRune(path, ':') != -1 || strings.IndexRune(path, '/') == 0 {
return path
} else {
entry.path = img.canonicalPathFor(line[:pipeIndex])
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]
}
return "/page/" + xclCanonicalName(img.hyphaName, path)
}
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, '*')
if xIndex == -1 { // If no x in dimensions
sizeH = strings.TrimSpace(dimensions)
sizeW = strings.TrimSpace(dimensions)
} else {
sizeH = strings.TrimSpace(dimensions[:xIndex])
sizeV = strings.TrimSpace(strings.TrimPrefix(dimensions, dimensions[:xIndex+1]))
sizeW = strings.TrimSpace(dimensions[:xIndex])
sizeH = strings.TrimSpace(strings.TrimPrefix(dimensions, dimensions[:xIndex+1]))
}
return
}
func (img Img) ToHtml() (html string) {
for _, entry := range img.entries {
html += fmt.Sprintf(`<figure>
<img src="%s" width="%s" height="%s">
`, 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>`
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 {
if hyphaName == entry.trimmedPath {
m[entry.trimmedPath] = true
}
}
})
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>`
}
return `<section class="img-gallery">
` + html + `
</section>`
return html + `</section>`
}

View File

@ -12,6 +12,9 @@ var HyphaExists func(string) bool
// HyphaAccess holds function that accesses a hypha by its name.
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.
type GemLexerState struct {
// Name of hypha being parsed
@ -21,7 +24,7 @@ type GemLexerState struct {
id int
buf string
// Temporaries
img Img
img *Img
}
type Line struct {
@ -84,7 +87,7 @@ func geminiLineToAST(line string, state *GemLexerState, ast *[]Line) {
imgState:
if shouldGoBackToNormal := state.img.Process(line); shouldGoBackToNormal {
state.where = ""
addLine(state.img)
addLine(*state.img)
}
return
@ -174,8 +177,13 @@ normalState:
case line == "----":
*ast = append(*ast, Line{id: -1, contents: "<hr/>"})
case MatchesImg(line):
state.where = "img"
state.img = ImgFromFirstLine(line, state.name)
img, shouldGoBackToNormal := ImgFromFirstLine(line, state.name)
if shouldGoBackToNormal {
addLine(*img)
} else {
state.where = "img"
state.img = img
}
default:
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()
case string:
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-audio audio {width: 100%}
.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; }
figcaption { padding-bottom: .5rem; }
nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;}
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-audio audio {width: 100%}
.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; }
figcaption { padding-bottom: .5rem; }
nav ul {display:flex; padding-left:0; flex-wrap:wrap; margin-top:0;}
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__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) {
//line templates/css.qtpl:51
//line templates/css.qtpl:54
qw422016 := qt422016.AcquireWriter(qq422016)
//line templates/css.qtpl:51
//line templates/css.qtpl:54
StreamDefaultCSS(qw422016)
//line templates/css.qtpl:51
//line templates/css.qtpl:54
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 {
//line templates/css.qtpl:51
//line templates/css.qtpl:54
qb422016 := qt422016.AcquireByteBuffer()
//line templates/css.qtpl:51
//line templates/css.qtpl:54
WriteDefaultCSS(qb422016)
//line templates/css.qtpl:51
//line templates/css.qtpl:54
qs422016 := string(qb422016.B)
//line templates/css.qtpl:51
//line templates/css.qtpl:54
qt422016.ReleaseByteBuffer(qb422016)
//line templates/css.qtpl:51
//line templates/css.qtpl:54
return qs422016
//line templates/css.qtpl:51
//line templates/css.qtpl:54
}