themes/html.go
Rama main · réplica de lectura del repositorio original.
package themes
// Functions directly related to HTML, CSS, Amber or GCSS
import (
"bytes"
"fmt"
"html"
"net/url"
"strings"
"github.com/microcosm-cc/bluemonday"
)
const (
// DefaultCSSFilename is the default CSS stylesheet
DefaultCSSFilename = "style.css"
// DefaultGCSSFilename is the default GCSS stylesheet
DefaultGCSSFilename = "style.gcss"
// DefaultTheme is the default theme when rendering Markdown and for error pages
DefaultTheme = "default"
)
// MetaKeywords contains a selection of allowed keywords for the HTML meta tag
var MetaKeywords = []string{"application-name", "author", "description", "generator", "keywords", "robots", "language", "googlebot", "Slurp", "bingbot", "geo.position", "geo.placename", "geo.region", "ICBM", "viewport"}
// MessagePage is an easy way to output a HTML page only given a title, the body
// (will be placed between the <body></body> tags) and the name of one of the
// built-in themes. Does not close <body> and <html>.
// Deprecated
func MessagePage(title, body, theme string) string {
return fmt.Sprintf("<!doctype html><html><head><title>%s</title>%s</head><body><h1>%s</h1>%s", title, StyleHead(theme), title, body)
}
var policy = bluemonday.UGCPolicy()
// StyleHead returns contents that goes in "<head>", as bytes.
// This is either CSS wrapped in a "<style>" tag, or "<link>" tags to CSS and JS.
func StyleHead(theme string) []byte {
// Sanitize the theme name
theme = policy.Sanitize(theme)
var buf bytes.Buffer
if theme == "material" {
buf.WriteString(MaterialHead())
}
if strings.HasSuffix(theme, ".css") {
buf.WriteString("<style>html { margin: 3em; }</style>")
buf.WriteString("<link rel=\"stylesheet\" href=\"" + theme + "\">")
} else {
buf.WriteString("<style>")
buf.Write(builtinThemes[theme])
buf.WriteString("</style>")
}
return buf.Bytes()
}
// MessagePageBytes provides the same functionalityt as MessagePage,
// but with []byte instead of string, and without closing </body></html>
func MessagePageBytes(title string, body []byte, theme string) []byte {
// Sanitize the theme and title
theme = policy.Sanitize(theme)
title = policy.Sanitize(title)
var buf bytes.Buffer
buf.WriteString("<!doctype html><html><head><title>")
buf.WriteString(title)
buf.WriteString("</title>")
buf.Write(StyleHead(theme))
buf.WriteString("</head><body><h1>")
buf.WriteString(title)
buf.WriteString("</h1>")
buf.Write(body)
return buf.Bytes()
}
// SimpleHTMLPage provides a quick way to build a HTML page
func SimpleHTMLPage(title, headline, inhead, body, language []byte) []byte {
// Sanitize the title, headline and language
titleString := policy.Sanitize(string(title))
headlineString := policy.Sanitize(string(headline))
languageString := policy.Sanitize(string(language))
var buf bytes.Buffer
if len(languageString) > 0 {
buf.WriteString("<!doctype html><html lang=\"")
buf.WriteString(languageString)
buf.WriteString("\">")
} else {
buf.WriteString("<!doctype html><html>")
}
if len(titleString) > 0 {
buf.WriteString("<head><title>")
buf.WriteString(titleString)
buf.WriteString("</title></head>")
}
buf.Write(inhead)
buf.WriteString("<body>")
if len(headlineString) > 0 {
buf.WriteString("<h1>")
buf.WriteString(headlineString)
buf.WriteString("</h1>")
}
buf.Write(body)
return buf.Bytes()
}
// HTMLLink builds an HTML link given the link text, the URL to a file/directory
// and a boolean that is true if the given URL is to a directory.
func HTMLLink(text, urlPath string, isDirectory bool) string {
return HTMLLinkWithBase(text, "", urlPath, isDirectory)
}
// HTMLLinkWithBase builds an HTML link, just like HTMLLink, but with the given
// base URL in front of the URL path. The base URL can be a path, like "/files",
// or an absolute URL. An empty base URL links relative to the server root.
func HTMLLinkWithBase(text, baseURL, urlPath string, isDirectory bool) string {
// Sanitize the link text
text = policy.Sanitize(text)
// Add a final slash, if needed
if isDirectory {
text += "/"
urlPath += "/"
}
// Percent-encode the URL path so filenames with newlines, spaces or
// other special bytes produce a valid href. See issue #144.
href := strings.TrimSuffix(baseURL, "/") + (&url.URL{Path: "/" + urlPath}).EscapedPath()
return "<a href=\"" + html.EscapeString(href) + "\">" + text + "</a><br>"
}
// StyleAmber modifies Amber source code so that a link to the given stylesheet URL is added
func StyleAmber(amberdata []byte, url string) []byte {
// Sanitize the URL
url = policy.Sanitize(url)
// If the given url is not already mentioned and the data contains "body"
if !bytes.Contains(amberdata, []byte(url)) && bytes.Contains(amberdata, []byte("html")) && bytes.Contains(amberdata, []byte("body")) {
// Extract one level of indendation
whitespace := OneLevelOfIndentation(&amberdata, "body")
// Check if there already is a head section
if bytes.Contains(amberdata, []byte("head")) {
// Add a link to the stylesheet
return bytes.Replace(amberdata, []byte("head\n"), []byte("head\n"+whitespace+whitespace+`link[href="`+url+`"][rel="stylesheet"][type="text/css"]`+"\n"), 1)
} else if bytes.Contains(amberdata, []byte("body")) {
// Add a link to the stylesheet
return bytes.Replace(amberdata, []byte("html\n"), []byte("html\n"+whitespace+"head\n"+whitespace+whitespace+`link[href="`+url+`"][rel="stylesheet"][type="text/css"]`+"\n"), 1)
}
}
return amberdata
}
// StyleHTML modifies HTML source code so that a link to the given stylesheet URL is added
func StyleHTML(htmldata []byte, url string) []byte {
// Sanitize the URL
url = policy.Sanitize(url)
// If the given url is not already mentioned and the data contains "body"
if !bytes.Contains(htmldata, []byte(url)) && bytes.Contains(htmldata, []byte("body")) {
if bytes.Contains(htmldata, []byte("</head>")) {
return bytes.Replace(htmldata, []byte("</head>"), []byte(" <link rel=\"stylesheet\" href=\""+url+"\">\n </head>"), 1)
} else if bytes.Contains(htmldata, []byte("<body>")) {
return bytes.Replace(htmldata, []byte("<body>"), []byte(" <head>\n <link rel=\"stylesheet\" href=\""+url+"\">\n </head>\n <body>"), 1)
}
}
return htmldata
}
// InsertDoctype inserts <doctype html> to the HTML, if missing.
// Does not check if the given data is HTML. Assumes it to be HTML.
func InsertDoctype(htmldata []byte) []byte {
// If there are more than two lines
if bytes.Count(htmldata, []byte("\n")) > 2 {
fields := bytes.SplitN(htmldata, []byte("\n"), 3)
line1 := strings.ToLower(string(fields[0]))
line2 := strings.ToLower(string(fields[1]))
if strings.Contains(line1, "doctype") || strings.Contains(line2, "doctype") {
return htmldata
}
// Doctype is missing from the first two lines, add it
return []byte("<!doctype html>\n" + string(htmldata))
}
return htmldata
}
// NoPage generates a HTML page for when a file is not found
func NoPage(filename, theme string) []byte {
// Sanitize the filename and the theme name
filename = policy.Sanitize(filename)
theme = policy.Sanitize(theme)
// Return a HTML page
return MessagePageBytes("Not found", []byte("File not found: "+filename), theme)
}