659 lines
21 KiB
Go
659 lines
21 KiB
Go
|
// Package template implements HTML page generation functions from a default
|
||
|
// or custom theme.
|
||
|
package template
|
||
|
|
||
|
import (
|
||
|
"embed"
|
||
|
"html"
|
||
|
"os"
|
||
|
"path/filepath"
|
||
|
"regexp"
|
||
|
"slices"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
image "lamium/image"
|
||
|
util "lamium/util"
|
||
|
)
|
||
|
|
||
|
type (
|
||
|
// A Set is a collection of images. The Set type contains properties
|
||
|
// about the collection such as the source directory for the images
|
||
|
// and how to display them.
|
||
|
//
|
||
|
// JSON tags are included for config file marshalling.
|
||
|
Set struct {
|
||
|
Name string `json:"name"`
|
||
|
ImageDir string `json:"imageDir"`
|
||
|
IndexHTML string `json:"indexHTML"`
|
||
|
Title string `json:"title"`
|
||
|
SortOrder string `json:"sortOrder"`
|
||
|
CSSClassIframe string `json:"cssClassIframe"`
|
||
|
CSSClassLink string `json:"cssClassLink"`
|
||
|
CSSClassThumb string `json:"cssClassThumb"`
|
||
|
LightboxOn bool `json:"lightboxOn"`
|
||
|
LinkType string `json:"linkType"`
|
||
|
ThumbDir string `json:"thumbDir"`
|
||
|
ThumbWidth int `json:"thumbWidth"`
|
||
|
ThumbHeight int `json:"thumbHeight"`
|
||
|
ThumbPct int `json:"thumbPercent"`
|
||
|
ThumbClip string `json:"thumbClip"`
|
||
|
ThumbUnifyMode string `json:"thumbUnifyMode"`
|
||
|
}
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// File extensions for captions.
|
||
|
CaptionExt = map[string]string{
|
||
|
"html": ".html",
|
||
|
"txt": ".txt",
|
||
|
}
|
||
|
|
||
|
// Supported HTML markup tags.
|
||
|
MarkupHTML = map[string]string{
|
||
|
"<b>": "<b>",
|
||
|
"</b>": "</b>",
|
||
|
"<blockquote>": "<blockquote>",
|
||
|
"</blockquote>": "</blockquote>",
|
||
|
"<br>": "<br>",
|
||
|
"<code>": "<code>",
|
||
|
"</code>": "</code>",
|
||
|
"<del>": "<del>",
|
||
|
"</del>": "</del>",
|
||
|
"<dd>": "<dd>",
|
||
|
"</dd>": "</dd>",
|
||
|
"<dl>": "<dl>",
|
||
|
"</dl>": "</dl>",
|
||
|
"<dt>": "<dt>",
|
||
|
"</dt>": "</dt>",
|
||
|
"<em>": "<em>",
|
||
|
"</em>": "</em>",
|
||
|
"<h1>": "<h1>",
|
||
|
"</h1>": "</h1>",
|
||
|
"<h2>": "<h2>",
|
||
|
"</h2>": "</h2>",
|
||
|
"<h3>": "<h3>",
|
||
|
"</h3>": "</h3>",
|
||
|
"<i>": "<i>",
|
||
|
"</i>": "</i>",
|
||
|
"<ol>": "<ol>",
|
||
|
"</ol>": "</ol>",
|
||
|
"<p>": "<p>",
|
||
|
"</p>": "</p>",
|
||
|
"<strong>": "<strong>",
|
||
|
"</strong>": "</strong>",
|
||
|
"<s>": "<s>",
|
||
|
"</s>": "</s>",
|
||
|
"<u>": "<u>",
|
||
|
"</u>": "</u>",
|
||
|
"<ul>": "<ul>",
|
||
|
"</ul>": "</ul>",
|
||
|
}
|
||
|
|
||
|
// Supported markup syntax regexp for text files and the HTML tag
|
||
|
// translations.
|
||
|
MarkupTxt = []map[string]string{
|
||
|
{`^\*\*`: "<strong>"},
|
||
|
{`\*\*$`: "</strong>"},
|
||
|
{`\*\*\n`: "</strong>\n"},
|
||
|
{`\t\*\*`: "\t<strong>"},
|
||
|
{`\*\*\t`: "</strong>\t"},
|
||
|
{`[[:space:]]\*\*`: " <strong>"},
|
||
|
{`\*\*[[:space:]]`: "</strong> "},
|
||
|
{`^\*`: "<em>"},
|
||
|
{`\*$`: "</em>"},
|
||
|
{`\*\n`: "</em>\n"},
|
||
|
{`\t\*`: "\t<em>"},
|
||
|
{`\*\t`: "</em>\t"},
|
||
|
{`[[:space:]]\*`: " <em>"},
|
||
|
{`\*[[:space:]]`: "</em> "},
|
||
|
{`^__`: "<u>"},
|
||
|
{`__$`: "</u>"},
|
||
|
{`__\n`: "</u>\n"},
|
||
|
{`\t__`: "\t<u>"},
|
||
|
{`__\t`: "</u>\t"},
|
||
|
{`[[:space:]]__`: " <u>"},
|
||
|
{`__[[:space:]]`: "</u> "},
|
||
|
{`^\~\~`: "<s>"},
|
||
|
{`\~\~$`: "</s>"},
|
||
|
{`\~\~\n`: "</s>\n"},
|
||
|
{`\t\~\~`: "\t<s>"},
|
||
|
{`\~\~\t`: "</s>\t"},
|
||
|
{`[[:space:]]\~\~`: " <s>"},
|
||
|
{`\~\~[[:space:]]`: "</s> "},
|
||
|
{"\n\n": "</p>\n<p>"},
|
||
|
{" \n": "<br>"},
|
||
|
}
|
||
|
|
||
|
// Template variables used in partials.
|
||
|
PartVar = map[string]string{
|
||
|
"id": "{{ id }}",
|
||
|
"iframeClass": "{{ iframe_class }}",
|
||
|
"link": "{{ link }}",
|
||
|
"linkClass": "{{ link_class }}",
|
||
|
"navNext": "{{ nav_next }}",
|
||
|
"navPrev": "{{ nav_prev }}",
|
||
|
"navTop": "{{ nav_top }}",
|
||
|
"thumb": "{{ thumb }}",
|
||
|
"thumbClass": "{{ thumb_class }}",
|
||
|
"set": "[set]",
|
||
|
}
|
||
|
|
||
|
// HTML partials that are inserted into templates.
|
||
|
PartHTML = map[string]string{
|
||
|
"img": `<a class="` + PartVar["linkClass"] + `" href="` +
|
||
|
PartVar["link"] + `"><img class="` + PartVar["thumbClass"] +
|
||
|
`" src="` + PartVar["thumb"] + `"></a>` + "\n",
|
||
|
"lightbox": `<iframe class="` + PartVar["iframeClass"] + `" id="` +
|
||
|
PartVar["id"] + `" src="` + PartVar["link"] + `"></iframe>
|
||
|
<nav class="nav">
|
||
|
<a class="prev" href="` + PartVar["navPrev"] + `">←</a>
|
||
|
<a class="close" href="#">☓</a>
|
||
|
<a class="next" href="` + PartVar["navNext"] + `">→</a>` +
|
||
|
`</nav>` + "\n",
|
||
|
"nav": `<a class="link" href="` + PartVar["navPrev"] + `">←</a>
|
||
|
<a class="link" href="` + PartVar["navTop"] + `">↑</a>
|
||
|
<a class="link" href="` + PartVar["navNext"] + `">→</a>`,
|
||
|
}
|
||
|
|
||
|
// Sample theme directory path relative to the album root.
|
||
|
//
|
||
|
// If changing this value, the go:embed directive for the ThemeFile variable
|
||
|
// also needs to be updated, to store the theme files at compile time.
|
||
|
SampleThemeDir = "themes/nettle"
|
||
|
|
||
|
// Theme template filenames.
|
||
|
Theme = struct {
|
||
|
Index string `json:"index"`
|
||
|
Image string `json:"_image"`
|
||
|
Set string `json:"_set"`
|
||
|
Sets string `json:"_sets"`
|
||
|
Text string `json:"_text"`
|
||
|
AlbumCSS string `json:"albumCSS"`
|
||
|
LightboxCSS string `json:"lightboxCSS"`
|
||
|
}{
|
||
|
Index: "index.html",
|
||
|
Image: "_image.html",
|
||
|
Set: "_set.html",
|
||
|
Sets: "_sets.html",
|
||
|
Text: "_text.html",
|
||
|
AlbumCSS: "album.css",
|
||
|
LightboxCSS: "lightbox.css",
|
||
|
}
|
||
|
|
||
|
// Template variables used in theme files.
|
||
|
ThemeVar = map[string]map[string]string{
|
||
|
Theme.Index: {
|
||
|
"content": "{{ index_content }}",
|
||
|
"relPath": "{{ index_relpath }}",
|
||
|
"title": "{{ index_title }}",
|
||
|
},
|
||
|
Theme.Image: {
|
||
|
"caption": "{{ image_caption }}",
|
||
|
"path": "{{ image_path }}",
|
||
|
"title": "{{ image_title }}",
|
||
|
"nav": "{{ nav }}",
|
||
|
},
|
||
|
Theme.Set: {
|
||
|
"content": "{{ set }}",
|
||
|
"desc": "{{ set_desc }}",
|
||
|
"lightbox": "{{ set_lightbox }}",
|
||
|
"name": "{{ set_name }}",
|
||
|
"title": "{{ set_title }}",
|
||
|
},
|
||
|
Theme.Sets: {
|
||
|
"content": "{{ set_[set] }}",
|
||
|
"desc": "{{ set_[set]_desc }}",
|
||
|
"lightbox": "{{ set_[set]_lightbox }}",
|
||
|
"name": "{{ set_[set]_name }}",
|
||
|
"title": "{{ set_[set]_title }}",
|
||
|
},
|
||
|
Theme.Text: {
|
||
|
"content": "{{ text_content }}",
|
||
|
"title": "{{ text_title }}",
|
||
|
},
|
||
|
}
|
||
|
)
|
||
|
|
||
|
// Compile-time theme embed directive.
|
||
|
// Ensure this path corresponds with SampleThemeDir.
|
||
|
//
|
||
|
//go:embed themes/nettle/*
|
||
|
var ThemeFile embed.FS
|
||
|
|
||
|
// GetHTMLFilename returns the value of path with the base filename reset to
|
||
|
// Theme.Index if path contains a conflicting or invalid filename. Otherwise,
|
||
|
// returns a copy of path.
|
||
|
func GetHTMLFilename(path string) string {
|
||
|
str := filepath.Join(filepath.Dir(path), Theme.Index)
|
||
|
if util.ReplaceExt(filepath.Base(path), "") != "" && filepath.Ext(path) ==
|
||
|
filepath.Ext(Theme.Index) {
|
||
|
str = path
|
||
|
}
|
||
|
return str
|
||
|
}
|
||
|
|
||
|
// CheckDup returns a renamed path if a file by the same name already
|
||
|
// exists in the same srcDir and is not a caption text file. If the file path
|
||
|
// does not already exist, returns the original path.
|
||
|
func CheckDup(path, srcDir, ext string) string {
|
||
|
files := util.GetFileList(srcDir, "name", false)
|
||
|
name := util.ReplaceExt(filepath.Base(path), "")
|
||
|
num := strconv.Itoa(len(files) + 1)
|
||
|
newPath := filepath.Join(filepath.Dir(path), name+num+ext)
|
||
|
for _, file := range files {
|
||
|
if util.ReplaceExt(filepath.Base(file), "") == name &&
|
||
|
filepath.Base(file) != name+CaptionExt["txt"] {
|
||
|
return newPath
|
||
|
}
|
||
|
}
|
||
|
return path
|
||
|
}
|
||
|
|
||
|
// GenTheme outputs the theme files to the album directory at the
|
||
|
// SampleThemeDir path.
|
||
|
func GenTheme(exitOnErr bool) {
|
||
|
util.MakeDir(SampleThemeDir, exitOnErr)
|
||
|
files := []string{Theme.Index, Theme.Image, Theme.Set, Theme.Sets,
|
||
|
Theme.Text, Theme.AlbumCSS, Theme.LightboxCSS}
|
||
|
for _, f := range files {
|
||
|
content, _ := ThemeFile.ReadFile(filepath.Join(SampleThemeDir, f))
|
||
|
util.SaveFile(filepath.Join(SampleThemeDir, f), string(content),
|
||
|
exitOnErr)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GetSetHTML returns an HTML string of image thumbnails for an image set.
|
||
|
//
|
||
|
// linkClass and thumbClass are CSS classes to assign to the links and
|
||
|
// thumbnails respectively for styling.
|
||
|
//
|
||
|
// If imageType is "image", the HTML target links will link directly to
|
||
|
// images instead of their corresponding HTML pages.
|
||
|
//
|
||
|
// If lightboxOn is true, the HTML target links will be set to anchors
|
||
|
// that are image iframe ids.
|
||
|
func GetSetHTML(srcPath, thumbPath, sortOrder, linkType, linkClass,
|
||
|
thumbClass string, lightboxOn, isTopLevel bool) string {
|
||
|
var htmlStr string
|
||
|
files := image.GetImageList(srcPath, sortOrder, false)
|
||
|
imgVars := map[string]string{
|
||
|
PartVar["linkClass"]: linkClass,
|
||
|
PartVar["thumbClass"]: thumbClass,
|
||
|
}
|
||
|
if linkType == "image" {
|
||
|
for _, file := range files {
|
||
|
// Adjust path for top-level page.
|
||
|
if isTopLevel {
|
||
|
imgVars[PartVar["link"]] = file
|
||
|
} else {
|
||
|
imgVars[PartVar["link"]] = filepath.Base(file)
|
||
|
}
|
||
|
imgVars[PartVar["thumb"]] = filepath.Join(thumbPath,
|
||
|
filepath.Base(file))
|
||
|
htmlStr += util.MultiReplace(PartHTML["img"], imgVars)
|
||
|
}
|
||
|
} else {
|
||
|
for _, file := range files {
|
||
|
if lightboxOn {
|
||
|
imgVars[PartVar["link"]] = "#" +
|
||
|
util.ReplaceExt(filepath.Base(file), "")
|
||
|
} else if !lightboxOn && isTopLevel {
|
||
|
imgVars[PartVar["link"]] = strings.Replace(file,
|
||
|
filepath.Ext(file), filepath.Ext(Theme.Index), 1)
|
||
|
} else {
|
||
|
imgVars[PartVar["link"]] = filepath.Base(
|
||
|
strings.Replace(file,
|
||
|
filepath.Ext(file), filepath.Ext(Theme.Index), 1))
|
||
|
}
|
||
|
imgVars[PartVar["thumb"]] = filepath.Join(thumbPath,
|
||
|
filepath.Base(file))
|
||
|
htmlStr += util.MultiReplace(PartHTML["img"], imgVars)
|
||
|
}
|
||
|
}
|
||
|
return strings.TrimRight(htmlStr, "\n")
|
||
|
}
|
||
|
|
||
|
// GetSetLBHTML returns an HTML string of image iframes and navigation links for // an image set.
|
||
|
//
|
||
|
// iframeClass is a CSS class to assign to the iframes for styling.
|
||
|
func GetSetLBHTML(srcPath, sortOrder, iframeClass string,
|
||
|
isTopLevel bool) string {
|
||
|
var htmlStr string
|
||
|
files := image.GetImageList(srcPath, sortOrder, false)
|
||
|
lbVars := map[string]string{
|
||
|
PartVar["iframeClass"]: iframeClass,
|
||
|
}
|
||
|
for f := 0; f < len(files); f++ {
|
||
|
prev, next := "#", "#"
|
||
|
if len(files) > 1 {
|
||
|
if f == 0 {
|
||
|
next = "#" + util.ReplaceExt(filepath.Base(files[f+1]), "")
|
||
|
} else if f == (len(files) - 1) {
|
||
|
prev = "#" + util.ReplaceExt(filepath.Base(files[f-1]), "")
|
||
|
next = "#"
|
||
|
} else {
|
||
|
prev = "#" + util.ReplaceExt(filepath.Base(files[f-1]), "")
|
||
|
next = "#" + util.ReplaceExt(filepath.Base(files[f+1]), "")
|
||
|
}
|
||
|
}
|
||
|
lbVars[PartVar["navNext"]] = next
|
||
|
lbVars[PartVar["navPrev"]] = prev
|
||
|
lbVars[PartVar["id"]] = util.ReplaceExt(filepath.Base(files[f]), "")
|
||
|
if isTopLevel {
|
||
|
lbVars[PartVar["link"]] = strings.Replace(files[f],
|
||
|
filepath.Ext(files[f]), filepath.Ext(Theme.Index), 1)
|
||
|
} else {
|
||
|
lbVars[PartVar["link"]] = filepath.Base(strings.Replace(
|
||
|
files[f], filepath.Ext(files[f]), filepath.Ext(Theme.Index), 1))
|
||
|
}
|
||
|
htmlStr += util.MultiReplace(PartHTML["lightbox"], lbVars)
|
||
|
}
|
||
|
return strings.TrimRight(htmlStr, "\n")
|
||
|
}
|
||
|
|
||
|
// TxtToHTML returns an HTML string with supported markup syntax in string str
|
||
|
// replaced with HTML tags.
|
||
|
func TxtToHTML(str string) string {
|
||
|
htmlStr := html.EscapeString(str)
|
||
|
for _, r := range MarkupTxt {
|
||
|
for reg, repl := range r {
|
||
|
re, _ := regexp.Compile(reg)
|
||
|
htmlStr = string(re.ReplaceAll([]byte(htmlStr), []byte(repl)))
|
||
|
}
|
||
|
}
|
||
|
htmlStr = "<p>" + strings.TrimRight(htmlStr, "\n") + "</p>"
|
||
|
return htmlStr
|
||
|
}
|
||
|
|
||
|
// FilterHTML returns an HTML string with some characters escaped
|
||
|
// (see html.EscapeString) and supported markup tags exempted.
|
||
|
func FilterHTML(str string) string {
|
||
|
htmlStr := html.EscapeString(str)
|
||
|
htmlStr = strings.TrimRight(util.MultiReplace(htmlStr, MarkupHTML), "\n")
|
||
|
return htmlStr
|
||
|
}
|
||
|
|
||
|
// GetCaption returns an HTML string with the contents of an image caption for
|
||
|
// string path, if the caption file exists. If the caption file does not exist,
|
||
|
// returns an empty string.
|
||
|
func GetCaption(path string) string {
|
||
|
for _, ext := range CaptionExt {
|
||
|
capPath := path
|
||
|
if strings.HasSuffix(path, ext) {
|
||
|
// Files.
|
||
|
capPath = strings.Replace(path, filepath.Ext(path), ext, 1)
|
||
|
} else {
|
||
|
// Directories.
|
||
|
capPath += ext
|
||
|
}
|
||
|
if util.FileExists(capPath) {
|
||
|
if ext == ".txt" {
|
||
|
return TxtToHTML(util.LoadFile(capPath))
|
||
|
} else {
|
||
|
return FilterHTML(util.LoadFile(capPath))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// Like GetCaption but for multiple files in a directory at string path,
|
||
|
// GetCaptions a map with image files as keys and caption contents as values.
|
||
|
//
|
||
|
// If titleFallback is true, sets the caption contents to the image title
|
||
|
// if no caption file is found for a given image.
|
||
|
func GetCaptions(path string, titleFallback bool) map[string]string {
|
||
|
caps := make(map[string]string)
|
||
|
files := image.GetImageList(path, "name", false)
|
||
|
// Load caption file if available, fallback to deriving title from the
|
||
|
// filename if title fallback is enabled, or empty string if unknown.
|
||
|
for _, file := range files {
|
||
|
caps[file] = GetCaption(file)
|
||
|
if caps[file] == "" && titleFallback {
|
||
|
caps[file] = image.GetImageTitle(file)
|
||
|
}
|
||
|
}
|
||
|
return caps
|
||
|
}
|
||
|
|
||
|
// CopyCSS copies the theme CSS files from themePath to destDir.
|
||
|
func CopyCSS(themePath, destDir string) {
|
||
|
util.MakeDir(destDir, true)
|
||
|
util.CopyFile(filepath.Join(themePath, Theme.LightboxCSS),
|
||
|
filepath.Join(destDir, Theme.LightboxCSS))
|
||
|
util.CopyFile(filepath.Join(themePath, Theme.AlbumCSS),
|
||
|
filepath.Join(destDir, Theme.AlbumCSS))
|
||
|
}
|
||
|
|
||
|
// PrepImages copies images in Set set to destDir, and creates a directory of
|
||
|
// thumbnails at destDir.
|
||
|
func PrepImages(destDir string, set Set) {
|
||
|
dsPath := filepath.FromSlash(destDir)
|
||
|
util.MakeDir(filepath.Join(dsPath, set.ImageDir))
|
||
|
image.CopyImages(set.ImageDir, filepath.Join(dsPath, set.ImageDir),
|
||
|
false)
|
||
|
|
||
|
util.MakeDir(filepath.Join(dsPath, set.ImageDir, set.ThumbDir))
|
||
|
image.ResizeImages(filepath.Join(dsPath, set.ImageDir),
|
||
|
filepath.Join(dsPath, set.ImageDir, set.ThumbDir),
|
||
|
set.ThumbWidth, set.ThumbHeight, set.ThumbPct, set.ThumbClip,
|
||
|
set.ThumbUnifyMode, false)
|
||
|
}
|
||
|
|
||
|
// GetPartial returns an HTML string of partial themeFile from theme themePath
|
||
|
// with template variables partialVars inserted.
|
||
|
func GetPartial(themePath, themeFile string,
|
||
|
partialVars map[string]string) string {
|
||
|
// Replace variables in partial template and trim end of file newlines.
|
||
|
partial := util.LoadFile(filepath.Join(filepath.FromSlash(themePath),
|
||
|
themeFile))
|
||
|
return strings.TrimRight(util.MultiReplace(partial, partialVars),
|
||
|
"\n")
|
||
|
}
|
||
|
|
||
|
// MakePage saves a page at destPath with the contents of themeFile from
|
||
|
// themePath and the values from pageVars template variables inserted.
|
||
|
func MakePage(themePath, themeFile, destPath string,
|
||
|
pageVars map[string]string) {
|
||
|
// Replace variables in index template.
|
||
|
dsPath := filepath.FromSlash(destPath)
|
||
|
util.MakeDir(filepath.Dir(dsPath))
|
||
|
index := util.LoadFile(filepath.Join(filepath.FromSlash(themePath),
|
||
|
themeFile))
|
||
|
page := util.MultiReplace(index, pageVars)
|
||
|
os.Remove(dsPath)
|
||
|
util.SaveFile(dsPath, page)
|
||
|
}
|
||
|
|
||
|
// MakeHTMLImagePages saves individual HTML pages for set images at destDir.
|
||
|
func MakeHTMLImagePages(themePath, destDir string, sets []Set) {
|
||
|
for _, set := range sets {
|
||
|
util.MakeDir(filepath.Join(destDir, set.ImageDir))
|
||
|
caps := GetCaptions(set.ImageDir, true)
|
||
|
files := image.GetImageList(set.ImageDir, set.SortOrder, false)
|
||
|
|
||
|
PrepImages(destDir, set)
|
||
|
for f := 0; f < len(files); f++ {
|
||
|
nav := ""
|
||
|
// No nav HTML emitted in lightbox mode.
|
||
|
if !set.LightboxOn {
|
||
|
prev, next := "#", "#"
|
||
|
if len(files) > 1 {
|
||
|
if f == 0 {
|
||
|
next = util.ReplaceExt(filepath.Base(files[f+1]),
|
||
|
filepath.Ext(Theme.Index))
|
||
|
} else if f == (len(files) - 1) {
|
||
|
prev = util.ReplaceExt(filepath.Base(files[f-1]),
|
||
|
filepath.Ext(Theme.Index))
|
||
|
next = "#"
|
||
|
} else {
|
||
|
prev = util.ReplaceExt(filepath.Base(files[f-1]),
|
||
|
filepath.Ext(Theme.Index))
|
||
|
next = util.ReplaceExt(filepath.Base(files[f+1]),
|
||
|
filepath.Ext(Theme.Index))
|
||
|
}
|
||
|
}
|
||
|
navVars := map[string]string{
|
||
|
PartVar["navNext"]: next,
|
||
|
PartVar["navPrev"]: prev,
|
||
|
PartVar["navTop"]: set.IndexHTML,
|
||
|
}
|
||
|
nav = util.MultiReplace(PartVar["nav"], navVars)
|
||
|
}
|
||
|
partialVars := map[string]string{
|
||
|
ThemeVar[Theme.Image]["caption"]: caps[files[f]],
|
||
|
ThemeVar[Theme.Image]["path"]: filepath.Base(files[f]),
|
||
|
ThemeVar[Theme.Image]["title"]: image.GetImageTitle(files[f]),
|
||
|
ThemeVar[Theme.Image]["nav"]: nav,
|
||
|
}
|
||
|
partial := GetPartial(themePath, Theme.Image, partialVars)
|
||
|
pageVars := map[string]string{
|
||
|
ThemeVar[Theme.Index]["content"]: partial,
|
||
|
ThemeVar[Theme.Index]["relPath"]: "..",
|
||
|
ThemeVar[Theme.Index]["title"]: image.GetImageTitle(files[f]),
|
||
|
}
|
||
|
pagePath := filepath.Join(destDir, set.ImageDir,
|
||
|
strings.Replace(filepath.Base(files[f]),
|
||
|
filepath.Ext(files[f]), filepath.Ext(Theme.Index), 1))
|
||
|
MakePage(themePath, Theme.Index, pagePath, pageVars)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MakeHTMLSetPages saves a page displaying each set, if enabled in the set's
|
||
|
// properties, at destDir.
|
||
|
func MakeHTMLSetPages(themePath, destDir string, sets []Set) {
|
||
|
tmPath := filepath.FromSlash(themePath)
|
||
|
dsPath := filepath.FromSlash(destDir)
|
||
|
setHTML := util.LoadFile(filepath.Join(tmPath, Theme.Set))
|
||
|
for _, set := range sets {
|
||
|
if set.IndexHTML != "" {
|
||
|
// Move images if not already present.
|
||
|
PrepImages(destDir, set)
|
||
|
|
||
|
// Replace variables in partial template.
|
||
|
setCont := GetSetHTML(set.ImageDir, set.ThumbDir, set.SortOrder,
|
||
|
set.LinkType, set.CSSClassLink, set.CSSClassThumb,
|
||
|
set.LightboxOn, false)
|
||
|
setDesc := GetCaption(filepath.Join(set.ImageDir, set.ImageDir))
|
||
|
setLB := ""
|
||
|
if set.LightboxOn {
|
||
|
setLB = GetSetLBHTML(set.ImageDir, set.SortOrder,
|
||
|
set.CSSClassIframe, false)
|
||
|
}
|
||
|
partialVars := map[string]string{
|
||
|
ThemeVar[Theme.Set]["content"]: setCont,
|
||
|
ThemeVar[Theme.Set]["desc"]: setDesc,
|
||
|
ThemeVar[Theme.Set]["lightbox"]: setLB,
|
||
|
ThemeVar[Theme.Set]["name"]: set.Name,
|
||
|
ThemeVar[Theme.Set]["title"]: set.Title,
|
||
|
}
|
||
|
pagePath := GetHTMLFilename(filepath.Join(dsPath, set.ImageDir,
|
||
|
set.IndexHTML))
|
||
|
pagePath = CheckDup(pagePath, set.ImageDir,
|
||
|
filepath.Ext(Theme.Index))
|
||
|
|
||
|
// Remove cached page.
|
||
|
os.Remove(pagePath)
|
||
|
setPage := strings.TrimRight(util.MultiReplace(setHTML,
|
||
|
partialVars), "\n")
|
||
|
// Replace variables in page template.
|
||
|
pageVars := map[string]string{
|
||
|
ThemeVar[Theme.Index]["content"]: setPage,
|
||
|
ThemeVar[Theme.Index]["relPath"]: "..",
|
||
|
ThemeVar[Theme.Index]["title"]: image.GetImageTitle(set.Title),
|
||
|
}
|
||
|
MakePage(themePath, Theme.Index, pagePath, pageVars)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MakeHTMLIndexPage saves a top-level HTML page at destDir.
|
||
|
func MakeHTMLIndexPage(themePath, destDir string, index map[string]string,
|
||
|
sets []Set) {
|
||
|
tmPath := filepath.FromSlash(themePath)
|
||
|
pagePath := GetHTMLFilename(filepath.Join(filepath.FromSlash(destDir),
|
||
|
index["html"]))
|
||
|
var cache []string
|
||
|
|
||
|
if index["type"] == "sets" {
|
||
|
for _, set := range sets {
|
||
|
setHTML := util.LoadFile(filepath.Join(tmPath, Theme.Sets))
|
||
|
setCont := GetSetHTML(set.ImageDir, filepath.Join(set.ImageDir,
|
||
|
set.ThumbDir), set.SortOrder, set.LinkType,
|
||
|
set.CSSClassLink, set.CSSClassThumb, set.LightboxOn, true)
|
||
|
setDesc := GetCaption(filepath.Join(set.ImageDir, set.ImageDir))
|
||
|
setLB := ""
|
||
|
if set.LightboxOn {
|
||
|
setLB = GetSetLBHTML(set.ImageDir, set.SortOrder,
|
||
|
set.CSSClassIframe, true)
|
||
|
}
|
||
|
contVar := strings.Replace(ThemeVar[Theme.Sets]["content"],
|
||
|
PartVar["set"], set.Name, 1)
|
||
|
descVar := strings.Replace(ThemeVar[Theme.Sets]["desc"],
|
||
|
PartVar["set"], set.Name, 1)
|
||
|
lbVar := strings.Replace(ThemeVar[Theme.Sets]["lightbox"],
|
||
|
PartVar["set"], set.Name, 1)
|
||
|
nameVar := strings.Replace(ThemeVar[Theme.Sets]["name"],
|
||
|
PartVar["set"], set.Name, 1)
|
||
|
titleVar := strings.Replace(ThemeVar[Theme.Sets]["title"],
|
||
|
PartVar["set"], set.Name, 1)
|
||
|
partialVars := map[string]string{
|
||
|
contVar: setCont,
|
||
|
descVar: setDesc,
|
||
|
lbVar: setLB,
|
||
|
nameVar: set.Name,
|
||
|
titleVar: set.Title,
|
||
|
}
|
||
|
|
||
|
// Remove cached page in the first pass.
|
||
|
// For subsequent sets on the same page, save file directly after
|
||
|
// replacing variables in the file already in the output directory.
|
||
|
if !slices.Contains(cache, pagePath) {
|
||
|
os.Remove(pagePath)
|
||
|
cache = append(cache, pagePath)
|
||
|
setHTML = strings.TrimRight(util.MultiReplace(setHTML,
|
||
|
partialVars), "\n")
|
||
|
pageVars := map[string]string{
|
||
|
ThemeVar[Theme.Index]["content"]: setHTML,
|
||
|
ThemeVar[Theme.Index]["relPath"]: ".",
|
||
|
ThemeVar[Theme.Index]["title"]: index["title"],
|
||
|
}
|
||
|
MakePage(themePath, Theme.Index, pagePath, pageVars)
|
||
|
} else {
|
||
|
setHTML = util.LoadFile(pagePath)
|
||
|
setHTML = strings.TrimRight(util.MultiReplace(setHTML, partialVars),
|
||
|
"\n")
|
||
|
util.SaveFile(pagePath, setHTML)
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
// Text type.
|
||
|
textHTML := util.LoadFile(filepath.Join(tmPath, Theme.Text))
|
||
|
content := GetCaption(GetHTMLFilename(index["html"]))
|
||
|
partialVars := map[string]string{
|
||
|
ThemeVar[Theme.Text]["content"]: content,
|
||
|
ThemeVar[Theme.Text]["title"]: index["title"],
|
||
|
}
|
||
|
partial := strings.TrimRight(util.MultiReplace(textHTML,
|
||
|
partialVars), "\n")
|
||
|
pageVars := map[string]string{
|
||
|
ThemeVar[Theme.Index]["content"]: partial,
|
||
|
ThemeVar[Theme.Index]["relPath"]: ".",
|
||
|
ThemeVar[Theme.Index]["title"]: index["title"],
|
||
|
}
|
||
|
MakePage(themePath, Theme.Index, pagePath, pageVars)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MakeAlbum generates a directory of album files.
|
||
|
func MakeAlbum(themePath, destDir string, index map[string]string, sets []Set) {
|
||
|
CopyCSS(themePath, destDir)
|
||
|
MakeHTMLImagePages(themePath, destDir, sets)
|
||
|
MakeHTMLSetPages(themePath, destDir, sets)
|
||
|
if index["html"] != "" {
|
||
|
MakeHTMLIndexPage(themePath, destDir, index, sets)
|
||
|
}
|
||
|
}
|