aboutsummaryrefslogtreecommitdiffstats
path: root/internal/port
diff options
context:
space:
mode:
Diffstat (limited to 'internal/port')
-rw-r--r--internal/port/gemini.go38
-rw-r--r--internal/port/main.go56
-rw-r--r--internal/port/tpl/_fonts.html12
-rw-r--r--internal/port/tpl/_header.html48
-rw-r--r--internal/port/tpl/_modals.html2
-rw-r--r--internal/port/tpl/gemini.html62
-rw-r--r--internal/port/tpl/gopher.html19
7 files changed, 105 insertions, 132 deletions
diff --git a/internal/port/gemini.go b/internal/port/gemini.go
index 0d8292c..740fccd 100644
--- a/internal/port/gemini.go
+++ b/internal/port/gemini.go
@@ -25,6 +25,7 @@ type GeminiTemplateVariables struct {
25 Assets AssetList 25 Assets AssetList
26 Sections []GeminiSection 26 Sections []GeminiSection
27 Nav []GeminiNavItem 27 Nav []GeminiNavItem
28 IsPlain bool
28} 29}
29 30
30type GeminiNavItem struct { 31type GeminiNavItem struct {
@@ -33,7 +34,7 @@ type GeminiNavItem struct {
33} 34}
34 35
35type GeminiSection struct { 36type GeminiSection struct {
36 Type libgemini.GeminiDocSectionType 37 Type string
37 Text string 38 Text string
38 URL template.URL 39 URL template.URL
39 Items []string 40 Items []string
@@ -83,19 +84,19 @@ func parseGeminiDocument(body *bytes.Buffer, uri string, hostport string) (secti
83 for _, section := range unpreppedSections { 84 for _, section := range unpreppedSections {
84 if section.Type != libgemini.LINK { 85 if section.Type != libgemini.LINK {
85 sections = append(sections, GeminiSection{ 86 sections = append(sections, GeminiSection{
86 Type: section.Type, 87 Type: section.Type.String(),
87 Text: section.Text, 88 Text: section.Text,
88 URL: template.URL(section.URL), 89 URL: template.URL(section.URL),
89 Items: section.Items, 90 Items: section.Items,
90 }) 91 })
92 } else {
93 sections = append(sections, GeminiSection{
94 Type: section.Type.String(),
95 Text: section.Text,
96 URL: template.URL(resolveURL(section.URL, baseURL)),
97 Items: section.Items,
98 })
91 } 99 }
92
93 sections = append(sections, GeminiSection{
94 Type: section.Type,
95 Text: section.Text,
96 URL: template.URL(resolveURL(section.URL, baseURL)),
97 Items: section.Items,
98 })
99 } 100 }
100 101
101 return 102 return
@@ -133,9 +134,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass
133 URL: hostport, 134 URL: hostport,
134 Assets: assetList, 135 Assets: assetList,
135 Sections: []GeminiSection{{ 136 Sections: []GeminiSection{{
136 Type: libgemini.RAW_TEXT, 137 Type: libgemini.RAW_TEXT.String(),
137 Text: fmt.Sprintf("Error: %s", err), 138 Text: fmt.Sprintf("Error: %s", err),
138 }}, 139 }},
140 IsPlain: true,
139 }); e != nil { 141 }); e != nil {
140 log.Println("Template error: " + e.Error()) 142 log.Println("Template error: " + e.Error())
141 log.Println(err.Error()) 143 log.Println(err.Error())
@@ -162,9 +164,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass
162 URL: fmt.Sprintf("%s/%s", hostport, uri), 164 URL: fmt.Sprintf("%s/%s", hostport, uri),
163 Assets: assetList, 165 Assets: assetList,
164 Sections: []GeminiSection{{ 166 Sections: []GeminiSection{{
165 Type: libgemini.RAW_TEXT, 167 Type: libgemini.RAW_TEXT.String(),
166 Text: fmt.Sprintf("Error: %s", err), 168 Text: fmt.Sprintf("Error: %s", err),
167 }}, 169 }},
170 IsPlain: true,
168 }); e != nil { 171 }); e != nil {
169 log.Println("Template error: " + e.Error()) 172 log.Println("Template error: " + e.Error())
170 log.Println(err.Error()) 173 log.Println(err.Error())
@@ -184,9 +187,10 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass
184 URL: fmt.Sprintf("%s/%s", hostport, uri), 187 URL: fmt.Sprintf("%s/%s", hostport, uri),
185 Assets: assetList, 188 Assets: assetList,
186 Sections: []GeminiSection{{ 189 Sections: []GeminiSection{{
187 Type: libgemini.RAW_TEXT, 190 Type: libgemini.RAW_TEXT.String(),
188 Text: fmt.Sprintf("Error: %s", err), 191 Text: fmt.Sprintf("Error: %s", err),
189 }}, 192 }},
193 IsPlain: true,
190 }); e != nil { 194 }); e != nil {
191 log.Println("Template error: " + e.Error()) 195 log.Println("Template error: " + e.Error())
192 log.Println(err.Error()) 196 log.Println(err.Error())
@@ -204,16 +208,17 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass
204 URL: fmt.Sprintf("%s/%s", hostport, uri), 208 URL: fmt.Sprintf("%s/%s", hostport, uri),
205 Assets: assetList, 209 Assets: assetList,
206 Sections: []GeminiSection{{ 210 Sections: []GeminiSection{{
207 Type: libgemini.RAW_TEXT, 211 Type: libgemini.RAW_TEXT.String(),
208 Text: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta), 212 Text: fmt.Sprintf("Error %d: %s", res.Header.Status, res.Header.Meta),
209 }}, 213 }},
214 IsPlain: true,
210 }); err != nil { 215 }); err != nil {
211 log.Println("Template error: " + err.Error()) 216 log.Println("Template error: " + err.Error())
212 } 217 }
213 return 218 return
214 } 219 }
215 220
216 if strings.HasPrefix(res.Header.Meta, "text/") { 221 if strings.HasPrefix(res.Header.Meta, "text/") && !strings.HasPrefix(res.Header.Meta, "text/html") && !strings.HasPrefix(res.Header.Meta, "text/css") {
217 buf := new(bytes.Buffer) 222 buf := new(bytes.Buffer)
218 223
219 _, params, err := mime.ParseMediaType(res.Header.Meta) 224 _, params, err := mime.ParseMediaType(res.Header.Meta)
@@ -230,12 +235,14 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass
230 } 235 }
231 236
232 var sections []GeminiSection 237 var sections []GeminiSection
238 isPlain := true
233 239
234 if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) { 240 if strings.HasPrefix(res.Header.Meta, libgemini.MIME_GEMINI) {
235 sections = parseGeminiDocument(buf, uri, hostport) 241 sections = parseGeminiDocument(buf, uri, hostport)
242 isPlain = false
236 } else { 243 } else {
237 sections = append(sections, GeminiSection{ 244 sections = append(sections, GeminiSection{
238 Type: libgemini.RAW_TEXT, 245 Type: libgemini.RAW_TEXT.String(),
239 Text: buf.String(), 246 Text: buf.String(),
240 }) 247 })
241 } 248 }
@@ -245,6 +252,7 @@ func GeminiHandler(tpl *template.Template, robotsdata *robotstxt.RobotsData, ass
245 URL: fmt.Sprintf("%s/%s", hostport, uri), 252 URL: fmt.Sprintf("%s/%s", hostport, uri),
246 Assets: assetList, 253 Assets: assetList,
247 Sections: sections, 254 Sections: sections,
255 IsPlain: isPlain,
248 }); err != nil { 256 }); err != nil {
249 log.Println("Template error: " + err.Error()) 257 log.Println("Template error: " + err.Error())
250 } 258 }
diff --git a/internal/port/main.go b/internal/port/main.go
index 267df44..763057b 100644
--- a/internal/port/main.go
+++ b/internal/port/main.go
@@ -18,12 +18,12 @@ import (
18) 18)
19 19
20type AssetList struct { 20type AssetList struct {
21 Style string 21 Style string
22 JS string 22 JS string
23 FontW string 23 FontRegularW string
24 FontW2 string 24 FontRegularW2 string
25 PropFontW string 25 FontBoldW string
26 PropFontW2 string 26 FontBoldW2 string
27} 27}
28 28
29type startTemplateVariables struct { 29type startTemplateVariables struct {
@@ -136,29 +136,29 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b
136 // 136 //
137 // Fonts 137 // Fonts
138 138
139 fontdataw, err := box.Find("iosevka-term-ss03-regular.woff") 139 fontRegularWData, err := box.Find("iosevka-fixed-ss03-regular.woff")
140 if err != nil { 140 if err != nil {
141 fontdataw = []byte{} 141 fontRegularWData = []byte{}
142 } 142 }
143 fontwAsset := fmt.Sprintf("/iosevka-term-ss03-regular-%x.woff", md5.Sum(fontdataw)) 143 fontRegularWAsset := fmt.Sprintf("/iosevka-fixed-ss03-regular-%x.woff", md5.Sum(fontRegularWData))
144 144
145 fontdataw2, err := box.Find("iosevka-term-ss03-regular.woff2") 145 fontRegularW2Data, err := box.Find("iosevka-fixed-ss03-regular.woff2")
146 if err != nil { 146 if err != nil {
147 fontdataw2 = []byte{} 147 fontRegularW2Data = []byte{}
148 } 148 }
149 fontw2Asset := fmt.Sprintf("/iosevka-term-ss03-regular-%x.woff2", md5.Sum(fontdataw2)) 149 fontRegularW2Asset := fmt.Sprintf("/iosevka-fixed-ss03-regular-%x.woff2", md5.Sum(fontRegularW2Data))
150 150
151 propfontdataw, err := box.Find("iosevka-aile-regular.woff") 151 fontBoldWData, err := box.Find("iosevka-fixed-ss03-bold.woff")
152 if err != nil { 152 if err != nil {
153 propfontdataw = []byte{} 153 fontBoldWData = []byte{}
154 } 154 }
155 propfontwAsset := fmt.Sprintf("/iosevka-aile-regular-%x.woff", md5.Sum(propfontdataw)) 155 fontBoldWAsset := fmt.Sprintf("/iosevka-fixed-ss03-bold-%x.woff", md5.Sum(fontBoldWData))
156 156
157 propfontdataw2, err := box.Find("iosevka-aile-regular.woff2") 157 fontBoldW2Data, err := box.Find("iosevka-fixed-ss03-bold.woff2")
158 if err != nil { 158 if err != nil {
159 propfontdataw2 = []byte{} 159 fontBoldW2Data = []byte{}
160 } 160 }
161 propfontw2Asset := fmt.Sprintf("/iosevka-aile-regular-%x.woff2", md5.Sum(propfontdataw2)) 161 fontBoldW2Asset := fmt.Sprintf("/iosevka-fixed-ss03-bold-%x.woff2", md5.Sum(fontBoldW2Data))
162 162
163 // 163 //
164 // Stylesheet 164 // Stylesheet
@@ -277,12 +277,12 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b
277 }) 277 })
278 278
279 assets := AssetList{ 279 assets := AssetList{
280 Style: styleAsset, 280 Style: styleAsset,
281 JS: jsAsset, 281 JS: jsAsset,
282 FontW: fontwAsset, 282 FontRegularW: fontRegularWAsset,
283 FontW2: fontw2Asset, 283 FontRegularW2: fontRegularW2Asset,
284 PropFontW: propfontwAsset, 284 FontBoldW: fontBoldWAsset,
285 PropFontW2: propfontw2Asset, 285 FontBoldW2: fontBoldW2Asset,
286 } 286 }
287 287
288 http.Handle("/", gziphandler.GzipHandler(DefaultHandler(startpageTpl, startpagetext, assets))) 288 http.Handle("/", gziphandler.GzipHandler(DefaultHandler(startpageTpl, startpagetext, assets)))
@@ -292,10 +292,10 @@ func ListenAndServe(bind, startpagefile string, robotsfile string, robotsdebug b
292 http.Handle("/favicon.ico", gziphandler.GzipHandler(FaviconHandler(favicondata))) 292 http.Handle("/favicon.ico", gziphandler.GzipHandler(FaviconHandler(favicondata)))
293 http.Handle(styleAsset, gziphandler.GzipHandler(StyleHandler(styledata))) 293 http.Handle(styleAsset, gziphandler.GzipHandler(StyleHandler(styledata)))
294 http.Handle(jsAsset, gziphandler.GzipHandler(JavaScriptHandler(jsdata))) 294 http.Handle(jsAsset, gziphandler.GzipHandler(JavaScriptHandler(jsdata)))
295 http.HandleFunc(fontwAsset, FontHandler(false, fontdataw)) 295 http.HandleFunc(fontRegularWAsset, FontHandler(false, fontRegularWData))
296 http.HandleFunc(fontw2Asset, FontHandler(true, fontdataw2)) 296 http.HandleFunc(fontRegularW2Asset, FontHandler(true, fontRegularW2Data))
297 http.HandleFunc(propfontwAsset, FontHandler(false, propfontdataw)) 297 http.HandleFunc(fontBoldWAsset, FontHandler(false, fontBoldWData))
298 http.HandleFunc(propfontw2Asset, FontHandler(true, propfontdataw2)) 298 http.HandleFunc(fontBoldW2Asset, FontHandler(true, fontBoldW2Data))
299 //http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/")))) 299 //http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("assets/"))))
300 300
301 return http.ListenAndServe(bind, nil) 301 return http.ListenAndServe(bind, nil)
diff --git a/internal/port/tpl/_fonts.html b/internal/port/tpl/_fonts.html
index b56aa22..a947222 100644
--- a/internal/port/tpl/_fonts.html
+++ b/internal/port/tpl/_fonts.html
@@ -3,14 +3,14 @@
3 font-family: 'Iosevka Term SS03'; 3 font-family: 'Iosevka Term SS03';
4 font-style: normal; 4 font-style: normal;
5 font-weight: normal; 5 font-weight: normal;
6 src: url('{{ .Assets.FontW2 }}') format('woff2'), 6 src: url('{{ .Assets.FontRegularW2 }}') format('woff2'),
7 url('{{ .Assets.FontW }}') format('woff'); 7 url('{{ .Assets.FontRegularW }}') format('woff');
8 } 8 }
9 @font-face { 9 @font-face {
10 font-family: 'Iosevka Aile'; 10 font-family: 'Iosevka Term SS03';
11 font-style: normal; 11 font-style: normal;
12 font-weight: normal; 12 font-weight: bold;
13 src: url('{{ .Assets.PropFontW2 }}') format('woff2'), 13 src: url('{{ .Assets.FontBoldW2 }}') format('woff2'),
14 url('{{ .Assets.PropFontW }}') format('woff'); 14 url('{{ .Assets.FontBoldW }}') format('woff');
15 } 15 }
16</style> 16</style>
diff --git a/internal/port/tpl/_header.html b/internal/port/tpl/_header.html
deleted file mode 100644
index 5bcd254..0000000
--- a/internal/port/tpl/_header.html
+++ /dev/null
@@ -1,48 +0,0 @@
1<header class="header header-base">
2 <div class="location">
3 <a class="location__prefix">{{ .Protocol }}://</a><a class="location__prefix location__prefix--mobile">://</a>
4
5 {{- if .URL -}}
6 {{- $page := . -}}
7 {{- $href := printf "/%s" .Protocol -}}
8 {{- $uriParts := split .URL "/" -}}
9
10 {{- $uriLast := $uriParts | last -}}
11 {{- $uriParts = $uriParts | pop -}}
12 {{- if eq $uriLast "" -}}
13 {{- $uriLast = $uriParts | last -}}
14 {{- $uriParts = $uriParts | pop -}}
15 {{- end -}}
16
17 {{- range $i, $part := $uriParts -}}
18 {{- if and (eq $page.Protocol "gopher") (eq $i 1) -}}
19 {{- $href = printf "%s/1" $href -}}
20 {{- $part = $part | trimLeftChar -}}
21 {{- if not (eq $part "") -}}
22 {{- $href = printf "%s/%s" $href $part -}}
23 <span class="location__slash">/</span><a href="{{ $href }}/" class="location__uripart">{{ $part }}</a>
24 {{- end -}}
25 {{- else -}}
26 {{- $href = printf "%s/%s" $href . -}}
27 {{- if ne $i 0 -}}
28 <span class="location__slash">/</span>
29 {{- end -}}
30 <a href="{{ $href }}/" class="location__uripart">{{ . }}</a>
31 {{- end -}}
32 {{- end -}}
33 {{- if ne (len $uriParts) 0 -}}
34 <span class="location__slash">/</span>
35 {{- end -}}
36 {{- if and (eq $page.Protocol "gopher") (eq (len $uriParts) 1) -}}
37 {{- $uriLast = $uriLast | trimLeftChar -}}
38 {{- end -}}
39 <span class="location__uripart">{{ $uriLast }}</span>
40 {{- end -}}
41 </div>
42 <div class="actions">
43 {{- if and (not .Lines) (not .Error) (eq .Protocol "gopher") -}}
44 <div class="action"><a href="/gopher/{{ .URL | replace "^([^/]*)/0" "$1/9" }}">View raw</a></div>
45 {{- end -}}
46 <div class="action"><button class="settings-btn">Settings</button></div>
47 </div>
48</header>
diff --git a/internal/port/tpl/_modals.html b/internal/port/tpl/_modals.html
index 3c08d9a..3bbdef2 100644
--- a/internal/port/tpl/_modals.html
+++ b/internal/port/tpl/_modals.html
@@ -9,7 +9,7 @@
9 <button class="setting__value">[N/A]</button> 9 <button class="setting__value">[N/A]</button>
10 </div> 10 </div>
11 <div class="setting setting--monospace-font"> 11 <div class="setting setting--monospace-font">
12 <strong class="setting__label">Monospace font</strong> 12 <strong class="setting__label">Gemini: Monospace font</strong>
13 <button class="setting__value">[N/A]</button> 13 <button class="setting__value">[N/A]</button>
14 </div> 14 </div>
15 <div class="setting setting--image-previews"> 15 <div class="setting setting--image-previews">
diff --git a/internal/port/tpl/gemini.html b/internal/port/tpl/gemini.html
index 08f1b8e..df50d50 100644
--- a/internal/port/tpl/gemini.html
+++ b/internal/port/tpl/gemini.html
@@ -7,28 +7,50 @@
7 <link rel="stylesheet" href="{{ .Assets.Style }}" /> 7 <link rel="stylesheet" href="{{ .Assets.Style }}" />
8 {{- template "_fonts.html" . -}} 8 {{- template "_fonts.html" . -}}
9 </head> 9 </head>
10 <body class="{{ if not .Lines }}is-plain{{ end }}"> 10 <body class="{{ if .IsPlain }}is-plain{{ end }}">
11 {{- template "_header.html" . -}} 11 <header class="header header-base">
12 <div class="location">
13 <a class="location__prefix">gemini://</a><a class="location__prefix location__prefix--mobile">://</a>
14 {{- range $i, $item := .Nav -}}
15 {{- if ne $i 0 -}}
16 <span class="location__slash">/</span>
17 {{- end -}}
18 {{- if .Current -}}
19 <span class="location__uripart">{{ .Label }}</span>
20 {{- else -}}
21 <a href="{{ .URL }}/" class="location__uripart">{{ .Label }}</a>
22 {{- end -}}
23 {{- end -}}
24 </div>
25 <div class="actions">
26 <div class="action"><button class="settings-btn">Settings</button></div>
27 </div>
28 </header>
12 29
13 <main class="wrap"> 30 <main class="wrap">
14 <pre class="content content--has-monospace-font{{ if .Lines }} content--has-type-annotations{{ end }}"> 31 <div class="content{{ if not .IsPlain }} content--has-type-annotations{{ end }}">
15 {{- if .Lines -}} 32 {{- range .Sections -}}
16 {{- $content := "" -}} 33 {{- if eq .Type "RAW_TEXT" -}}
17 {{- range .Lines -}} 34 <div class="section"><span class="section__type">```</span><pre class="section__content">{{- .Text -}}</pre></div>
18 {{- if ne $content "" -}} 35 {{- else if eq .Type "REFLOW_TEXT" -}}
19 {{- $content = printf "%s\n" $content -}} 36 <div class="section"><p class="section__content">{{- .Text -}}</p></div>
20 {{- end -}} 37 {{- else if eq .Type "LINK" -}}
21 {{- if .Link -}} 38 <div class="section"><span class="section__type">=></span><a class="section__content" href="{{ .URL }}">{{- .Text -}}</a></div>
22 {{- $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)) -}} 39 {{- else if eq .Type "HEADING_1" -}}
23 {{- else -}} 40 <div class="section"><span class="section__type">#</span><h1 class="section__content">{{- .Text -}}</h1></div>
24 {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\"> </span>%s" (.Text | HTMLEscape)) -}} 41 {{- else if eq .Type "HEADING_2" -}}
25 {{- end -}} 42 <div class="section"><span class="section__type">##</span><h2 class="section__content">{{- .Text -}}</h2></div>
26 {{- end -}} 43 {{- else if eq .Type "HEADING_3" -}}
27 {{- $content | safeHtml -}} 44 <div class="section"><span class="section__type">###</span><h3 class="section__content">{{- .Text -}}</h3></div>
28 {{- else -}} 45 {{- else if eq .Type "LIST" -}}
29 {{- .RawText -}} 46 <div class="section"><ul class="section__content">
30 {{- end -}} 47 {{- range .Items -}}
31 </pre> 48 <li>{{- . -}}</li>
49 {{- end -}}
50 </ul></div>
51 {{- end -}}
52 {{- end -}}
53 </div>
32 </main> 54 </main>
33 55
34 {{- template "_modals.html" . -}} 56 {{- template "_modals.html" . -}}
diff --git a/internal/port/tpl/gopher.html b/internal/port/tpl/gopher.html
index c971847..5436123 100644
--- a/internal/port/tpl/gopher.html
+++ b/internal/port/tpl/gopher.html
@@ -31,25 +31,16 @@
31 </header> 31 </header>
32 32
33 <main class="wrap"> 33 <main class="wrap">
34 <pre class="content content--has-monospace-font{{ if not .IsPlain }} content--has-type-annotations{{ end }}"> 34 <div class="content content--monospace{{ if not .IsPlain }} content--has-type-annotations{{ end }}">
35 {{- $content := "" -}}
36 {{- $page := . -}} 35 {{- $page := . -}}
37 {{- range .Lines -}} 36 {{- range .Lines -}}
38 {{- if ne $content "" -}} 37 {{- if .Link -}}
39 {{- $content = printf "%s\n" $content -}} 38 <div class="section"><span class="section__type">{{- .Type -}}</span><a class="section__content" href="{{ .Link }}">{{- .Text -}}</a></div>
40 {{- end -}}
41 {{- if $page.IsPlain -}}
42 {{- $content = printf "%s%s" $content (.Text | HTMLEscape) -}}
43 {{- else -}} 39 {{- else -}}
44 {{- if .Link -}} 40 <div class="section"><span class="section__type"></span><pre class="section__content">{{- .Text -}}</pre></div>
45 {{- $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)) -}}
46 {{- else -}}
47 {{- $content = printf "%s%s" $content (printf "<span class=\"type-annotation\"> </span>%s" (.Text | HTMLEscape)) -}}
48 {{- end -}}
49 {{- end -}} 41 {{- end -}}
50 {{- end -}} 42 {{- end -}}
51 {{- $content | safeHtml -}} 43 </div>
52 </pre>
53 </main> 44 </main>
54 45
55 {{- template "_modals.html" . -}} 46 {{- template "_modals.html" . -}}