669 行
		
	
	
		
			22 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			669 行
		
	
	
		
			22 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> "},
 | |
| 		{`\[url=`: `<a href="`},
 | |
| 		{`\[\/url\]`: "</a>"},
 | |
| 		{`\[h1\]`: "<h1>"},
 | |
| 		{`\[\/h1\]`: "</h1>"},
 | |
| 		{`\[h2\]`: "<h2>"},
 | |
| 		{`\[\/h2\]`: "</h2>"},
 | |
| 		{`\[h3\]`: "<h3>"},
 | |
| 		{`\[\/h3\]`: "</h3>"},
 | |
| 		{`\]`: `">`},
 | |
| 		{"\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, filepath.Ext(path)) &&
 | |
| 			filepath.Ext(path) != "" {
 | |
| 			// 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 if ext == ".html" {
 | |
| 				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)
 | |
| 	}
 | |
| }
 |