diff options
author | Feuerfuchs <git@feuerfuchs.dev> | 2019-06-23 17:29:07 +0200 |
---|---|---|
committer | Feuerfuchs <git@feuerfuchs.dev> | 2019-06-23 17:29:07 +0200 |
commit | c448abd99a470e1ec541027077dcdef0745270d8 (patch) | |
tree | 433def56b6f0e81457cbe39fc85f402a9e4e18a8 | |
parent | Check type instead of file ext to render text via tpl (diff) | |
download | gopherproxy-c448abd99a470e1ec541027077dcdef0745270d8.tar.gz gopherproxy-c448abd99a470e1ec541027077dcdef0745270d8.tar.bz2 gopherproxy-c448abd99a470e1ec541027077dcdef0745270d8.zip |
Show expandable thumbnails for images
-rw-r--r-- | Makefile | 1 | ||||
-rw-r--r-- | assets/main.js | 39 | ||||
-rw-r--r-- | assets/style.css | 2 | ||||
-rw-r--r-- | css/main.scss | 58 | ||||
-rw-r--r-- | go.mod | 2 | ||||
-rw-r--r-- | go.sum | 4 | ||||
-rw-r--r-- | gopherproxy.go | 42 | ||||
-rw-r--r-- | js/main.ts | 42 | ||||
-rw-r--r-- | template.go | 16 |
9 files changed, 176 insertions, 30 deletions
@@ -7,6 +7,7 @@ dev: build | |||
7 | 7 | ||
8 | build: clean | 8 | build: clean |
9 | sassc -t compressed css/main.scss assets/style.css | 9 | sassc -t compressed css/main.scss assets/style.css |
10 | tsc --strict --outFile assets/main.js js/main.ts | ||
10 | pyftsubset fonts/iosevka-term-ss03-regular.ttf --name-IDs+=0,4,6 --text-file=glyphs.txt --flavor='woff' --output-file='assets/iosevka-term-ss03-regular.woff' | 11 | pyftsubset fonts/iosevka-term-ss03-regular.ttf --name-IDs+=0,4,6 --text-file=glyphs.txt --flavor='woff' --output-file='assets/iosevka-term-ss03-regular.woff' |
11 | pyftsubset fonts/iosevka-term-ss03-regular.ttf --name-IDs+=0,4,6 --text-file=glyphs.txt --flavor='woff2' --output-file='assets/iosevka-term-ss03-regular.woff2' | 12 | pyftsubset fonts/iosevka-term-ss03-regular.ttf --name-IDs+=0,4,6 --text-file=glyphs.txt --flavor='woff2' --output-file='assets/iosevka-term-ss03-regular.woff2' |
12 | go build -o ./gopherproxy ./cmd/gopherproxy/main.go | 13 | go build -o ./gopherproxy ./cmd/gopherproxy/main.go |
diff --git a/assets/main.js b/assets/main.js new file mode 100644 index 0000000..46cb7ee --- /dev/null +++ b/assets/main.js | |||
@@ -0,0 +1,39 @@ | |||
1 | "use strict"; | ||
2 | var linkQryEls = document.getElementsByClassName('link--QRY'); | ||
3 | var i = linkQryEls.length; | ||
4 | while (i--) { | ||
5 | linkQryEls[i].addEventListener('click', function (e) { | ||
6 | e.preventDefault(); | ||
7 | var resp = prompt('Please enter required input: ', ''); | ||
8 | if ((resp !== null) && (resp !== "")) { | ||
9 | window.location.href = e.target.href + '?' + resp; | ||
10 | } | ||
11 | return false; | ||
12 | }); | ||
13 | } | ||
14 | var imgPreviewEls = document.getElementsByClassName('img-preview'); | ||
15 | i = imgPreviewEls.length; | ||
16 | var _loop_1 = function () { | ||
17 | var imgPreviewEl = imgPreviewEls[i]; | ||
18 | var child = imgPreviewEl.children[0]; | ||
19 | var thumbnailUrl = child.src; | ||
20 | child.addEventListener('load', function (e) { | ||
21 | child.classList.remove('faded'); | ||
22 | }); | ||
23 | imgPreviewEls[i].addEventListener('click', function (e) { | ||
24 | e.preventDefault(); | ||
25 | child.classList.add('faded'); | ||
26 | if (child.classList.contains('expanded')) { | ||
27 | child.classList.remove('expanded'); | ||
28 | child.src = thumbnailUrl; | ||
29 | } | ||
30 | else { | ||
31 | child.classList.add('expanded'); | ||
32 | child.src = imgPreviewEl.href; | ||
33 | } | ||
34 | return false; | ||
35 | }); | ||
36 | }; | ||
37 | while (i--) { | ||
38 | _loop_1(); | ||
39 | } | ||
diff --git a/assets/style.css b/assets/style.css index 5891d3b..46ad8cb 100644 --- a/assets/style.css +++ b/assets/style.css | |||
@@ -1 +1 @@ | |||
@font-face{font-family:'Iosevka Term SS03';font-style:normal;font-weight:normal;src:url("/iosevka-term-ss03-regular.woff2") format("woff2"),url("/iosevka-term-ss03-regular.woff") format("woff")}body{margin:0;padding:0;background-color:#14171a;color:#cad1d8}::selection{color:#000;background-color:rgba(239,198,138,0.996)}:link{color:#fff}:visited{color:#cad1d8}.header{padding:.9em 1em;border-bottom:1px solid #353a3f;font-family:'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace;font-size:1.0625em;line-height:1.3;color:#686f76}.header__uripart{color:#929ba3}.header__uripart--last{color:#fff}.wrap{text-align:center}.content{display:inline-block;min-width:50em;min-width:85ch;margin:0;padding:2em 1em;text-align:left;font-family:'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace;font-size:1.0625em;line-height:1.5}.link-type{color:#929ba3} | @font-face{font-family:'Iosevka Term SS03';font-style:normal;font-weight:normal;src:url("/iosevka-term-ss03-regular.woff2") format("woff2"),url("/iosevka-term-ss03-regular.woff") format("woff")}body{margin:0;padding:0;background-color:#14171a;color:#cad1d8}button{font-family:'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace;font-size:1.0625em;line-height:1.5;background:none;border:0;padding:0;color:#fff}img{display:inline-block;vertical-align:top;max-width:8em;margin:.1em 0}img::selection{background-color:rgba(239,198,138,0.35)}img.expanded{max-width:40em;max-width:80ch}img.faded{opacity:.5}::selection{color:#000;background-color:rgba(239,198,138,0.996)}:link{color:#fff}:visited{color:#cad1d8}.header{font-family:'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace;font-size:1.0625em;line-height:1.5;padding:.9em 1em;border-bottom:1px solid #353a3f;line-height:1.3;color:#686f76}.header__uripart{color:#929ba3}.header__uripart--last{color:#fff}.wrap{text-align:center}.content{font-family:'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace;font-size:1.0625em;line-height:1.5;display:inline-block;min-width:50em;min-width:85ch;margin:0;padding:2em 1em;text-align:left}.type-annotation{color:#929ba3} | ||
diff --git a/css/main.scss b/css/main.scss index d0646d7..0d5a5d9 100644 --- a/css/main.scss +++ b/css/main.scss | |||
@@ -10,6 +10,12 @@ $border: mix(hsl(210, 100%, 95%), $background, 16%); | |||
10 | $sel-background: rgba($accent, .996); | 10 | $sel-background: rgba($accent, .996); |
11 | $sel-text: #000; | 11 | $sel-text: #000; |
12 | 12 | ||
13 | @mixin monospace-font { | ||
14 | font-family: 'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace; | ||
15 | font-size: 1 / 16 * 17em; | ||
16 | line-height: 1.5; | ||
17 | } | ||
18 | |||
13 | @font-face { | 19 | @font-face { |
14 | font-family: 'Iosevka Term SS03'; | 20 | font-family: 'Iosevka Term SS03'; |
15 | font-style: normal; | 21 | font-style: normal; |
@@ -31,6 +37,35 @@ body { | |||
31 | color: $text; | 37 | color: $text; |
32 | } | 38 | } |
33 | 39 | ||
40 | button { | ||
41 | @include monospace-font; | ||
42 | |||
43 | background: none; | ||
44 | border: 0; | ||
45 | padding: 0; | ||
46 | color: $text-plus; | ||
47 | } | ||
48 | |||
49 | img { | ||
50 | display: inline-block; | ||
51 | vertical-align: top; | ||
52 | max-width: 8em; | ||
53 | margin: .1em 0; | ||
54 | |||
55 | &::selection { | ||
56 | background-color: rgba($sel-background, .35); | ||
57 | } | ||
58 | |||
59 | &.expanded { | ||
60 | max-width: 40em; | ||
61 | max-width: 80ch; | ||
62 | } | ||
63 | |||
64 | &.faded { | ||
65 | opacity: .5; | ||
66 | } | ||
67 | } | ||
68 | |||
34 | ::selection { | 69 | ::selection { |
35 | color: $sel-text; | 70 | color: $sel-text; |
36 | background-color: $sel-background; | 71 | background-color: $sel-background; |
@@ -51,10 +86,10 @@ body { | |||
51 | // } | 86 | // } |
52 | 87 | ||
53 | .header { | 88 | .header { |
89 | @include monospace-font; | ||
90 | |||
54 | padding: .9em 1em; | 91 | padding: .9em 1em; |
55 | border-bottom: 1px solid $border; | 92 | border-bottom: 1px solid $border; |
56 | font-family: 'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace; | ||
57 | font-size: 1 / 16 * 17em; | ||
58 | line-height: 1.3; | 93 | line-height: 1.3; |
59 | color: $text-faint; | 94 | color: $text-faint; |
60 | } | 95 | } |
@@ -72,17 +107,16 @@ body { | |||
72 | } | 107 | } |
73 | 108 | ||
74 | .content { | 109 | .content { |
75 | display: inline-block; | 110 | @include monospace-font; |
76 | min-width: 50em; | 111 | |
77 | min-width: 5ch + 80; | 112 | display: inline-block; |
78 | margin: 0; | 113 | min-width: 50em; |
79 | padding: 2em 1em; | 114 | min-width: 5ch + 80; |
80 | text-align: left; | 115 | margin: 0; |
81 | font-family: 'Iosevka Term SS03', 'IBM Plex Mono', 'Fira Code', 'Fira Mono', 'Roboto Mono', 'Droid Sans Mono', Monaco, Consolas, Courier, monospace; | 116 | padding: 2em 1em; |
82 | font-size: 1 / 16 * 17em; | 117 | text-align: left; |
83 | line-height: 1.5; | ||
84 | } | 118 | } |
85 | 119 | ||
86 | .link-type { | 120 | .type-annotation { |
87 | color: $text-minus; | 121 | color: $text-minus; |
88 | } | 122 | } |
@@ -1,7 +1,9 @@ | |||
1 | module git.feuerfuchs.dev/Feuerfuchs/gopherproxy | 1 | module git.feuerfuchs.dev/Feuerfuchs/gopherproxy |
2 | 2 | ||
3 | require ( | 3 | require ( |
4 | github.com/davidbyttow/govips v0.0.0-20190304175058-d272f04c0fea | ||
4 | github.com/gobuffalo/packr v1.25.0 | 5 | github.com/gobuffalo/packr v1.25.0 |
6 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 | ||
5 | github.com/prologic/go-gopher v0.0.0-20181230133552-0c68ed5f58b0 | 7 | github.com/prologic/go-gopher v0.0.0-20181230133552-0c68ed5f58b0 |
6 | github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea | 8 | github.com/temoto/robotstxt v0.0.0-20180810133444-97ee4a9ee6ea |
7 | ) | 9 | ) |
@@ -1,6 +1,8 @@ | |||
1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 1 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | 2 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= |
3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= |
4 | github.com/davidbyttow/govips v0.0.0-20190304175058-d272f04c0fea h1:ZtETbJTO1R3qVLdVbpjrDhD5fR8bYVhhq2RMi7rOlH4= | ||
5 | github.com/davidbyttow/govips v0.0.0-20190304175058-d272f04c0fea/go.mod h1:a3qO525EPfJNYa0NXBcNtXzJvyQsJAxphEDa7OOHPBk= | ||
4 | github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= | 6 | github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= |
5 | github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= | 7 | github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= |
6 | github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= | 8 | github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= |
@@ -33,6 +35,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | |||
33 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | 35 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= |
34 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= | 36 | github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= |
35 | github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= | 37 | github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= |
38 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= | ||
39 | github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= | ||
36 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | 40 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
37 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= | 41 | github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= |
38 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | 42 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= |
diff --git a/gopherproxy.go b/gopherproxy.go index 11c08d5..62b7446 100644 --- a/gopherproxy.go +++ b/gopherproxy.go | |||
@@ -10,6 +10,7 @@ import ( | |||
10 | "log" | 10 | "log" |
11 | "net/http" | 11 | "net/http" |
12 | "net/url" | 12 | "net/url" |
13 | "regexp" | ||
13 | "strings" | 14 | "strings" |
14 | 15 | ||
15 | "github.com/temoto/robotstxt" | 16 | "github.com/temoto/robotstxt" |
@@ -17,6 +18,8 @@ import ( | |||
17 | "github.com/prologic/go-gopher" | 18 | "github.com/prologic/go-gopher" |
18 | 19 | ||
19 | "github.com/gobuffalo/packr" | 20 | "github.com/gobuffalo/packr" |
21 | |||
22 | "github.com/davidbyttow/govips/pkg/vips" | ||
20 | ) | 23 | ) |
21 | 24 | ||
22 | type Item struct { | 25 | type Item struct { |
@@ -25,7 +28,7 @@ type Item struct { | |||
25 | Text string | 28 | Text string |
26 | } | 29 | } |
27 | 30 | ||
28 | func renderDirectory(w http.ResponseWriter, tpl *template.Template, styletext string, uri string, hostport string, d gopher.Directory) error { | 31 | func renderDirectory(w http.ResponseWriter, tpl *template.Template, styletext string, jstext string, uri string, hostport string, d gopher.Directory) error { |
29 | var title string | 32 | var title string |
30 | 33 | ||
31 | out := make([]Item, len(d.Items)) | 34 | out := make([]Item, len(d.Items)) |
@@ -78,9 +81,10 @@ func renderDirectory(w http.ResponseWriter, tpl *template.Template, styletext st | |||
78 | Title string | 81 | Title string |
79 | URI string | 82 | URI string |
80 | Style string | 83 | Style string |
84 | Script string | ||
81 | Lines []Item | 85 | Lines []Item |
82 | RawText string | 86 | RawText string |
83 | }{title, fmt.Sprintf("%s/%s", hostport, uri), styletext, out, ""}) | 87 | }{title, fmt.Sprintf("%s/%s", hostport, uri), styletext, jstext, out, ""}) |
84 | } | 88 | } |
85 | 89 | ||
86 | // GopherHandler returns a Handler that proxies requests | 90 | // GopherHandler returns a Handler that proxies requests |
@@ -88,7 +92,7 @@ func renderDirectory(w http.ResponseWriter, tpl *template.Template, styletext st | |||
88 | // to the request path and renders the content using the provided template. | 92 | // to the request path and renders the content using the provided template. |
89 | // The optional robots parameters points to a robotstxt.RobotsData struct | 93 | // The optional robots parameters points to a robotstxt.RobotsData struct |
90 | // to test user agents against a configurable robotst.txt file. | 94 | // to test user agents against a configurable robotst.txt file. |
91 | func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, robotsdebug bool, styletext string, uri string) http.HandlerFunc { | 95 | func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, robotsdebug bool, styletext string, jstext string, uri string) http.HandlerFunc { |
92 | return func(w http.ResponseWriter, req *http.Request) { | 96 | return func(w http.ResponseWriter, req *http.Request) { |
93 | agent := req.UserAgent() | 97 | agent := req.UserAgent() |
94 | path := strings.TrimPrefix(req.URL.Path, "/") | 98 | path := strings.TrimPrefix(req.URL.Path, "/") |
@@ -140,14 +144,23 @@ func GopherHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, rob | |||
140 | Title string | 144 | Title string |
141 | URI string | 145 | URI string |
142 | Style string | 146 | Style string |
147 | Script string | ||
143 | RawText string | 148 | RawText string |
144 | Lines []Item | 149 | Lines []Item |
145 | }{uri, fmt.Sprintf("%s/%s", hostport, uri), styletext, buf.String(), nil}) | 150 | }{uri, fmt.Sprintf("%s/%s", hostport, uri), styletext, jstext, buf.String(), nil}) |
151 | } else if parts[1] == "T" { | ||
152 | _, _, err = vips.NewTransform(). | ||
153 | Load(res.Body). | ||
154 | ResizeStrategy(vips.ResizeStrategyAuto). | ||
155 | ResizeWidth(160). | ||
156 | Quality(75). | ||
157 | Output(w). | ||
158 | Apply() | ||
146 | } else { | 159 | } else { |
147 | io.Copy(w, res.Body) | 160 | io.Copy(w, res.Body) |
148 | } | 161 | } |
149 | } else { | 162 | } else { |
150 | if err := renderDirectory(w, tpl, styletext, uri, hostport, res.Dir); err != nil { | 163 | if err := renderDirectory(w, tpl, styletext, jstext, uri, hostport, res.Dir); err != nil { |
151 | io.WriteString(w, fmt.Sprintf("<b>Error:</b><pre>%s</pre>", err)) | 164 | io.WriteString(w, fmt.Sprintf("<b>Error:</b><pre>%s</pre>", err)) |
152 | return | 165 | return |
153 | } | 166 | } |
@@ -240,6 +253,11 @@ func ListenAndServe(bind, robotsfile string, robotsdebug bool, uri string) error | |||
240 | styletext = "" | 253 | styletext = "" |
241 | } | 254 | } |
242 | 255 | ||
256 | jstext, err := box.FindString("main.js") | ||
257 | if err != nil { | ||
258 | jstext = "" | ||
259 | } | ||
260 | |||
243 | favicondata, err := box.Find("favicon.ico") | 261 | favicondata, err := box.Find("favicon.ico") |
244 | if err != nil { | 262 | if err != nil { |
245 | favicondata = []byte{} | 263 | favicondata = []byte{} |
@@ -257,6 +275,9 @@ func ListenAndServe(bind, robotsfile string, robotsdebug bool, uri string) error | |||
257 | "safeCss": func(s string) template.CSS { | 275 | "safeCss": func(s string) template.CSS { |
258 | return template.CSS(s) | 276 | return template.CSS(s) |
259 | }, | 277 | }, |
278 | "safeJs": func(s string) template.JS { | ||
279 | return template.JS(s) | ||
280 | }, | ||
260 | "HTMLEscape": func(s string) string { | 281 | "HTMLEscape": func(s string) string { |
261 | return html.EscapeString(s) | 282 | return html.EscapeString(s) |
262 | }, | 283 | }, |
@@ -267,6 +288,11 @@ func ListenAndServe(bind, robotsfile string, robotsdebug bool, uri string) error | |||
267 | "pop": func(s []string) []string { | 288 | "pop": func(s []string) []string { |
268 | return s[:len(s)-1] | 289 | return s[:len(s)-1] |
269 | }, | 290 | }, |
291 | "replace": func(pattern, output string, input interface{}) string { | ||
292 | var re = regexp.MustCompile(pattern) | ||
293 | var inputStr = fmt.Sprintf("%v", input) | ||
294 | return re.ReplaceAllString(inputStr, output) | ||
295 | }, | ||
270 | } | 296 | } |
271 | 297 | ||
272 | tpl, err = template.New("gophermenu").Funcs(funcMap).Parse(tpltext) | 298 | tpl, err = template.New("gophermenu").Funcs(funcMap).Parse(tpltext) |
@@ -274,7 +300,11 @@ func ListenAndServe(bind, robotsfile string, robotsdebug bool, uri string) error | |||
274 | log.Fatal(err) | 300 | log.Fatal(err) |
275 | } | 301 | } |
276 | 302 | ||
277 | http.HandleFunc("/", GopherHandler(tpl, robotsdata, robotsdebug, styletext, uri)) | 303 | vips.Startup(&vips.Config{ |
304 | ConcurrencyLevel: 2, | ||
305 | }) | ||
306 | |||
307 | http.HandleFunc("/", GopherHandler(tpl, robotsdata, robotsdebug, styletext, jstext, uri)) | ||
278 | http.HandleFunc("/robots.txt", RobotsTxtHandler(robotstxtdata)) | 308 | http.HandleFunc("/robots.txt", RobotsTxtHandler(robotstxtdata)) |
279 | http.HandleFunc("/favicon.ico", FaviconHandler(favicondata)) | 309 | http.HandleFunc("/favicon.ico", FaviconHandler(favicondata)) |
280 | http.HandleFunc("/iosevka-term-ss03-regular.woff", FontHandler(false, fontdataw)) | 310 | http.HandleFunc("/iosevka-term-ss03-regular.woff", FontHandler(false, fontdataw)) |
diff --git a/js/main.ts b/js/main.ts new file mode 100644 index 0000000..21e589d --- /dev/null +++ b/js/main.ts | |||
@@ -0,0 +1,42 @@ | |||
1 | let linkQryEls = document.getElementsByClassName('link--QRY'); | ||
2 | let i = linkQryEls.length; | ||
3 | while (i--) { | ||
4 | linkQryEls[i].addEventListener('click', e => { | ||
5 | e.preventDefault(); | ||
6 | |||
7 | const resp = prompt('Please enter required input: ', ''); | ||
8 | if ((resp !== null) && (resp !== "")) { | ||
9 | window.location.href = (e.target as HTMLAnchorElement).href + '?' + resp; | ||
10 | } | ||
11 | |||
12 | return false; | ||
13 | }); | ||
14 | } | ||
15 | |||
16 | let imgPreviewEls = document.getElementsByClassName('img-preview'); | ||
17 | i = imgPreviewEls.length; | ||
18 | while (i--) { | ||
19 | const imgPreviewEl = imgPreviewEls[i] as HTMLAnchorElement; | ||
20 | const child = imgPreviewEl.children[0] as HTMLImageElement; | ||
21 | const thumbnailUrl = child.src; | ||
22 | |||
23 | child.addEventListener('load', e => { | ||
24 | child.classList.remove('faded'); | ||
25 | }); | ||
26 | |||
27 | imgPreviewEls[i].addEventListener('click', e => { | ||
28 | e.preventDefault(); | ||
29 | |||
30 | child.classList.add('faded'); | ||
31 | |||
32 | if (child.classList.contains('expanded')) { | ||
33 | child.classList.remove('expanded'); | ||
34 | child.src = thumbnailUrl; | ||
35 | } else { | ||
36 | child.classList.add('expanded'); | ||
37 | child.src = imgPreviewEl.href; | ||
38 | } | ||
39 | |||
40 | return false; | ||
41 | }); | ||
42 | } | ||
diff --git a/template.go b/template.go index 33486d5..f5f4f2b 100644 --- a/template.go +++ b/template.go | |||
@@ -47,7 +47,10 @@ var tpltext = `<!doctype html> | |||
47 | {{- $content = printf "%s\n" $content -}} | 47 | {{- $content = printf "%s\n" $content -}} |
48 | {{- end -}} | 48 | {{- end -}} |
49 | {{- if .Link -}} | 49 | {{- if .Link -}} |
50 | {{- $content = printf "%s%s" $content (printf "<span class=\"link-type\">%s </span><a class=\"link link--%s\" href=\"%s\">%s</a>" .Type .Type .Link (.Text | HTMLEscape)) -}} | 50 | {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\">%s </span><a class=\"link link--%s\" href=\"%s\">%s</a>" .Type .Type .Link (.Text | HTMLEscape)) -}} |
51 | {{- if or (eq .Type "IMG") (eq .Type "GIF") -}} | ||
52 | {{- $content = printf "%s\n%s" $content (printf "<span class=\"type-annotation\"> -> </span><a class=\"img-preview\" href=\"%s\"><img src=\"%s\" /></a>" .Link (.Link | replace "^/(.*?)/I" "/$1/T")) -}} | ||
53 | {{- end -}} | ||
51 | {{- else -}} | 54 | {{- else -}} |
52 | {{- $content = printf "%s%s" $content (printf " %s" (.Text | HTMLEscape)) -}} | 55 | {{- $content = printf "%s%s" $content (printf " %s" (.Text | HTMLEscape)) -}} |
53 | {{- end -}} | 56 | {{- end -}} |
@@ -59,16 +62,7 @@ var tpltext = `<!doctype html> | |||
59 | </pre> | 62 | </pre> |
60 | </main> | 63 | </main> |
61 | <script type="text/javascript"> | 64 | <script type="text/javascript"> |
62 | var qry=document.getElementsByClassName('link--QRY') | 65 | {{ .Script | safeJs }} |
63 | var i=qry.length | ||
64 | while (i--) { | ||
65 | qry[i].addEventListener('click', function(e) { | ||
66 | e.preventDefault(); | ||
67 | var resp=prompt("Please enter required input: ", "") | ||
68 | if (resp !== null && resp !== "") window.location = e.target.href + "?" + resp | ||
69 | return false; | ||
70 | }) | ||
71 | } | ||
72 | </script> | 66 | </script> |
73 | </body> | 67 | </body> |
74 | </html>` | 68 | </html>` |