aboutsummaryrefslogtreecommitdiff
path: root/views
diff options
context:
space:
mode:
authorMiquel Lionel <lionelmiquel@sfr.fr>2021-02-18 17:25:32 +0100
committerMiquel Lionel <lionel@les-miquelots.net>2021-02-18 17:25:32 +0100
commit4e513641d311f1a3895a13efb45179e7b88e5b6d (patch)
tree6f9bdd76b46072f7a2fbe42ce5f4cbbd8491a561 /views
parentc9129233298e6586e2242fd6892cd84531390b83 (diff)
downloadhonk_custom-4e513641d311f1a3895a13efb45179e7b88e5b6d.tar.gz
honk_custom-4e513641d311f1a3895a13efb45179e7b88e5b6d.zip
now this branch will work on tip
Diffstat (limited to 'views')
-rw-r--r--views/header.html90
-rw-r--r--views/honk.html140
-rw-r--r--views/i18n/about.html15
-rw-r--r--views/i18n/account.html62
-rw-r--r--views/i18n/chatter.html66
-rw-r--r--views/i18n/combos.html13
-rw-r--r--views/i18n/funzone.html16
-rw-r--r--views/i18n/header.html100
-rw-r--r--views/i18n/hfcs.html73
-rw-r--r--views/i18n/honk.html146
-rw-r--r--views/i18n/honkers.html62
-rw-r--r--views/i18n/honkform.html49
-rw-r--r--views/i18n/honkfrags.html11
-rw-r--r--views/i18n/honkpage.html45
-rw-r--r--views/i18n/honkpage.js390
-rw-r--r--views/i18n/local.css58
-rw-r--r--views/i18n/login.html12
-rw-r--r--views/i18n/msg.html7
-rw-r--r--views/i18n/onts.html16
-rw-r--r--views/i18n/pleroma.css7
-rw-r--r--views/i18n/style.css334
-rw-r--r--views/i18n/xzone.html17
-rw-r--r--views/local.css8
23 files changed, 1737 insertions, 0 deletions
diff --git a/views/header.html b/views/header.html
new file mode 100644
index 0000000..4498ca1
--- /dev/null
+++ b/views/header.html
@@ -0,0 +1,90 @@
+<!doctype html>
+<html>
+<head>
+ <link rel="shortcut icon" href="/favicon.ico"/>
+<title>honk</title>
+<link href="/style.css{{ .StyleParam }}" rel="stylesheet">
+{{ if .LocalStyleParam }}
+<link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet">
+{{ end }}
+<style>
+{{ .UserStyle }}
+</style>
+<link href="/icon.png" rel="icon">
+<meta name="theme-color" content="#305">
+<meta name="viewport" content="width=device-width">
+<meta name="description" content="Federated ActivityPub instance running the Honk server. things happen">
+<meta name="keywords" content="honk">
+</head>
+<body>
+<header>
+{{ if .UserInfo }}
+<details id="topmenu">
+ <summary>menu <span> {{ .UserInfo.Username }}</span></summary>
+<ul>
+<li><a id="homelink" href="/">home</a>
+<li><a id="atmelink" href="/atme">@me</a>
+<li><a id="firstlink" href="/first">first</a>
+<li style="list-style-type:none; margin-left:-1em">
+<details>
+<summary>combos</summary>
+<ul>
+{{ range .Combos }}
+<li><a class="combolink" href="/c/{{ . }}">{{ . }}</a>
+{{ end }}
+</ul>
+</details>
+<li><a href="/chatter">chatter</a>
+<li><a href="/o">tags</a>
+<li><a href="/events">events</a>
+<li><a id="longagolink" href="/longago">long ago</a>
+<li><a id="savedlink" href="/saved">saved</a>
+<li><a href="/honkers">honkers</a>
+<li><a href="/hfcs">filters</a>
+<li><a href="/account">account</a>
+<li style="list-style-type:none; margin-left:-1em">
+<details>
+<summary>more stuff</summary>
+<ul>
+<li><a href="/{{ .UserSep }}/{{ .UserInfo.Username }}">my honks</a>
+<li><a href="/about">about</a>
+<li><a href="/front">front</a>
+<li><a href="/funzone">funzone</a>
+<li><a href="/xzone">xzone</a>
+</ul>
+</details>
+<li><a href="/help/honk.1.html">help</a>
+<li>
+<form action="/q" method="GET">
+<input type="text" name="q" autocomplete=off size=10 placeholder="search">
+</form>
+</ul>
+</details>
+
+<!-- CUSTOM HONK NAVIGATION BAR -->
+ <div id="altnavbar">
+ <a href="/newhonk" tabindex="2">new honk</a> |
+ <a href="/front">front</a> |
+ <a href="/">home</a> |
+ <a href="/xzone">fetch</a> |
+ <a href="/atme">@me</a> |
+ <a href="/u/{{ .UserInfo.Username}}">profile</a> |
+ <a href="/account">settings</a> |
+ <form style="display:inline" action="/q" method="GET">
+ <input type="text" tabindex="1" name="q" autocomplete="off" size="10" placeholder="search">
+ </form>
+ </div>
+<!-- END -->
+
+<p id="topspacer"></p>
+{{ else }}
+<span><a id="homelink" href="/">home</a></span>
+<span><a href="/o">tags</a></span>
+<span><a href="/events">events</a></span>
+<span><a href="/about">about</a></span>
+{{ if .ShowRSS }}
+<span><a href="/rss">rss</a></span>
+{{ end }}
+<span><a href="/login">login</a></span>
+{{ end }}
+</header>
diff --git a/views/honk.html b/views/honk.html
new file mode 100644
index 0000000..2d1e465
--- /dev/null
+++ b/views/honk.html
@@ -0,0 +1,140 @@
+<article class="honk {{ .Honk.Style }}" data-convoy="{{ .Honk.Convoy }}">
+{{ $bonkcsrf := .BonkCSRF }}
+{{ $IsPreview := .IsPreview }}
+{{ $maplink := .MapLink }}
+{{ $omitimages := .OmitImages }}
+{{ with .Honk }}
+<header>
+{{ if $bonkcsrf }}
+<a class="honkerlink" href="/h?xid={{ .Honker }}" data-xid="{{ .Honker }}">
+{{ else }}
+<a href="{{ .Honker }}" rel=noreferrer>
+{{ end }}
+<img alt="" src="/a?a={{ .Honker}}">
+{{ if $bonkcsrf }} </a> {{ end }}
+{{ if .Oonker }}
+{{ if $bonkcsrf }}
+<a class="honkerlink" href="/h?xid={{ .Oonker }}" data-xid="{{ .Oonker }}">
+{{ else }}
+<a href="{{ .Oonker }}" rel=noreferrer>
+{{ end }}
+<img alt="" src="/a?a={{ .Oonker}}">
+{{ if $bonkcsrf }} </a> {{ end }}
+{{ end }}
+<p>
+{{ if $bonkcsrf }}
+<a class="honkerlink" href="/h?xid={{ .Honker }}" data-xid="{{ .Honker }}">{{ .Username }}</a>
+{{ else }}
+<a href="{{ .Honker }}" rel=noreferrer>{{ .Username }}</a>
+{{ end }}
+<span class="clip"><a href="{{ .URL }}" rel=noreferrer>{{ .What }}</a> {{ .Date.Local.Format "02 Jan 2006 15:04 -0700" }}</span>
+{{ if .Oonker }}
+<br>
+<span style="margin-left: 1em;" class="clip">
+{{ if $bonkcsrf }}
+original: <a class="honkerlink" href="/h?xid={{ .Oonker }}" data-xid="{{ .Oonker }}">{{ .Oondle }}</a>
+{{ else }}
+original: <a href="{{ .Oonker }}" rel=noreferrer>{{ .Oondle }}</a>
+{{ end }}
+</span>
+{{ else }}
+{{ if .RID }}
+<br>
+<span style="margin-left: 1em;" class="clip">
+in reply to: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
+</span>
+{{ end }}
+{{ end }}
+<br>
+{{ if $bonkcsrf }}
+<span style="margin-left: 1em;" class="clip">convoy: <a class="convoylink" href="/t?c={{ .Convoy }}">{{ .Convoy }}</a></span>
+{{ else }}
+<span style="margin-left: 1em;" class="clip">convoy: {{ .Convoy }}</span>
+{{ end }}
+</header>
+<p>
+<details class="noise" {{ .Open }} >
+<summary>{{ .HTPrecis }}<p></summary>
+<p>{{ .HTPrecis }}
+<p>{{ .HTML }}
+{{ with .Time }}
+<p>Time: {{ .StartTime.Local.Format "03:04PM EDT Mon Jan 02"}}
+{{ if .Duration }}<br>Duration: {{ .Duration }}{{ end }}
+{{ end }}
+{{ with .Place }}
+<p>Location: {{ with .Url }}<a href="{{ . }}" rel=noreferrer>{{ end }}{{ .Name }}{{ if .Url }}</a>{{ end }}{{ if or .Latitude .Longitude }} <a href="{{ if eq $maplink "apple" }}https://maps.apple.com/?q={{ or .Name "here" }}&z=16&ll={{ .Latitude }},{{ .Longitude }}{{ else }}https://www.openstreetmap.org/?mlat={{ .Latitude }}&mlon={{ .Longitude}}#map=16/{{ .Latitude }}/{{ .Longitude }}{{ end }}" rel=noreferrer>{{ .Latitude }} {{ .Longitude }}</a>{{ end }}
+{{ end }}
+{{ range .Donks }}
+{{ if .Local }}
+{{ if eq .Media "text/plain" }}
+<p><a href="/d/{{ .XID }}">Attachment: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else if eq .Media "application/pdf" }}
+<p><a href="/d/{{ .XID }}">Attachment: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else }}
+{{ if $omitimages }}
+<p><a href="/d/{{ .XID }}">Image: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else }}
+<p><img src="/d/{{ .XID }}" title="{{ .Desc }}" alt="{{ .Desc }}">
+{{ end }}
+{{ end }}
+{{ else }}
+{{ if .External }}
+<p><a href="{{ .URL }}" rel=noreferrer>External Attachment: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else }}
+{{ if eq .Media "video/mp4" }}
+<p><video controls src="{{ .URL }}">{{ .Name }}</video>
+{{ else }}
+<p><img src="{{ .URL }}" title="{{ .Desc }}" alt="{{ .Desc }}">
+{{ end }}
+{{ end }}
+{{ end }}
+{{ end }}
+</details>
+{{ end }}
+{{ if and $bonkcsrf (not $IsPreview) }}
+<p>
+<div>
+<p>
+{{ if .Honk.Public }}
+{{ if .Honk.IsBonked }}
+<button onclick="return unbonk(this, '{{ .Honk.XID }}');">unbonk</button>
+{{ else }}
+<button onclick="return bonk(this, '{{ .Honk.XID }}');">bonk</button>
+{{ end }}
+{{ else }}
+<button disabled>nope</button>
+{{ end }}
+<button onclick="return showhonkform(this, '{{ .Honk.XID }}', '{{ .Honk.Handles }}');"><a href="/newhonk?rid={{ .Honk.XID }}">honk back</a></button>
+<button onclick="return muteit(this, '{{ .Honk.Convoy }}');">mute</button>
+<button onclick="return showelement('evenmore{{ .Honk.ID }}')">even more</button>
+</div>
+<div id="evenmore{{ .Honk.ID }}" style="display:none">
+<p>
+<button onclick="return zonkit(this, '{{ .Honk.XID }}');">zonk</button>
+{{ if .Honk.IsAcked }}
+<button onclick="return flogit(this, 'deack', '{{ .Honk.XID }}');">deack</button>
+{{ else }}
+<button onclick="return flogit(this, 'ack', '{{ .Honk.XID }}');">ack</button>
+{{ end }}
+{{ if .Honk.IsSaved }}
+<button onclick="return flogit(this, 'unsave', '{{ .Honk.XID }}');">unsave</button>
+{{ else }}
+<button onclick="return flogit(this, 'save', '{{ .Honk.XID }}');">save</button>
+{{ end }}
+{{ if .Honk.IsUntagged }}
+<button disabled>untagged</button>
+{{ else }}
+<button onclick="return flogit(this, 'untag', '{{ .Honk.XID }}');">untag me</button>
+{{ end }}
+<button><a href="/edit?xid={{ .Honk.XID }}">edit</a></button>
+{{ if not (eq .Badonk "none") }}
+{{ if .Honk.IsReacted }}
+<button disabled>badonked</button>
+{{ else }}
+<button onclick="return flogit(this, 'react', '{{ .Honk.XID }}');">{{ .Badonk }}</button>
+{{ end }}
+{{ end }}
+</div>
+<p>
+{{ end }}
+</article>
diff --git a/views/i18n/about.html b/views/i18n/about.html
new file mode 100644
index 0000000..962e7c3
--- /dev/null
+++ b/views/i18n/about.html
@@ -0,0 +1,15 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+{{ .AboutMsg }}<br>
+<p>
+<table style="font-size:0.8em">
+<tbody>
+<tr><td>{{ .i18n.Version }}:<td style="text-align:right">{{ .HonkVersion }}
+<tr><td>{{ .i18n.Memory }}:<td style="text-align:right">{{ printf "%.02f" .Sensors.Memory }}MB
+<tr><td>{{ .i18n.Uptime }}:<td style="text-align:right">{{ printf "%.02f" .Sensors.Uptime }}s
+<tr><td>{{ .i18n.Cputime }}:<td style="text-align:right">{{ printf "%.02f" .Sensors.CPU }}s
+</table>
+<p>
+</div>
+</main>
diff --git a/views/i18n/account.html b/views/i18n/account.html
new file mode 100644
index 0000000..2d5a3f3
--- /dev/null
+++ b/views/i18n/account.html
@@ -0,0 +1,62 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+<p>{{ .i18n.Account }} - <a href="/logout?CSRF={{ .LogoutCSRF }}">{{ .i18n.Logout }}</a>
+<p>{{ .i18n.Username }}: {{ .User.Name }}
+<div>
+<form id="aboutform" action="/saveuser" method="POST">
+<input type="hidden" name="CSRF" value="{{ .UserCSRF }}">
+<p>{{ .i18n.Aboutme }}:
+<p><textarea name="whatabout">{{ .WhatAbout }}</textarea>
+
+<p><label class="button" for="skinny">{{ .i18n.Slayout }}:</label>
+<input tabindex=1 type="checkbox" id="skinny" name="skinny" value="skinny" {{ if .User.Options.SkinnyCSS }}checked{{ end }}><span></span>
+
+<p><label class="button" for="omitimages">{{ .i18n.NoImg }}:</label>
+<input tabindex=1 type="checkbox" id="omitimages" name="omitimages" value="omitimages" {{ if .User.Options.OmitImages }}checked{{ end }}><span></span>
+
+<p><label class="button" for="mentionall">{{ .i18n.MentionAll }}:</label>
+<input tabindex=1 type="checkbox" id="mentionall" name="mentionall" value="mentionall" {{ if .User.Options.MentionAll }}checked{{ end }}><span></span>
+
+<p><label class="button" for="maps">{{ .i18n.AppleMapsLinks }}:</label>
+<input tabindex=1 type="checkbox" id="maps" name="maps" value="apple" {{ if eq "apple" .User.Options.MapLink }}checked{{ end }}><span></span>
+
+<p><label class="button" for="reaction">{{ .i18n.Reaction }}:</label>
+<select tabindex=1 name="reaction">
+ <option {{ and (eq .User.Options.Reaction "none") "selected" }}>none</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F61E") "selected" }}>{{ "\U0001F61E" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F937") "selected" }}>{{ "\U0001F937" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F648") "selected" }}>{{ "\U0001F648" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F9BE") "selected" }}>{{ "\U0001F9BE" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F346") "selected" }}>{{ "\U0001F346" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F351") "selected" }}>{{ "\U0001F351" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F32E") "selected" }}>{{ "\U0001F32E" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F951") "selected" }}>{{ "\U0001F951" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F5FF") "selected" }}>{{ "\U0001F5FF" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F99A") "selected" }}>{{ "\U0001F99A" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F3BB") "selected" }}>{{ "\U0001F3BB" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001FA93") "selected" }}>{{ "\U0001FA93" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F1EB") "selected" }}>{{ "\U0001F1EB" }}</option>
+ <option {{ and (eq .User.Options.Reaction "\U0001F1FD") "selected" }}>{{ "\U0001F1FD" }}</option>
+</select>
+
+<p><label class="button" for="lang">{{ .i18n.LanguageLabel }}:</label>
+<select name="lang">
+ <option {{ and (eq .Lang "en") "selected"}}>en</option>
+ <option {{ and (eq .Lang "fr") "selected"}}>fr</option>
+</select>
+
+<p><button>{{ .i18n.UpdateSettings }}</button>
+</form>
+</div>
+<hr>
+<div>
+<form action="/chpass" method="POST">
+<input type="hidden" name="CSRF" value="{{ .LogoutCSRF }}">
+<p>{{ .i18n.ChangePwd }}
+<p><input tabindex=1 type="password" name="oldpass"> - {{ .i18n.OldPwd }}
+<p><input tabindex=1 type="password" name="newpass"> - {{ .i18n.NewPwd }}
+<p><button>{{ .i18n.ChangePwdBtn }}</button>
+</form>
+</div>
+</main>
diff --git a/views/i18n/chatter.html b/views/i18n/chatter.html
new file mode 100644
index 0000000..0173224
--- /dev/null
+++ b/views/i18n/chatter.html
@@ -0,0 +1,66 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+<p>
+<form action="/sendchonk" method="POST" enctype="multipart/form-data">
+<h3>{{ .i18n.NewChatter }}</h3>
+<input type="hidden" name="CSRF" value="{{ .ChonkCSRF }}">
+<p><label for=target>{{ .i18n.Target }}:</label><br>
+<input type="text" name="target" value="" autocomplete=off>
+<p><label for=noise>{{ .i18n.Noise }}:</label><br>
+<textarea name="noise" id="noise"></textarea>
+<p><button name="chonk" value="chonk">{{ .i18n.Chonk }}</button>
+<label class=button id="donker">{{ .i18n.Attach }}: <input onchange="updatedonker(this);" type="file" name="donk"><span></span></label>
+</form>
+<script>
+function updatedonker(el) {
+ el = el.parentElement
+ el.children[1].textContent = el.children[0].value.slice(-20)
+}
+</script>
+</div>
+{{ $chonkcsrf := .ChonkCSRF }}
+{{ range .Chatter }}
+<section class="honk">
+<p class="chattarget">
+chatter: {{ .Target }}
+{{ $target := .Target }}
+{{ range .Chonks }}
+<div class="chat">
+<p>
+<span class="chatstamp">{{ .Date.Local.Format "15:04" }} {{ .Handle }}:</span>
+{{ .HTML }}
+{{ range .Donks }}
+{{ if .Local }}
+{{ if eq .Media "text/plain" }}
+<p><a href="/d/{{ .XID }}">{{ .i18n.Attachment }}: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else if eq .Media "application/pdf" }}
+<p><a href="/d/{{ .XID }}">{{ .i18n.Attachment }}: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else }}
+<p><img src="/d/{{ .XID }}" title="{{ .Desc }}" alt="{{ .Desc }}">
+{{ end }}
+{{ else }}
+{{ if .XID }}
+<p><a href="{{ .URL }}" rel=noreferrer>{{ .i18n.ExtAttachment }}: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else }}
+{{ if eq .Media "video/mp4" }}
+<p><video controls src="{{ .URL }}">{{ .Name }}</video>
+{{ else }}
+<p><img src="{{ .URL }}" title="{{ .Desc }}" alt="{{ .Desc }}">
+{{ end }}
+{{ end }}
+{{ end }}
+{{ end }}
+</div>
+{{ end }}
+<form action="/sendchonk" method="POST" enctype="multipart/form-data">
+<input type="hidden" name="CSRF" value="{{ $chonkcsrf }}">
+<input type="hidden" name="target" value="{{ $target }}" autocomplete=off>
+<p><label for=noise>{{ .i18n.Noise }}:</label><br>
+<textarea name="noise" id="noise"></textarea>
+<p><button name="chonk" value="chonk">{{ .i18n.Chonk }}</button>
+<label class=button id="donker">{{ .i18n.Attach }}: <input onchange="updatedonker(this);" type="file" name="donk"><span></span></label>
+</form>
+</section>
+{{ end }}
+</main>
diff --git a/views/i18n/combos.html b/views/i18n/combos.html
new file mode 100644
index 0000000..9cd227d
--- /dev/null
+++ b/views/i18n/combos.html
@@ -0,0 +1,13 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+<p>{{ .i18n.Combos }}
+</div>
+{{ range .Combos }}
+<section class="honk">
+<header>
+<p style="font-size: 1.8em"><a href="/c/{{ . }}">{{ . }}</a>
+</header>
+</section>
+{{ end }}
+</main>
diff --git a/views/i18n/funzone.html b/views/i18n/funzone.html
new file mode 100644
index 0000000..d5e69e3
--- /dev/null
+++ b/views/i18n/funzone.html
@@ -0,0 +1,16 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+<p>{{ .i18n.FunZoneGreet }}
+<ul>
+{{ range .Emus }}
+<li><img class="emu" src="/emu/{{ . }}.png"> :{{ . }}:
+{{ end }}
+</ul>
+<ul>
+{{ range .Memes }}
+<li>meme: <a href="/meme/{{ . }}">{{ . }}</a>
+{{ end }}
+</ul>
+</div>
+</main>
diff --git a/views/i18n/header.html b/views/i18n/header.html
new file mode 100644
index 0000000..77995e7
--- /dev/null
+++ b/views/i18n/header.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<html lang="{{ .Lang }}">
+<head>
+ <link rel="shortcut icon" href="/favicon.ico"/>
+<title>honk</title>
+<link href="/style.css{{ .StyleParam }}" rel="stylesheet">
+{{ if .LocalStyleParam }}
+<link href="/local.css{{ .LocalStyleParam }}" rel="stylesheet">
+{{ end }}
+<style>
+{{ .UserStyle }}
+</style>
+<link href="/icon.png" rel="icon">
+<meta name="theme-color" content="#305">
+<meta name="viewport" content="width=device-width">
+<meta name="description" content="Federated ActivityPub instance running the Honk server. things happen">
+<meta name="keywords" content="honk">
+</head>
+<body>
+<header>
+{{ if .UserInfo }}
+<details id="topmenu">
+ <summary>menu <span> {{ .UserInfo.Username }}</span></summary>
+<ul>
+<li><a id="homelink" href="/">{{ .i18n.Home }}</a>
+<li><a id="atmelink" href="/atme">{{ .i18n.Atme }}</a>
+<li><a id="firstlink" href="/first">{{ .i18n.First }}</a>
+<li style="list-style-type:none; margin-left:-1em">
+<details>
+<summary>{{ .i18n.Combos }}</summary>
+<ul>
+{{ range .Combos }}
+<li><a class="combolink" href="/c/{{ . }}">{{ . }}</a>
+{{ end }}
+</ul>
+</details>
+<li><a href="/chatter">{{ .i18n.Chatter }}</a>
+<li><a href="/o">{{ .i18n.Tags }}</a>
+<li><a href="/events">{{ .i18n.Events }}</a>
+<li><a id="longagolink" href="/longago">{{ .i18n.Longago }}</a>
+<li><a id="savedlink" href="/saved">{{ .i18n.Saved }}</a>
+<li><a href="/honkers">{{ .i18n.Honkers }}</a>
+<li><a href="/hfcs">{{ .i18n.Hcfs }}</a>
+<li><a href="/account">{{ .i18n.Account }}</a>
+<li style="list-style-type:none; margin-left:-1em">
+<details>
+<summary>{{ .i18n.Morestuff }}</summary>
+<ul>
+<li><a href="/{{ .UserSep }}/{{ .UserInfo.Username }}">{{ .i18n.Myhonks }}</a>
+<li><a href="/about">{{ .i18n.About }}</a>
+<li><a href="/front">{{ .i18n.Front }}</a>
+<li><a href="/funzone">{{ .i18n.Funzone }}</a>
+<li><a href="/xzone">{{ .i18n.Xzone }}</a>
+</ul>
+</details>
+<li><a href="/help/honk.1.html">{{ .i18n.Help }}</a>
+<li>
+<form action="/q" method="GET">
+<input type="text" name="q" autocomplete=off size=10 placeholder="{{ .i18n.Search }}">
+</form>
+</ul>
+</details>
+
+<!-- CUSTOM HONK NAVIGATION BAR -->
+ <div id="altnavbar">
+ <a href="/newhonk" tabindex="2">{{ .i18n.Newhonk }}</a> |
+ <a href="/front">{{ .i18n.Front }}</a> |
+ <a href="/">{{ .i18n.Home }}</a> |
+ <a href="/xzone">{{ .i18n.Xzone }}</a> |
+ <a href="/atme">{{ .i18n.Atme }}</a> |
+ <a href="/u/{{ .UserInfo.Username}}">{{ .i18n.Myhonks }}</a> |
+ <a href="/account">{{ .i18n.Account }}</a> |
+ <form style="display:inline" action="/q" method="GET">
+ <input type="text" tabindex="1" name="q" autocomplete="off"
+ size="10" placeholder="{{ .i18n.Search }}">
+ </form>
+ </div>
+<!-- END -->
+
+<p id="topspacer"></p>
+{{ else }}
+<span><a id="homelink" href="/">{{ .i18n.Home }}</a></span>
+<span><a href="/o">{{ .i18n.Tags }}</a></span>
+<span><a href="/events">{{ .i18n.Events }}</a></span>
+<span><a href="/about">{{ .i18n.About }}</a></span>
+{{ if .ShowRSS }}
+<span><a href="/rss">rss</a></span>
+{{ end }}
+<span><a href="/login">{{ .i18n.Login }}</a>
+<form style="display:inline" action="/langcookie" method="POST">
+<select name="lang">
+ <option {{ and (eq .Lang "en") "selected"}}>en</option>
+ <option {{ and (eq .Lang "fr") "selected"}}>fr</option>
+</select>
+<button>{{ .i18n.SetLang }}</button>
+</form>
+
+</span>
+{{ end }}
+</header>
diff --git a/views/i18n/hfcs.html b/views/i18n/hfcs.html
new file mode 100644
index 0000000..75b6f4f
--- /dev/null
+++ b/views/i18n/hfcs.html
@@ -0,0 +1,73 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+<p>
+{{ .i18n.Hfcs }}
+<form action="/savehfcs" method="POST">
+<input type="hidden" name="CSRF" value="{{ .FilterCSRF }}">
+<hr>
+<h3>{{ .i18n.NewFilter }}</h3>
+<p><label for="name">{{ .i18n.FilterName }}:</label><br>
+<input tabindex=1 type="text" name="name" value="" autocomplete=off>
+<p><label for="filtnotes">{{ .i18n.Notes }}:</label><br>
+<textarea tabindex=1 name="filtnotes" height=4>
+</textarea>
+<hr>
+<h3>{{ .i18n.Matches }}</h3>
+<p><label for="actor">{{ .i18n.WhoWhere }}:</label><br>
+<input tabindex=1 type="text" name="actor" value="" autocomplete=off>
+<p><span><label class=button for="incaud">{{ .i18n.IncludeAudience }}:
+<input tabindex=1 type="checkbox" id="incaud" name="incaud" value="yes"><span></span></label></span>
+<p><label for="filttext">{{ .i18n.TextMatches }}:</label><br>
+<input tabindex=1 type="text" name="filttext" value="" autocomplete=off>
+<p><span><label class=button for="isannounce">{{ .i18n.IsAnnounce }}:
+<input tabindex=1 type="checkbox" id="isannounce" name="isannounce" value="yes"><span></span></label></span>
+<p><label for="announceof">{{ .i18n.AnnounceOf }}:</label><br>
+<input tabindex=1 type="text" name="announceof" value="" autocomplete=off>
+<hr>
+<h3>action</h3>
+<p class="buttonarray">
+<span><label class=button for="doreject">{{ .i18n.Reject }}:
+<input tabindex=1 type="checkbox" id="doreject" name="doreject" value="yes"><span></span></label></span>
+<span><label class=button for="doskipmedia">{{ .i18n.SkipMedia }}:
+<input tabindex=1 type="checkbox" id="doskipmedia" name="doskipmedia" value="yes"><span></span></label></span>
+<span><label class=button for="dohide">{{ .i18n.Hide }}:
+<input tabindex=1 type="checkbox" id="dohide" name="dohide" value="yes"><span></span></label></span>
+<span><label class=button for="docollapse">{{ .i18n.Collapse }}:
+<input tabindex=1 type="checkbox" id="docollapse" name="docollapse" value="yes"><span></span></label></span>
+<p><label for="rewrite">{{ .i18n.Rewrite }}:</label><br>
+<input tabindex=1 type="text" name="filtrewrite" value="" autocomplete=off>
+<p><label for="replace">{{ .i18n.Replace }}:</label><br>
+<input tabindex=1 type="text" name="filtreplace" value="" autocomplete=off>
+<hr>
+<h3>{{ .i18n.Expiration }}</h3>
+<p><label for="filtduration">{{ .i18n.Duration }}:</label><br>
+<input tabindex=1 type="text" name="filtduration" value="" autocomplete=off>
+<hr>
+<p><button>{{ .i18n.BanHammerBtn }}</button>
+</form>
+</div>
+{{ $i18n := .i18n }}
+{{ $csrf := .FilterCSRF }}
+{{ range .Filters }}
+<section class="honk">
+<p>{{ $i18n.FilterName }}: {{ .Name }}
+{{ with .Notes }}<p>{{ $i18n.Notes }}: {{ . }}{{ end }}
+<p>Date: {{ .Date.Format "2006-01-02" }}
+{{ with .Actor }}<p>{{ $i18n.Who }}: {{ . }}{{ end }} {{ with .IncludeAudience }} (inclusive) {{ end }}
+{{ if .IsAnnounce }}<p>Announce: {{ .AnnounceOf }}{{ end }}
+{{ with .Text }}<p>Text: {{ . }}{{ end }}
+<p>{{ $i18n.Action }}: {{ range .Actions }} {{ . }} {{ end }}
+{{ with .Rewrite }}<p>{{ $i18n.Rewrite }}: {{ . }}{{ end }}
+{{ with .Replace }}<p>{{ $i18n.Replace }}: {{ . }}{{ end }}
+{{ if not .Expiration.IsZero }}<p>{{ $i18n.Expiration }}: {{ .Expiration.Format "2006-01-02 03:04" }}{{ end }}
+<form action="/savehfcs" method="POST">
+<input type="hidden" name="CSRF" value="{{ $csrf }}">
+<input type="hidden" name="hfcsid" value="{{ .ID }}">
+<input type="hidden" name="itsok" value="iforgiveyou">
+<button name="pardon" value="pardon">{{ $i18n.Pardon }}</button>
+</form>
+<p>
+</section>
+{{ end }}
+</main>
diff --git a/views/i18n/honk.html b/views/i18n/honk.html
new file mode 100644
index 0000000..52f63a7
--- /dev/null
+++ b/views/i18n/honk.html
@@ -0,0 +1,146 @@
+<article class="honk {{ .Honk.Style }}" data-convoy="{{ .Honk.Convoy }}">
+{{ $bonkcsrf := .BonkCSRF }}
+{{ $IsPreview := .IsPreview }}
+{{ $maplink := .MapLink }}
+{{ $omitimages := .OmitImages }}
+{{ $i18n := .i18n }}
+{{ $lang := .Lang }}
+{{ with .Honk }}
+<header>
+{{ if $bonkcsrf }}
+<a class="honkerlink" href="/h?xid={{ .Honker }}" data-xid="{{ .Honker }}">
+{{ else }}
+<a href="{{ .Honker }}" rel=noreferrer>
+{{ end }}
+<img alt="" src="/a?a={{ .Honker}}">
+{{ if $bonkcsrf }} </a> {{ end }}
+{{ if .Oonker }}
+{{ if $bonkcsrf }}
+<a class="honkerlink" href="/h?xid={{ .Oonker }}" data-xid="{{ .Oonker }}">
+{{ else }}
+<a href="{{ .Oonker }}" rel=noreferrer>
+{{ end }}
+<img alt="" src="/a?a={{ .Oonker}}">
+{{ if $bonkcsrf }} </a> {{ end }}
+{{ end }}
+<p>
+{{ if $bonkcsrf }}
+<a class="honkerlink" href="/h?xid={{ .Honker }}" data-xid="{{ .Honker }}">{{ .Username }}</a>
+{{ else }}
+<a href="{{ .Honker }}" rel=noreferrer>{{ .Username }}</a>
+{{ end }}
+<span class="clip"><a href="{{ .URL }}" rel=noreferrer>{{ .What }}</a> {{ .Date.Local.Format "02 Jan 2006 15:04 -0700" }}</span>
+{{ if .Oonker }}
+<br>
+<span style="margin-left: 1em;" class="clip">
+{{ if $bonkcsrf }}
+{{ $i18n.Original }}: <a class="honkerlink" href="/h?xid={{ .Oonker }}" data-xid="{{ .Oonker }}">{{ .Oondle }}</a>
+{{ else }}
+{{ $i18n.Original }}: <a href="{{ .Oonker }}" rel=noreferrer>{{ .Oondle }}</a>
+{{ end }}
+</span>
+{{ else }}
+{{ if .RID }}
+<br>
+<span style="margin-left: 1em;" class="clip">
+{{ $i18n.InReplyTo }}: <a href="{{ .RID }}" rel=noreferrer>{{ .RID }}</a>
+</span>
+{{ end }}
+{{ end }}
+<br>
+{{ if $bonkcsrf }}
+ <span style="margin-left: 1em;" class="clip">{{ $i18n.Convoy }}: <a class="convoylink" href="/t?c={{ .Convoy }}">{{ .Convoy }}</a></span>
+{{ else }}
+ <span style="margin-left: 1em;" class="clip">{{ $i18n.Convoy }}: {{ .Convoy }}</span>
+
+{{ end }}
+</header>
+<p>
+<details class="noise" {{ .Open }} >
+<summary>{{ .HTPrecis }}<p></summary>
+<p>{{ .HTPrecis }}
+<p>{{ .HTML }}
+{{ with .Time }}
+<p>{{ $i18n.Time }}: {{ .StartTime.Local.Format "03:04PM EDT Mon Jan 02"}}
+{{ if .Duration }}<br>{{ $i18n.Duration }}: {{ .Duration }}{{ end }}
+{{ end }}
+{{ with .Place }}
+<p>{{ $i18n.Location }}: {{ with .Url }}<a href="{{ . }}" rel=noreferrer>{{ end }}{{ .Name }}{{ if .Url }}</a>{{ end }}{{ if or .Latitude .Longitude }} <a href="{{ if eq $maplink "apple" }}https://maps.apple.com/?q={{ or .Name "here" }}&z=16&ll={{ .Latitude }},{{ .Longitude }}{{ else }}https://www.openstreetmap.org/?mlat={{ .Latitude }}&mlon={{ .Longitude}}#map=16/{{ .Latitude }}/{{ .Longitude }}{{ end }}" rel=noreferrer>{{ .Latitude }} {{ .Longitude }}</a>{{ end }}
+{{ end }}
+{{ range .Donks }}
+{{ if .Local }}
+{{ if eq .Media "text/plain" }}
+<p><a href="/d/{{ .XID }}">{{ $i18n.Attachment }}: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else if eq .Media "application/pdf" }}
+<p><a href="/d/{{ .XID }}">{{ $i18n.Attachment }}: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else }}
+{{ if $omitimages }}
+<p><a href="/d/{{ .XID }}">{{ $i18n.Image }}: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else }}
+<p><img src="/d/{{ .XID }}" title="{{ .Desc }}" alt="{{ .Desc }}">
+{{ end }}
+{{ end }}
+{{ else }}
+{{ if .External }}
+<p><a href="{{ .URL }}" rel=noreferrer>{{ $i18n.ExtAttachment }}: {{ .Name }}</a>{{ if not (eq .Desc .Name) }} {{ .Desc }}{{ end }}
+{{ else }}
+{{ if eq .Media "video/mp4" }}
+<p><video controls src="{{ .URL }}">{{ .Name }}</video>
+{{ else }}
+<p><img src="{{ .URL }}" title="{{ .Desc }}" alt="{{ .Desc }}">
+{{ end }}
+{{ end }}
+{{ end }}
+{{ end }}
+</details>
+{{ end }}
+{{ if and $bonkcsrf (not $IsPreview) }}
+<p>
+<details class="actions">
+<summary>{{ $i18n.Actions }}</summary>
+<div>
+<p>
+{{ if .Honk.Public }}
+{{ if .Honk.IsBonked }}
+<button onclick="return unbonk(this, '{{ .Honk.XID }}');">{{ $i18n.Unbonk }}</button>
+{{ else }}
+<button onclick="return bonk(this, '{{ .Honk.XID }}');">{{ $i18n.Bonk }}</button>
+{{ end }}
+{{ else }}
+<button disabled>{{ $i18n.Nope }}</button>
+{{ end }}
+<button onclick="return showhonkform(this, '{{ .Honk.XID }}', '{{ .Honk.Handles }}');"><a href="/newhonk?rid={{ .Honk.XID }}">{{ $i18n.HonkBack }}</a></button>
+<button onclick="return muteit(this, '{{ .Honk.Convoy }}');">{{ $i18n.Mute }}</button>
+ <button onclick="return showelement('evenmore{{ .Honk.ID }}')">{{ $i18n.EvenMore }} </button>
+</div>
+<div id="evenmore{{ .Honk.ID }}" style="display:none">
+<p>
+<button onclick="return zonkit(this, '{{ .Honk.XID }}');">{{ $i18n.Zonk }}</button>
+{{ if .Honk.IsAcked }}
+<button onclick="return flogit(this, 'deack', '{{ .Honk.XID }}');">{{ $i18n.Deack }}</button>
+{{ else }}
+<button onclick="return flogit(this, 'ack', '{{ .Honk.XID }}');">{{ $i18n.Ack }}</button>
+{{ end }}
+{{ if .Honk.IsSaved }}
+<button onclick="return flogit(this, 'unsave', '{{ .Honk.XID }}');">{{ $i18n.Unsave }}</button>
+{{ else }}
+<button onclick="return flogit(this, 'save', '{{ .Honk.XID }}');">{{ $i18n.Save }}</button>
+{{ end }}
+{{ if .Honk.IsUntagged }}
+<button disabled>{{ $i18n.Untagged }}</button>
+{{ else }}
+<button onclick="return flogit(this, 'untag', '{{ .Honk.XID }}');">{{ $i18n.UntagMe }}</button>
+{{ end }}
+<button><a href="/edit?xid={{ .Honk.XID }}">{{ $i18n.Edit }}</a></button>
+{{ if not (eq .Badonk "none") }}
+{{ if .Honk.IsReacted }}
+<button disabled>badonked</button>
+{{ else }}
+<button onclick="return flogit(this, 'react', '{{ .Honk.XID }}');">{{ .Badonk }}</button>
+{{ end }}
+{{ end }}
+</div>
+</details>
+<p>
+{{ end }}
+</article>
diff --git a/views/i18n/honkers.html b/views/i18n/honkers.html
new file mode 100644
index 0000000..5b9ecf4
--- /dev/null
+++ b/views/i18n/honkers.html
@@ -0,0 +1,62 @@
+{{ template "header.html" . }}
+<main>
+{{ $i18n := .i18n }}
+<div class="info">
+<p>
+<form action="/submithonker" method="POST">
+<h3>{{ $i18n.AddNewHonker }}</h3>
+<input type="hidden" name="CSRF" value="{{ .HonkerCSRF }}">
+<p><label for=url>url:</label><br>
+<input tabindex=1 type="text" name="url" value="" autocomplete=off>
+<p><label for=name>{{ $i18n.Name }}:</label><br>
+<input tabindex=1 type="text" name="name" value="" placeholder="{{ $i18n.Optional }}" autocomplete=off>
+<p><label for=combos>{{ $i18n.Combos }}:</label><br>
+<input tabindex=1 type="text" name="combos" value="" placeholder="{{ $i18n.Optional }}">
+<p><span><label class=button for="peep">{{ $i18n.SkipSub }}:
+<input tabindex=1 type="checkbox" id="peep" name="peep" value="peep"><span></span></label></span>
+<p><label for="notes">notes:</label><br>
+<textarea tabindex=1 name="notes">
+</textarea>
+<p><button tabindex=1 name="add honker" value="add honker">{{ $i18n.AddHonkerBtn }}</button>
+</form>
+</div>
+{{ $honkercsrf := .HonkerCSRF }}
+<div class="info">
+<script>
+function expandstuff() {
+ var els = document.querySelectorAll(".honk details")
+ for (var i = 0; i < els.length; i++) {
+ els[i].open = true
+ }
+}
+</script>
+<p><button onclick="expandstuff()">{{ $i18n.Expand }}</button>
+</div>
+{{ range .Honkers }}
+<section class="honk">
+<header>
+<img alt="avatar" src="/a?a={{ .XID }}">
+<p style="font-size: 1.8em"><a href="/h/{{ .Name }}">{{ .Name }}<a>
+</header>
+<p>
+<details>
+<p>url: <a href="{{ .XID }}" rel=noreferrer>{{ .XID }}</a>
+<p>{{ $i18n.Flavor }}: {{ .Flavor }}
+<form action="/submithonker" method="POST">
+<input type="hidden" name="CSRF" value="{{ $honkercsrf }}">
+<input type="hidden" name="honkerid" value="{{ .ID }}">
+<p>{{ $i18n.Name }}: <input type="text" name="name" value="{{ .Name }}">
+<p><label for="notes">{{ $i18n.Notes }}:</label><br>
+<textarea name="notes">{{ .Meta.Notes }}</textarea>
+<p>{{ $i18n.Combos }}: <input type="text" name="combos" value="{{ range .Combos }}{{ . }} {{end}}">
+<p>
+<button name="save" value="save">{{ $i18n.Save }}</button>
+<button name="sub" value="sub">{{ $i18n.Resub }}</button>
+<button name="unsub" value="unsub">{{ $i18n.Unsub }}</button>
+<button name="delete" value="delete">{{ $i18n.Delete }}</button>
+</form>
+</details>
+<p>
+</section>
+{{ end }}
+</main>
diff --git a/views/i18n/honkform.html b/views/i18n/honkform.html
new file mode 100644
index 0000000..ce17fca
--- /dev/null
+++ b/views/i18n/honkform.html
@@ -0,0 +1,49 @@
+<p id="honkformhost">
+<button id="honkingtime" onclick="return showhonkform();" {{ if .IsPreview }}style="display:none"{{ end }}><a href="/newhonk">{{ .i18n.HonkingTime }}</a></button>
+<form id="honkform" action="/honk" method="POST" enctype="multipart/form-data" {{ if not .IsPreview }}style="display: none"{{ end }}>
+<input type="hidden" name="CSRF" value="{{ .HonkCSRF }}">
+<input type="hidden" name="updatexid" id="updatexidinput" value = "{{ .UpdateXID }}">
+<input type="hidden" name="rid" id="ridinput" value="{{ .InReplyTo }}">
+<h3>{{ .i18n.MakeNoise }}</h3>
+<p>
+<details>
+<summary>{{ .i18n.MoreOptions }}</summary>
+<p>
+<label class=button id="donker">{{ .i18n.Attach }}: <input onchange="updatedonker();" type="file" name="donk"><span>{{ .SavedFile }}</span></label>
+<input type="hidden" id="saveddonkxid" name="donkxid" value="{{ .SavedFile }}">
+<p id="donkdescriptor"><label for=donkdesc>{{ .i18n.Description }}:</label><br>
+<input type="text" name="donkdesc" value="{{ .DonkDesc }}" autocomplete=off>
+{{ with .SavedPlace }}
+<p><button id=checkinbutton type=button onclick="fillcheckin()">{{ .i18n.Checkin }}</button>
+<div id=placedescriptor>
+ <p><label>name:</label><br><input type="text" name="placename" id=placenameinput value="{{ .Name }}">
+ <p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="{{ .Url }}">
+ <p><label>lat: </label><input type="text" size=9 name="placelat" id=placelatinput value="{{ .Latitude}}">
+ <label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="{{ .Longitude }}">
+</div>
+{{ else }}
+<p><button id=checkinbutton type=button onclick="fillcheckin()">{{ .i18n.Checkin }}</button>
+<div id=placedescriptor style="display: none">
+<p><label>{{ .i18n.Location }}:</label><br><input type="text" name="placename" id=placenameinput value="">
+<p><label>url:</label><br><input type="text" name="placeurl" id=placeurlinput value="">
+<p><label>lat: </label><input type="text" size=9 name="placelat" id=placelatinput value="">
+<label>lon: </label><input type="text" size=9 name="placelong" id=placelonginput value="">
+</div>
+{{ end }}
+<p><button id=addtimebutton type=button
+onclick="showelement('timedescriptor')">{{ .i18n.AddTime }}</button>
+<div id=timedescriptor style="{{ or .ShowTime "display: none" }}">
+<p><label for=timestart>{{ .i18n.Start }}:</label><br>
+<input type="text" name="timestart" value="{{ .StartTime }}">
+<p><label for=timeend>{{ .i18n.Duration }}:</label><br>
+<input type="text" name="timeend" value="{{ .Duration }}">
+</div>
+</details>
+<p>
+<textarea name="noise" id="honknoise">{{ .Noise }}</textarea>
+<p class="buttonarray">
+<button>{{ .i18n.GonBHonked }}</button>
+<button name="preview" value="preview">{{ .i18n.Preview }}</button>
+<button type=button name="cancel" value="cancel"
+onclick="cancelhonking()">{{ .i18n.Cancel }}</button>
+</form>
diff --git a/views/i18n/honkfrags.html b/views/i18n/honkfrags.html
new file mode 100644
index 0000000..5ccc2da
--- /dev/null
+++ b/views/i18n/honkfrags.html
@@ -0,0 +1,11 @@
+<div>{{ .TopHID }}</div>
+{{ $BonkCSRF := .HonkCSRF }}
+{{ $MapLink := .MapLink }}
+{{ $Badonk := .User.Options.Reaction }}
+{{ $OmitImages := .User.Options.OmitImages }}
+<div><p>{{ .ServerMessage }}</div>
+<div>
+{{ range .Honks }}
+{{ template "honk.html" map "Honk" . "MapLink" $MapLink "BonkCSRF" $BonkCSRF "Badonk" $Badonk "OmitImages" $OmitImages }}
+{{ end }}
+</div>
diff --git a/views/i18n/honkpage.html b/views/i18n/honkpage.html
new file mode 100644
index 0000000..0ae42ef
--- /dev/null
+++ b/views/i18n/honkpage.html
@@ -0,0 +1,45 @@
+{{ template "header.html" . }}
+<main>
+<div class="info" id="infobox">
+<div id="srvmsg">
+{{ if .Name }}
+<p>{{ .Name }} <span style="margin-left:1em;"><a href="/u/{{ .Name }}/rss">rss</a></span>
+<p>{{ .WhatAbout }}
+{{ end }}
+<p>{{ .ServerMessage }}
+</div>
+{{ if .HonkCSRF }}
+{{ template "honkform.html" . }}
+<script>
+var csrftoken = {{ .HonkCSRF }}
+var honksforpage = { }
+var curpagestate = { name: "{{ .PageName }}", arg : "{{ .PageArg }}" }
+var tophid = { }
+tophid[curpagestate.name + ":" + curpagestate.arg] = "{{ .TopHID }}"
+var servermsgs = { }
+servermsgs[curpagestate.name + ":" + curpagestate.arg] = "{{ .ServerMessage }}"
+</script>
+<script src="/honkpage.js{{ .JSParam }}"></script>
+{{ end }}
+</div>
+{{ if and .HonkCSRF (not .IsPreview) }}
+<div class="info" id="refreshbox">
+<p><button onclick="refreshhonks(this)">{{ .i18n.Refresh }}</button><span></span>
+<button onclick="oldestnewest(this)">{{ .i18n.ScrollDown }}</button>
+</div>
+{{ if eq .ServerMessage .i18n.MoarHonks }} <script> hideelement("refreshbox")</script> {{ end }}
+{{ end }}
+<div id="honksonpage">
+<div>
+{{ $BonkCSRF := .HonkCSRF }}
+{{ $IsPreview := .IsPreview }}
+{{ $MapLink := .MapLink }}
+{{ $Badonk := .User.Options.Reaction }}
+{{ $i18n := .i18n }}
+{{ $OmitImages := .User.Options.OmitImages }}
+{{ range .Honks }}
+{{ template "honk.html" map "Honk" . "MapLink" $MapLink "BonkCSRF" $BonkCSRF "IsPreview" $IsPreview "Badonk" $Badonk "OmitImages" $OmitImages "i18n" $i18n }}
+{{ end }}
+</div>
+</div>
+</main>
diff --git a/views/i18n/honkpage.js b/views/i18n/honkpage.js
new file mode 100644
index 0000000..351cde4
--- /dev/null
+++ b/views/i18n/honkpage.js
@@ -0,0 +1,390 @@
+function getCookie(name){
+ if(document.cookie.length == 0)
+ return null;
+
+ var regSepCookie = new RegExp('(; )', 'g');
+ var cookies = document.cookie.split(regSepCookie);
+
+ for(var i = 0; i < cookies.length; i++){
+ var regInfo = new RegExp('=', 'g');
+ var infos = cookies[i].split(regInfo);
+ if(infos[0] == name){
+ return unescape(infos[1]);
+ }
+ }
+ return null;
+}
+
+
+function encode(hash) {
+ var s = []
+ for (var key in hash) {
+ var val = hash[key]
+ s.push(escape(key) + "=" + escape(val))
+ }
+ return s.join("&")
+}
+function post(url, data) {
+ var x = new XMLHttpRequest()
+ x.open("POST", url)
+ x.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")
+ x.send(data)
+}
+function get(url, whendone) {
+ var x = new XMLHttpRequest()
+ x.open("GET", url)
+ x.responseType = "document"
+ x.onload = function() { whendone(x) }
+ x.send()
+}
+function bonk(el, xid) {
+ el.innerHTML = i18n_bonked
+ el.disabled = true
+ post("/bonk", encode({"js": "2", "CSRF": csrftoken, "xid": xid}))
+ return false
+}
+function unbonk(el, xid) {
+ el.innerHTML = i18n_unbonked
+ el.disabled = true
+ post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "unbonk", "what": xid}))
+}
+function muteit(el, convoy) {
+ el.innerHTML = i18n_muted
+ el.disabled = true
+ post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "zonvoy", "what": convoy}))
+ var els = document.querySelectorAll('article.honk')
+ for (var i = 0; i < els.length; i++) {
+ var e = els[i]
+ if (e.getAttribute("data-convoy") == convoy) {
+ e.remove()
+ }
+ }
+}
+function zonkit(el, xid) {
+ el.innerHTML = i18n_zonked
+ el.disabled = true
+ post("/zonkit", encode({"CSRF": csrftoken, "wherefore": "zonk", "what": xid}))
+ var p = el
+ while (p && p.tagName != "ARTICLE") {
+ p = p.parentElement
+ }
+ if (p) {
+ p.remove()
+ }
+}
+function flogit(el, how, xid) {
+ var s = how
+ if (honklang == 'en') {
+ if (s[s.length-1] != "e") { s += "e" }
+ s += "d"
+ if (s == "untaged") s = i18n_untagged
+ if (s == "reacted") s = i18n_badonked
+ } else {
+ if (s == "deack") s = i18n_deack
+ if (s == "ack") s = i18n_ack
+ if (s == "unsave") s = i18n_unsave
+ if (s == "save") s = i18n_save
+ if (s == "untag") s = i18n_untagged
+ if (s == "react") s = i18n_badonked
+ }
+
+ el.innerHTML = s
+ el.disabled = true
+ post("/zonkit", encode({"CSRF": csrftoken, "wherefore": how, "what": xid}))
+}
+
+var lehonkform = document.getElementById("honkform")
+var lehonkbutton = document.getElementById("honkingtime")
+
+function oldestnewest(btn) {
+ var els = document.getElementsByClassName("glow")
+ if (els.length) {
+ els[els.length-1].scrollIntoView()
+ }
+}
+function removeglow() {
+ var els = document.getElementsByClassName("glow")
+ while (els.length) {
+ els[0].classList.remove("glow")
+ }
+}
+
+function fillinhonks(xhr, glowit) {
+ var doc = xhr.responseXML
+ var stash = curpagestate.name + ":" + curpagestate.arg
+ tophid[stash] = doc.children[0].children[1].children[0].innerText
+ var srvmsg = doc.children[0].children[1].children[1]
+ var honks = doc.children[0].children[1].children[2].children
+
+ var srvel = document.getElementById("srvmsg")
+ while (srvel.children[0]) {
+ srvel.children[0].remove()
+ }
+ srvel.prepend(srvmsg)
+
+ var frontload = true
+ if (curpagestate.name == "convoy") {
+ frontload = false
+ }
+
+ var honksonpage = document.getElementById("honksonpage")
+ var holder = honksonpage.children[0]
+ var lenhonks = honks.length
+ for (var i = honks.length; i > 0; i--) {
+ var h = honks[i-1]
+ if (glowit)
+ h.classList.add("glow")
+ if (frontload) {
+ holder.prepend(h)
+ } else {
+ holder.append(h)
+ }
+
+ }
+ relinklinks()
+ return lenhonks
+}
+function hydrargs() {
+ var name = curpagestate.name
+ var arg = curpagestate.arg
+ var args = { "page" : name }
+ if (name == "convoy") {
+ args["c"] = arg
+ } else if (name == "combo") {
+ args["c"] = arg
+ } else if (name == "honker") {
+ args["xid"] = arg
+ }
+ return args
+}
+function refreshupdate(msg) {
+ var el = document.querySelector("#refreshbox p span")
+ if (el) {
+ el.innerHTML = msg
+ }
+}
+function refreshhonks(btn) {
+ removeglow()
+ btn.innerHTML = i18n_refreshing
+ btn.disabled = true
+ var args = hydrargs()
+ var stash = curpagestate.name + ":" + curpagestate.arg
+ args["tophid"] = tophid[stash]
+ get("/hydra?" + encode(args), function(xhr) {
+ var lenhonks = fillinhonks(xhr, true)
+ btn.innerHTML = i18n_refreshing
+ btn.disabled = false
+ refreshupdate(" " + lenhonks + " new")
+ })
+}
+function statechanger(evt) {
+ var data = evt.state
+ if (!data) {
+ return
+ }
+ switchtopage(data.name, data.arg)
+}
+function switchtopage(name, arg) {
+ var stash = curpagestate.name + ":" + curpagestate.arg
+ var honksonpage = document.getElementById("honksonpage")
+ var holder = honksonpage.children[0]
+ holder.remove()
+ var srvel = document.getElementById("srvmsg")
+ var msg = srvel.children[0]
+ if (msg) {
+ msg.remove()
+ servermsgs[stash] = msg
+ }
+ showelement("refreshbox")
+
+ honksforpage[stash] = holder
+
+ curpagestate.name = name
+ curpagestate.arg = arg
+ // get the holder for the target page
+ var stash = name + ":" + arg
+ holder = honksforpage[stash]
+ if (holder) {
+ honksonpage.prepend(holder)
+ msg = servermsgs[stash]
+ if (msg) {
+ srvel.prepend(msg)
+ }
+ } else {
+ // or create one and fill it
+ honksonpage.prepend(document.createElement("div"))
+ var args = hydrargs()
+ get("/hydra?" + encode(args), function(xhr) { fillinhonks(xhr, false) })
+ }
+ refreshupdate("")
+}
+function newpagestate(name, arg) {
+ return { "name": name, "arg": arg }
+}
+function pageswitcher(name, arg) {
+ return function(evt) {
+ var topmenu = document.getElementById("topmenu")
+ topmenu.open = false
+ if (name == curpagestate.name && arg == curpagestate.arg) {
+ return false
+ }
+ switchtopage(name, arg)
+ var url = evt.srcElement.href
+ if (!url) {
+ url = evt.srcElement.parentElement.href
+ }
+ history.pushState(newpagestate(name, arg), "some title", url)
+ window.scrollTo(0, 0)
+ return false
+ }
+}
+function relinklinks() {
+ var els = document.getElementsByClassName("convoylink")
+ while (els.length) {
+ els[0].onclick = pageswitcher("convoy", els[0].text)
+ els[0].classList.remove("convoylink")
+ }
+ els = document.getElementsByClassName("combolink")
+ while (els.length) {
+ els[0].onclick = pageswitcher("combo", els[0].text)
+ els[0].classList.remove("combolink")
+ }
+ els = document.getElementsByClassName("honkerlink")
+ while (els.length) {
+ var el = els[0]
+ var xid = el.getAttribute("data-xid")
+ el.onclick = pageswitcher("honker", xid)
+ el.classList.remove("honkerlink")
+ }
+}
+(function() {
+ var el = document.getElementById("homelink")
+ el.onclick = pageswitcher("home", "")
+ el = document.getElementById("atmelink")
+ el.onclick = pageswitcher("atme", "")
+ el = document.getElementById("firstlink")
+ el.onclick = pageswitcher("first", "")
+ el = document.getElementById("savedlink")
+ el.onclick = pageswitcher("saved", "")
+ el = document.getElementById("longagolink")
+ el.onclick = pageswitcher("longago", "")
+ relinklinks()
+ window.onpopstate = statechanger
+ history.replaceState(curpagestate, "some title", "")
+})();
+(function() {
+ hideelement("donkdescriptor")
+})();
+function showhonkform(elem, rid, hname) {
+ var form = lehonkform
+ form.style = "display: block"
+ if (elem) {
+ form.remove()
+ elem.parentElement.parentElement.parentElement.insertAdjacentElement('beforebegin', form)
+ } else {
+ hideelement(lehonkbutton)
+ elem = document.getElementById("honkformhost")
+ elem.insertAdjacentElement('afterend', form)
+ }
+ var ridinput = document.getElementById("ridinput")
+ if (rid) {
+ ridinput.value = rid
+ honknoise.value = hname + " "
+ } else {
+ ridinput.value = ""
+ honknoise.value = ""
+ }
+ var updateinput = document.getElementById("updatexidinput")
+ updateinput.value = ""
+ document.getElementById("honknoise").focus()
+ return false
+}
+function cancelhonking() {
+ hideelement(lehonkform)
+ showelement(lehonkbutton)
+}
+function showelement(el) {
+ if (typeof(el) == "string")
+ el = document.getElementById(el)
+ if (!el) return
+ el.style.display = "block"
+}
+function hideelement(el) {
+ if (typeof(el) == "string")
+ el = document.getElementById(el)
+ if (!el) return
+ el.style.display = "none"
+}
+function updatedonker() {
+ var el = document.getElementById("donker")
+ el.children[1].textContent = el.children[0].value.slice(-20)
+ var el = document.getElementById("donkdescriptor")
+ el.style.display = ""
+ var el = document.getElementById("saveddonkxid")
+ el.value = ""
+}
+var checkinprec = 100.0
+var gpsoptions = {
+ enableHighAccuracy: false,
+ timeout: 1000,
+ maximumAge: 0
+};
+function fillcheckin() {
+ if (navigator.geolocation) {
+ navigator.geolocation.getCurrentPosition(function(pos) {
+ showelement("placedescriptor")
+ var el = document.getElementById("placelatinput")
+ el.value = Math.round(pos.coords.latitude * checkinprec) / checkinprec
+ el = document.getElementById("placelonginput")
+ el.value = Math.round(pos.coords.longitude * checkinprec) / checkinprec
+ checkinprec = 10000.0
+ gpsoptions.enableHighAccuracy = true
+ gpsoptions.timeout = 2000
+ }, function(err) {
+ showelement("placedescriptor")
+ el = document.getElementById("placenameinput")
+ el.value = err.message
+ }, gpsoptions)
+ }
+}
+
+window.getCookie = function(name) {
+ var match = document.cookie.match(new RegExp('(^| )' + name + '=([^;]+)'));
+ if (match) return match[2];
+}
+
+var honklang = getCookie('lang')
+switch(honklang) {
+ case "fr":
+ var i18n_deack = "marqué non lu"
+ var i18n_ack = "marqué lu"
+ var i18n_unsave = "oublié"
+ var i18n_save = "sauvegardé"
+
+ var i18n_bonked = "reposté"
+ var i18n_unbonked = "départagé"
+ var i18n_muted = "mis en sourdine"
+ var i18n_badonked = "réagi"
+ var i18n_zonked = "supprimé"
+ var i18n_untagged = "débalisé"
+ var i18n_refreshing = "rafraîchissement"
+ break;
+
+ default:
+ var i18n_deack = "deacked"
+ var i18n_ack = "ack"
+ var i18n_unsave = "unsave"
+ var i18n_save = "save"
+
+ var i18n_bonked = "bonked"
+ var i18n_unbonked = "unbonked"
+ var i18n_muted = "muted"
+ var i18n_badonked = "badonked"
+ var i18n_untagged = "untagged"
+ var i18n_zonked = "zonked"
+ var i18n_refreshing = "refreshing"
+
+}
+
+//console.log("i18n_bonked: "+ i18n_bonked + "\n"+ "cookie: "+ getCookie("lang"))
+
diff --git a/views/i18n/local.css b/views/i18n/local.css
new file mode 100644
index 0000000..0e6f7e1
--- /dev/null
+++ b/views/i18n/local.css
@@ -0,0 +1,58 @@
+html{
+ --bg-page: beige;
+ --fg: #222;
+ --fg-subtle: teal;
+ --bg-dark:lightblue;
+}
+
+#honksonpage td {
+ border:1px solid #222;
+ padding: 0.2em;
+}
+
+#altnavbar a {
+ text-decoration:none
+}
+#altnavbar{
+ margin-left:17%;
+ position:fixed;
+ background:var(--bg-page);
+ opacity:0.7;
+ top:0;
+ padding: 0.5em 0.5em 0.5em 0.5em;
+}
+
+#altnavbar input {
+ height:0.3em;
+}
+
+
+#setlang{
+ vertical-align:middle;
+ display:inline;
+ margin-left:2em;
+}
+
+@media screen and (max-width: 740px) {
+ #altnavbar{
+ font-size:0.9em;
+ margin-left:30%;
+ }
+
+ #altnavbar input {
+ height:0.3em;
+ width:4em
+ }
+
+ header > details {
+ padding: 0.5em 0.5em 0.5em 0.5em;
+ }
+
+ #setlang {
+ display:block;
+ }
+}
+
+@media print{
+ #altnavbar,#setlang{display:none;}
+}
diff --git a/views/i18n/login.html b/views/i18n/login.html
new file mode 100644
index 0000000..e5ea195
--- /dev/null
+++ b/views/i18n/login.html
@@ -0,0 +1,12 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+{{ .LoginMsg }}
+<form action="/dologin" method="POST">
+ <p><input tabindex=1 type="text" name="username" autocomplete=off> -
+ {{ .i18n.Username }}
+ <p><input tabindex=1 type="password" name="password"> - {{ .i18n.Pwd }}
+ <p><button tabindex=1 name="login" value="login">{{ .i18n.Login }}</button>
+</form>
+</div>
+</main>
diff --git a/views/i18n/msg.html b/views/i18n/msg.html
new file mode 100644
index 0000000..b729caa
--- /dev/null
+++ b/views/i18n/msg.html
@@ -0,0 +1,7 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+<p>
+{{ .ServerMessage }}
+</div>
+</main>
diff --git a/views/i18n/onts.html b/views/i18n/onts.html
new file mode 100644
index 0000000..4a208c8
--- /dev/null
+++ b/views/i18n/onts.html
@@ -0,0 +1,16 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+<p>{{ .i18n.Onts }}
+{{ $letter := 0 }}
+<ul>
+{{ range .Onts }}
+{{ if not (eq $letter (index .Name 0)) }}
+{{ $letter = (index .Name 0) }}
+<li><p>
+{{ end }}
+<span style="white-space: nowrap;"><a href="/o/{{ .Name }}">#{{ .Name }}</a> ({{ .Count }})</span>
+{{ end }}
+</ul>
+</div>
+</main>
diff --git a/views/i18n/pleroma.css b/views/i18n/pleroma.css
new file mode 100644
index 0000000..b25439a
--- /dev/null
+++ b/views/i18n/pleroma.css
@@ -0,0 +1,7 @@
+html {
+ --bg-page: #1b2735;
+ --bg-dark: #121a24;
+ --fg: #b9b9ba;
+ --hl: #d8a070;
+ --fg-subtle: rgba(185, 185, 186, 0.5);
+}
diff --git a/views/i18n/style.css b/views/i18n/style.css
new file mode 100644
index 0000000..b1da23d
--- /dev/null
+++ b/views/i18n/style.css
@@ -0,0 +1,334 @@
+html {
+ --bg-page: #306;
+ --bg-dark: #002;
+ --fg: #dcf;
+ --hl: #dcf;
+ --fg-subtle: #a9c;
+ --fg-limited: #a79;
+}
+
+body {
+ background: var(--bg-page);
+ color: var(--fg);
+ font-size: 1em;
+ word-wrap: break-word;
+ font-family: sans-serif, "Noto Color Emoji";
+ line-height: 1.2;
+ overscroll-behavior-y: contain;
+}
+pre, code {
+ white-space: pre-wrap;
+}
+blockquote {
+ margin-left: 0em;
+ padding-left: 0.5em;
+ border-left: 1px solid var(--fg-subtle);
+}
+blockquote cite {
+ margin-left: 2em;
+}
+table {
+ display: block;
+ max-width: 100%;
+ overflow-x: auto;
+}
+a {
+ color: var(--fg);
+}
+form, input, textarea {
+ font-family: monospace, "Noto Color Emoji";
+}
+p {
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+input {
+ background: var(--bg-page);
+ color: var(--fg);
+ font-size: 1.0em;
+ line-height: 1.2em;
+ padding: 0.4em;
+}
+#honkform input {
+ font-size: 0.8em;
+}
+body > header {
+ margin: 1em auto;
+ font-size: 1.5em;
+}
+body > header span {
+ margin-left: 2em;
+}
+body > header p {
+ padding: 1em;
+}
+header > details {
+ background: var(--bg-page);
+ padding: 1em 1em 1em 1em;
+ position: fixed;
+ top: 0;
+ left: 0;
+ display: inline;
+ max-height: calc(100% - 1em);
+ overflow: auto;
+ opacity: 0.7;
+ overscroll-behavior: contain;
+}
+header > details[open] {
+ padding: 1em 1em 0em 1em;
+ background: var(--bg-dark);
+ border: 1px solid var(--hl);
+ margin-bottom: 1em;
+ opacity: 1.0;
+}
+header > details summary span {
+ display: none;
+}
+header > details[open] summary span {
+ display: inline;
+}
+header > details li {
+ margin: 1em 0em 1em 0em;
+}
+details summary {
+ display: inline;
+}
+@supports (-moz-appearance:none) {
+ details summary {
+ display: list-item;
+ }
+}
+main {
+ max-width: 1200px;
+ margin: auto;
+ font-size: 1.5em;
+}
+hr {
+ border-color: var(--hl);
+}
+.info {
+ background: var(--bg-dark);
+ border: 1px solid var(--hl);
+ margin-bottom: 1em;
+ padding: 0em 1em 0em 1em;
+}
+.info div {
+ margin-top: 1em;
+ margin-bottom: 1em;
+}
+label {
+ font-size: 0.8em;
+}
+label.button, button, select {
+ font-size: 16px;
+ font-family: monospace;
+ color: var(--fg);
+ background: var(--bg-page);
+ border: 1px solid var(--hl);
+ padding: 0.5em;
+ white-space: nowrap;
+}
+.buttonarray {
+ margin-top: -2.0em;
+}
+.buttonarray button, .buttonarray > span {
+ margin-top: 2.0em;
+ display: inline-block;
+}
+button a {
+ text-decoration: none;
+}
+form {
+ margin-top: 1em;
+}
+textarea {
+ padding: 0.5em;
+ font-size: 1em;
+ background: var(--bg-page);
+ color: var(--fg);
+ width: 600px;
+ height: 4em;
+ margin-bottom: 0.5em;
+ box-sizing: border-box;
+ max-width: 100%;
+}
+textarea#honknoise {
+ height: 10em;
+}
+input[type="checkbox"] {
+ position: fixed;
+ top: -9999px;
+}
+input[type="checkbox"] + span:after {
+ content: "no";
+}
+input[type="checkbox"]:checked + span:after {
+ content: "yes";
+}
+input[type="checkbox"]:focus + span:after {
+ outline: 1px solid var(--fg);
+}
+input[type=file] {
+ display: none;
+}
+
+.glow {
+ /* box-shadow: 0px 0px 16px var(--hl);*/
+ box-shadow: 0px 0px 16px black;
+}
+
+.honk {
+ margin: auto;
+ background: var(--bg-dark);
+ border: 1px solid var(--hl);
+ border-radius: 1em;
+ margin-bottom: 1em;
+ padding-left: 1em;
+ padding-right: 1em;
+ padding-top: 0;
+ overflow: hidden;
+}
+
+.chat {
+ border-bottom: 0.5px solid var(--fg-subtle);
+ padding-left: 1em;
+}
+.chat p {
+ margin-top: 0.2em;
+ margin-bottom: 0.2em;
+}
+.chattarget {
+ border-bottom: 1px solid var(--fg-subtle);
+}
+.chatstamp {
+ margin-left: -1em;
+}
+
+.honk #honkform {
+ padding: 1em;
+ border: 1px solid var(--fg);
+ }
+.honk a {
+ color: var(--fg);
+ }
+.honk header {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ font-size: 0.8em;
+ line-height: 1.1;
+ margin-top: 1em;
+ height: 64px;
+ }
+
+.honk header .clip a {
+ color: var(--fg-subtle);
+ }
+.honk header img {
+ float: left;
+ margin-right: 1em;
+ width: 64px;
+ height: 64px;
+ }
+.honk header p {
+ margin-top: 0px;
+ }
+.honk .actions button {
+ margin-left: 4em;
+ margin-top: 2em;
+ }
+.honk .noise {
+ line-height: 1.4;
+ }
+
+.honk .noise code .kw { font-weight: bold; }
+.honk .noise code .bi { font-weight: bold; }
+.honk .noise code .st { color: var(--fg-subtle); }
+.honk .noise code .nm { color: #ba88ff; }
+.honk .noise code .op { color: #ba88ff; }
+.honk .noise code .tp { font-weight: bold; }
+.honk .noise code .cm { color: var(--fg-subtle); font-style: italic; }
+.honk .noise code .al { color: #aaffbb; }
+.honk .noise code .dl { color: #ffaabb; }
+
+.honk details.actions summary {
+ color: var(--fg-subtle);
+}
+.subtle .noise {
+ color: var(--fg-subtle);
+ font-size: 0.8em;
+}
+.subtle .noise a {
+ color: var(--fg-subtle);
+}
+.limited {
+ border: 1px solid var(--fg-limited);
+ color: var(--fg-limited);
+}
+.limited .glow {
+ box-shadow: 0px 0px 16px var(--fg-limited);
+}
+.limited .noise {
+ color: var(--fg-limited);
+ }
+.limited .noise a {
+ color: var(--fg-limited);
+ }
+.limited details.actions summary {
+ color: var(--fg-limited);
+ }
+details.noise[open] summary {
+ display: none;
+}
+h1, h2 {
+ font-size: 1.2em;
+}
+h3, h4 {
+ font-size: 1.1em;
+}
+
+img:not(.emu) {
+ background: var(--bg-page);
+}
+img, video {
+ max-width: 100%;
+ max-height: 600px;
+}
+.noise img:not(.emu) {
+ display: block;
+}
+img.emu {
+ width: 2em;
+ height: 2em;
+ vertical-align: middle;
+ margin: -2px;
+ object-fit: contain;
+}
+@media screen and (max-width: 740px) {
+ body {
+ font-size: 12px;
+ }
+ .honk header {
+ height: 52px;
+ }
+ .honk header img {
+ width: 48px;
+ height: 48px;
+ }
+ details summary {
+ outline: none;
+ }
+}
+@media print {
+ #topmenu, #topspacer, #infobox, #refreshbox, .actions {
+ display: none;
+ }
+
+ html {
+ --bg-page: white;
+ --bg-dark: white;
+ --fg: black;
+ --fg-subtle: black;
+ --fg-limited: #a79;
+ }
+}
diff --git a/views/i18n/xzone.html b/views/i18n/xzone.html
new file mode 100644
index 0000000..e2ab0d7
--- /dev/null
+++ b/views/i18n/xzone.html
@@ -0,0 +1,17 @@
+{{ template "header.html" . }}
+<main>
+<div class="info">
+<form action="/ximport" method="POST">
+<input type="hidden" name="CSRF" value="{{ .XCSRF }}">
+<p><span class="title">{{ .i18n.Import }}</span>
+<p><input tabindex=1 type="text" name="xid" autocomplete=off> - xid
+<p><button tabindex=1 name="fetch" value="{{ .i18n.Fetch }}">{{ .i18n.Fetch }}</button>
+</form>
+</div>
+{{ $i18n := .i18n }}
+{{ range .Honkers }}
+<section class="honk">
+<p><a href="/h?xid={{ .XID }}">{{ $i18n.Honks }}</a> {{ $i18n.HonksFrom }} {{ .Handle }}
+</section>
+{{ end }}
+</main>
diff --git a/views/local.css b/views/local.css
new file mode 100644
index 0000000..680d300
--- /dev/null
+++ b/views/local.css
@@ -0,0 +1,8 @@
+#altnavbar{
+ margin-left:17%;
+ position:fixed;
+ background:var(--bg-page);
+ opacity:0.7;
+ top:0;
+ padding:5px
+}