diff options
Diffstat (limited to 'views')
-rw-r--r-- | views/css/themes/default.css | 249 | ||||
-rw-r--r-- | views/css/themes/gruvbox.css | 235 | ||||
-rw-r--r-- | views/index.html | 53 | ||||
-rw-r--r-- | views/js/footerscript.js | 44 | ||||
-rw-r--r-- | views/js/posts.js | 344 | ||||
-rw-r--r-- | views/js/themes.js | 40 | ||||
-rw-r--r-- | views/js/timer.js | 38 | ||||
-rw-r--r-- | views/layouts/main.html | 50 | ||||
-rw-r--r-- | views/npost.html | 66 | ||||
-rw-r--r-- | views/nposts.html | 70 | ||||
-rw-r--r-- | views/partials/bottom.html | 48 | ||||
-rw-r--r-- | views/partials/footer.html | 13 | ||||
-rw-r--r-- | views/partials/posts.html | 122 | ||||
-rw-r--r-- | views/partials/postscripts.html | 3 | ||||
-rw-r--r-- | views/partials/top.html | 128 |
15 files changed, 1503 insertions, 0 deletions
diff --git a/views/css/themes/default.css b/views/css/themes/default.css new file mode 100644 index 0000000..98edc29 --- /dev/null +++ b/views/css/themes/default.css @@ -0,0 +1,249 @@ +a, a:link, a:visited, a:hover, a:active { + text-decoration: none +} + +a:link, a:visited, a:active { + color: black; +} + +a:hover { + color: #de0808; +} + +body { + background-color: #eef2fe; + color: black; +} + +body.nsfw { + background-color: #ffffee; + color: #820404 +} + +h1, h2, h3, h4, h5, h6 { + color: #af0a0f; +} + +.popup-box { + border: 4px solid #d3caf0; + background-color: #eff5ff; +} + +.nsfw .popup-box { + border: 4px solid #f0e2d9; + background-color: #f9f9e0; +} + +.box { + background-color: #eff5ff; +} + +.nsfw .box { + background-color: #f9f9e0; +} + +.box-alt { + background-color: #d3caf0; +} + +.nsfw .box-alt { + background-color: #f0e2d9; +} + + +.quote { + color: #789922; +} + +.post { + background-color: #d5daf0; +} + +.nsfw .post { + background-color: #f0e0d6; +} + +:target > div > .post { + background-color: #d6bad0; +} + +.nsfw :target > div > .post { + background-color: #f0c0b0; +} + +.title { + color: #0f0c5d; +} + +.name, .tripcode { + color: #117743; +} + +a.reply { + color: #af0a0f; + text-decoration: 1px underline; +} + +.replyLink { + color: #000080; + font-size: 0.8em; +} + +#newpostbtn { + text-align: center; + margin-top: 80px; +} + +#postForm { + margin: auto; +} + +#postForm tr > td:first-child { + background-color: #98e; + border: 1px black; + padding-left: 0.5em; + padding-right: 0.5em; +} + +.nsfw #postForm tr > td:first-child { + background-color: #ea8; +} + +#postForm input[type="text"], +#postForm textarea, +#reply-name, #reply-options, #reply-comment { + box-sizing: border-box; + -webkit-box-sizing:border-box; + -moz-box-sizing: border-box; +} + +#postForm input[type="text"], +#postForm textarea, +#reply-name, #reply-options, #reply-comment { + box-sizing: border-box; + -webkit-box-sizing:border-box; + -moz-box-sizing: border-box; +} + +#reply-comment { + min-width: 300px; + width: 396px; + height: 200px; +} + +#reply-name { + width: 75%; + float: left; +} + +#reply-options { + width: 25%; + float: right; +} + +#reply-header { + display: inline-block; + width: 100%; + cursor: move; +} + +#postForm #captcha { + display: block; + width: 100%; +} + +.popup-box { + position: fixed; + min-width: 300px; + width: min-content; + z-index: 9; + display: block; +} + +/* TODO: rename */ +.box2 { + border: 4px solid #f0e2d9; + background-color: #f9f9e0; +} + +.newsbox { + padding: 25px; + border: 4px solid #f0e2d9; + background-color: #f9f9e0; +} + +.newsbox h2 { + margin: 0; + padding: 0; +} + +.newsbox-news { + text-align: left; + margin-top: 25px; + padding: 25px; +} + +.newsbox-news p, +.newsbox-news h3 { + margin: 0; +} + +#stopTablePost { + float: right; + display: none; +} + +#boardGrid { + display: grid; + grid-auto-columns: 1fr; + border: 4px solid #820404; + background-color: #f9f9e0; +} + +#boardGridHeader { + border-bottom: 2px solid #820404; + display: inline-grid; +} + +.boardGridCell { + white-space: nowrap; + display: inline-grid; + text-align: left; + padding: 5px; + border-top: 2px solid #820404; + border-left: 2px solid #820404; +} + +/* these may or may not work. my CSS is poor so i just kinda did stuff until it worked. */ +.boardGridCell:nth-child(-n+4) { + border-top: none; +} + +.boardGridCell:nth-child(3n+2) { + border-left: none; +} + +#threadfooter { + width: 100%; + table-layout: fixed; + border-collapse: collapse; +} + +#threadfooter td { + padding: 0; + margin: 0; +} + +#threadfooter #threadStats { + float: right; +} + +#navlinks, #boardlinks { + padding: 0; + margin: 0; +} + +#navlinks > li, +#boardlinks > li { + display: inline; +} diff --git a/views/css/themes/gruvbox.css b/views/css/themes/gruvbox.css new file mode 100644 index 0000000..8704482 --- /dev/null +++ b/views/css/themes/gruvbox.css @@ -0,0 +1,235 @@ +a, a:link, a:visited, a:active { + color: #b16286; + text-decoration: none +} + +a.reply { + color: #cc241d; + text-decoration: 1px underline; +} + +a:hover.reply { + color: #fb4934; +} + +body { + background: #282828; + color: #ebdbb2; + + font-family: monospace, sans-serif; + font-size: 0.9em; +} + +.popup-box { + border: 4px solid #928374; + background-color: #3c3836; +} + +.box, .box-alt { + background-color: #3c3836; +} + +.quote { + color: #98971a; +} + +.post { + background-color: #1d2021; +} + +:target > div > .post { + background-color: #504945; +} + +.subject { + color: #458588; +} + +.name { + color: #b8bb26; +} + +.tripcode { + color: #689d6a; +} + +h1,h2,h3,h4,h5,h6 { + color: #fb4934; + margin-bottom: 0.1em; +} + +.replyLink { + color: #83a598; + font-size: 0.8em; +} + +#newpostbtn { + text-align: center; + margin-top: 80px; +} + +input[type="text"] { + -webkit-appearance: none; + -webkit-border-radius: 0; +} + +#postForm { + border: 4px solid #928374; + background-color: #3c3836; + margin: auto; +} + +#postForm tr > td:first-child { + background-color: #504945; + padding-left: 0.5em; + padding-right: 0.5em; +} + +#postForm input[type="text"], +#postForm textarea, +#reply-name, #reply-options, #reply-comment { + background-color: #504945; + color: #ebdbb2; + border: 0; + border-bottom: 2px solid #3c3836; + font-family: monospace, sans-serif; + + box-sizing: border-box; + -webkit-box-sizing:border-box; + -moz-box-sizing: border-box; +} + +#postForm input[type="text"]:focus, +#postForm textarea:focus, +#reply-name:focus, #reply-options:focus, #reply-comment:focus { + outline: none; +} + +#reply-comment { + min-width: 300px; + width: 396px; + height: 200px; +} + +#reply-name { + width: 75%; + float: left; +} + +#reply-options { + width: 25%; + border-left: 2px solid #3c3836; + float: right; +} + +#reply-header { + display: inline-block; + width: 100%; + cursor: move; +} + +#postForm #captcha { + display: block; + width: 100%; +} + +.popup-box { + position: fixed; + min-width: 300px; + width: min-content; + z-index: 9; + display: block; +} + +/* TODO: rename */ +.box2 { + border: 4px solid #928374; + background-color: #3c3836; +} + +.newsbox { + padding: 25px; + border: 4px solid #928374; + background-color: #3c3836; +} + +.newsbox h2 { + margin: 0; + padding: 0; +} + +.newsbox-news { + text-align: left; + background-color: #504945; + margin-top: 25px; + padding: 25px; +} + +.newsbox-news p, +.newsbox-news h3 { + margin: 0; +} + +#stopTablePost { + float: right; + display: none; +} + +#boardGrid { + display: grid; + grid-auto-columns: 1fr; + border: 4px solid #928374; + background-color: #3c3836; +} + +#boardGridHeader { + border-bottom: 2px solid #928374; + display: inline-grid; +} + +.boardGridCell { + white-space: nowrap; + display: inline-grid; + text-align: left; + padding: 5px; + border-top: 2px solid #928374; + border-left: 2px solid #928374; +} + +/* these may or may not work. my CSS is poor so i just kinda did stuff until it worked. */ +.boardGridCell:nth-child(-n+4) { + border-top: none; +} + +.boardGridCell:nth-child(3n+2) { + border-left: none; +} + +#threadfooter { + width: 100%; + table-layout: fixed; + border-collapse: collapse; +} + +#threadfooter td { + padding: 0; + margin: 0; +} + +#threadfooter #threadStats { + float: right; +} + +#navlinks, #boardlinks { + padding: 0; + margin: 0; +} + +#navlinks > li, +#boardlinks > li { + display: inline; +} + +hr { + border: 1px solid #928374; +} diff --git a/views/index.html b/views/index.html new file mode 100644 index 0000000..f426d5d --- /dev/null +++ b/views/index.html @@ -0,0 +1,53 @@ +<div style="text-align: center; max-width: 800px; margin: 0 auto;"> + <h1>{{ .page.Title }}</h1> + <p style="text-align: justify">{{ .page.PreferredUsername }} is a federated image board based on <a href="https://activitypub.rocks/">ActivityPub</a>. The current version of the code running on the server is still a work-in-progress product, expect a bumpy ride for the time being. Get the server code here: <a href="https://github.com/FChannel0">https://github.com/FChannel0</a>.</p> + + {{ if .page.Boards }} + {{ $l := len .page.Boards }} + <div style="margin-top:50px;"> + <div id="boardGrid"> + {{ if lt $l 2 }} + <div id="boardGridHeader"><span style="font-size: 1.5em;font-weight: bold;">Local boards</span></div> + {{ else if eq $l 2 }} + <div id="boardGridHeader" style="grid-column: 1 / 3;"><span style="font-size: 1.5em;font-weight: bold;">Local boards</span></div> + {{ else }} + <div id="boardGridHeader" style="grid-column: 1 / 4;"><span style="font-size: 1.5em;font-weight: bold;">Local boards</span></div> + {{ end }} + {{ range .page.Boards }} + <div class="boardGridCell"><a href="{{.Location}}"><b>/{{.Name}}/</b> - {{.PrefName}} {{ if not .Restricted }} [NSFW] {{ end }}</a></div> + {{ end }} + {{ if gt $l 2 }} + {{ range .page.BoardRemainer }} + <div class="boardGridCell"></div> + {{ end }} + {{ end }} + </div> + </div> + {{ end }} + + {{ if .page.NewsItems }} + <div class="newsbox" style="margin-top:50px;"> + <h2><a href="/news">{{ .page.PreferredUsername }} News</a></h2> + {{ range $i, $e := .page.NewsItems }} + <div class="newsbox-news"> + <h3><a href="/news/{{.Time}}">{{unixtoreadable $e.Time}} - {{$e.Title}}</a>{{ if $.Board.ModCred }} <a href="/{{ $.Key }}/newsdelete/{{ $e.Time }}">[Delete] </a>{{end}}</h3> + <br> + + <p>{{$e.Content}}</p> + </div> + {{ end }} + </div> + {{ end }} + + <div class="box2" style="margin-top:50px;"> + <h4 style="margin-bottom:5px;">Current known instances</h4> + <span>(always use a proxy)</span> + <table style="text-align: left; margin: 25px;"> + {{ range .page.InstanceIndex }} + <tr> + <td><a href="{{ .Id }}">{{ .Id }}</a></td> + </tr> + {{ end }} + </table> + </div> +</div> diff --git a/views/js/footerscript.js b/views/js/footerscript.js new file mode 100644 index 0000000..69e56e7 --- /dev/null +++ b/views/js/footerscript.js @@ -0,0 +1,44 @@ +var imgs = document.querySelectorAll('#img'); +var imgArray = [].slice.call(imgs); + +imgArray.forEach(function(img, i){ + img.addEventListener("click", function(e){ + var id = img.getAttribute("id"); + var media = document.getElementById("media-" + id); + var sensitive = document.getElementById("sensitive-" + id); + + if(img.getAttribute("enlarge") == "0") + { + var attachment = img.getAttribute("attachment"); + img.setAttribute("enlarge", "1"); + img.setAttribute("style", "float: left; margin-right: 10px; cursor: pointer;"); + img.src = attachment; + } + else + { + var preview = img.getAttribute("preview"); + img.setAttribute("enlarge", "0"); + if(img.getAttribute("main") == 1) + { + img.setAttribute("style", "float: left; margin-right: 10px; max-width: 250px; max-height: 250px; cursor: pointer;"); + img.src = preview; + } + else + { + img.setAttribute("style", "float: left; margin-right: 10px; max-width: 125px; max-height: 125px; cursor: pointer;"); + img.src = preview; + } + } + }); +}); + + +function viewLink(board, actor) { + var posts = document.querySelectorAll('#view'); + var postsArray = [].slice.call(posts); + + postsArray.forEach(function(p, i){ + var id = p.getAttribute("post"); + p.href = "/" + board + "/" + shortURL(actor, id); + }); +} diff --git a/views/js/posts.js b/views/js/posts.js new file mode 100644 index 0000000..87f6228 --- /dev/null +++ b/views/js/posts.js @@ -0,0 +1,344 @@ +function startNewPost(){ + var el = document.getElementById("newpostbtn"); + el.style="display:none;"; + el.setAttribute("state", "1"); + document.getElementById("newpost").style = ""; + document.getElementById("stopTablePost").style = "display:unset;"; + sessionStorage.setItem("newpostState", true); +} + +function stopNewPost(){ + var el = document.getElementById("newpostbtn"); + el.style="display:block;margin-bottom:100px;"; + el.setAttribute("state", "0"); + document.getElementById("newpost").style = "display: none;"; + sessionStorage.setItem("newpostState", false); +} + +function shortURL(actorName, url) +{ + re = /.+\//g; + temp = re.exec(url); + + var output; + + if(stripTransferProtocol(temp[0]) == stripTransferProtocol(actorName) + "/") + { + var short = url.replace("https://", ""); + short = short.replace("http://", ""); + short = short.replace("www.", ""); + + var re = /^.{3}/g; + + var u = re.exec(short); + + re = /\w+$/g; + + output = re.exec(short); + }else{ + var short = url.replace("https://", ""); + short = short.replace("http://", ""); + short = short.replace("www.", ""); + + var re = /^.{3}/g; + + var u = re.exec(short); + + re = /\w+$/g; + + u = re.exec(short); + + str = short.replace(/\/+/g, " "); + + str = str.replace(u, " ").trim(); + + re = /(\w|[!@#$%^&*<>])+$/; + + v = re.exec(str); + + output = "f" + v[0] + "-" + u + } + + return output; +} + +function getBoardId(url) +{ + var re = /\/([^/\n]+)(.+)?/gm; + var matches = re.exec(url); + return matches[1]; +} + +function convertContent(actorName, content, opid) +{ + var re = /(>>)(https?:\/\/)?(www\.)?.+\/\w+/gm; + var match = content.match(re); + var newContent = content; + if(match) + { + match.forEach(function(quote, i){ + var link = quote.replace('>>', ''); + var isOP = ""; + if(link == opid) + { + isOP = " (OP)"; + } + + var q = link; + + if(document.getElementById(link + "-content") != null) { + q = document.getElementById(link + "-content").innerText; + q = q.replaceAll('>', '/\>'); + q = q.replaceAll('"', ''); + q = q.replaceAll("'", ""); + } + newContent = newContent.replace(quote, '<a class="reply" title="' + q + '" href="'+ (actorName) + "/" + shortURL(actorName, opid) + '#' + shortURL(actorName, link) + '";">>>' + shortURL(actorName, link) + isOP + '</a>'); + + }); + } + + re = /^(\s+)?>.+/gm; + + match = newContent.match(re); + if(match) + { + match.forEach(function(quote, i) { + + newContent = newContent.replace(quote, '<span class="quote">' + quote + '</span>'); + }); + } + + return newContent.replaceAll('/\>', '>'); +} + +function convertContentNoLink(actorName, content, opid) +{ + var re = /(>>)(https?:\/\/)?(www\.)?.+\/\w+/gm; + var match = content.match(re); + var newContent = content; + if(match) + { + match.forEach(function(quote, i){ + var link = quote.replace('>>', ''); + var isOP = ""; + if(link == opid) + { + isOP = " (OP)"; + } + + var q = link; + + if(document.getElementById(link + "-content") != null) { + q = document.getElementById(link + "-content").innerText; + } + + newContent = newContent.replace(quote, '>>' + shortURL(actorName, link) + isOP); + }); + } + newContent = newContent.replaceAll("'", ""); + return newContent.replaceAll('"', ''); +} + +function closeReply() +{ + document.getElementById("reply-box").style.display = "none"; + document.getElementById("reply-comment").value = ""; + + sessionStorage.setItem("element-closed-reply", true); +} + +function closeReport() +{ + document.getElementById("report-box").style.display = "none"; + document.getElementById("report-comment").value = ""; + + sessionStorage.setItem("element-closed-report", true); +} + +function quote(actorName, opid, id) +{ + sessionStorage.setItem("element-closed-reply", false); + var box = document.getElementById("reply-box"); + var header = document.getElementById("reply-header"); + var header_text = document.getElementById("reply-header-text"); + var comment = document.getElementById("reply-comment"); + var inReplyTo = document.getElementById("inReplyTo-box"); + + var w = window.innerWidth / 2 - 200; + var h = 300; //document.getElementById(id + "-content").offsetTop - 348; + + const boxStyle = "top: " + h + "px; left: " + w + "px;"; + box.setAttribute("style", boxStyle); + sessionStorage.setItem("element-reply-style", boxStyle); + sessionStorage.setItem("reply-top", h); + sessionStorage.setItem("reply-left", w); + + + if (inReplyTo.value != opid) + comment.value = ""; + + header_text.innerText = "Replying to Thread No. " + shortURL(actorName, opid); + inReplyTo.value = opid; + sessionStorage.setItem("element-reply-actor", actorName); + sessionStorage.setItem("element-reply-id", inReplyTo.value); + + if(id != "reply") + comment.value += ">>" + id + "\n"; + sessionStorage.setItem("element-reply-comment", comment.value); + + dragElement(header); +} + +function report(actorName, id) +{ + sessionStorage.setItem("element-closed-report", false); + var box = document.getElementById("report-box"); + var header = document.getElementById("report-header"); + var comment = document.getElementById("report-comment"); + var inReplyTo = document.getElementById("report-inReplyTo-box"); + + var w = window.innerWidth / 2 - 200; + var h = 300; //document.getElementById(id + "-content").offsetTop - 348; + + const boxStyle = "top: " + h + "px; left: " + w + "px;"; + box.setAttribute("style", boxStyle); + sessionStorage.setItem("element-report-style", boxStyle); + sessionStorage.setItem("report-top", h); + sessionStorage.setItem("report-left", w); + + header.innerText = "Report Post No. " + shortURL(actorName, id); + inReplyTo.value = id; + sessionStorage.setItem("element-report-actor", actorName); + sessionStorage.setItem("element-report-id", id); + + dragElement(header); +} + +var pos1, pos2, pos3, pos4; +var elmnt; + +function closeDragElement(e) { + // stop moving when mouse button is released: + document.onmouseup = null; + document.onmousemove = null; + sessionStorage.setItem("eventhandler", false); +} + +function elementDrag(e) { + e = e || window.event; + e.preventDefault(); + // calculate the new cursor position: + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + sessionStorage.setItem("pos1", pos1); + sessionStorage.setItem("pos2", pos2); + sessionStorage.setItem("pos3", pos3); + sessionStorage.setItem("pos4", pos4); + + // set the element's new position: + elmnt.parentElement.style.top = (elmnt.parentElement.offsetTop - pos2) + "px"; + elmnt.parentElement.style.left = (elmnt.parentElement.offsetLeft - pos1) + "px"; + if(elmnt.id.startsWith("report")){ + sessionStorage.setItem("report-top", elmnt.parentElement.style.top); + sessionStorage.setItem("report-left", elmnt.parentElement.style.left); + }else if(elmnt.id.startsWith("reply")){ + sessionStorage.setItem("reply-top", elmnt.parentElement.style.top); + sessionStorage.setItem("reply-left", elmnt.parentElement.style.left); + } +} + +function dragMouseDown(e) { + e = e || window.event; + e.preventDefault(); + + // get the mouse cursor position at startup: + pos3 = e.clientX; + pos4 = e.clientY; + sessionStorage.setItem("pos3", pos3); + sessionStorage.setItem("pos4", pos4); + + elmnt = e.currentTarget; + + // call a function whenever the cursor moves: + document.onmouseup = closeDragElement; + document.onmousemove = elementDrag; + sessionStorage.setItem("eventhandler", true); + +} + +function dragElement(elmnt) { + elmnt.onmousedown = dragMouseDown; +} + +const stateLoadHandler = function(event){ + pos1 = parseInt(sessionStorage.getItem("pos1")); + pos2 = parseInt(sessionStorage.getItem("pos2")); + pos3 = parseInt(sessionStorage.getItem("pos3")); + pos4 = parseInt(sessionStorage.getItem("pos4")); + + if(sessionStorage.getItem("element-closed-report") === "false"){ + var box = document.getElementById("report-box"); + var header = document.getElementById("report-header"); + var comment = document.getElementById("report-comment"); + var inReplyTo = document.getElementById("report-inReplyTo-box"); + + header.onmousedown = dragMouseDown; + inReplyTo.value = parseInt(sessionStorage.getItem("element-report-id")); + header.innerText = "Report Post No. " + shortURL(sessionStorage.getItem("element-report-actor"), sessionStorage.getItem("element-report-id")); + comment.value = sessionStorage.getItem("element-report-comment"); + + box.setAttribute("style", sessionStorage.getItem("element-report-style")); + + box.style.top = sessionStorage.getItem("report-top"); + box.style.left = sessionStorage.getItem("report-left"); + + if(sessionStorage.getItem("eventhandler") === "true"){ + elmnt = header; + document.onmouseup = closeDragElement; + document.onmousemove = elementDrag; + }else{ + document.onmouseup = null; + document.onmousemove = null; + } + } + if(sessionStorage.getItem("element-closed-reply") === "false"){ + var box = document.getElementById("reply-box"); + var header = document.getElementById("reply-header"); + var header_text = document.getElementById("reply-header-text"); + var comment = document.getElementById("reply-comment"); + var inReplyTo = document.getElementById("inReplyTo-box"); + + header.onmousedown = dragMouseDown; + inReplyTo.value = parseInt(sessionStorage.getItem("element-reply-id")); + header_text.innerText = "Replying to Thread No. " + shortURL(sessionStorage.getItem("element-reply-actor"), sessionStorage.getItem("element-reply-id")); + comment.value = sessionStorage.getItem("element-reply-comment"); + + pos1 = parseInt(sessionStorage.getItem("pos1")); + pos2 = parseInt(sessionStorage.getItem("pos2")); + pos3 = parseInt(sessionStorage.getItem("pos3")); + pos4 = parseInt(sessionStorage.getItem("pos4")); + + box.setAttribute("style", sessionStorage.getItem("element-reply-style")); + + box.style.top = sessionStorage.getItem("reply-top"); + box.style.left = sessionStorage.getItem("reply-left"); + + if(sessionStorage.getItem("eventhandler") === "true"){ + elmnt = header; + document.onmouseup = closeDragElement; + document.onmousemove = elementDrag; + }else{ + document.onmouseup = null; + document.onmousemove = null; + } + } +}; + +document.addEventListener("DOMContentLoaded", stateLoadHandler, false); + +function stripTransferProtocol(value){ + var re = /(https:\/\/|http:\/\/)?(www.)?/; + return value.replace(re, ""); +} diff --git a/views/js/themes.js b/views/js/themes.js new file mode 100644 index 0000000..ccdd277 --- /dev/null +++ b/views/js/themes.js @@ -0,0 +1,40 @@ +function setCookie(key, value, age) { + document.cookie = key + "=" + encodeURIComponent(value) + ";sameSite=strict;max-age=" + (60 * 60 * 24 * age) + ";path=/"; +} + +function getCookie(key) { + if (document.cookie.length != 0) { + return document.cookie.split('; ').find(row => row.startsWith(key)).split('=')[1]; + } + return ""; +} + +function setTheme(name) { + for (let i = 0, tags = document.getElementsByTagName("link"); i < tags.length; i++) { + if (tags[i].type === "text/css" && tags[i].title) { + tags[i].disabled = !(tags[i].title === name); + } + } + + setCookie("theme", name, 3650); +} + +function applyTheme() { + // HACK: disable all of the themes first. this for some reason makes things work. + for (let i = 0, tags = document.getElementsByTagName("link"); i < tags.length; i++) { + if (tags[i].type === "text/css" && tags[i].title) { + tags[i].disabled = true; + } + } + let theme = getCookie("theme") || "default"; + setTheme(theme); + + // reflect this in the switcher + let switcher = document.getElementById("themeSwitcher"); + for(var i = 0; i < switcher.options.length; i++) { + if (switcher.options[i].value === theme) { + switcher.selectedIndex = i; + break; + } + } +} diff --git a/views/js/timer.js b/views/js/timer.js new file mode 100644 index 0000000..d8be9de --- /dev/null +++ b/views/js/timer.js @@ -0,0 +1,38 @@ +var timerCount; +var timerToggle = false; +var timer; +const contentLoadHandler = function(event){ + timerToggle = !!document.getElementById("autoreload-checkbox").checked; + if(timerToggle){ + timerCount = 45; + document.getElementById("autoreload-countdown").innerHTML = "45"; + document.getElementById("autoreload-countdown").style.visibility = "visible"; + timer = setInterval(timerFunction, 1000); + document.removeEventListener("DOMContentLoaded", contentLoadHandler, false); + } +}; + +document.addEventListener("DOMContentLoaded", contentLoadHandler, false); + +function timerFunction(){ + timerCount--; + document.getElementById("autoreload-countdown").innerHTML = timerCount; + if(timerCount <= 0){ + document.getElementById("autoreload-countdown").innerHTML = "Refreshing..."; + clearInterval(timer); + location.reload(); + } +} + +function autoTimer(){ + timerToggle = !timerToggle; + if(timerToggle === true){ + timerCount = 45; + document.getElementById("autoreload-countdown").innerHTML = "45"; + document.getElementById("autoreload-countdown").style.visibility = "visible"; + timer = setInterval(timerFunction, 1000); + }else{ + clearInterval(timer); + document.getElementById("autoreload-countdown").style.visibility = "hidden"; + } +} diff --git a/views/layouts/main.html b/views/layouts/main.html new file mode 100644 index 0000000..4cd48f9 --- /dev/null +++ b/views/layouts/main.html @@ -0,0 +1,50 @@ +<!DOCTYPE html> +<html> + <head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="keywords" content="Federated Imageboard based on Activtypub"> + <meta property="og:locale" content="en_US" /> + <meta property="og:type" content="website" /> + <link rel="icon" type="image/png" href="/static/favicon.png"> + {{ if gt (len .page.ThemeCookie) 0 }} + <link rel="stylesheet" type="text/css" href="/static/css/themes/{{.page.ThemeCookie}}.css" title="selected theme"> + {{ else }} + <link rel="stylesheet" type="text/css" href="/static/css/themes/default.css" title="default"> + {{ end }} + {{ range .page.Themes }} + <link rel="alternate stylesheet" type="text/css" href="/static/css/themes/{{.}}.css" title="{{.}}" disabled> + {{ end }} + + {{ template "header" .page }} + </head> + <body {{ if not .page.Board.Restricted }}class="nsfw"{{ end }} onload="applyTheme()"> + <ul id="boardlinks"> + {{ $l := len .page.Boards }} + <li>[<a href="/">Home</a>]</li> + {{range $i, $e := .page.Boards}} + {{ if eq (sub $l 1) 0 }} + <li>[ <a href="{{.Location}}">{{$e.Name}} </a>]</li> + {{ else if eq $i 0 }} + <li>[<a href="{{.Location}}">{{$e.Name}} </a>/</li> + {{ else if eq $i (sub $l 1) }} + <li><a href="{{.Location}}">{{$e.Name}}</a>]</li> + {{ else }} + <li><a href="{{.Location}}">{{$e.Name}} </a>/</li> + {{ end }} + {{ end }} + </ul> + {{ if .page.Board.ModCred }} + {{ if eq .page.Board.ModCred .page.Board.Domain .page.Board.Actor.Id }} + <span style="float: right;">[<a href="/{{ .page.Key }}/{{ .page.Board.Name }}">Manage Board</a>]</span> + {{ end }} + {{ end }} + + {{ embed }} + + {{ template "partials/footer" .page }} + + <script src="/static/js/themes.js"></script> + {{ template "scripts" .page }} + </body> +</html> diff --git a/views/npost.html b/views/npost.html new file mode 100644 index 0000000..5a382ed --- /dev/null +++ b/views/npost.html @@ -0,0 +1,66 @@ +{{ define "header" }} +<title>/{{ .Board.Name }}/ - {{ .PostId }}</title> +<meta name="description" content="{{ (index .Posts 0).Content }}"> +<meta property="og:url" content="{{ (index .Posts 0).Id }}"> +<meta property="og:site_name" content="{{ .Instance.PreferredUsername }}" /> + +<meta property="og:title" content="{{ (index .Posts 0).Name }}"> +<meta property="og:description" content="{{ (index .Posts 0).Content }}"> + +<meta name="twitter:title" content="{{ (index .Posts 0).Name }}"> +<meta name="twitter:description" content="{{ (index .Posts 0).Content }}"> +<meta name="twitter:card" content="summary_large_image"> + +{{ if (index .Posts 0).Preview }} +<meta property="og:image" content="{{ (index .Posts 0).Preview.Href }}" /> +<meta name="twitter:image" content="{{ (index .Posts 0).Preview.Href }}" /> +{{ end }} +{{ end }} + +{{ template "partials/top" .page }} + +{{ $board := .Board }} + +<hr> +<ul id="navlinks"> + <li>[<a href="/{{ $board.Name }}">Return</a>]</li> + <li>[<a href="/{{ $board.Name }}/catalog">Catalog</a>]</li> + <li>[<a href="#bottom">Bottom</a>]</li> + <li>[<a href="javascript:location.reload()">Refresh</a>]</li> +</ul> +<hr> + +{{ template "partials/posts" .page }} + +<hr> + +<table id="threadfooter"><tr> + <td> + <ul id="navlinks"> + <li>[<a href="/{{ $board.Name }}">Return</a>]</li> + <li>[<a href="/{{ $board.Name }}/catalog">Catalog</a>]</li> + <li>[<a id="bottom" href="#top">Top</a>]</li> + <li>[<a href="javascript:location.reload()">Refresh</a>]</li> + <li><input id="autoreload-checkbox" type="checkbox" onclick="autoTimer()"> Auto refresh <span id="autoreload-countdown" style="visibility: hidden;">0</span></li> + </ul> + </td> + + {{ if eq (index .page.Posts 0).Type "Note" }} + <td style="text-align: center;"> + <span>[<a id="reply-content" href="javascript:quote('{{ $board.Actor.Id }}', '{{ (index .page.Posts 0).Id }}', 'reply')">Post a Reply</a>]</span> + </td> + {{ end }} + + <td> + {{ $replies := (index .page.Posts 0).Replies }} + <span id="threadStats" data-total="{{ $replies.TotalItems }}" data-imgs="{{ $replies.TotalImgs }}">{{ $replies.TotalItems }} / {{ $replies.TotalImgs }}</span> + </td> +</tr></table> + +<hr> + +{{ template "partials/bottom" .page }} + +{{ define "scripts" }} +{{ template "partials/postscripts" . }} +{{ end }} diff --git a/views/nposts.html b/views/nposts.html new file mode 100644 index 0000000..ae5a9b6 --- /dev/null +++ b/views/nposts.html @@ -0,0 +1,70 @@ +{{ define "header" }} +<title>{{ .Title }}</title> +<meta name="description" content="{{ .Board.Summary }}"> +<meta property="og:url" content="{{ .Board.Actor.Id }}"> +<meta property="og:site_name" content="{{ .Instance.PreferredUsername }}" /> + +<meta property="og:title" content="{{ .Title }}"> +<meta property="og:description" content="{{ .Board.Summary }}"> + +<meta name="twitter:title" content="{{ .Title }}"> +<meta name="twitter:description" content="{{ .Board.Summary }}"> +<meta name="twitter:card" content="summary_large_image"> +{{ end }} + +{{ template "partials/top" .page }} + +{{ $board := .page.Board }} +<hr> +<ul id="navlinks"> + <li>[<a href="/{{ $board.Name }}/catalog">Catalog</a>]</li> + <!-- TODO: showArchive function needs to be fixed + \{\{ if showArchive }} + <li>[<a href="/{{ $board.Name }}/archive">Archive</a>]</li> + \{\{ end }} + --> + <li>[<a href="#bottom">Bottom</a>]</li> + <li>[<a href="javascript:location.reload()">Refresh</a>]</li> +</ul> + +{{ template "partials/posts" .page }} + +<hr> + +<ul id="navlinks"> + <li>[<a href="/{{ $board.Name }}/catalog">Catalog</a>]</li> + <!-- TODO: showArchive function needs to be fixed + \{\{ if showArchive }} + <li>[<a href="/{{ $board.Name }}/archive">Archive</a>]</li> + \{\{ end }} + --> + <li>[<a href="#top" id="bottom">Top</a>]</li> + <li>[<a href="javascript:location.reload()">Refresh</a>]</li> +</ul> + +<hr> +{{ if gt .page.TotalPage 0 }} +{{ $totalPage := .page.TotalPage }} +<ul style="float: right; margin: 0; padding: 0; display: inline"> + {{ $page := .page.CurrentPage }} + {{ if gt $page 0 }} + <li style="display: inline">[<a href="/{{ $board.Name }}?page={{ sub $page 1 }}"><</a>]</li> + {{ end }} + {{ range $i, $e := .page.Pages }} + {{ if eq $i $page}} + <li style="display: inline">[<a href="/{{ $board.Name }}?page={{ $i }}"><b>{{ $i }}</b></a>]</li> + {{ else }} + <li style="display: inline">[<a href="/{{ $board.Name }}?page={{ $i }}">{{ $i }}</a>]</li> + {{ end }} + {{ end }} + {{ if lt .page.CurrentPage .page.TotalPage }} + <li style="display: inline">[<a href="/{{ $board.Name }}?page={{ add $page 1 }}">></a>]</li> + {{ end }} +</ul> +{{ end }} + +{{ template "partials/bottom" .page }} + +{{ define "scripts" }} +{{ template "partials/postscripts" . }} +{{ end }} diff --git a/views/partials/bottom.html b/views/partials/bottom.html new file mode 100644 index 0000000..35e8c4a --- /dev/null +++ b/views/partials/bottom.html @@ -0,0 +1,48 @@ +<div id="reply-box" class="popup-box" style="display: none;"> + <div id="reply-header"> + <span id="reply-header-text">...</span> + <div id="reply-close" style="display: inline-block; float: right;"><a href="javascript:closeReply()">[X]</a></div> + </div> + <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="reply-post" action="/post" method="post" enctype="multipart/form-data"> + <input id="reply-name" name="name" type="text" placeholder="Name" maxlength="100"> + <input id="reply-options" name="options" type="text" placeholder="Options" maxlength="100"> + <textarea id="reply-comment" name="comment" maxlength="2000" oninput="sessionStorage.setItem('element-reply-comment', document.getElementById('reply-comment').value)"></textarea> + <input id="reply-file" name="file" type="file"> + <input id="reply-submit" type="submit" value="Reply" style="float: right;"> + <input type="hidden" id="inReplyTo-box" name="inReplyTo" value="{{ .Board.InReplyTo }}"> + <input type="hidden" id="sendTo" name="sendTo" value="{{ .Board.To }}"> + <input type="hidden" id="boardName" name="boardName" value="{{ .Board.Name }}"> + <input type="hidden" id="captchaCode" name="captchaCode" value="{{ .Board.CaptchaCode }}"> + <input type="hidden" id="returnTo" name="returnTo" value="{{ .ReturnTo }}"><br> + <input type="checkbox" name="sensitive"><span>Mark attachment as sensitive</span><br> + <div style="width: 202px; margin: 0 auto; padding-top: 12px;"> + <label for="captcha">Captcha:</label><br> + <input style="display: inline-block;" type="text" id="captcha" name="captcha" autocomplete="off"><br> + </div> + <div style="width: 230px; margin: 0 auto;"> + <img src="{{ .Board.Captcha }}"> + </div> + </form> +</div> + +<div id="report-box" class="popup-box" style="display: none;"> + <div id="report-header" style="text-align: center; display: inline-block; z-index: 0; cursor: move;"></div><div id="report-close" style="display: inline-block; float: right;"><a href="javascript:closeReport()">[X]</a></div> + <form onsubmit="sessionStorage.setItem('element-closed-report', true)" id="report-post" action="/report" method="post"> + <label for="comment">Reason:</label><br> + <textarea id="report-comment" name="comment" rows="12" cols="54" style="width: 396px;" maxlength="100" oninput="sessionStorage.setItem('element-report-comment', document.getElementById('report-comment').value)"></textarea> + <br> + <input id="report-submit" type="submit" value="Report" style="float: right;"> + <input type="hidden" id="report-inReplyTo-box" name="id" value="{{ .Board.InReplyTo }}"> + <input type="hidden" id="sendTo" name="sendTo" value="{{ .Board.To }}"> + <input type="hidden" id="boardName" name="board" value="{{ .Board.Name }}"> + <input type="hidden" name="close" value="0"> + <input type="hidden" id="captchaCode" name="captchaCode" value="{{ .Board.CaptchaCode }}"> + <div style="width: 202px; margin: 0 auto; padding-top: 12px;"> + <label for="captcha">Captcha:</label><br> + <input style="display: inline-block;" type="text" id="captcha" name="captcha" autocomplete="off"><br> + </div> + <div style="width: 230px; margin: 0 auto;"> + <img src="{{ .Board.Captcha }}"> + </div> + </form> +</div> diff --git a/views/partials/footer.html b/views/partials/footer.html new file mode 100644 index 0000000..efbde4f --- /dev/null +++ b/views/partials/footer.html @@ -0,0 +1,13 @@ +<div align="center" style="width: 500px; margin:0 auto; margin-top: 50px;"> + [<a href="/">Home</a>] [<a href="/static/rules.html">Rules</a>] [<a href="/static/faq.html">FAQ</a>] + <p>All trademarks and copyrights on this page are owned by their respective parties.</p> +</div> + +<div style="float: right; margin-bottom: 25px;"> + Theme: + <select id="themeSwitcher" onchange="setTheme(this.options[this.selectedIndex].value)"> + {{ range .Themes }} + <option value="{{.}}">{{.}}</option> + {{ end }} + </select> +</div> diff --git a/views/partials/posts.html b/views/partials/posts.html new file mode 100644 index 0000000..f82d2d5 --- /dev/null +++ b/views/partials/posts.html @@ -0,0 +1,122 @@ +{{ $board := .Board }} +{{ $len := len .Posts }} +{{ range .Posts }} +{{ $thread := . }} +{{ $opId := .Id }} +{{ if eq $board.InReplyTo "" }} +<hr> +{{ end }} +<div style="overflow: auto;"> + <div id="{{ short $board.Actor.Outbox .Id }}" style="overflow: visible; margin-bottom: 12px;"> + {{ if eq $board.ModCred $board.Domain $board.Actor.Id }} + [<a href="/delete?id={{ .Id }}&board={{ $board.Actor.Name }}">Delete Post</a>] + {{ end }} + {{ if .Attachment }} + {{ if eq $board.ModCred $board.Domain $board.Actor.Id }} + [<a href="/banmedia?id={{ .Id }}&board={{ $board.Actor.Name }}">Ban Media</a>] + [<a href="/deleteattach?id={{ .Id }}&board={{ $board.Actor.Name }}">Delete Attachment</a>] + [<a href="/marksensitive?id={{ .Id }}&board={{ $board.Actor.Name }}">Mark Sensitive</a>] + {{ end }} + <span style="display: block;">File: <a id="{{ .Id }}-img" href="{{ proxy (index .Attachment 0).Href}}">{{ shortImg (index .Attachment 0).Name }}</a><span id="{{ .Id }}-size"> ({{ convertSize (index .Attachment 0).Size }})</span></span> + <div id="hide-{{ .Id }}" style="display: none;">[Hide]</div> + <div id="sensitive-{{ .Id }}" style="display: none;"><div style="position: relative; text-align: center;"><img id="sensitive-img-{{ .Id }}" style="float: left; margin-right: 10px; margin-bottom: 10px; max-width: 250px; max-height: 250px;" src="/static/sensitive.png"><div id="sensitive-text-{{ .Id }}" style="width: 240px; position: absolute; margin-top: 110px; padding: 5px; background-color: black; color: white; cursor: default; ">NSFW Content</div></div></div> + <div id="media-{{ .Id }}">{{ parseAttachment . false }}</div> + <script> + media = document.getElementById("media-{{ .Id }}") + if(({{ .Sensitive }} && {{ $board.Actor.Restricted }}) || ({{ isOnion .Id }} && !{{ isOnion $board.Domain }})){ + sensitive = document.getElementById("sensitive-{{ .Id }}") + hide = document.getElementById("hide-{{ .Id }}") + sensitive.onclick = function(){document.getElementById("media-{{ .Id }}").style="display: block;"; document.getElementById("sensitive-{{ .Id }}").style="display: none;"; document.getElementById("hide-{{ .Id }}").style="display: block; cursor: pointer;"} + hide.onclick = function(){document.getElementById("media-{{ .Id }}").style="display: none;"; document.getElementById("sensitive-{{ .Id }}").style="display: block;"; document.getElementById("hide-{{ .Id }}").style="display: none;"} + sensitive.style = "display: block" + media.style = "display: none;" + } + + if({{ isOnion .Id }} && !{{ isOnion $board.Domain }}) { + sensitive = document.getElementById("sensitive-{{ .Id }}") + document.getElementById("sensitive-img-{{ .Id }}").src = "/static/onion.png" + document.getElementById("sensitive-text-{{ .Id }}").innerText = "Tor Instance" + hide = document.getElementById("hide-{{ .Id }}") + sensitive.onclick = function(){document.getElementById("media-{{ .Id }}").style="display: block;"; document.getElementById("sensitive-{{ .Id }}").style="display: none;"; document.getElementById("hide-{{ .Id }}").style="display: block; cursor: pointer;"} + hide.onclick = function(){document.getElementById("media-{{ .Id }}").style="display: none;"; document.getElementById("sensitive-{{ .Id }}").style="display: block;"; document.getElementById("hide-{{ .Id }}").style="display: none;"} + sensitive.style = "display: block" + media.style = "display: none;" + } + </script> + {{ end }} + <span class="subject"><b>{{ .Name }}</b></span> + <span class="name"><b>{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }}</b></span> + <span class="tripcode"> {{ .TripCode }} </span> + <span class="timestamp" data-utc="{{.Published | timeToUnix}}">{{ .Published | timeToReadableLong }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ short $board.Actor.Outbox $opId }}#{{ short $board.Actor.Outbox .Id }}">No.</a> <a id="{{ .Id }}-link" title="{{ .Id }}" {{ if eq .Type "Note" }} href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')" {{ end }}>{{ short $board.Actor.Outbox .Id }}</a> {{ if ne .Type "Tombstone" }}[<a href="javascript:report('{{ $board.Actor.Id }}', '{{ .Id }}')">Report</a>]{{ end }}</span> + <p id="{{ .Id }}-content" style="white-space: pre-wrap; margin: 10px 30px 10px 30px;">{{ parseContent $board.Actor $opId .Content $thread }}</p> + {{ if .Replies }} + {{ $replies := .Replies }} + {{ if gt $replies.TotalItems 5 }} + {{ if gt $len 1 }} + <span>{{ $replies.TotalItems }} replies{{ if gt $replies.TotalImgs 0}} and {{ $replies.TotalImgs }} images{{ end }}, Click <a id="view" post="{{.Id}}" href="/{{ $board.Name }}/{{ short $board.Actor.Outbox .Id }}">here</a> to view all.</span> + {{ end }} + {{ end }} + {{ range $replies.OrderedItems }} + <div id="{{ short $board.Actor.Outbox .Id }}"> + <div style="display: inline-block; overflow: auto;"> + <div style="float: left; display: block; margin-right: 5px;">>></div> + <div class="post" style="overflow: auto; padding: 5px; margin-bottom: 2px;"> + {{ if eq $board.ModCred $board.Domain $board.Actor.Id }} + [<a href="/delete?id={{ .Id }}&board={{ $board.Actor.Name }}">Delete Post</a>] + {{ end }} + {{ if .Attachment }} + {{ if eq $board.ModCred $board.Domain $board.Actor.Id }} + [<a href="/banmedia?id={{ .Id }}&board={{ $board.Actor.Name }}">Ban Media</a>] + [<a href="/deleteattach?id={{ .Id }}&board={{ $board.Actor.Name }}">Delete Attachment</a>] + [<a href="/marksensitive?id={{ .Id }}&board={{ $board.Actor.Name }}">Mark Sensitive</a>] + {{ end }} + <span style="display: block;">File <a id="{{ .Id }}-img" href="{{ proxy (index .Attachment 0).Href}}">{{ shortImg (index .Attachment 0).Name }}</a> <span id="{{ .Id }}-size">({{ convertSize (index .Attachment 0).Size }})</span></span> + <div id="hide-{{ .Id }}" style="display: none;">[Hide]</div> + <div id="sensitive-{{ .Id }}" style="display: none;"><div style="position: relative; text-align: center;"><img id="sensitive-img-{{ .Id }}" style="float: left; margin-right: 10px; margin-bottom: 10px; max-width: 250px; max-height: 250px;" src="/static/sensitive.png"><div id="sensitive-text-{{ .Id }}" style="width: 240px; position: absolute; margin-top: 110px; padding: 5px; background-color: black; color: white; cursor: default; ">NSFW Content</div></div></div> + <div> </div> + <div id="media-{{ .Id }}" sensitive="0">{{ parseAttachment . false }}</div> + <script> + media = document.getElementById("media-{{ .Id }}") + + if(({{ .Sensitive }} && {{ $board.Actor.Restricted }}) || {{ isOnion .Id }} && !{{ isOnion $board.Domain }}){ + sensitive = document.getElementById("sensitive-{{ .Id }}") + hide = document.getElementById("hide-{{ .Id }}") + sensitive.onclick = function(){document.getElementById("media-{{ .Id }}").style="display: block;"; document.getElementById("sensitive-{{ .Id }}").style="display: none;"; document.getElementById("hide-{{ .Id }}").style="display: block; cursor: pointer;"} + hide.onclick = function(){document.getElementById("media-{{ .Id }}").style="display: none;"; document.getElementById("sensitive-{{ .Id }}").style="display: block;"; document.getElementById("hide-{{ .Id }}").style="display: none;"} + sensitive.style = "display: block" + media.style = "display: none;" + } + + if({{ isOnion .Id }} && !{{ isOnion $board.Domain }}) { + sensitive = document.getElementById("sensitive-{{ .Id }}") + document.getElementById("sensitive-img-{{ .Id }}").src = "/static/onion.png" + document.getElementById("sensitive-text-{{ .Id }}").innerText = "Tor Instance" + hide = document.getElementById("hide-{{ .Id }}") + sensitive.onclick = function(){document.getElementById("media-{{ .Id }}").style="display: block;"; document.getElementById("sensitive-{{ .Id }}").style="display: none;"; document.getElementById("hide-{{ .Id }}").style="display: block; cursor: pointer;"} + hide.onclick = function(){document.getElementById("media-{{ .Id }}").style="display: none;"; document.getElementById("sensitive-{{ .Id }}").style="display: block;"; document.getElementById("hide-{{ .Id }}").style="display: none;"} + sensitive.style = "display: block" + media.style = "display: none;" + } + </script> + {{ end }} + <span class="subject"><b>{{ .Name }}</b></span> + <span class="name"><b>{{ if .AttributedTo }} {{.AttributedTo }} {{ else }} Anonymous {{ end }}</b></span> + <span class="tripcode"> {{ .TripCode }} </span> + <span class="timestamp" data-utc="{{ .Published | timeToUnix }}">{{ .Published | timeToReadableLong }} <a id="{{ .Id }}-anchor" href="/{{ $board.Name }}/{{ short $board.Actor.Outbox $opId }}#{{ short $board.Actor.Outbox .Id }}">No. </a><a id="{{ .Id }}-link" title="{{ .Id }}" {{ if eq .Type "Note" }} href="javascript:quote('{{ $board.Actor.Id }}', '{{ $opId }}', '{{ .Id }}')" {{ end }}>{{ short $board.Actor.Outbox .Id }}</a> {{ if ne .Type "Tombstone" }}[<a href="javascript:report('{{ $board.Actor.Id }}', '{{ .Id }}')">Report</a>]{{ end }}</span> + {{ $parentId := .Id }} + {{ if .Replies.OrderedItems }} + {{ range .Replies.OrderedItems }} + + <!--TODO: fix parse reply link function with other routes mainly getactor--> + <span id="{{$parentId}}-replyto-{{.Id}}"><!-- fix \{\{ parseReplyLink $board.Actor.Id $opId .Id .Content }} --></span> + {{ end }} + {{ end }} + <p id="{{ .Id }}-content" style="white-space: pre-wrap; margin: 10px 30px 10px 30px;">{{ parseContent $board.Actor $opId .Content $thread }}</p> + </div> + </div> + </div> + {{ end }} + {{ end }} + </div> +</div> +{{ end }} diff --git a/views/partials/postscripts.html b/views/partials/postscripts.html new file mode 100644 index 0000000..f26e354 --- /dev/null +++ b/views/partials/postscripts.html @@ -0,0 +1,3 @@ +<script src="/static/js/posts.js"></script> +<script src="/static/js/footerscript.js"></script> +<script src="/static/js/timer.js"></script> diff --git a/views/partials/top.html b/views/partials/top.html new file mode 100644 index 0000000..7c03c36 --- /dev/null +++ b/views/partials/top.html @@ -0,0 +1,128 @@ +<div style="margin: 0 auto; width: 700px;"> + <h1 style="text-align: center;">/{{ .Board.Name }}/ - {{ .Board.PrefName }}</h1> + <p style="text-align: center;">{{ .Board.Summary }}</p> + {{ $len := len .Posts }} + {{ if eq $len 0 }} + {{ if .Board.InReplyTo }} + <h3 id="newpostbtn" state="0" style="display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Post a Reply</a>]</h3> + {{ else }} + <h3 id="newpostbtn" state="0" style="display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Start a New Thread</a>]</h3> + {{ end }} <!-- end if inreplyto--> + <div id="newpost"> + <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="new-post" action="/post" method="post" enctype="multipart/form-data"> + <table id="postForm"> + <tr> + <tr> + <td><label for="name">Name:</label></td> + <td><input type="text" id="name" name="name" placeholder="Anonymous" maxlength="100"> + <a id="stopTablePost" onclick="stopNewPost()">[X]</a> + </td> + </tr> + <tr> + <td><label for="options">Options:</label></td> + <td><input type="text" id="options" name="options" maxlength="100" style="margin-right:10px">{{ if .Board.InReplyTo }}<input type="submit" value="Post">{{ end }}</td> + </tr> + {{ if eq .Board.InReplyTo "" }} + <tr> + <td><label for="subject">Subject:</label></td> + <td><input type="text" id="subject" name="subject" maxlength="100" style="margin-right:10px"><input type="submit" value="Post"></td> + </tr> + {{ end }} + <tr> + <td><label for="comment">Comment:</label></td> + <td><textarea rows="10" cols="50" id="comment" name="comment" maxlength="2000"></textarea></td> + </tr> + <tr> + <td><label for="file">Image</label></td> + <td><input type="file" id="file" name="file" {{ if gt $len 1 }} required {{ else }} {{ if eq $len 0 }} required {{ end }} {{ end }} > + <br><input type="checkbox" name="sensitive">Mark sensitive</input></td> + </tr> + <tr> + <td><label for="captcha">Captcha:</label></td> + <td> + <div style="height: 65px; display: inline;"> + <img src="{{ .Board.Captcha }}"> + </div> + <input type="text" id="captcha" name="captcha" autocomplete="off"> + </td> + </tr> + </table> + + <input type="hidden" id="inReplyTo" name="inReplyTo" value="{{ .Board.InReplyTo }}"> + <input type="hidden" id="sendTo" name="sendTo" value="{{ .Board.To }}"> + <input type="hidden" id="boardName" name="boardName" value="{{ .Board.Name }}"> + <input type="hidden" id="captchaCode" name="captchaCode" value="{{ .Board.CaptchaCode }}"> + <input type="hidden" id="returnTo" name="returnTo" value="{{ .ReturnTo }}"> + </form> + </div> + + {{ else }} <!-- special case to distinquish Notes and Archived formatting --> + + {{ if eq (index .Posts 0).Type "Note" }} + {{ if .Board.InReplyTo }} + <h3 id="newpostbtn" state="0" style="text-align: center; margin-top: 80px; display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Post a Reply</a>]</h3> + {{ else }} + <h3 id="newpostbtn" state="0" style="text-align: center; margin-top: 80px; display: none; margin-bottom:100px;">[<a href="javascript:startNewPost()">Start a New Thread</a>]</h3> + {{ end }} <!-- end if inreplyto--> + {{ $len := len .Posts }} + <div id="newpost"> + <form onsubmit="sessionStorage.setItem('element-closed-reply', true)" id="new-post" action="/post" method="post" enctype="multipart/form-data"> + <table id="postForm"> + <tr> + <tr> + <td><label for="name">Name:</label></td> + <td><input type="text" id="name" name="name" placeholder="Anonymous" maxlength="100"> + <a id="stopTablePost" onclick="stopNewPost()">[X]</a> + </tr> + <tr> + <td><label for="options">Options:</label></td> + <td><input type="text" id="options" name="options" maxlength="100" style="margin-right:10px">{{ if .Board.InReplyTo }}<input type="submit" value="Post">{{ end }}</td> + </tr> + {{ if eq .Board.InReplyTo "" }} + <tr> + <td><label for="subject">Subject:</label></td> + <td><input type="text" id="subject" name="subject" maxlength="100" style="margin-right:10px"><input type="submit" value="Post"></td> + </tr> + {{ end }} + <tr> + <td><label for="comment">Comment:</label></td> + <td><textarea rows="10" cols="50" id="comment" name="comment" maxlength="2000"></textarea></td> + </tr> + <tr> + <td><label for="file">Image</label></td> + <td><input type="file" id="file" name="file" {{ if gt $len 1 }} required {{ else }} {{ if eq $len 0 }} required {{ end }} {{ end }} > + <br><input type="checkbox" name="sensitive">Mark sensitive</input></td> + </tr> + <tr> + <td><label for="captcha">Captcha:</label></td> + <td> + <div style="height: 65px; display: inline;"> + <img src="{{ .Board.Captcha }}"> + </div> + <input type="text" id="captcha" name="captcha" autocomplete="off"> + </td> + </tr> + </table> + + <input type="hidden" id="inReplyTo" name="inReplyTo" value="{{ .Board.InReplyTo }}"> + <input type="hidden" id="sendTo" name="sendTo" value="{{ .Board.To }}"> + <input type="hidden" id="boardName" name="boardName" value="{{ .Board.Name }}"> + <input type="hidden" id="captchaCode" name="captchaCode" value="{{ .Board.CaptchaCode }}"> + <input type="hidden" id="returnTo" name="returnTo" value="{{ .ReturnTo }}"> </form> + </div> + + </div> + {{ else }} + <h1 style="text-align: center;">Archived Post</h1> + {{ end }} + {{ end }} <!-- end of len eq 0--> +</div> +<script> + newpostbtn = document.getElementById("newpostbtn"); + newpost = document.getElementById("newpost"); + + if(newpostbtn) + newpostbtn.style.display = "block"; + if(newpost) + newpost.style.display = "none"; +</script> |