1diff --git a/go.mod b/go.mod
2index 5bd4d7647b43cb737a873b5636a162a1ea25df69..cc63e5d26352e2c39488c00f139e5fdbe17cf13f 100644
3--- a/go.mod
4+++ b/go.mod
5@@ -5,9 +5,11 @@
6 require (
7 git.sr.ht/~emersion/go-scfg v0.0.0-20240128091534-2ae16e782082
8 github.com/alecthomas/chroma/v2 v2.13.0
9+ github.com/andybalholm/brotli v1.1.0
10 github.com/go-git/go-git/v5 v5.12.0
11 github.com/gomarkdown/markdown v0.0.0-20240419095408-642f0ee99ae2
12 github.com/google/go-cmp v0.6.0
13+ github.com/klauspost/compress v1.17.8
14 github.com/valyala/quicktemplate v1.7.0
15 golang.org/x/sync v0.7.0
16 )
17diff --git a/go.sum b/go.sum
18index 69c34b73d113fa1f0ae49c5068a2517c3ee91725..f010dc9d81e57367a63071c716ea39fe1f0960a3 100644
19--- a/go.sum
20+++ b/go.sum
21@@ -15,6 +15,8 @@ github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
22 github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
23 github.com/andybalholm/brotli v1.0.2/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
24 github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
25+github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
26+github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
27 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
28 github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
29 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
30@@ -59,6 +61,8 @@ github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
31 github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
32 github.com/klauspost/compress v1.13.4/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
33 github.com/klauspost/compress v1.13.5/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
34+github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
35+github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
36 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
37 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
38 github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
39diff --git a/pkg/ext/compression.go b/pkg/ext/compression.go
40new file mode 100644
41index 0000000000000000000000000000000000000000..92144b82132968aa7db6115aad78f37d71c8e354
42--- /dev/null
43+++ b/pkg/ext/compression.go
44@@ -0,0 +1,142 @@
45+package ext
46+
47+import (
48+ "compress/gzip"
49+ "compress/lzw"
50+ "errors"
51+ "io"
52+ "log/slog"
53+ "net/http"
54+ "strconv"
55+ "strings"
56+
57+ "git.gabrielgio.me/cerrado/pkg/u"
58+ "github.com/andybalholm/brotli"
59+ "github.com/klauspost/compress/zstd"
60+)
61+
62+var (
63+ invalidParamErr = errors.New("Invalid weighted param")
64+)
65+
66+type CompressionResponseWriter struct {
67+ innerWriter http.ResponseWriter
68+ compressWriter io.Writer
69+}
70+
71+func Compress(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
72+ return func(w http.ResponseWriter, r *http.Request) {
73+ if accept, ok := r.Header["Accept-Encoding"]; ok {
74+ if compress, algo := GetCompressionWriter(u.FirstOrZero(accept), w); algo != "" {
75+ defer compress.Close()
76+ w.Header().Add("Content-Encoding", algo)
77+ w = &CompressionResponseWriter{
78+ innerWriter: w,
79+ compressWriter: compress,
80+ }
81+ }
82+ }
83+ next(w, r)
84+ }
85+}
86+
87+func GetCompressionWriter(header string, inner io.Writer) (io.WriteCloser, string) {
88+ c := GetCompression(header)
89+ switch c {
90+ case "br":
91+ return GetBrotliWriter(inner), c
92+ case "gzip":
93+ return GetGZIPWriter(inner), c
94+ case "compress":
95+ return GetLZWWriter(inner), c
96+ case "zstd":
97+ return GetZSTDWriter(inner), c
98+ default:
99+ return nil, ""
100+ }
101+
102+}
103+
104+func (c *CompressionResponseWriter) Header() http.Header {
105+ return c.innerWriter.Header()
106+}
107+func (c *CompressionResponseWriter) Write(b []byte) (int, error) {
108+ return c.compressWriter.Write(b)
109+}
110+
111+func (c *CompressionResponseWriter) WriteHeader(statusCode int) {
112+ c.innerWriter.WriteHeader(statusCode)
113+}
114+
115+func GetCompression(header string) string {
116+ c := "*"
117+ q := 0.0
118+
119+ if header == "" {
120+ return c
121+ }
122+
123+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Encoding
124+ for _, e := range strings.Split(header, ",") {
125+ ps := strings.Split(e, ";")
126+ if len(ps) == 2 {
127+ w, err := getWeighedValue(ps[1])
128+ if err != nil {
129+ slog.Error(
130+ "Error parsing weighting from Accept-Encoding",
131+ "error", err,
132+ )
133+ continue
134+ }
135+ // gettting weighting value
136+ if w > q {
137+ q = w
138+ c = strings.Trim(ps[0], " ")
139+ }
140+ } else {
141+ if 1 > q {
142+ q = 1
143+ c = strings.Trim(ps[0], " ")
144+ }
145+ }
146+ }
147+
148+ return c
149+}
150+
151+func GetGZIPWriter(w io.Writer) io.WriteCloser {
152+ // error can be ignored here since it will only err when compression level
153+ // is not valid
154+ r, _ := gzip.NewWriterLevel(w, gzip.BestCompression)
155+ return r
156+}
157+
158+func GetBrotliWriter(w io.Writer) io.WriteCloser {
159+ return brotli.NewWriterLevel(w, brotli.BestCompression)
160+}
161+
162+func GetZSTDWriter(w io.Writer) io.WriteCloser {
163+ // error can be ignored here since it will only opts are given
164+ r, _ := zstd.NewWriter(w)
165+ return r
166+}
167+
168+func GetLZWWriter(w io.Writer) io.WriteCloser {
169+ return lzw.NewWriter(w, lzw.LSB, 8)
170+}
171+
172+func getWeighedValue(part string) (float64, error) {
173+ ps := strings.SplitN(part, "=", 2)
174+ if len(ps) != 2 {
175+ return 0, invalidParamErr
176+ }
177+ if name := strings.TrimSpace(ps[0]); name == "q" {
178+ w, err := strconv.ParseFloat(ps[1], 64)
179+ if err != nil {
180+ return 0, err
181+ }
182+ return w, nil
183+ }
184+
185+ return 0, invalidParamErr
186+}
187diff --git a/pkg/ext/compression_test.go b/pkg/ext/compression_test.go
188new file mode 100644
189index 0000000000000000000000000000000000000000..64243780d97c90cee130ae60d5652275dd7d9814
190--- /dev/null
191+++ b/pkg/ext/compression_test.go
192@@ -0,0 +1,42 @@
193+// go:build unit
194+package ext
195+
196+import "testing"
197+
198+func TestGetCompression(t *testing.T) {
199+ testCases := []struct {
200+ name string
201+ header string
202+ compression string
203+ }{
204+ {
205+ name: "Empty",
206+ header: "",
207+ compression: "*",
208+ },
209+ {
210+ name: "Weighted",
211+ header: "gzip;q=1.0, *;q=0.5",
212+ compression: "gzip",
213+ },
214+ {
215+ name: "Mixed",
216+ header: "deflate, gzip;q=1.0, *;q=0.5",
217+ compression: "deflate",
218+ },
219+ {
220+ name: "Not weighted",
221+ header: "zstd, deflate, gzip",
222+ compression: "zstd",
223+ },
224+ }
225+
226+ for _, tc := range testCases {
227+ t.Run(tc.name, func(t *testing.T) {
228+ got := GetCompression(tc.header)
229+ if got != tc.compression {
230+ t.Errorf("Wrong compression returned: got %s want %s", got, tc.compression)
231+ }
232+ })
233+ }
234+}
235diff --git a/pkg/ext/mime.go b/pkg/ext/mime.go
236new file mode 100644
237index 0000000000000000000000000000000000000000..6da66e3db03709439e1a8731116081dd5fe738bb
238--- /dev/null
239+++ b/pkg/ext/mime.go
240@@ -0,0 +1,24 @@
241+package ext
242+
243+import "net/http"
244+
245+type ContentType = string
246+
247+const (
248+ TextHTML ContentType = "text/html"
249+)
250+
251+func Html(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
252+ return func(w http.ResponseWriter, r *http.Request) {
253+ next(w, r)
254+ }
255+}
256+
257+func SetHTML(w http.ResponseWriter) {
258+ SetMIME(w, TextHTML)
259+
260+}
261+
262+func SetMIME(w http.ResponseWriter, mime ContentType) {
263+ w.Header().Add("Content-Type", mime)
264+}
265diff --git a/pkg/handler/git/handler.go b/pkg/handler/git/handler.go
266index f3e74c747e433b33f1b68d13473ef5ab507b5b95..28cc99e1ffe8564b7d63b31a3452134a51dc33aa 100644
267--- a/pkg/handler/git/handler.go
268+++ b/pkg/handler/git/handler.go
269@@ -6,6 +6,7 @@ "log/slog"
270 "net/http"
271 "path/filepath"
272
273+ "git.gabrielgio.me/cerrado/pkg/ext"
274 "git.gabrielgio.me/cerrado/pkg/service"
275 "git.gabrielgio.me/cerrado/templates"
276 "github.com/alecthomas/chroma/v2"
277@@ -50,6 +51,7 @@ templates.WritePageTemplate(w, gitList)
278 }
279
280 func (g *GitHandler) Summary(w http.ResponseWriter, r *http.Request) {
281+ ext.SetHTML(w)
282 name := r.PathValue("name")
283 ref, err := g.gitService.GetHead(name)
284 if err != nil {
285@@ -66,6 +68,7 @@ templates.WritePageTemplate(w, gitList)
286 }
287
288 func (g *GitHandler) About(w http.ResponseWriter, r *http.Request) {
289+ ext.SetHTML(w)
290 name := r.PathValue("name")
291 ref, err := g.gitService.GetHead(name)
292 if err != nil {
293@@ -81,6 +84,7 @@ templates.WritePageTemplate(w, gitList)
294 }
295
296 func (g *GitHandler) Refs(w http.ResponseWriter, r *http.Request) {
297+ ext.SetHTML(w)
298 name := r.PathValue("name")
299
300 tags, err := g.gitService.ListTags(name)
301@@ -113,6 +117,7 @@ templates.WritePageTemplate(w, gitList)
302 }
303
304 func (g *GitHandler) Tree(w http.ResponseWriter, r *http.Request) {
305+ ext.SetHTML(w)
306 name := r.PathValue("name")
307 ref := r.PathValue("ref")
308 rest := r.PathValue("rest")
309@@ -137,6 +142,7 @@ templates.WritePageTemplate(w, gitList)
310 }
311
312 func (g *GitHandler) Blob(w http.ResponseWriter, r *http.Request) {
313+ ext.SetHTML(w)
314 name := r.PathValue("name")
315 ref := r.PathValue("ref")
316 rest := r.PathValue("rest")
317@@ -178,6 +184,7 @@ templates.WritePageTemplate(w, gitList)
318 }
319
320 func (g *GitHandler) Log(w http.ResponseWriter, r *http.Request) {
321+ ext.SetHTML(w)
322 name := r.PathValue("name")
323 ref := r.PathValue("ref")
324
325diff --git a/pkg/handler/router.go b/pkg/handler/router.go
326index ed782f75d525c637e539a4e6c07795cc0d8ac8d2..de5117c9e0a6b8836117e40c1b237bdb1afa6346 100644
327--- a/pkg/handler/router.go
328+++ b/pkg/handler/router.go
329@@ -4,6 +4,7 @@ import (
330 "net/http"
331
332 serverconfig "git.gabrielgio.me/cerrado/pkg/config"
333+ "git.gabrielgio.me/cerrado/pkg/ext"
334 "git.gabrielgio.me/cerrado/pkg/handler/about"
335 "git.gabrielgio.me/cerrado/pkg/handler/config"
336 "git.gabrielgio.me/cerrado/pkg/handler/git"
337@@ -31,15 +32,19 @@ }
338
339 mux := http.NewServeMux()
340
341- mux.HandleFunc("/static/{file}", staticHandler)
342- mux.HandleFunc("/{name}/about/{$}", gitHandler.About)
343- mux.HandleFunc("/{name}", gitHandler.Summary)
344- mux.HandleFunc("/{name}/refs/{$}", gitHandler.Refs)
345- mux.HandleFunc("/{name}/tree/{ref}/{rest...}", gitHandler.Tree)
346- mux.HandleFunc("/{name}/blob/{ref}/{rest...}", gitHandler.Blob)
347- mux.HandleFunc("/{name}/log/{ref}", gitHandler.Log)
348- mux.HandleFunc("/config", configHander)
349- mux.HandleFunc("/about", aboutHandler.About)
350- mux.HandleFunc("/", gitHandler.List)
351+ mux.HandleFunc("/static/{file}", m(staticHandler))
352+ mux.HandleFunc("/{name}/about/{$}", m(gitHandler.About))
353+ mux.HandleFunc("/{name}", m(gitHandler.Summary))
354+ mux.HandleFunc("/{name}/refs/{$}", m(gitHandler.Refs))
355+ mux.HandleFunc("/{name}/tree/{ref}/{rest...}", m(gitHandler.Tree))
356+ mux.HandleFunc("/{name}/blob/{ref}/{rest...}", m(gitHandler.Blob))
357+ mux.HandleFunc("/{name}/log/{ref}", m(gitHandler.Log))
358+ mux.HandleFunc("/config", m(configHander))
359+ mux.HandleFunc("/about", m(aboutHandler.About))
360+ mux.HandleFunc("/", m(gitHandler.List))
361 return mux, nil
362 }
363+
364+func m(next func(w http.ResponseWriter, r *http.Request)) func(w http.ResponseWriter, r *http.Request) {
365+ return ext.Compress(next)
366+}
367diff --git a/pkg/handler/static/handler.go b/pkg/handler/static/handler.go
368index a8b458324580082c2424b21a65d92c2849e6a836..5155068306666b2303de7f129585108f32ea5dfa 100644
369--- a/pkg/handler/static/handler.go
370+++ b/pkg/handler/static/handler.go
371@@ -2,8 +2,11 @@ package static
372
373 import (
374 "io/fs"
375+ "mime"
376 "net/http"
377+ "path/filepath"
378
379+ "git.gabrielgio.me/cerrado/pkg/ext"
380 "git.gabrielgio.me/cerrado/static"
381 )
382
383@@ -14,8 +17,12 @@ return nil, err
384 }
385
386 return func(w http.ResponseWriter, r *http.Request) {
387- f := r.PathValue("file")
388-
389+ var (
390+ f = r.PathValue("file")
391+ e = filepath.Ext(f)
392+ m = mime.TypeByExtension(e)
393+ )
394+ ext.SetMIME(w, m)
395 http.ServeFileFS(w, r, staticFs, f)
396 }, nil
397 }
398diff --git a/pkg/u/list.go b/pkg/u/list.go
399index 34eafd11492cecb0eddfe8cc7ad4c4df8bf96511..cf71909e439d7726b4c6d9c6fcea771ad776a5bf 100644
400--- a/pkg/u/list.go
401+++ b/pkg/u/list.go
402@@ -8,6 +8,14 @@ }
403 return v[0], true
404 }
405
406+func FirstOrZero[T any](v []T) T {
407+ if len(v) == 0 {
408+ var zero T
409+ return zero
410+ }
411+ return v[0]
412+}
413+
414 func ChunkBy[T any](items []T, chunkSize int) [][]T {
415 var chunks = make([][]T, 0, (len(items)/chunkSize)+1)
416 for chunkSize < len(items) {
417diff --git a/pkg/u/list_test.go b/pkg/u/list_test.go
418index a6d84c7eaa6ceb401da5f22978f0f285ea1b7478..805a2091b59ce4c644f748b4b6c49a28aec50cdb 100644
419--- a/pkg/u/list_test.go
420+++ b/pkg/u/list_test.go
421@@ -94,3 +94,38 @@ }
422 })
423 }
424 }
425+
426+func TestFirstOrZero(t *testing.T) {
427+ testCases := []struct {
428+ name string
429+ slice []int
430+ first int
431+ }{
432+ {
433+ name: "multiple items slice",
434+ slice: []int{1, 2, 3},
435+ first: 1,
436+ },
437+ {
438+ name: "single item slice",
439+ slice: []int{1},
440+ first: 1,
441+ },
442+ {
443+ name: "empty slice",
444+ slice: []int{},
445+ first: 0,
446+ },
447+ }
448+ for _, tc := range testCases {
449+ t.Run(tc.name, func(t *testing.T) {
450+
451+ first := FirstOrZero(tc.slice)
452+
453+ if first != tc.first {
454+ t.Errorf("Error first, want %d got %d", tc.first, first)
455+ }
456+
457+ })
458+ }
459+}
460diff --git a/templates/base.qtpl b/templates/base.qtpl
461index 180b1ab428bc964511451c36569b198be2f5d3ee..497aa6d59d5fa49671e8ad4da524712bf62c40e8 100644
462--- a/templates/base.qtpl
463+++ b/templates/base.qtpl
464@@ -37,12 +37,14 @@ %}
465
466 Page prints a page implementing Page interface.
467 {% func PageTemplate(p Page) %}
468+<!DOCTYPE html>
469 <html lang="en">
470 <head>
471 <meta charset="utf-8">
472 <link rel="icon" href="data:,">
473 <title>cerrado | {%= p.Title() %}</title>
474 <link rel="stylesheet" href="/static/main{%s Slug%}.css">
475+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
476 <meta name="viewport" content="width=device-width, initial-scale=1" />
477 </head>
478 <body>
479diff --git a/templates/base.qtpl.go b/templates/base.qtpl.go
480index c5570c8ee4119c1861d86e15aec1f7e24a4a35a9..5f39e8dd4040b5dbe896fc5c44dc5b423636f9eb 100644
481--- a/templates/base.qtpl.go
482+++ b/templates/base.qtpl.go
483@@ -82,168 +82,170 @@ //line base.qtpl:39
484 func StreamPageTemplate(qw422016 *qt422016.Writer, p Page) {
485 //line base.qtpl:39
486 qw422016.N().S(`
487+<!DOCTYPE html>
488 <html lang="en">
489 <head>
490 <meta charset="utf-8">
491 <link rel="icon" href="data:,">
492 <title>cerrado | `)
493-//line base.qtpl:44
494+//line base.qtpl:45
495 p.StreamTitle(qw422016)
496-//line base.qtpl:44
497+//line base.qtpl:45
498 qw422016.N().S(`</title>
499 <link rel="stylesheet" href="/static/main`)
500-//line base.qtpl:45
501+//line base.qtpl:46
502 qw422016.E().S(Slug)
503-//line base.qtpl:45
504+//line base.qtpl:46
505 qw422016.N().S(`.css">
506+ <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
507 <meta name="viewport" content="width=device-width, initial-scale=1" />
508 </head>
509 <body>
510 `)
511-//line base.qtpl:49
512+//line base.qtpl:51
513 p.StreamNavbar(qw422016)
514-//line base.qtpl:49
515+//line base.qtpl:51
516 qw422016.N().S(`
517 <div class="container">
518 `)
519-//line base.qtpl:51
520+//line base.qtpl:53
521 p.StreamContent(qw422016)
522-//line base.qtpl:51
523+//line base.qtpl:53
524 qw422016.N().S(`
525 </div>
526 </body>
527 `)
528-//line base.qtpl:54
529+//line base.qtpl:56
530 p.StreamScript(qw422016)
531-//line base.qtpl:54
532+//line base.qtpl:56
533 qw422016.N().S(`
534 </html>
535 `)
536-//line base.qtpl:56
537+//line base.qtpl:58
538 }
539
540-//line base.qtpl:56
541+//line base.qtpl:58
542 func WritePageTemplate(qq422016 qtio422016.Writer, p Page) {
543-//line base.qtpl:56
544+//line base.qtpl:58
545 qw422016 := qt422016.AcquireWriter(qq422016)
546-//line base.qtpl:56
547+//line base.qtpl:58
548 StreamPageTemplate(qw422016, p)
549-//line base.qtpl:56
550+//line base.qtpl:58
551 qt422016.ReleaseWriter(qw422016)
552-//line base.qtpl:56
553+//line base.qtpl:58
554 }
555
556-//line base.qtpl:56
557+//line base.qtpl:58
558 func PageTemplate(p Page) string {
559-//line base.qtpl:56
560+//line base.qtpl:58
561 qb422016 := qt422016.AcquireByteBuffer()
562-//line base.qtpl:56
563+//line base.qtpl:58
564 WritePageTemplate(qb422016, p)
565-//line base.qtpl:56
566+//line base.qtpl:58
567 qs422016 := string(qb422016.B)
568-//line base.qtpl:56
569+//line base.qtpl:58
570 qt422016.ReleaseByteBuffer(qb422016)
571-//line base.qtpl:56
572+//line base.qtpl:58
573 return qs422016
574-//line base.qtpl:56
575+//line base.qtpl:58
576 }
577
578-//line base.qtpl:58
579+//line base.qtpl:60
580 type BasePage struct{}
581
582-//line base.qtpl:59
583+//line base.qtpl:61
584 func (p *BasePage) StreamTitle(qw422016 *qt422016.Writer) {
585-//line base.qtpl:59
586+//line base.qtpl:61
587 qw422016.N().S(`Empty`)
588-//line base.qtpl:59
589+//line base.qtpl:61
590 }
591
592-//line base.qtpl:59
593+//line base.qtpl:61
594 func (p *BasePage) WriteTitle(qq422016 qtio422016.Writer) {
595-//line base.qtpl:59
596+//line base.qtpl:61
597 qw422016 := qt422016.AcquireWriter(qq422016)
598-//line base.qtpl:59
599+//line base.qtpl:61
600 p.StreamTitle(qw422016)
601-//line base.qtpl:59
602+//line base.qtpl:61
603 qt422016.ReleaseWriter(qw422016)
604-//line base.qtpl:59
605+//line base.qtpl:61
606 }
607
608-//line base.qtpl:59
609+//line base.qtpl:61
610 func (p *BasePage) Title() string {
611-//line base.qtpl:59
612+//line base.qtpl:61
613 qb422016 := qt422016.AcquireByteBuffer()
614-//line base.qtpl:59
615+//line base.qtpl:61
616 p.WriteTitle(qb422016)
617-//line base.qtpl:59
618+//line base.qtpl:61
619 qs422016 := string(qb422016.B)
620-//line base.qtpl:59
621+//line base.qtpl:61
622 qt422016.ReleaseByteBuffer(qb422016)
623-//line base.qtpl:59
624+//line base.qtpl:61
625 return qs422016
626-//line base.qtpl:59
627+//line base.qtpl:61
628 }
629
630-//line base.qtpl:60
631+//line base.qtpl:62
632 func (p *BasePage) StreamBody(qw422016 *qt422016.Writer) {
633-//line base.qtpl:60
634+//line base.qtpl:62
635 qw422016.N().S(`HelloWorld`)
636-//line base.qtpl:60
637+//line base.qtpl:62
638 }
639
640-//line base.qtpl:60
641+//line base.qtpl:62
642 func (p *BasePage) WriteBody(qq422016 qtio422016.Writer) {
643-//line base.qtpl:60
644+//line base.qtpl:62
645 qw422016 := qt422016.AcquireWriter(qq422016)
646-//line base.qtpl:60
647+//line base.qtpl:62
648 p.StreamBody(qw422016)
649-//line base.qtpl:60
650+//line base.qtpl:62
651 qt422016.ReleaseWriter(qw422016)
652-//line base.qtpl:60
653+//line base.qtpl:62
654 }
655
656-//line base.qtpl:60
657+//line base.qtpl:62
658 func (p *BasePage) Body() string {
659-//line base.qtpl:60
660+//line base.qtpl:62
661 qb422016 := qt422016.AcquireByteBuffer()
662-//line base.qtpl:60
663+//line base.qtpl:62
664 p.WriteBody(qb422016)
665-//line base.qtpl:60
666+//line base.qtpl:62
667 qs422016 := string(qb422016.B)
668-//line base.qtpl:60
669+//line base.qtpl:62
670 qt422016.ReleaseByteBuffer(qb422016)
671-//line base.qtpl:60
672+//line base.qtpl:62
673 return qs422016
674-//line base.qtpl:60
675+//line base.qtpl:62
676 }
677
678-//line base.qtpl:61
679+//line base.qtpl:63
680 func (p *BasePage) StreamScript(qw422016 *qt422016.Writer) {
681-//line base.qtpl:61
682+//line base.qtpl:63
683 }
684
685-//line base.qtpl:61
686+//line base.qtpl:63
687 func (p *BasePage) WriteScript(qq422016 qtio422016.Writer) {
688-//line base.qtpl:61
689+//line base.qtpl:63
690 qw422016 := qt422016.AcquireWriter(qq422016)
691-//line base.qtpl:61
692+//line base.qtpl:63
693 p.StreamScript(qw422016)
694-//line base.qtpl:61
695+//line base.qtpl:63
696 qt422016.ReleaseWriter(qw422016)
697-//line base.qtpl:61
698+//line base.qtpl:63
699 }
700
701-//line base.qtpl:61
702+//line base.qtpl:63
703 func (p *BasePage) Script() string {
704-//line base.qtpl:61
705+//line base.qtpl:63
706 qb422016 := qt422016.AcquireByteBuffer()
707-//line base.qtpl:61
708+//line base.qtpl:63
709 p.WriteScript(qb422016)
710-//line base.qtpl:61
711+//line base.qtpl:63
712 qs422016 := string(qb422016.B)
713-//line base.qtpl:61
714+//line base.qtpl:63
715 qt422016.ReleaseByteBuffer(qb422016)
716-//line base.qtpl:61
717+//line base.qtpl:63
718 return qs422016
719-//line base.qtpl:61
720+//line base.qtpl:63
721 }