M file/proxy.go => file/proxy.go +2 -0
@@ 143,6 143,7 @@ func Proxy(w http.ResponseWriter, r *http.Request, u *url.URL) error {
source := r.URL.Query().Get("source") != ""
td := templates.TemplateData{
URL: u.String(),
+ RootURL: util.RootURL(*u),
ParentURL: util.ParentURL(*u),
Charset: "utf-8",
Lang: settings.Current.DefaultLang,
@@ 180,6 181,7 @@ func Proxy(w http.ResponseWriter, r *http.Request, u *url.URL) error {
source := r.URL.Query().Get("source") != ""
td := templates.TemplateData{
URL: u.String(),
+ RootURL: util.RootURL(*u),
ParentURL: util.ParentURL(*u),
Charset: "utf-8",
Lang: settings.Current.DefaultLang,
M finger/finger.go => finger/finger.go +1 -0
@@ 58,6 58,7 @@ func Proxy(w http.ResponseWriter, _ *http.Request, u *url.URL) error {
rd := bufio.NewReader(conn)
td := templates.TemplateData{
URL: u.String(),
+ RootURL: util.RootURL(*u),
ParentURL: util.ParentURL(*u),
}
return text.TextToHTML(w, u, rd, td)
M gemini/gemini.go => gemini/gemini.go +1 -0
@@ 104,6 104,7 @@ func ProxyGemini(w http.ResponseWriter, r *http.Request, u *url.URL) (*url.URL,
td := templates.TemplateData{
URL: u.String(),
+ RootURL: util.RootURL(*u),
ParentURL: util.ParentURL(*u),
Charset: "utf-8",
Lang: settings.Current.DefaultLang,
M handlers.go => handlers.go +1 -0
@@ 269,6 269,7 @@ func proxy(w http.ResponseWriter, r *http.Request) {
td := templates.TemplateData{
Error: err.Error(),
URL: u.String(),
+ RootURL: util.RootURL(*u),
ParentURL: util.ParentURL(*u),
}
err = templates.Templates.ExecuteTemplate(w, "home.html.tmpl", td)
M templates/template_strings.go => templates/template_strings.go +2 -0
@@ 10,6 10,7 @@ const Footer string = `{{define "footer"}}
<div id="footer">
<div id="footer-menu">
<a href="/">Home</a>
+{{if .RootURL}}<a href="/?url={{.RootURL}}">Root</a>{{end}}
{{if .ParentURL}}<a href="/?url={{.ParentURL}}">Parent</a>{{end}}
<a href="#header">Top</a>
<span style="float:right;">
@@ 89,6 90,7 @@ const Header string = `{{define "header"}}
<div id="header">
<div id="header-menu">
<a href="/">Home</a>
+{{if .RootURL}}<a href="/?url={{.RootURL}}">Root</a>{{end}}
{{if .ParentURL}}<a href="/?url={{.ParentURL}}">Parent</a>{{end}}
<a href="#footer">Bottom</a>
<span style="float:right;">
M templates/templates.go => templates/templates.go +1 -0
@@ 21,6 21,7 @@ type TemplateData struct {
Lang string
Meta string
URL string
+ RootURL string
ParentURL string
Warning string
Settings settings.Settings
M util/url.go => util/url.go +16 -2
@@ 20,9 20,23 @@ func AbsoluteURL(baseURL *url.URL, lineURL string) (*url.URL, error) {
return baseURL.ResolveReference(u), nil
}
+// RootURL returns the root of the supplied URL as a string. If the path
+// component of the supplied URL is empty or a single slash (it's already the
+// root) it returns an empty string.
+func RootURL(u url.URL) string {
+ if u.Path == "" || u.Path == "/" {
+ return ""
+ }
+ u.Path = "/"
+ // Strip queries and fragments since they might be invalid in the root.
+ u.RawQuery = ""
+ u.Fragment = ""
+ return u.String()
+}
+
// ParentURL returns the parent of the supplied URL as a string. If the path
-// component of the supplied URL is empty or is just a slash it returns an
-// empty string.
+// component of the supplied URL is empty or a single slash (it has no parent)
+// it returns an empty string.
func ParentURL(u url.URL) string {
u.Path = path.Dir(strings.TrimRight(u.Path, "/"))
if u.Path == "." {
M util/url_test.go => util/url_test.go +32 -0
@@ 40,6 40,38 @@ func TestAbsoluteURL(t *testing.T) {
}
}
+func TestRootURL(t *testing.T) {
+ type testData struct {
+ Input string
+ Want string
+ }
+ data := []testData{
+ {"", ""},
+ {"gemini://example.com", ""},
+ {"gemini://example.com/", ""},
+ {"gemini://example.com/foo", "gemini://example.com/"},
+ {"gemini://example.com/foo/", "gemini://example.com/"},
+ {"http://example.com/foo/bar", "http://example.com/"},
+ {"http://example.com/foo/bar/", "http://example.com/"},
+ {"http://example.com/foo?q=1", "http://example.com/"},
+ {"http://example.com/foo/bar?q=1", "http://example.com/"},
+ {"http://example.com/foo?q=1&p=0", "http://example.com/"},
+ {"http://example.com/foo/bar?q=1&p=0", "http://example.com/"},
+ {"http://example.com/foo#f", "http://example.com/"},
+ {"http://example.com/foo/bar#f", "http://example.com/"},
+ }
+ for _, d := range data {
+ u, err := url.Parse(d.Input)
+ if err != nil {
+ t.Fatalf(`url.Parse(%q) failed: %v`, d.Input, err)
+ }
+ have := RootURL(*u)
+ if have != d.Want {
+ t.Fatalf(`RootURL(%q) = %q, wanted %q`, d.Input, have, d.Want)
+ }
+ }
+}
+
func TestParentURL(t *testing.T) {
type testData struct {
Input string