1 Commits

Author SHA1 Message Date
jeff
9405848c07 initial base, probably will not work 2015-10-07 13:50:15 -04:00
188 changed files with 689 additions and 21294 deletions

29
.gitignore vendored
View File

@@ -8,35 +8,10 @@
.\#*
# srnd config files
srnd.ini
feeds.ini
*.ini
# default article store directory
articles
# generated files
webroot
# built binaries
go
srndv2
# private key
*.key
*.txt
# certificates
certs
rebuild.sh
vendor
.gx
# generated js
contrib/static/nntpchan.js
contrib/static/js/nntpchan.js
contrib/static/miner-js.js
#docs trash
doc/.trash
webroot

View File

@@ -1,34 +0,0 @@
#
# .gxignore for nntpchan repo
#
# emacs temp files
*~
\#*
.\#*
# srnd config files
srnd.ini
feeds.ini
# default article store directory
articles/
# generated files
webroot/
# built binaries
go/
srndv2
nntpchan
# private key
*.key
*.txt
# certificates
certs/
rebuild.sh
.git

View File

@@ -1,56 +1,34 @@
NNTPChan
========
# NNTPChan #
**NNTPChan** (previously known as overchan) is a decentralized imageboard that uses the [NNTP protocol](https://en.wikipedia.org/wiki/Network_News_Transfer_Protocol) (network-news transfer protocol) to synchronize content between many different servers. It utilizes cryptographically signed posts to perform optional/opt-in decentralized moderation.
NNTPChan (previously known as overchan) is a decentralized imageboard that uses nntp to synchronize content between many different servers. It utilizes cryptographically signed posts to perform optional/opt-in decentralized moderation.
This repository contains resources used by the core daemon which is located on [GitHub](https://github.com/majestrate/srndv2) (for now) along with general documentation, [here](doc/).
This repository contains resources used by the core daemon which is located [here](https://github.com/majestrate/srndv2) along with general documentation, [here](doc/)
##Getting started
## getting started ##
[This](doc) is a step-by-step guide for getting up-and-running with NNTPChan as well as documentation for developers who want to either work on NNTPChan directly or use NNTPChan in their aplications with the API.
After you [built and installed the daemon](doc/build.md) and [set up your database](doc/database.md), clone this repository and start up the daemon
##Bugs and issues
# clone it
git clone https://github.com/majestrate/nntpchan ~/nntpchan
# get the latest stable release
cd ~/nntpchan/
git checkout tags/0.2.1
*PLEASE* report any bugs you find while building, setting-up or using NNTPChan on the [GitHub issue tracker](https://github.com/majestrate/nntpchan/issues) or on the [GitGud issue tracker](https://gitgud.io/uguu/nntpchan/issues) so that the probelms can be resolved or discussed.
# set up the workspace
srndv2 setup
##Active NNTPChan nodes
Below is a list of known NNTPChan nodes:
1. [2hu-ch.org](https://2hu-ch.org)
2. [nsfl.tk](https://nsfl.tk)
3. [gchan](https://gchan.xyz/)
Tor node list:
1. [textpunk](http://ucavviu7wl6azuw7.onion/)
2. [chan](http://ev7fnjzjdbtu3miq.onion/)
3. [oniichan](http://sfw.oniichanylo2tsi4.onion/)
##Clients
NNTP (confirmed working):
* Thunderbird
Web:
* [Yukko](https://github.com/faissaloo/Yukko): ncurses based nntpchan web ui reader
# run the daemon
srndv2 run
##Support
Then open http://127.0.0.1:18000/ukko.html in your browser.
Need help? Join us on IRC.
*PLEASE* report any bugs you find while setting up or building [(here)](https://github.com/majestrate/nntpchan/issues) so that the problems get fixed (^:
1. [freenode: #nntpchan](https://webchat.freenode.net/?channels=#nntpchan)
2. [rizon: #nntpchan](https://qchat.rizon.net/?channels=#nntpchan) - Most active
For peering requests, questions or support find me on [rizon](https://qchat.rizon.net/?channels=#nntpchan) as \__uguu\__
##Donations
Like this project? Why not help by funding it?
Like this project? Fund it:
Bitcoin: [15yuMzuueV8y5vPQQ39ZqQVz5Ey98DNrjE](bitcoin://15yuMzuueV8y5vPQQ39ZqQVz5Ey98DNrjE)
bitcoin: 15yuMzuueV8y5vPQQ39ZqQVz5Ey98DNrjE
##Acknowledgements
* [Deavmi](https://deavmi.carteronline.net/) - Making the documentation beautiful.

View File

@@ -1,7 +0,0 @@
## TODO ##
* extra stylesheets
* more alternative templates
* javascript free mod panel
* liveui
* easier peering

View File

@@ -1,119 +0,0 @@
#!/usr/bin/env bash
neochan="yes"
if [ "$1" == "--disable-neochan" ] ; then
neochan="no"
fi
root=$(readlink -e "$(dirname "$0")")
set -e
if [ "x" == "x$root" ] ; then
root=$PWD/${0##*}
fi
cd "$root"
if [ -z "$GOPATH" ]; then
export GOPATH=$root/go
mkdir -p "$GOPATH"
fi
if [ ! -f "$GOPATH/bin/minify" ]; then
echo "set up minifiy"
go get -v github.com/tdewolff/minify/cmd/minify
fi
outfile="$PWD/contrib/static/nntpchan.js"
neochan_js_outfile="$PWD/contrib/static/neochan.js"
neochan_css_outfile="$PWD/contrib/static/neochan.css"
mini() {
echo "minify $1"
echo "" >> $2
echo "/* begin $1 */" >> $2
"$GOPATH/bin/minify" --mime=text/javascript >> $2 < $1
echo "" >> $2
echo "/* end $1 */" >> $2
}
css() {
echo "minify $1"
echo "" >> $2
echo "/* begin $1 */" >> $2
lessc $1 >> $2
echo "" >> $2
echo "/* end $1 */" >> $2
}
initfile() {
rm -f "$1"
echo '/*' >> "$1"
echo ' * For source code and license information please check https://github.com/majestrate/nntpchan' >> "$1"
brandingfile=./contrib/branding.txt
if [ -e "$brandingfile" ] ; then
echo ' *' >> "$1"
while read line; do
echo -n ' * ' >> "$1";
echo $line >> "$1";
done < $brandingfile;
fi
echo ' */' >> "$1"
}
echo
echo "building nntpchan.js ..."
echo
initfile "$outfile"
if [ -e ./contrib/js/contrib/*.js ] ; then
for f in ./contrib/js/contrib/*.js ; do
mini "$f" "$outfile"
done
fi
mini ./contrib/js/entry.js "$outfile"
# local js
for f in ./contrib/js/nntpchan/*.js ; do
mini "$f" "$outfile"
done
# vendor js
for f in ./contrib/js/vendor/*.js ; do
mini "$f" "$outfile"
done
if [ "$neochan" == "yes" ] ; then
set +e
for exe in lessc coffee ; do
which $exe &> /dev/null
if [ "$?" != "0" ] ; then
echo "$exe not installed";
exit 1
fi
done
echo
echo "building neochan.js ..."
echo
initfile "$neochan_js_outfile"
for f in ./contrib/js/neochan/*.coffee ; do
echo "compile $f"
coffee -cs < "$f" > "$f.js"
done
for f in ./contrib/js/neochan/*.js ; do
mini "$f" "$neochan_js_outfile"
done
echo
echo "building neochan.css ..."
echo
initfile "$neochan_css_outfile"
for f in ./contrib/js/neochan/*.less ; do
css "$f" "$neochan_css_outfile"
done
fi
echo
echo "ok"

106
build.sh
View File

@@ -1,106 +0,0 @@
#!/usr/bin/env bash
root=$(readlink -e "$(dirname "$0")")
set -e
if [ "" == "$root" ] ; then
root=$PWD/${0##*}
fi
cd "$root"
tags="-tags disable_redis"
help_text="usage: $0 [--disable-neochan]"
# check for help flags first
for arg in "$@" ; do
case $arg in
-h|--help)
echo "$help_text"
exit 0
;;
esac
done
rev="QmPAqM7anxdr1ngPmJz9J9AAxDLinDz2Eh9aAzLF9T7LNa"
ipfs="no"
rebuildjs="yes"
_next=""
unstable="no"
neochan="yes"
# check for build flags
for arg in "$@" ; do
case $arg in
"--disable-neochan")
neochan="no"
;;
"--unstable")
unstable="yes"
;;
"--no-js")
rebuildjs="no"
;;
"--ipfs")
ipfs="yes"
;;
"--revision")
_next="rev"
;;
"--revision=*")
rev=$(echo "$arg" | cut -d'=' -f2)
;;
*)
if [ "x$_next" == "xrev" ] ; then
rev="$arg"
fi
esac
done
if [ "$rev" == "" ] ; then
echo "revision not specified"
exit 1
fi
cd "$root"
if [ "$rebuildjs" == "yes" ] ; then
echo "rebuilding generated js..."
if [ "$neochan" == "no" ] ; then
./build-js.sh --disable-neochan
else
./build-js.sh
fi
fi
unset GOPATH
export GOPATH=$PWD/go
mkdir -p "$GOPATH"
if [ "$ipfs" == "yes" ] ; then
if [ ! -e "$GOPATH/bin/gx" ] ; then
echo "obtaining gx"
go get -u -v github.com/whyrusleeping/gx
fi
if [ ! -e "$GOPATH/bin/gx-go" ] ; then
echo "obtaining gx-go"
go get -u -v github.com/whyrusleeping/gx-go
fi
echo "building stable revision, this will take a bit. to speed this part up install and run ipfs locally"
mkdir -p "$GOPATH/src/gx/ipfs"
cd "$GOPATH/src/gx/ipfs"
"$GOPATH/bin/gx" get "$rev"
cd "$root"
go get -d -v
go build -v .
mv nntpchan srndv2
echo -e "Built\n"
echo "Now configure NNTPChan with ./srndv2 setup"
else
if [ "$unstable" == "yes" ] ; then
go get -u -v github.com/majestrate/srndv2/cmd/nntpchan
cp "$GOPATH/bin/nntpchan" "$root"
echo "built unstable, if you don't know what to do, run without --unstable"
else
go get -u -v github.com/majestrate/srndv2
cp "$GOPATH/bin/srndv2" "$root"
echo -e "Built\n"
echo "Now configure NNTPChan with ./srndv2 setup"
fi
fi

View File

@@ -1,13 +0,0 @@
[Unit]
Description=NNTPChan Server
Requires=redis_6379.service
[Service]
Type=simple
WorkingDirectory=/opt/nntpchan
ExecStart=/opt/nntpchan/srndv2 run
ExecStop=/bin/kill -15 $MAINPID
Restart=always
[Install]
WantedBy=multi-user.target

View File

@@ -1,76 +0,0 @@
Date: October 2015.
Getting the srndv2 tool
I am using debian, you should be able to use most any linux distro for this. Known to work are: debian, arch linux, <TODO: add more>.
Most commands should be done as a normal user, but some special commands need to be done as root. I find it useful to have two terminals open. I'll denote normal user level commands with '$' and root command with '#'.
Some dependencies you will need to install (as root) are:
# apt-get install build-essential golang git
# apt-get install libsodium-dev ffmpegthumbnailer
# apt-get install imagemagick ffmpegthumbnailer sox
The source code is in these two repos:
* https://github.com/majestrate/nntpchan
* https://github.com/majestrate/srndv2
set up your GOPATH (notes on that here: https://golang.org/doc/code.html#GOPATH ) and then install and build it:
$ go get -u github.com/majestrate/srndv2
If that command didn't work read the errors and check if you lacked any dependencies.
Now you have the srndv2 tool which you can run, but it will not work yet: You need to step up an SQL database first.
--------------
Setting up an SQL database
* https://wiki.postgresql.org/wiki/Detailed_installation_guides
Install postgresql.
# apt-get install postgresql postgresql-client
Create a postgresql user called 'srnd' and a database 'srnd':
# su postgres
$ whoami
postgres
$ psql -f nntpchan/nntp.psql
TODO: Get correct filename here.
Test if you can log in to that SQL user this way:
$ psql -d srnd -U srnd
If there is an issue with that try the following from the debian wiki:
------------
edit pg_hba.conf in /etc/postgresql/X.Y/main/pg_hba.conf
local all all trust # replace ident or peer with trust
reload postgresql
# /etc/init.d/postgresql reload
------------
hit Contol-D to get back your root terminal after doing this.
Once SQL setup is successful..
Now as your regular user that installed the srndv2 tool, you should be able to set up srndv2
First clone nntpchan and cd into it, then ask the srndv2 tool to setup your node:
$ git clone https://github.com/majestrate/nntpchan.git
$ cd nntpchann
ntpchan/$ srndv2 setup
ntpchan/$ srndv2 tool keygen

View File

@@ -1,89 +0,0 @@
<?php
function gennntp($headers, $files) {
if (count($files) == 0) {
}
else if (count($files) == 1 && $files[0]['type'] == 'text/plain') {
$content = $files[0]['text'] . "\r\n";
$headers['Content-Type'] = "text/plain; charset=UTF-8";
}
else {
$boundary = sha1($headers['Message-Id']);
$content = "";
$headers['Content-Type'] = "multipart/mixed; boundary=$boundary";
foreach ($files as $file) {
$content .= "--$boundary\r\n";
if (isset($file['name'])) {
$file['name'] = preg_replace('/[\r\n\0"]/', '', $file['name']);
$content .= "Content-Disposition: form-data; filename=\"$file[name]\"; name=\"attachment\"\r\n";
}
$type = explode('/', $file['type'])[0];
if ($type == 'text') {
$file['type'] .= '; charset=UTF-8';
}
$content .= "Content-Type: $file[type]\r\n";
if ($type != 'text' && $type != 'message') {
$file['text'] = base64_encode($file['text']);
$content .= "Content-Transfer-Encoding: base64\r\n";
}
$content .= "\r\n";
$content .= $file['text'];
$content .= "\r\n";
}
$content .= "--$boundary--\r\n";
}
//$headers['Content-Length'] = strlen($content);
$headers['Mime-Version'] = '1.0';
$headers['Date'] = date('r', $headers['Date']);
$out = "";
foreach ($headers as $id => $val) {
$val = str_replace("\n", "\n\t", $val);
$out .= "$id: $val\r\n";
}
$out .= "\r\n";
$out .= $content;
return $out;
}
function shoveitup($msg, $id) {
$s = fsockopen("tcp://localhost:1119");
fgets($s);
fputs($s, "MODE STREAM\r\n");
fgets($s);
fputs($s, "TAKETHIS $id\r\n");
fputs($s, $msg);
fputs($s, "\r\n.\r\n");
fgets($s);
fclose($s);
}
$time = time();
echo "\n@@@@ Thread:\n";
echo $m0 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.0000.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None"],
[['type' => 'text/plain', 'text' => "THIS IS A NEW TEST THREAD"]]);
echo "\n@@@@ Single msg:\n";
echo $m1 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.1234.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
[['type' => 'text/plain', 'text' => "hello world, with no image :("]]);
echo "\n@@@@ Single msg and pseudoimage:\n";
echo $m2 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.2137.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
[['type' => 'text/plain', 'text' => "hello world, now with an image!"],
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"]]);
echo "\n@@@@ Single msg and two pseudoimages:\n";
echo $m3 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.1488.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
[['type' => 'text/plain', 'text' => "hello world, now WITH TWO IMAGES!!!"],
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"],
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif2.gif"]]);
shoveitup($m0, "<1234.0000.".$time."@example.vichan.net>");
sleep(1);
shoveitup($m1, "<1234.1234.".$time."@example.vichan.net>");
sleep(1);
shoveitup($m2, "<1234.2137.".$time."@example.vichan.net>");
shoveitup($m3, "<1234.2131.".$time."@example.vichan.net>");

File diff suppressed because it is too large Load Diff

View File

@@ -1 +0,0 @@
3rd party javascript

View File

@@ -1,11 +0,0 @@
onready_callbacks = [];
function onready(fnc) {
onready_callbacks.push(fnc);
}
function ready() {
for (var i = 0; i < onready_callbacks.length; i++) {
onready_callbacks[i]();
}
}

View File

@@ -1 +0,0 @@
*.coffee.js

View File

@@ -1 +0,0 @@

View File

@@ -1,3 +0,0 @@
# neochan javascript directory
sass files for neochan templates

View File

@@ -1,41 +0,0 @@
// call an api method
// handler(json_object) on success
// handler(null) on fail
function nntpchan_apicall(url, handler, err_handler) {
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if (ajax.readyState == XMLHttpRequest.DONE ) {
var status = ajax.status;
var j = null;
if (status == 200) {
// found
try {
j = JSON.parse(ajax.responseText);
} catch (e) {} // ignore parse error
} else if (status == 410) {
if (err_handler) {err_handler("cannot fetch post: api disabled");}
return;
}
handler(j);
}
};
ajax.open("GET", url);
ajax.send();
}
// build post from json
// inject into parent
// if j is null then inject "not found" post
function nntpchan_buildpost(parent, j) {
var post = document.createElement("div");
if (j) {
// huehuehue
post.innerHTML = j.PostMarkup;
inject_hover_for_element(post);
} else {
post.setAttribute("class", "notfound post");
post.appendChild(document.createTextNode("post not found"));
}
parent.appendChild(post);
}

View File

@@ -1,13 +0,0 @@
var banner_count = 5;
// inject a banner into an element
function nntpchan_inject_banners(elem, prefix) {
var n = Math.floor(Math.random() * banner_count);
var banner = prefix + "static/banner_"+n+".jpg";
var e = document.createElement("img");
e.src = banner;
e.id = "nntpchan_banner";
elem.appendChild(e);
}

View File

@@ -1,20 +0,0 @@
function setSrcQuery(e, q) {
var src = e.src;
var p = src.indexOf('?');
if (p >= 0) {
src = src.substr(0, p);
}
e.src = src + "?" + q
}
function reload(el) {
setSrcQuery(el, "reload=" + (new Date()).getTime());
return false;
}
onready(function(){
document.getElementById("captcha_img").onclick = function() {
reload(document.getElementById("captcha_img"));
};
});

View File

@@ -1,70 +0,0 @@
//
// expand images inline
//
// released into the public domain by Jeff on 2016-04-30
//
// is the filename matching an image?
function filenameIsImage(fname) {
return /\.(gif|jpeg|jpg|png|webp)/.test(fname.toLowerCase());
}
// setup image inlining for 1 element
function setupInlineImage(thumb, url) {
if(thumb.inlineIsSetUp) return;
thumb.inlineIsSetUp = true;
var img = thumb.querySelector("img.thumbnail");
var expanded = false;
var oldurl = img.src;
thumb.onclick = function() {
if (expanded) {
img.setAttribute("class", "thumbnail");
img.src = oldurl;
expanded = false;
} else {
img.setAttribute("class", "expanded-thumbnail");
img.src = url;
expanded = true;
}
return false;
}
}
// set up image inlining for all applicable children in an element
function setupInlineImageIn(element) {
var thumbs = element.querySelectorAll("a.file");
for ( var i = 0 ; i < thumbs.length ; i++ ) {
var url = thumbs[i].href;
if (filenameIsImage(url)) {
// match
console.log("matched url", url);
setupInlineImage(thumbs[i], url);
}
}
}
onready(function(){
// Setup Javascript events for document
setupInlineImageIn(document);
// Setup Javascript events via updatoer
if (window.MutationObserver) {
var observer = new MutationObserver(function(mutations) {
for (var i = 0; i < mutations.length; i++) {
var additions = mutations[i].addedNodes;
if (additions == null) continue;
for (var j = 0; j < additions.length; j++) {
var node = additions[j];
if (node.nodeType == 1) {
setupInlineImageIn(node);
}
}
}
});
observer.observe(document.body, {childList: true, subtree: true});
}
});

View File

@@ -1,246 +0,0 @@
/* This file is dedicated to the public domain; you may do as you wish with it. */
/* Note: This code expects the global variable configRoot to be set. */
var configRoot = "";
if (typeof _ == 'undefined') {
var _ = function(a) { return a; };
}
function setupVideo(thumb, url) {
if (thumb.videoAlreadySetUp) return;
thumb.videoAlreadySetUp = true;
var video = null;
var videoContainer, videoHide;
var expanded = false;
var hovering = false;
//var loop = setting("videoloop");
var loop = true;
var loopControls = [document.createElement("span"), document.createElement("span")];
var fileInfo = thumb.parentNode.querySelector(".fileinfo");
var mouseDown = false;
function unexpand() {
if (expanded) {
expanded = false;
if (video.pause) video.pause();
videoContainer.style.display = "none";
thumb.style.display = "inline";
video.style.maxWidth = "inherit";
video.style.maxHeight = "inherit";
}
}
function unhover() {
if (hovering) {
hovering = false;
if (video.pause) video.pause();
videoContainer.style.display = "none";
video.style.maxWidth = "inherit";
video.style.maxHeight = "inherit";
}
}
// Create video element if does not exist yet
function getVideo() {
if (video == null) {
video = document.createElement("video");
video.src = url;
video.loop = loop;
video.innerText = _("Your browser does not support HTML5 video.");
videoHide = document.createElement("img");
videoHide.src = configRoot + "static/collapse.gif";
videoHide.alt = "[ - ]";
videoHide.title = "Collapse video";
videoHide.style.marginLeft = "-15px";
videoHide.style.cssFloat = "left";
videoHide.addEventListener("click", unexpand, false);
videoContainer = document.createElement("div");
videoContainer.style.paddingLeft = "15px";
videoContainer.style.display = "none";
videoContainer.appendChild(videoHide);
videoContainer.appendChild(video);
thumb.parentNode.insertBefore(videoContainer, thumb.nextSibling);
// Dragging to the left collapses the video
video.addEventListener("mousedown", function(e) {
if (e.button == 0) mouseDown = true;
}, false);
video.addEventListener("mouseup", function(e) {
if (e.button == 0) mouseDown = false;
}, false);
video.addEventListener("mouseenter", function(e) {
mouseDown = false;
}, false);
video.addEventListener("mouseout", function(e) {
if (mouseDown && e.clientX - video.getBoundingClientRect().left <= 0) {
unexpand();
}
mouseDown = false;
}, false);
}
}
// Clicking on thumbnail expands video
thumb.addEventListener("click", function(e) {
if (!e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey) {
getVideo();
expanded = true;
hovering = false;
video.style.position = "static";
video.style.pointerEvents = "inherit";
video.style.display = "inline";
videoHide.style.display = "inline";
videoContainer.style.display = "block";
videoContainer.style.position = "static";
video.parentNode.parentNode.removeAttribute('style');
thumb.style.display = "none";
//video.muted = (setting("videovolume") == 0);
//video.volume = setting("videovolume");
video.controls = true;
if (video.readyState == 0) {
video.addEventListener("loadedmetadata", expand2, false);
} else {
setTimeout(expand2, 0);
}
video.play();
e.preventDefault();
}
}, false);
function expand2() {
video.style.maxWidth = "100%";
video.style.maxHeight = window.innerHeight + "px";
var bottom = video.getBoundingClientRect().bottom;
if (bottom > window.innerHeight) {
window.scrollBy(0, bottom - window.innerHeight);
}
// work around Firefox volume control bug
//video.volume = Math.max(setting("videovolume") - 0.001, 0);
//video.volume = setting("videovolume");
}
// Hovering over thumbnail displays video
thumb.addEventListener("mouseover", function(e) {
//if (setting("videohover")) {
if (false) { // DOESN'T WORK YET
getVideo();
expanded = false;
hovering = true;
var docRight = document.documentElement.getBoundingClientRect().right;
var thumbRight = thumb.querySelector("img, video").getBoundingClientRect().right;
var maxWidth = docRight - thumbRight - 20;
if (maxWidth < 250) maxWidth = 250;
video.style.position = "fixed";
video.style.right = "0px";
video.style.top = "0px";
var docRight = document.documentElement.getBoundingClientRect().right;
var thumbRight = thumb.querySelector("img, video").getBoundingClientRect().right;
video.style.maxWidth = maxWidth + "px";
video.style.maxHeight = "100%";
video.style.pointerEvents = "none";
video.style.display = "inline";
videoHide.style.display = "none";
videoContainer.style.display = "inline";
videoContainer.style.position = "fixed";
//video.muted = (setting("videovolume") == 0);
//video.volume = setting("videovolume");
video.controls = false;
video.play();
}
}, false);
thumb.addEventListener("mouseout", unhover, false);
// Scroll wheel on thumbnail adjusts default volume
thumb.addEventListener("wheel", function(e) {
//if (setting("videohover")) {
if (true) {
//var volume = setting("videovolume");
if (e.deltaY > 0) volume -= 0.1;
if (e.deltaY < 0) volume += 0.1;
if (volume < 0) volume = 0;
if (volume > 1) volume = 1;
if (video != null) {
video.muted = (volume == 0);
video.volume = volume;
}
//changeSetting("videovolume", volume);
e.preventDefault();
}
}, false);
// [play once] vs [loop] controls
/*function setupLoopControl(i) {
loopControls[i].addEventListener("click", function(e) {
loop = (i != 0);
thumb.href = thumb.href.replace(/([\?&])loop=\d+/, "$1loop=" + i);
if (video != null) {
video.loop = loop;
if (loop && video.currentTime >= video.duration) {
video.currentTime = 0;
}
}
loopControls[i].style.fontWeight = "bold";
loopControls[1-i].style.fontWeight = "inherit";
}, false);
}
loopControls[0].textContent = _("[play once]");
loopControls[1].textContent = _("[loop]");
loopControls[(setting("videoloop") ? 1 : 0)].style.fontWeight = "bold";
for (var i = 0; i < 2; i++) {
setupLoopControl(i);
loopControls[i].style.whiteSpace = "nowrap";
fileInfo.appendChild(document.createTextNode(" "));
fileInfo.appendChild(loopControls[i]);
}*/
}
function setupVideosIn(element) {
var thumbs = element.querySelectorAll("a.file");
for (var i = 0; i < thumbs.length; i++) {
if (/(\.webm)|(\.mp4)$/.test(thumbs[i].pathname)) {
setupVideo(thumbs[i], thumbs[i].href);
} else {
var url = thumbs[i].href;
if (/(\.webm)|(\.mp4)$/.test(url)) setupVideo(thumbs[i], url);
}
}
}
onready(function(){
// Insert menu from settings.js
if (typeof settingsMenu != "undefined" && typeof Options == "undefined")
document.body.insertBefore(settingsMenu, document.getElementsByTagName("hr")[0]);
// Setup Javascript events for videos in document now
setupVideosIn(document);
// Setup Javascript events for videos added by updater
if (window.MutationObserver) {
var observer = new MutationObserver(function(mutations) {
for (var i = 0; i < mutations.length; i++) {
var additions = mutations[i].addedNodes;
if (additions == null) continue;
for (var j = 0; j < additions.length; j++) {
var node = additions[j];
if (node.nodeType == 1) {
setupVideosIn(node);
}
}
}
});
observer.observe(document.body, {childList: true, subtree: true});
}
});

View File

@@ -1,206 +0,0 @@
/** hidepost.js -- hides posts from page given $things */
function get_hidden_posts() {
var st = get_storage();
var prefix = "nntpchan_hide_post_";
return {
all : function() {
var msgids = [];
for ( var k in st) {
if (k.indexOf(prefix) == 0) {
var m = k.substring(prefix.length);
msgids.push(m);
}
}
return msgids;
},
add : function (msg) {
st[prefix+msg] = "post";
},
del : function (msg) {
st.removeItem(prefix+msg);
}
}
}
// is a post elem an OP?
function postIsOP(elem) {
var ds = elem.dataset;
return ds && ds.rootmsgid == ds.msgid ;
}
function _hide_elem(elem, fade) {
if(!fade) {
if (elem.style) {
elem.style.display = "none";
} else {
elem.style = {display: "none" };
}
elem.dataset.userhide = "yes";
} else {
$(elem).fadeOut(400, function() {
_hide_elem(elem);
});
}
}
function _unhide_elem(elem) {
$(elem).fadeIn();
elem.dataset.userhide = "no";
}
// return true if element is hidden
function _elemIsHidden(elem) {
return elem.dataset && elem.dataset.userhide == "yes";
}
// hide a post
function hidepost(elem, nofade) {
console.log("hidepost("+elem.dataset.msgid+")");
var posts = get_hidden_posts();
if (posts) {
// add to persitant hide
posts.add(elem.dataset.msgidhash);
}
if(postIsOP(elem)) {
// hide thread it's an OP
var thread = document.getElementById("thread_"+elem.dataset.rootmsgidhash);
if (thread) {
var e = thread.getElementsByClassName("post");
for ( var idx = 0; idx < e.length ; idx ++ ) {
if (e[idx].dataset.msgid == elem.dataset.msgid) continue; // don't apply
hidepost(e[idx]);
}
}
}
// hide attachments and post body
var es = elem.getElementsByClassName("attachments");
for (var idx = 0; idx < es.length ; idx ++ ) {
_hide_elem(es[idx], !nofade);
}
es = elem.getElementsByClassName("post_body");
for (var idx = 0; idx < es.length ; idx ++ ) {
_hide_elem(es[idx], !nofade);
}
es = elem.getElementsByClassName("postheader");
for (var idx = 0; idx < es.length ; idx ++ ) {
_hide_elem(es[idx], !nofade);
}
elem.dataset.userhide = "yes";
elem.setHideLabel("[show]");
}
// unhide a post
function unhidepost(elem) {
console.log("unhidepost("+elem.dataset.msgid+")");
var posts = get_hidden_posts();
if (posts) {
// remove from persiting hide
posts.del(elem.dataset.msgidhash);
}
if(postIsOP(elem)) {
var thread = document.getElementById("thread_"+elem.dataset.rootmsgidhash);
if(thread) {
var e = thread.getElementsByClassName("post");
for ( var idx = 0; idx < e.length ; idx ++ ) {
if(e[idx].dataset.msgid == elem.dataset.msgid) continue;
unhidepost(e[idx]);
}
}
}
// unhide attachments and post body
var es = elem.getElementsByClassName("attachments");
for (var idx = 0; idx < es.length ; idx ++ ) {
_unhide_elem(es[idx]);
}
es = elem.getElementsByClassName("post_body");
for (var idx = 0; idx < es.length ; idx ++ ) {
_unhide_elem(es[idx]);
}
es = elem.getElementsByClassName("postheader");
for (var idx = 0; idx < es.length ; idx ++ ) {
_unhide_elem(es[idx]);
}
elem.dataset.userhide = "no";
elem.setHideLabel("[hide]");
}
// hide a post given a callback that checks each post
function hideposts(check_func) {
var es = document.getElementsByClassName("post");
for ( var idx = 0; idx < es.length ; idx ++ ) {
var elem = es[idx];
if(check_func && elem && check_func(elem)) {
hidepost(elem);
}
}
}
// unhide all posts given callback
// if callback is null unhide all
function unhideall(check_func) {
var es = document.getElementsByClassName("post");
for (var idx=0 ; idx < es.length; idx ++ ) {
var elem = es[idx];
if(!check_func) { unhide(elem); }
else if(check_func(elem)) { unhide(elem); }
}
}
// inject posthide into page
onready(function() {
var posts = document.getElementsByClassName("post");
for (var idx = 0 ; idx < posts.length; idx++ ) {
console.log("inject hide: "+posts[idx].dataset.msgid);
var inject = function (elem) {
var hider = document.createElement("a");
hider.setAttribute("class", "hider");
elem.setHideLabel = function (txt) {
var e_hider = hider;
e_hider.innerHTML = txt;
}
elem.hidepost = function() {
var e_self = elem;
var e_hider = hider;
hidepost(e_self);
}
elem.unhidepost = function() {
var e_self = elem;
var e_hider = hider;
unhidepost(e_self);
}
elem.isHiding = function() {
var e_self = elem;
return _elemIsHidden(e_self);
}
hider.appendChild(document.createTextNode("[hide]"));
hider.onclick = function() {
var e_self = elem;
if(e_self.isHiding()) {
e_self.unhidepost();
} else {
e_self.hidepost();
}
}
elem.appendChild(hider);
};
inject(posts[idx]);
}
// apply persiting hidden posts
var posts = get_hidden_posts();
if(posts) {
var all = posts.all();
for ( var idx = 0 ; idx < all.length; idx ++ ) {
var id = all[idx];
var elem = document.getElementById(id);
if(elem)
hidepost(elem, true);
}
}
});

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +0,0 @@
function get_storage() {
var st = null;
if (window.localStorage) {
st = window.localStorage;
} else if (localStorage) {
st = localStorage;
}
return st;
}

View File

@@ -1,65 +0,0 @@
function livechan_got_post(widget, j) {
// do scroll
while (widget.children.length > 5) {
// remove top element
widget.removeChild(widget.children[0]);
}
nntpchan_buildpost(widget, j);
// scroll to bottom
widget.scrollTop = widget.scrollHeight;
}
// inject post form into an element
function inject_postform(prefix, parent) {
}
// inject livechan widget into parent
function inject_livechan_widget(prefix, parent) {
if ( "WebSocket" in window ) {
var url = "ws://"+document.location.host+prefix+"live";
if ( document.location.protocol == "https:" ) {
url = "wss://"+document.location.host+prefix+"live";
}
var socket = new WebSocket(url);
var progress = function(str) {
parent.innerHTML = "<pre>livechan: "+str+"</pre>";
};
progress("initialize");
socket.onopen = function () {
progress("streaming");
}
socket.onmessage = function(ev) {
var j = null;
try {
j = JSON.parse(ev.data);
} catch(e) {
// ignore
}
if (j) {
livechan_got_post(parent, j);
}
}
socket.onclose = function(ev) {
progress("connection closed");
setTimeout(function() {
inject_livechan_widget(prefix, parent);
}, 1000);
}
} else {
parent.innerHTML = "<pre>livechan mode requires websocket support</pre>";
setTimeout(function() {
parent.innerHTML = "";
}, 5000);
}
}
function ukko_livechan(prefix) {
var ukko = document.getElementById("ukko_threads");
if (ukko) {
// remove children
ukko.innerHTML = "";
inject_livechan_widget(prefix, ukko);
}
}

View File

@@ -1 +0,0 @@
main nntpchan javascript files

View File

@@ -1,426 +0,0 @@
function getReplyTo() {
if(!document.dynreply) {
var e = document.getElementById("postform_container");
if (e) {
// use existing postform
document.dynreply = new DynReply(e);
} else {
// build a new postform
document.dynreply = new DynReply();
}
e = document.dynreply.elem;
e.style.position = "fixed";
e.setAttribute("class", "shadow");
}
return document.dynreply;
}
function table_insert_row(table, header, items) {
var tr = document.createElement("tr");
// insert header element
var th = document.createElement("th");
th.appendChild(header);
tr.appendChild(th);
// insert the rest of the elements
for (var idx = 0; idx < items.length; idx ++ ) {
var elem = document.createElement("td");
elem.appendChild(items[idx]);
tr.appendChild(elem);
}
table.appendChild(tr);
}
/**
build dynamic reply box
*/
function DynReply(existingElem) {
if (existingElem) {
// wrap existing post form
// XXX: wrap it here
this.elem = existingElem;
this.form = this.elem.querySelector("form");
this._error = document.getElementById("postform_msg");
this.url = this.form.action + "?t=json";
this.x = 1;
this.y = 1;
return;
}
// build new post form
var elem = document.createElement("div");
elem.setAttribute("id", "postform_container");
this.elem = elem;
// build post form
this.form = document.createElement("form");
this.form.enctype = "multipart/form-data";
this.form.name = "post";
this.form.method = "post";
// reference
elem = document.createElement("input");
elem.setAttribute("id", "postform_reference");
elem.name = "reference";
elem.type = "hidden";
this.form.appendChild(elem);
var table = document.createElement("table");
table.setAttribute("class", "postform");
var tbody = document.createElement("tbody");
var span = document.createElement("span");
// name
elem = document.createElement("input");
elem.setAttribute("name", "name");
elem.setAttribute("value", "Anonymous");
elem.setAttribute("id", "postform_name");
span.appendChild(elem);
// error message
var err_elem = document.createElement("span");
err_elem.setAttribute("id", "postform_msg");
span.appendChild(err_elem);
this._error = err_elem;
table_insert_row(tbody, document.createTextNode("Name"), [span])
// subject
elem = document.createElement("input");
elem.setAttribute("name", "subject");
elem.setAttribute("value", "");
elem.setAttribute("id", "postform_subject");
// submit
var submit = document.createElement("input");
submit.setAttribute("value", "reply");
submit.setAttribute("class", "button");
submit.setAttribute("type", "submit");
submit.setAttribute("id", "postform_submit");
table_insert_row(tbody, document.createTextNode("Subject"), [elem, submit]);
// Comment
elem = document.createElement("textarea");
elem.setAttribute("id", "postform_message");
elem.setAttribute("name", "message");
elem.setAttribute("cols", "40");
elem.setAttribute("rows", "5");
table_insert_row(tbody, document.createTextNode("Comment"), [elem]);
// file
elem = document.createElement("input");
elem.setAttribute("class", "postform_attachment");
elem.setAttribute("id", "postform_attachments");
elem.setAttribute("type", "file");
elem.setAttribute("name", "attachment_uploaded");
elem.setAttribute("multiple", "multiple");
table_insert_row(tbody, document.createTextNode("Files"), [elem]);
// dubs
elem = document.createElement("input");
elem.setAttribute("type", "checkbox");
elem.setAttribute("name", "dubs");
table_insert_row(tbody, document.createTextNode("Get Dubs"), [elem]);
// captcha
elem = document.createElement("img");
elem.setAttribute("id", "captcha_img");
elem.alt = "captcha";
table_insert_row(tbody, document.createTextNode("Captcha"), [elem]);
// captcha solution
elem = document.createElement("input");
elem.name = "captcha";
elem.autocomplete = "off";
elem.setAttribute("id", "captcha_solution");
table_insert_row(tbody, document.createTextNode("Solution"), [elem])
table.appendChild(tbody);
this.form.appendChild(table);
this.elem.appendChild(this.form);
document.body.appendChild(this.elem);
this.board = null;
this.roothash = null;
this.prefix = null;
this.url = null;
this.x = 1;
this.y = 1;
}
DynReply.prototype.update = function() {
if (this.prefix) {
// update captcha
this.updateCaptcha();
if (this.board) {
// update post form
var ref = document.getElementById("postform_reference");
if (this.roothash) {
ref.setAttribute("value", this.roothash);
} else {
ref.setAttribute("value", "");
}
this.url = this.prefix + "post/" + this.board + "?t=json";
}
}
}
DynReply.prototype.show = function() {
console.log("show dynreply");
this.update();
this.elem.style.display = 'inline';
}
DynReply.prototype.hide = function() {
console.log("hide dynreply");
this.elem.style.display = "none";
}
// clear all fields
DynReply.prototype.clear = function() {
this.clearSolution();
this.clearPostbox();
}
// clear captcha solution
DynReply.prototype.clearSolution = function() {
var e = document.getElementById("captcha_solution");
// reset value
e.value = "";
}
// clear postform elements
DynReply.prototype.clearPostbox = function() {
var e = document.getElementById("postform_subject");
e.value = "";
e = document.getElementById("postform_message");
e.value = "";
e = document.getElementById("postform_attachments");
e.value = null;
}
DynReply.prototype.post = function(cb, err_cb) {
if (this.url && this.form) {
var data = new FormData(this.form);
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function(ev) {
if (ajax.readyState == XMLHttpRequest.DONE) {
var j = null;
try {
j = JSON.parse(ajax.responseText);
cb(j);
} catch (e) {
if(err_cb) {
err_cb(e);
}
}
}
}
ajax.open("POST", this.url);
ajax.send(data);
}
}
DynReply.prototype.updateCaptcha = function() {
if (this.prefix) {
var captcha_img = document.getElementById("captcha_img");
captcha_img.src = this.prefix + "captcha/img";
}
this.clearSolution();
}
DynReply.prototype.setPrefix = function(prefix) {
this.prefix = prefix;
}
DynReply.prototype.hide = function() {
this.elem.style.display = 'none';
}
DynReply.prototype.setBoard = function(boardname) {
if (boardname) {
this.board = boardname;
}
}
DynReply.prototype.setRoot = function(roothash) {
if (roothash) {
this.roothash = roothash;
}
}
DynReply.prototype.showError = function(msg) {
console.log("error in dynreply: "+msg);
this._error.setAttribute("class", "error message");
this._error.appendChild(document.createTextNode(msg));
this.updateCaptcha();
}
DynReply.prototype.showMessage = function(msg) {
this._error.setAttribute("class", "message");
this._error.innerHTML = "";
this._error.appendChild(document.createTextNode(msg));
var e = this._error;
setTimeout(function() {
// clear it
e.innerHTML = "";
}, 2000);
}
// reply box function
function nntpchan_reply(parent, shorthash) {
if (parent && document.dynreply) {
var boardname = parent.getAttribute("boardname");
var roothash = parent.getAttribute("root");
var replyto = getReplyTo();
// set target
replyto.setBoard(boardname);
replyto.setRoot(roothash);
// show it
replyto.show();
}
var elem = document.getElementById("postform_message");
if ( elem )
{
elem.value += ">>" + shorthash.substr(0,10) + "\n";
}
}
// inject post hover behavior
function inject_hover(prefix, el, parent) {
if (!prefix) { throw "prefix is not defined"; }
var linkhash = el.getAttribute("backlinkhash");
if (!linkhash) { throw "linkhash undefined"; }
console.log("rewrite linkhash "+linkhash);
var elem = document.createElement("span");
elem.setAttribute("class", "backlink_rewritten");
elem.appendChild(document.createTextNode(">>"+linkhash.substr(0,10)));
if (!parent) {
parent = el.parentNode;
}
parent.removeChild(el);
parent.appendChild(elem);
elem.onclick = function(ev) {
if(parent.backlink) {
nntpchan_apicall(prefix+"api/find?hash="+linkhash, function(j) {
var wrapper = document.createElement("div");
wrapper.setAttribute("class", "hover "+linkhash);
if (j == null) {
// not found?
wrapper.setAttribute("class", "hover notfound-hover "+linkhash);
wrapper.appendChild(document.createTextNode("not found"));
} else {
// wrap backlink
nntpchan_buildpost(wrapper, j);
}
parent.appendChild(wrapper);
parent.backlink = false;
}, function(msg) {
var wrapper = document.createElement("div");
wrapper.setAttribute("class", "hover "+linkhash);
wrapper.appendChild(document.createTextNode(msg));
parent.appendChild(wrapper);
parent.backlink = false;
});
} else {
var elems = document.getElementsByClassName(linkhash);
if (!elems) throw "bad state, no backlinks open?";
for (var idx = 0 ; idx < elems.length; idx ++ ) {
elems[idx].parentNode.removeChild(elems[idx]);
}
parent.backlink = true;
}
};
parent.backlink = true;
}
// inject post hover for all backlinks in an element
function inject_hover_for_element(elem) {
var elems = elem.getElementsByClassName("backlink");
var ls = [];
var l = elems.length;
for ( var idx = 0 ; idx < l ; idx ++ ) {
var e = elems[idx];
ls.push(e);
}
for( var elem in ls ) {
inject_hover(prefix, ls[elem]);
}
}
function init(prefix) {
// because no one cares about this feature :|
return;
// inject posthover ...
inject_hover_for_element(document);
if ( /\.html$/.test(document.location.pathname) && ! (/ukko/.test(document.location.pathname)) ) {
// board / thread page
console.log("not loading reply widget");
} else {
// ukko / livechan page
var rpl = getReplyTo();
rpl.setPrefix(prefix);
// set livechan
rpl.setBoard("overchan.random");
rpl.update();
rpl.updateCaptcha();
// position replyto widget
var e = rpl.elem;
var mouseDownX, mouseDownY;
var $dragging = null;
$(rpl.elem).on("mousemove", function(ev) {
if ($dragging) {
var x = ev.pageX - $(this).width() / 2,
y = ev.pageY - $(this).height() / 2;
$dragging.offset({
top: y,
left: x
});
}
});
$(rpl.elem).on("mousedown", e, function (ev) {
$dragging = $(rpl.elem);
});
$(rpl.elem).on("mouseup", function (e) {
$dragging = null;
});
// add replyto post handlers
e = document.getElementById("postform_submit");
var postit = function() {
var f = document.querySelector("form");
// do ajax request to post data
var r = getReplyTo();
r.showMessage("posting... ");
r.post(function(j) {
if(j.error) {
// an error happened
r.showError(j.error);
} else {
// we're good
r.showMessage("posted :^)");
r.updateCaptcha();
r.clear();
}
}, function(err) {
r.showError(err);
r.clearSolution();
});
}
var f = document.querySelector("form");
f.onsubmit = function() {
postit();
return false;
}
}
}

View File

@@ -1,15 +0,0 @@
function enable_theme(prefix, name) {
if (prefix && name) {
var theme = document.getElementById("current_theme");
if (theme) {
theme.href = prefix + "static/"+ name + ".css";
var st = get_storage();
st.nntpchan_prefix = prefix;
st.nntpchan_theme = name;
}
}
}
// apply themes
var st = get_storage();
enable_theme(st.nntpchan_prefix, st.nntpchan_theme);

View File

@@ -1,66 +0,0 @@
var easiness = 55.0;
var miner_threads = 4;
var randoffs = 64;
/*
onready(function(){
document.getElementById("start_miner").onclick = function() {
var btn = document.getElementById("start_miner");
var label = btn.value;
btn.value = "..."
btn.disabled = true;
var b = new Uint8Array(randoffs);
window.crypto.getRandomValues(b);
var b_cur = 0;
var b_i = 0;
var tmp = new Uint8Array(randoffs+b_i+1);
tmp.set(b)
tmp[b.length]=0
b = tmp;
var workers = new Array(miner_threads);
var worker_cb = function(e) {
if (e.data[0] == "ok") {
miner_cb(e.data[1]);
btn.value=label;
btn.disabled = false;
for (i=0; i<miner_threads; i++) {
workers[i].terminate();
}
} else {
if (b_cur >= 256) {
var tmp = new Uint8Array(randoffs+b_i+1);
tmp.set(b)
tmp[b.length]=0
b = tmp;
b_i++;
b_cur=0;
}
b[randoffs+b_i]=b_cur;
b_cur++;
var params = [b, easiness, e.data[2]];
workers[e.data[2]].postMessage(params);
}
}
for (i=0; i<miner_threads; i++) {
b[randoffs+b_i]=b_cur;
b_cur++;
var params = [b, easiness, i];
workers[i] = new Worker("./static/mineworker.js");
workers[i].onmessage = worker_cb;
workers[i].postMessage(params); // Start the worker.
}
b_cur=4;
};
});
function miner_cb(s) {
document.getElementById("miner_result").value = s;
}
*/

View File

@@ -1,2 +0,0 @@
this directory holds unused javascript files for nntpchan
don't delete files move them here

View File

@@ -1,2 +0,0 @@
javascript files for nntpchan

16
contrib/live/index.html Normal file
View File

@@ -0,0 +1,16 @@
<html>
<head>
<title> nntpchan liveposting </title>
<link rel="stylesheet" src="live.css" />
<script type="text/javascript" src="live.js">
</script>
</head>
<body>
<noscript>
<p>nntpchan liveposting is done entirly via javascript and websockets</P>
<p>this frontend will not work without javascript enabled</p>
</noscript>
<div onload="nntpchan_load_ui(this)">
</div>
</body>
</html>

83
contrib/live/live.js Normal file
View File

@@ -0,0 +1,83 @@
function create_captcha_pane() {
var elem = document.createElement("div");
elem.setAttribute("class", "nntpchan_captcha_slash");
}
// create the base ui
// pass in a function that does posting
// return the model
function create_ui(elem) {
var pane = document.createElement("div");
var output = document.createElement("div");
output.setAttribute("class", "nntpchan_output");
var output_elem = document.createElemen("div");
output_elem.setAttribute("class", "nntpchan_output_root");
output.appendChild(output_elem);
pane.appendChild(output);
var input = document.createElement("div");
input.setAttribute("class", "nntpchan_input");
var input_elem = document.createElement("textarea");
input_elem.setAttribute("class", "nntpchan_textarea");
input.appendChild(input_elem);
var submit_elem = document.createElement("input");
submit_elem.setAttrbute("type", "button");
input.appendChild(submit_elem);
pane.appendChild(input);
elem.appendChild(pane);
var captcha_elem = create_captcha_pane();
elem.appendChild(captcha_elem);
return {
input: input_elem,
submit: submit_elem,
output: output_elem,
captcha: captcha_elem
}
}
// load ui elements and start stuff up
function nntpchan_load_ui(elem) {
// check for websockets
if (!("WebSocket" in window)) {
elem.value = "websockets are needed for nntpchan liveposting";
return;
}
// TODO: make configurable url
var url = "ws://" + location.hostname + ":18080/ws";
var socket = new WebSocket(url);
var send = function(obj) {
socket.send(JSON.stringify(obj));
}
var ui = create_ui(elem);
ui.submit.addEventListener("click", function(ev) {
});
socket.onopen = function() {
}
socket.onmessage = function() {
}
}

View File

@@ -1,3 +0,0 @@
nntpchan.js
neochan.js
neochan.css

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

BIN
contrib/static/about.mp3 Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 550 B

View File

@@ -1,112 +0,0 @@
/**
bloodgod theme css override
*/
body {
color: #666;
background: #111;
}
.post_body > pre {
color: #666;
}
input, textarea, button, input[type="text"], input[type="password"],
input[type="checkbox"], input[type="file"], input[type="submit"],
input[type="button"] {
color: #666;
background: #050505;
border-color: #D80000;
}
.navbar {
background: #980000;
color: black;
}
#captcha_img {
background: #D80000;
}
.reply, .ukko_thread_header {
border-color: #0c0c0c;
}
.ukko_thread_header {
border-style: solid;
border-width: 1px;
border-radius: 3px;
}
hr, .name {
color: #6B1919;
}
hr {
height: 0px;
border-width: 1px medium medium;
border-color: #0C0C0C;
border-style: solid none none;
clear: both;
}
.subject {
color: #4E0000;
}
.reply , .ukko_thread_header {
background: #0C0C0C;
}
.op {
background-color: #1a1a1a;
border-style: hidden;
}
.pagelist {
background: #0c0c0c;
border-style: hidden;
}
a {
color: #500707;
}
.post:target {
background-color: #1a1a1a;
}
.postno, .postnol {
color: #B70000;
}
.postform >tr, th {
background: #6B1919;
}
#postform_container {
background-color: #111;
}
table tbody tr:nth-of-type( even ) {
background-color: #430000;
}
table thead th {
color: #6B1919;
background: #430000;
border-color: #270000;
}
.navbar-sep {
color: #270000;
}
.navbar-link > label:hover {
color: red;
}
.origin > img , .not_found > img {
-webkit-filter: invert(1);
filter: invert(1);
}

View File

@@ -1,52 +0,0 @@
<!doctype html><html><head><link rel="stylesheet" href="/static/chen-chan.css"><title>nntpchan faq</title></head><body>
<audio loop controls><source src="/static/about.mp3" type="audio/mpeg">[can't play audio.]</audio>
<p>nntpchan faq</p>
<dl>
<dt>What is nntpchan?</dt>
<dd>NNTPCchan is a federated imageboard that spans across several networks including tor, i2p and anonet. There are no central servers so that as long as 1 server is left the forum will continue to function.</dd>
<br>
<dt>How is this different from *chan?</dt>
<dd>NNTPChan is by design invulnerable to global censorship. All moderation is local to each server.</dd>
<br>
<dt>How does moderation work then?</dt>
<dd>Moderation is done with our special tripcodes (ed25519-sha512 signed posts). Anyone can give moderation suggestions, but that doesn't mean anyone will accept them. Those who trust the actions of a moderator can have their frontend whitelist the moderation actions and have them performed without oversight by another moderator.</dd>
<br>
<dt>How can I remove content from nntpchan completely?</dt>
<dd>‾\(._.)/‾ I don't know. You probably can't unless every server agrees to remove the content, even then, nothing prevents someone from reposting it. You can't delete what you post on the internet.</dd>
<br>
<dt>do you allow child porn?</dt>
<dd>no.</dd>
<br>
<dt>Do you allow XYZ content?</dt>
<dd>If it violates USA Law or causes problems with my host, no. Otherwise, probably.</dd>
<br>
<dt>Someone posted something I don't like but it's not illegal</dt>
<dd>That is not my problem. All posts on this site are the responsibility of the individual poster and not the administration of this server</dd>
<br>
<dt>code, bugs, feature requests</dt>
<dd><a href="https://github.com/majestrate/nntpchan/">frontend</a> <a href="https://github.com/majestrate/srndv2/">core</a></dd>
<br></dl>
<p>Please send any gripes/questions/inqueries/suggestions/complaints to ampernand [|at\] gmail {dot} com with subject starting with "nntpchan question"</p>
<hr>
<p>ucavviu7wl6azuw7.onion frontend specific</p>
<dl>
<dt>admin's pubkey</dt>
<dd>06833a90237c61f59558c1726fbe71c63e972722b2cf1147867be286cb020b32<br>
▆☃►☐▣◼◡♵☕◘♁◲◯☾◱♆▾☗▧▢☲♏░◇☆◻♢☆♋▂▋▲</dd>
<br>
<dt>peering</dt>
<dd>just open a thread for now.</dd>
<br>
<dt>rules</dt>
<dd>basically no stuff that will fuck with the functioning of the site.<br>
- stuff that will get me in trouble like cp.<br>
- flood/spam. duplicate posts may also get baleeted.
</dd>
<br>
<dt>guidelines and things to keep in mind</dt>
<dd>
- i don't owe you shiieet. you don't owe me shieet.<br>
- administration style: benevolent dictatorship.
</dd>
</dl>
</body></html>

View File

@@ -1,7 +0,0 @@
body{font-family:monospace}
dl{max-width:84ch}
dd{margin-left:4ch;overflow:auto}
.memearrows{color:green}
pre{margin: 0 0 0 0}
table,th,td{border:1px solid gray}
table {border-collapse:collapse}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 B

View File

@@ -1,38 +0,0 @@
/*
dayman
fighter of the night man
champion of the sun
*/
body {
background-color: #FFEFC9;
background-repeat: no-repeat;
background-attachment: fixed;
background-size: 100% 100%;
background-image: url('maymface.png');
}
.reply, .ukko_thread_header, th, .postform > tr , tbody > tr:nth-of-type( even ) {
background: #FBFFC9;
}
#postform_container {
background-color: rgba(0,0,0,0);
}
.post:target {
background-color: #ffda9b;
box-shadow: 0px 0px 5px 1px;
}
img#nntpchan_banner {
box-shadow: 0px 0px 10px 0px #FFECBE;
}
#postform_inner {
box-shadow: 0px 1px 5px 1px;
}
.navbar, table, thead, th, table, pre, .op {
background: #FFECBE;
}

View File

@@ -1,9 +1,7 @@
<!DOCTYPE html5>
<html>
<head>
<link rel="stylesheet" href="site.css" />
<title> NNTPChan Frequently Asked Questions </title>
<meta charset="utf-8">
<link rel="stylesheet" href="site.css"></link>
<title> NNTPChan Frequently Asked Questions</title>
</head>
<body>
<h2>NNTPChan faq </h2>
@@ -49,6 +47,14 @@
<div><a href="https://github.com/majestrate/nntpchan/issues">github</a></div>
</p>
<hr />
<p>
<div>Please send any gripes/questions/inqueries/suggestions/complaints to ampernand [|at\] gmail {dot} com with subject starting with "nntpchan question" </div>
</p>
<hr />
</div>
<audio autoplay="autoplay" loop="loop">
<source src="about.mp3" type="audio/mpeg">
<embed src="about.mp3">
</audio>
</body>
</html>

View File

@@ -1,107 +0,0 @@
function createConnectionElement(j) {
var e = document.createElement("div");
e.setAttribute("class", "connection");
var auth = document.createElement("div");
auth.appendChild(document.createTextNode("Connection: "+j.name));
// authentication state
if (j.authed) {
auth.setAttribute("class", "authed");
auth.appendChild(document.createTextNode("(authenticated)"));
} else {
auth.appendChild(document.createTextNode("(not authenticated)"));
}
e.appendChild(auth);
// connection mode
var mode = document.createElement("div");
mode.setAttribute("class", "mode");
mode.appendChild(document.createTextNode("mode: "+j.mode));
e.appendChild(mode);
var pending = document.createElement("div");
pending.setAttribute("class", "pending");
// pending articles
var articles = Object.keys(j.pending);
pending.appendChild(document.createTextNode("pending articles: "+articles.length));
for ( var idx = 0 ; idx < articles.length; idx ++ ) {
var msgid = articles[idx];
var state = j.pending[msgid];
var elem = document.createElement("div");
elem.appendChild(document.createTextNode(msgid + ": " + state));
elem.setAttribute("class", "pending_item "+state);
pending.appendChild(elem);
}
e.appendChild(pending);
// e.appendChild(document.createTextNode(JSON.stringify(j)));
return e;
}
function inject_nntp_feed_element(feed, elem) {
elem.appendChild(document.createElement("hr"));
var name = document.createElement("div");
name.setAttribute("class", "feeds_name");
name_elem = document.createTextNode("Name: "+feed.State.Config.Name);
name.appendChild(name_elem);
elem.appendChild(name);
var conns = document.createElement("div");
conns.setAttribute("class", "connections");
for ( var idx = 0 ; idx < feed.Conns.length; idx ++ ) {
conns.appendChild(createConnectionElement(feed.Conns[idx]));
}
elem.appendChild(conns);
}
function update_nntpchan_feed_ticker(elem, result_elem) {
nntpchan_admin("feed.list", null, function(j) {
if (j) {
if (j.error) {
console.log("nntpchan_feed_ticker: error, "+j.error);
} else {
// remove all children
while(elem.children.length) {
elem.children[0].remove();
}
var result = j.result;
for (var idx = 0; idx < result.length; idx++) {
var item = result[idx];
var entry = document.createElement("div");
inject_nntp_feed_element(item, entry);
elem.appendChild(entry);
}
}
}
}, result_elem);
}
function nntp_feed_add() {
var param = {};
var e = document.getElementById("add_feed_name");
param.name = e.value;
e = document.getElementById("add_feed_host");
param.host = e.value;
e = document.getElementById("add_feed_port");
param.port = parseInt(e.value);
e = document.getElementById("nntpchan_feed_result");
nntpchan_admin("feed.add", param, null, e);
}
function nntp_feed_del() {
var e = document.getElementById("del_feed_name");
var name = e.value;
e = document.getElementById("nntpchan_feed_result");
nntpchan_admin("feed.del", {name: name}, null, e);
}
function nntp_feed_update() {
var e = document.getElementById("nntpchan_feeds");
if (e) {
setInterval(function(){
var e1 = document.getElementById("nntpchan_feed_result");
update_nntpchan_feed_ticker(e, e1);
}, 1000);
}
}

BIN
contrib/static/fieri.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -1,249 +0,0 @@
input {
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
}
.livechan_captcha_input {
color: black;
}
textarea, select {
-moz-border-radius: 0px;
-webkit-border-radius: 0px;
border-radius: 0px;
}
.livechan_chat_input {
padding: 0;
margin: 0;
position: fixed;
width: 100%;
bottom: 0;
left: 0;
right: 0;
background: #d6daf0;
}
.livechan_chat_input_name, .livechan_chat_input_convo {
padding: 0;
padding-left: none;
margin: 0;
width: 80%;
color: black;
}
.livechan_chat_input_left {
width: 19%;
}
.livechan_chat_input_message_div {
padding:0;
margin:0;
position: absolute;
width: 70%;
left: 20%;
top: 3px;
bottom: 0;
}
.livechan_chat_input_message {
padding: 0;
margin: 0;
border:none;
height: 100%;
width: 100%;
resize: none;
}
.livechan_chat_input_submit {
position: absolute;
width: 8%;
top: 3px;
right: 1px;
bottom: 0;
border: none;
color: black;
}
.livechan_chat_output {
position: fixed;
top: 20px;
left: 0;
right: 0;
bottom: 60px;
overflow: auto;
width: 89%;
-webkit-overflow-scrolling: touch;
}
.livechan_chat_output_chat {
max-height: 200px;
overflow: hidden;
}
.livechan_chat_output_date {
margin: 0 4px;
}
.livechan_chat_output_count:hover {
cursor: pointer;
}
.livechan_chat_capcode {
margin: 0 4px;
font-style: italic;
font-weight: lighter;
}
.livechan_image_thumb {
max-width: 300px;
max-height: 200px;
float: left;
margin: 10px;
}
.livechan_captcha {
left: 0px;
top: 0px;
bottom: 0px;
right: 0px;
position: fixed;
opacity: 0.9;
}
.livechan_captcha_inner {
padding: 200px;
}
.livechan_captcha_image {
}
.livechan_captcha_input {
float: down;
}
.livechan_spoiler {
color: black;
background: black;
}
.livechan_chat_output_chat {
background: #d6daf0;
}
.livechan_spoiler:hover {
color: white;
}
.livechan_convo_label {
padding: 5px;
}
.livechan_convobar_root {
position: fixed;
top: 20px;
right: 0;
width: 10%;
}
.livechan_convobar_item {
padding: 5px;
margin: 5px;
background: #d6daf0;
}
.livechan_navbar {
z-index: 3;
position: fixed;
top: 0;
width: 100%;
height: 20px;
}
.livechan_navbar_mod_indicator_inactive, .livechan_navbar_mod_indicator_active, .livechan_navbar_status, .livechan_navbar_channel_label {
padding-left: 10px;
padding-right: 10px;
background: #d6daf0;
}
.hover {
position: relative;
padding: 1px;
left: -1000px
border: 1px dashed black;
visibility: hidden;
}
.hover > img {
position: fixed;
top: 0%;
right: 0;
max-width: 75%;
max-height: 75%;
visibility: visible;
}
.livechan_captcha, .livechan_convobar_root, #chat {
background: #EEF2FF;
}
.livechan_chat_output_chat {
font-family: monospace;
margin: 4px;
padding: 4px;
}
.livechan_chat_output_name {
font-weight: bold;
color: green;
}
.livechan_chat_output_count:hover {
color: red;
}
.livechan_greentext {
color: #789922;
}
.livechan_boldtext {
font-weight: bold;
}
.livechan_internallink , a {
color: blue;
}
.livechan_internallink:hover , a {
color: red;
cursor: pointer;
}
.livechan_chat_selected {
background: blue;
}
.livechan_navbar_mod_indicator_active {
background: #4a4ad4;
color: #34d434;
}
.livechan_navbar_mod_indicator_admin {
background: #4a4ad4;
color: red;
}
.livechan_navbar_mod_indicator_inactive {
color: #aaaaaa;
background: #EEF2FF;
}
.livechan_redtext {
color: #af0a0f;
font-weight: bold;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -1,8 +0,0 @@
importScripts('./siphash-lib.js');
importScripts('./miner-js.js');
onmessage = function(e) {
var s = cuckoo["mine_cuckoo"](e.data[0], e.data[1]);
s.push(e.data[2]);
postMessage(s);
}

View File

@@ -64,51 +64,28 @@ function nntpchan_key_add() {
});
}
function get_nntp_username() {
var e = document.getElementById("nntpchan_nntp_username");
return e.value;
}
function get_nntp_passwd() {
var e = document.getElementById("nntpchan_nntp_passwd");
return e.value;
}
function nntpchan_admin_nntp(method) {
nntpchan_admin(method, {
username: get_nntp_username(),
passwd: get_nntp_passwd()
})
}
function nntpchan_admin_board(method) {
nntpchan_admin(method, {
newsgroup: get_board_target()
})
}
function nntpchan_admin(method, param, handler_cb, result_elem) {
if (handler_cb) {
// we got a handler already set
} else {
// no handler set
var handler_cb = function(j) {
if (j.result) {
return document.createTextNode(j.result);
} else {
return "nothing happened?";
}
}
}
function nntpchan_admin(method, param) {
nntpchan_mod({
name:"admin",
parser: function(target) {
return method;
},
handle: handler_cb,
handle: function(j) {
if (j.result) {
return document.createTextNode(j.result);
} else {
return "nothing happened?";
}
},
method: ( param && "POST" ) || "GET",
data: param
}, result_elem)
})
}
@@ -142,83 +119,70 @@ function nntpchan_delete() {
});
}
function nntpchan_mod(mod_action, result_elem) {
function nntpchan_mod(mod_action) {
// get the element
var input = document.getElementById("nntpchan_mod_target");
var target = null;
if (input) {
target = input.value;
}
var target = input.value;
if (mod_action.parser) {
target = mod_action.parser(target);
}
var elem;
if (result_elem) {
elem = result_elem;
} else {
elem = document.getElementById("nntpchan_mod_result");
}
var elem = document.getElementById("nntpchan_mod_result");
// clear old results
while( elem.firstChild ) {
elem.removeChild(elem.firstChild);
}
var csrf_ajax = new XMLHttpRequest();
csrf_ajax.onreadystatechange = function() {
if (csrf_ajax.readyState == XMLHttpRequest.DONE) {
// get csrf token
var csrf = csrf_ajax.getResponseHeader("X-CSRF-Token");
// fire off ajax
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if (ajax.readyState == XMLHttpRequest.DONE) {
var status = ajax.status;
// we gud?
if (status == 200) {
// yah
var txt = ajax.responseText;
var j = JSON.parse(txt);
if (j.error) {
var e = document.createTextNode(j.error);
elem.appendChild(e);
} else {
if (mod_action.handle) {
var result = mod_action.handle(j);
if (result) {
elem.appendChild(result);
}
}
}
} else if (status) {
// nah
// http error
elem.innerHTML = "error: HTTP "+status;
}
// clear input
if (input) {
input.value = "";
}
}
}
if (mod_action.name) {
var url = mod_action.name + "/" + target;
ajax.open(mod_action.method || "GET", url);
ajax.setRequestHeader("X-CSRF-Token", csrf);
var data = mod_action.data;
if (data) {
ajax.setRequestHeader("Content-type","text/json");
ajax.send(JSON.stringify(data));
// fire off ajax
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function() {
if (ajax.readyState == XMLHttpRequest.DONE) {
var status = ajax.status;
// we gud?
if (status == 200) {
// yah
var txt = ajax.responseText;
var j = JSON.parse(txt);
if (j.error) {
var e = document.createTextNode(j.error);
elem.appendChild(e);
} else {
ajax.send();
if (mod_action.handle) {
var result = mod_action.handle(j);
if (result) {
elem.appendChild(result);
} else {
// fail
alert("mod action failed, handler returned nothing");
}
} else {
// fail
alert("mod action has no handler");
}
}
} else {
alert("mod action has no name");
} else if (status) {
// nah
// http error
elem.innerHTML = "error: HTTP "+status;
}
// clear input
input.value = "";
}
}
csrf_ajax.open("GET", "");
csrf_ajax.send();
if (mod_action.name) {
var url = mod_action.name + "/" + target;
ajax.open(mod_action.method || "GET", url);
var data = mod_action.data;
if (data) {
ajax.setRequestHeader("Content-type","text/json");
ajax.send(JSON.stringify(data));
} else {
ajax.send();
}
} else {
alert("mod action has no name");
}
}

View File

@@ -0,0 +1,14 @@
//
// nntpchan.js -- frontend ui niceness
//
// insert a backlink for a post given its short hash
function nntpchan_backlink(shorthash)
{
var elem = document.getElementById("postform_message");
if ( elem )
{
elem.value += ">>" + shorthash + "\n";
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1,94 +0,0 @@
var SipHash = (function() {
function u8to64(p , i) {
return { l: (p[i] | p[i+1]<<8 | p[i+2]<<16 | p[i+3]<<24) , h: (p[i+4] | p[i+5]<<8 | p[i+6]<<16 | p[i+7]<<24) };
}
function _add(a, b) {
var rl = a.l + b.l,
a2 = { h: a.h + b.h + (rl / 2 >>> 31) >>> 0,
l: rl >>> 0 };
a.h = a2.h; a.l = a2.l;
}
function _xor(a, b) {
a.h ^= b.h; a.h >>>= 0;
a.l ^= b.l; a.l >>>= 0;
}
function _rotl(a, n) {
var a2 = {
h: a.h << n | a.l >>> (32 - n),
l: a.l << n | a.h >>> (32 - n)
};
a.h = a2.h; a.l = a2.l;
}
function _rotl32(a) {
var al = a.l;
a.l = a.h; a.h = al;
}
function _compress(v0, v1, v2, v3) {
_add(v0, v1);
_add(v2, v3);
_rotl(v1, 13);
_rotl(v3, 16);
_xor(v1, v0);
_xor(v3, v2);
_rotl32(v0);
_add(v2, v1);
_add(v0, v3);
_rotl(v1, 17);
_rotl(v3, 21);
_xor(v1, v2);
_xor(v3, v0);
_rotl32(v2);
}
function hash(v0h, v0l, v1h, v1l, v2h, v2l, v3h, v3l, mh, ml) {
var mi = { h: mh, l: ml };
var v0 = { h: v0h, l: v0l };
var v1 = { h: v1h, l: v1l };
var v2 = { h: v2h, l: v2l };
var v3 = { h: v3h, l: v3l };
/*var k0 = u8to64(key, 0);
var k1 = u8to64(key, 8);
var v0 = { h: k0.h, l: k0.l }, v2 = k0;
var v1 = { h: k1.h, l: k1.l }, v3 = k1;
_xor(v0, { h: 0x736f6d65, l: 0x70736575 });
_xor(v1, { h: 0x646f7261, l: 0x6e646f6d });
_xor(v2, { h: 0x6c796765, l: 0x6e657261 });
_xor(v3, { h: 0x74656462, l: 0x79746573 });*/
_xor(v3, mi);
_compress(v0, v1, v2, v3);
_compress(v0, v1, v2, v3);
_xor(v0, mi);
_xor(v2, { h: 0, l: 0xff });
_compress(v0, v1, v2, v3);
_compress(v0, v1, v2, v3);
_compress(v0, v1, v2, v3);
_compress(v0, v1, v2, v3);
var h = v0;
_xor(h, v1);
_xor(h, v2);
_xor(h, v3);
var res = new Uint32Array(2);
res[0]=h.h; res[1]=h.l;
return res;
};
return {
hash: hash,
};
})();
var module = module || { }, exports = module.exports = SipHash;

View File

@@ -1,59 +1,18 @@
body {
background: #EEF2FF url('bg.png') repeat-x 50% 0%;
color: black;
font-family: arial,helvetica,sans-serif;
font-size: 10pt;
margin: 0 4px;
padding-left: 4px;
padding-right: 4px;
.thread {
margin-bottom: 1em;
display: inline-block;
word-wrap: break-word;
width: 100%;
}
main,
aside,
section {
display: block;
margin: 0 auto;
width: 100%;
}
.hover {
position: absolute;
}
.notfound-hover {
background: #EEF2FF;
padding: 10px;
}
#postform_container {
background-color: #EEF2FF;
}
.shadow {
box-shadow: 0 0 10px rgba(0,0,0,0.55);
}
.hover > div > .post , .notfound-hover {
background-color: #D6DAF0;
box-shadow: 0 0 10px rgba(0,0,0,0.55);
}
.frontend , .subject, .catalog_subject {
color: #0F0C5D;
.frontend , .subject {
color: #928BFF;
}
.name {
color: #117743;
padding-left: 5px;
}
.postno {
padding-right: 5px;
}
.error {
color: red;
}
.name , .subject {
font-weight: bold;
@@ -64,6 +23,14 @@ section {
}
input {
width: 70%;
}
input.button {
width: 25%;
}
textarea {
width: 350px;
}
@@ -76,116 +43,16 @@ textarea {
padding: 10px 10px;
}
.download_link,.fname_link {
display: block;
text-align: center;
font-size: 90%;
height: auto
padding: 0.5em 0;
margin: auto;
max-width: 200px;
overflow: hidden;
text-overflow: ellipsis;
max-height: 2.4em;
pre > p {
display: block-inline;
}
.post_body > pre {
font-size: 10pt;
font-weight: unset;
}
pre {
white-space: pre-wrap;
align: center;
font-size: 12pt;
color: black;
display: inline-block;
overflow-wrap: break-word;
word-wrap: break-word;
font-weight: bold;
font-family: sans;
margin-left: auto;
margin-right: auto;
}
table {
margin: auto;
}
table.board-list-table {
width: 100%;
}
table tbody td {
margin: 0;
padding: 4px 15px 4px 4px;
vertical-align: top;
text-align: left;
}
table thead th {
border: 1px solid #000333;
padding: 4px 15px 5px 5px;
background: #98E;
color: #000333;
text-align: left;
white-space: nowrap;
}
table tbody tr:nth-of-type( even ) {
background-color: #D6DAF0;
}
.postform > tr, th {
background: #98E;
font-size: 10pt;
text-align: left;
padding-right: 10px;
padding-left: 4px;
}
.postform td{
padding: 0px 15px 0px 4px;
}
.button {
max-width: 10em;
height: 2em;
.reply, th, .ukko_thread_header {
background: #202331;
}
tr {
background-color: transparent!important;
}
input[type="text"],input[type="password"],textarea {
border: 1px solid #a9a9a9;
text-indent: 0;
text-shadow: none;
text-transform: none;
word-spacing: normal;
font-size: inherit;
font-family: sans-serif;
}
#board_td {
vertical-align: top;
}
.reply, .pagelist, .op {
background: #D6DAF0;
border-width: 1px;
border-style: none solid solid none;
border-color: #B7C5D9;
max-width: 94%!important;
}
.reply, .ukko_thread_header {
background: #D6DAF0;
margin-left: 24px;
}
legend {
/* background-color: #525252; */
background: #202123;
}
hr {
@@ -193,78 +60,19 @@ hr {
border-width: 1px medium medium;
border-color: #b7c5d9;
border-style: solid none none;
clear: both;
}
.board_header {
font-family: tahoma;
letter-spacing: -2px;
font-size: 20pt;
margin: 0;
color: #AF0A0F;
text-align: center;
font-weight: bold;
margin-top: 2em;
a:hover , a:visited:hover {
color: #f13333;
}
.board_header_catalog {
text-align: center;
margin-top: 1em;
a {
color: #4A4AFF;
}
.catalog_thread {
display: inline-block;
width: 180px;
text-align: center;
padding: 0.25em;
height: 240px;
overflow: hidden;
background-color: rgba(182, 182, 182 ,0.12);
margin: 0.1em;
border: 2px solid rgba(111, 111, 111, 0.34);
}
.catalog_thread:hover {
background-color: #D6DAF0;
}
#catalog_container {
width: 95%;
margin: 1em auto;
text-align: center;
}
.catalog_header, .catalog_subject {
font-weight: bold;
}
.linkThumb img {
max-height: 128px;
max-width: 128px;
box-shadow: 0 0 4px rgba(0,0,0,0.55);
border: 2px solid rgba(153,153,153,0);
}
a:hover , a:visited:hover, .navbar-link > label:hover {
color: #ff0000;
}
a, a:visited, .navbar-link > label {
color: #34345C;
}
.postno {
font-style: italic;
}
.postno, .postnol, .postnol:visited {
color: black;
text-decoration: none;
}
.postno:hover, .postnol:hover, .postnol:visited:hover {
color: #ff0000;
text-decoration: underline;
a:visited {
color: #5E00A0;
}
.navbar {
@@ -274,97 +82,74 @@ a, a:visited, .navbar-link > label {
right: 0px;
margin-top: 0px;
z-index: 20;
box-shadow: 1px 10px 20px rgba(0, 0, 0, 0.15);
border-bottom: 1px solid;
background: #D6DAF0;
color: #34345C;
min-height: 20px;
padding-top: 2px;
box-shadow: 0px 1px 20px rgba(0, 0, 0, 0.15);
background: #202331;
color: #616383;
}
.navbar-sep {
color: darkgray;
}
.navbar-link > label {
text-decoration: underline;
color: black;
}
#postform_container {
margin-top: 1em;
}
.navLinks {
padding-bottom: 50px;
margin-top: 5em;
}
.ukko_thread_header {
float: left;
clear: both;
padding: 5px 5px;
margin: 5px 5px;
display: inline-block;
}
th {
font-size: 10pt;
padding-left: 10px;
padding-right: 15px;
}
#captcha_img {
background: #d3d3d3;
}
input, textarea {
color: whitesmoke;
background: #2d2d2d;
border-color: #4D28CA;
border-radius: 3px;
}
html {
color: #ededed;
background: #262230;
height: 100%;
font-family: arial,helvetica,sans-serif;
font-size: 10pt;
margin: 0 4px;
padding-left: 4px;
padding-right: 4px;
}
input, textarea {
}
.ukko_thread_header {
border-color: #C0C3D4;
border-top-left-radius: 0px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 0px;
border-style: dashed;
.reply {
margin-left: 1.8em;
display: inline-block;
border-radius: 3px;
}
.frontend {
margin-top: 0px;
display: inline-block;
margin-right: 5px;
}
.op , .reply {
float: left;
clear: both;
}
.op {
margin-top: 5px;
margin-bottom: 1px;
}
.post:target {
background-color: #A99AFF;
}
.postreply {
float: right;
margin-right: 20px;
margin-bottom: 10px;
}
.post {
background-color: #D6DAF0;
border: 1px solid #B7C5D9;
border-left: none;
border-top: none;
display: table;
padding: 2px;
margin: 2px;
display: inline;
margin-bottom: 5px;
display: inline-block;
float: left;
clear: both;
min-width: 500px;
}
.postheader {
width: 100%;
padding-top: 3px;
padding-right: 5px
}
.tripcode {
@@ -387,21 +172,19 @@ input, textarea {
100% {background-color: green; color: white; }
}
.psy {
.psy > p {
animation: psych 2s linear infinite;
display: inline-block;
}
.memearrows {
color: #789922;
margin-left: none;
color: green;
}
.redtext {
color: #AF0A0F;
font-size: 12pt;
color: #d50505;
font-weight: bold;
margin-left: none;
}
.spoiler {
@@ -426,70 +209,37 @@ input, textarea {
float: left;
}
.expanded-thumbnail {
display: inline;
max-width: 100%;
max-height: 100%;
margin: 10px 10px 10px;
}
.thumbnail {
display: block;
margin: 5px 20px;
.file-thumbnail {
display: block;
margin: 5px 20px 10px;
border: medium none;
max-width: 300px;
max-height: 200px;
}
.reply {
padding: 3px 5px 3px 5px;
float: left;
clear: both;
.reply, .ukko_thread_header , pre {
padding: 7px 7px;
border-radius: 5px;
box-shadow: 1px 1px 3px black;
}
.post_body > pre {
background: #3f3f3f;
color: #d17600;
}
.post_body > p {
margin: 0px 0px;
}
.post_body {
margin-left: 15px;
margin-top: 3px;
margin-bottom: 10px;
}
.post_body_line {
margin-left: inherit;
margin-top: 2px;
margin-bottom: 2px;
padding-top: 0px;
padding-bottom: 0px;
display: inline;
width: inherit;
}
.pagelist {
.post {
display: inline-block;
overflow: hidden;
margin-left: 15px;
margin-top: 20px;
margin-bottom: 50px;
padding: 10px 10px 10px 10px;
font-size: 12pt;
font-weight: medium;
}
.backlink_rewritten {
z-index: 5;
}
.backlink, .backlink:hover, .backlink:visited, .backlink:visited:hover, .backlink_rewritten {
margin-left: none;
color: #D00
}
.origin > img{
height: 25px;
vertical-align: text-top;
margin-right: 5px;
}
.postform_attachment {
max-width: 330px;
width: 80%;
}
#faq > div {
@@ -499,87 +249,5 @@ input, textarea {
}
#captcha_img {
background: white;
}
figure {
float: left;
margin: 1ex;
}
img.thumbnail {
max-width: 350px;
max-height: 350px;
}
.replybar {
float: left;
position: fixed;
top: 2ex;
width: 500px;
}
@media (max-width: 100ex) {
.replybar {
width: 100%;
position: static;
}
}
.hide-reply:checked ~ div {
width: 100%;
position: static;
}
textarea#reply-text {
width: 100%;
resize: vertical;
}
.hide-reply {
float: right;
position: fixed;
top: 0;
right: 0;
}
#nntpchan_banner {
align: center;
}
.legal {
font-size: 10px;
text-align: center;
}
.attachments {
float: left;
margin-bottom: 5px;
}
.navbar-links, .navbar-link, .navbar-name {
padding-left: 2px;
padding-right: 2px;
}
.board_title {
padding-right: 4px;
}
.thread {
padding-left: 10px;
padding-top: 10px;
padding-bottom: 10px;
width: 80%;
}
.hider {
float: right;
}
@keyframes rotate {
0% { transform:rotate(0deg); }
25% { transform:rotate(-1deg); }
50% { transform:rotate(0deg); }
75% { transform:rotate(1deg); }
}
background: grey;
}

View File

@@ -1,649 +0,0 @@
body
{
background: #0B0B0B;
color: #DADADA;
font-family: arial, helvetica, sans-serif;
font-size: 10pt;
margin: 0 4px;
padding-left: 4px;
padding-right: 4px;
}
.post_body > pre {
color: #DADADA;
text-align: left;
}
main,
aside,
section
{
display: block;
margin: 0 auto;
width: 100%;
}
.frontend,
.subject,
.catalog_subject
{
color: #A03F4C;
}
.name
{
color: #117743;
}
.name,
.subject
{
font-weight: bold;
}
#postform-outer
{
text-align: center;
}
textarea
{
width: 350px;
}
#postform-inner
{
display: inline-block;
}
.post
{
padding: 10px 10px;
}
pre
{
white-space: pre-wrap;
text-align: center;
font-size: 13pt;
background: #98E;
color: black;
display: inline-block;
overflow-wrap: break-word;
word-wrap: break-word;
font-weight: bold;
padding: 20px 20px 20px 20px;
margin-left: auto;
margin-right: auto;
}
table
{
margin: auto;
}
table.board-list-table
{
width: 100%;
}
table tbody td
{
margin: 0;
padding: 4px 15px 4px 4px;
vertical-align: top;
text-align: left;
}
table thead th
{
background-color: #121212;
border: solid 1px #535353;
color: #DADADA;
text-align: left;
white-space: nowrap;
}
table tbody tr:nth-of-type( even)
{
background-color: #121212;
border: solid 1px #535353;
}
.postform >tr,
th
{
background-color: #121212;
border: solid 1px #535353;
color: #DADADA;
font-size: 10pt;
text-align: left;
padding-right: 10px;
padding-left: 4px;
}
#postform-inner tr
{
background-color: transparent!important;
}
input[type="text"],
input[type="password"],
textarea
{
text-indent: 0;
text-shadow: none;
text-transform: none;
word-spacing: normal;
font-size: inherit;
font-family: sans-serif;
}
#board_td
{
vertical-align: top;
}
.reply,
.pagelist
{
background: #D6DAF0;
border-width: 1px;
border-style: none solid solid none;
border-color: #B7C5D9;
display: inline-block;
max-width: 94%!important;
}
.reply,
.ukko_thread_header
{
background-color: #121212;
border: solid 1px #535353;
color: #DADADA;
margin-left: 24px;
}
hr
{
height: 0px;
border-width: 1px medium medium;
border-color: #b7c5d9;
border-style: solid none none;
clear: both;
}
.board_header
{
font-family: tahoma;
letter-spacing: -2px;
font-size: 20pt;
margin: 0;
color: #AF0A0F;
text-align: center;
font-weight: bold;
margin-top: 2em;
}
.board_header_catalog
{
text-align: center;
margin-top: 1em;
}
.catalog_thread
{
display: inline-block;
width: 180px;
text-align: center;
padding: 0.25em;
height: 240px;
overflow: hidden;
background-color: rgba(182, 182, 182, 0.12);
margin: 0.1em;
border: 2px solid rgba(111, 111, 111, 0.34);
}
.catalog_thread:hover
{
background-color: #D6DAF0;
}
#catalog_container
{
width: 95%;
margin: 1em auto;
text-align: center;
}
.catalog_header,
.catalog_subject
{
font-weight: bold;
}
.linkThumb img
{
max-height: 128px;
max-width: 128px;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.55);
border: 2px solid rgba(153, 153, 153, 0);
margin-bottom: 5px;
}
a:hover,
a:visited:hover,
.navbar-link > label:hover
{
color: #ff0000;
}
a,
a:visited,
.navbar-link > label
{
color: #B8B8F7;
;
}
.postno
{
font-style: italic;
}
.postno,
.postnol,
.postnol:visited
{
color: #B8B8F7;
text-decoration: none;
}
.postno:hover,
.postnol:hover,
.postnol:visited:hover
{
color: #ff0000;
text-decoration: underline;
}
.navbar
{
position: fixed;
padding: 0.25em;
text-align: center;
top: 0px;
left: 0px;
right: 0px;
margin-top: 0px;
z-index: 20;
background-color: #121212;
border-bottom: solid 1px #535353;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
color: #B8B8F7;
}
.navbar-sep
{
color: darkgray;
}
.navbar-link > label
{
text-decoration: underline;
}
#postform_container
{
margin-top: 1em;
}
.navLinks
{
padding-bottom: 50px;
}
.ukko_thread_header
{
float: left;
clear: both;
display: inline-block;
padding-left: 4px;
padding-right: 4px;
}
input,
textarea
{
background-color: #121212;
border: solid 1px #535353;
color: #DADADA;
}
.ukko_thread_header
{
border-color: #C0C3D4;
border-top-left-radius: 0px;
border-top-right-radius: 3px;
border-bottom-right-radius: 3px;
border-bottom-left-radius: 0px;
border-style: dashed;
}
.frontend
{
margin-top: 0px;
display: inline-block;
}
.op
{
margin-bottom: 1px;
float: left;
clear: both;
background-color: #0f0f0f;
border-color: #242424;
}
pre {
background-color: #0f0f0f;
}
.post
{
margin-bottom: 5px;
min-width: 500px;
}
.postheader
{
padding-top: 3px;
padding-right: 5px
}
.tripcode
{
color: #de04ef;
}
@keyframes psych
{
0%
{
background-color: red;
color: blue;
}
10%
{
background-color: yellow;
color: red;
}
20%
{
background-color: blue;
color: green;
}
30%
{
background-color: green;
color: yellow;
}
40%
{
background-color: red;
color: blue;
}
50%
{
background-color: yellow;
color: green;
}
60%
{
background-color: blue;
color: yellow;
}
70%
{
background-color: green;
color: blue;
}
80%
{
background-color: red;
color: green;
}
90%
{
background-color: yellow;
color: red;
}
95%
{
background-color: blue;
color: yellow;
}
100%
{
background-color: green;
color: white;
}
}
.psy
{
animation: psych 2s linear infinite;
display: inline-block;
}
.memearrows
{
color: #789922;
margin-left: 0;
}
.redtext
{
color: #AF0A0F;
font-size: 12pt;
font-weight: bold;
margin-left: 0;
}
.spoiler
{
display: inline-block;
}
.spoiler > p
{
background: black;
color: black;
}
.spoiler:hover > p
{
background: black;
color: white;
}
.intro
{
margin-bottom: 0.75em;
}
.file:not(.multiple) .file-thumbnail
{
float: left;
}
.file-thumbnail
{
display: block;
margin: 5px 20px 10px;
border: medium none;
max-width: 300px;
max-height: 200px;
}
.reply
{
padding: 3px 5px 3px 5px;
float: left;
clear: both;
}
.post_body
{
margin-left: 15px;
margin-top: 3px;
margin-bottom: 10px;
}
.post_body_line
{
margin-left: inherit;
margin-top: 2px;
margin-bottom: 2px;
padding-top: 0px;
padding-bottom: 0px;
}
.post
{
display: inline-block;
overflow: hidden;
}
.pagelist
{
display: inline-block;
background-color: #121212;
border: solid 1px #535353;
color: #DADADA;
text-align: center;
overflow: hidden;
margin-left: 2em;
padding: 10px 10px 10px 10px;
font-weight: 700;
}
.backlink,
.backlink:hover,
.backlink:visited,
.backlink:visited:hover
{
margin-left: 0;
color: #D00
}
.origin > img
{
height: 20px;
vertical-align: text-top;
}
.postform_attachment
{
max-width: 330px;
}
#faq > div
{
margin-left: 20%;
width: 50%;
padding: 10px;
}
#captcha_img
{
background-color: #121212;
border: solid 1px #535353;
}
figure
{
float: left;
margin: 1ex;
}
img.thumbnail
{
max-width: 350px;
max-height: 350px;
}
.replybar
{
float: left;
position: fixed;
top: 2ex;
right: 0;
width: 34%
}
@media (max-width: 100ex)
{
.replybar
{
width: 100%;
position: static;
}
}
.hide-reply:checked ~ div
{
width: 100%;
position: static;
}
textarea#reply-text
{
width: 100%;
resize: vertical;
}
.hide-reply
{
float: right;
position: fixed;
top: 0;
right: 0;
}
#nntpchan_banner
{
text-align: center;
margin-top: 1em;
}
.legal
{
font-size: 10px;
text-align: center;
}
.attachments
{
float: left;
}
hr
{
border: 1px solid #535353;
height: 1px
}
img#nntpchan_banner {
box-shadow: 0px 0px 10px 0px;
}
#postform_container {
background-color: rgba(0, 0, 0, 0);
}
.post:target {
background-color: #2c2d3e;
box-shadow: 0px 0px 10px 2px;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -1,3 +0,0 @@
/*
put your custom stylesheet here
*/

View File

@@ -1,27 +0,0 @@
{{!
board.mustache, displays the contents of page N of the board
template parameters:
- board ( the model of the current page )
- page ( the page number of the current page )
board has the properties:
- Prefix ( absolute path like //site.tld/ or /path/ )
- Board ( the name of the board )
- Navbar ( a function that renders the navbar, should not be escaped )
- Threads ( a list of Thread Models with the last few replies )
}}
<!doctype html><html><head><link rel="stylesheet" href="/static/chen-chan.css"><title>{{board.Board}}</title></head><body>
{{{board.Navbar}}}
{{{form}}}
<dl>
<hr>
{{#board.Threads}}
{{{OP.Truncate.RenderPost}}}
{{#Truncate.Replies}}
{{{Truncate.RenderPost}}}
{{/Truncate.Replies}}
<hr>
{{/board.Threads}}
</dl>
</body></html>

View File

@@ -1,33 +0,0 @@
{{!
boardlist.mustache -- full list of every board
template parameters:
- graph ( a list of 4 string tuples: (board, posts_per_hour, posts_per_day, total_posts) )
- frontend ( the name of the frontend )
- prefix ( the site's prefix )
}}
<!doctype html><html><head><link rel="stylesheet" href="/static/chen-chan.css"><title>{{frontend}} boards</title></head><body>
<table>
<tr>
<th>board</th>
<th>posts per hour</th>
<th>posts per day</th>
<th>total</th>
</tr>
{{# graph}}
<tr>
<td>
<a href="{{prefix}}{{Board}}-0.html">{{Board}}</a>
</td>
<td>
{{Hour}}
</td>
<td>
{{Day}}
</td>
<td>
{{All}}
</td>
</tr>
{{/ graph}}
</table>
</body></html>

View File

@@ -1,11 +0,0 @@
{{!
frontpage.mustache -- template for index.html
template parameters:
- boardgraph ( a boardPageRows instance, see srnd/model.go )
- postgraph ( a postsGraph instance , see srnd/model.go )
- overview ( an overviewModel instance, see srnd/model.go )
- totalposts ( the number of total posts we have ever seen )
- frontend ( the name of the frontend )
- prefix ( the site's prefix )
}}
<!doctype html><html><head><link rel="stylesheet" href="/static/chen-chan.css"><title>{{frontend}} on nntpchan</title></head><body><p>{{frontend}} on nntpchan</p><p><a href="ukko.html">overboard</a> <a href="{{prefix}}boards.html">boards</a> <a href="{{prefix}}static/chen-chan-faq.html">faq</a></p><dl><dt>last posts:</dt><dd>{{{overview.Render}}}</dd></dl><dl><dt>board stats:</dt><dd><table><tr><th>board</th><th>posts this hour</th><th>posts today</th><th>total</th></tr>{{# boardgraph}}<tr><td><a href="{{prefix}}{{Board}}-0.html">{{Board}}</a></td><td>{{Hour}}</td><td>{{Day}}</td><td>{{All}}</td></tr>{{/ boardgraph}}</table></dd></dl><dl><dt>total stats:</dt><dd>{{{postsgraph.Render}}}</dd></dl><p>{{totalposts}} posts total since 2015-12-23</p></body></html>

View File

@@ -1,36 +0,0 @@
{{!
graph_history.mustache
template parameters:
- history ( a list of PostEntry instances, see srnd/model.go )
}}
<html>
<head>
<meta charset="utf-8"></meta>
<link rel="stylesheet" href="{{prefix}}site.css" />
<link rel="stylesheet" href="{{prefix}}user.css" />
<title> Post History</title>
</head>
<body>
<td>
<table id="history_graph">
<thead>
<tr>
<th>Month</th>
<th>Posts</th>
<th></th>
</tr>
</thead>
<tbody>
{{#history.Scale}}
<tr>
<td>{{Date}}</td>
<td class="history_num">{{Num}}</td>
<td>{{OvercockGraph}}</td>
</tr>
{{/history.Scale}}
</tbody>
</table>
</td>
</body>
</html>

View File

@@ -1,15 +0,0 @@
{{!
keygen.mustache -- page containing a newly generated tripcode
template parameters:
- prefix ( the site prefix )
- public ( the public key in hex )
- secret ( the secret key in hex )
- tripcode ( html version of the public key )
}}
<!doctype html><html><head><title>new tripcode</title></head><body><pre>
new tripcode
secret: {{secret}}
public: {{public}}
tripcode: {{{tripcode}}}
</pre></body></html>

View File

@@ -1,7 +0,0 @@
<!doctype html><html><head><link rel="stylesheet" href="{{prefix}}static/chen-chan.css"><title>login</title></head><body>
<form action="login" method="POST" name="modlogin">
<label for="mod_key">secret</label>
<input type="password" id="mod_key" name="privkey">
<input type="submit" value="login">
</form>
</body></html>

View File

@@ -1,9 +0,0 @@
{{!
modlogin_result.mustache -- shows the result of a login attempt
template parameters:
- prefix ( the site prefix )
- mod_prefix ( the prefix to the mod panel, could be something like https://mod.site.tld/ or /mod/ )
- message ( the message returned from the login attempt )
- fail ( present if the login failed ) // not yet added
}}
<!doctype html><html><head><meta http-equiv="refresh" content="1; {{mod_prefix}}"><title>login</title></head><body><p>{{message}}</p></body></html>

View File

@@ -1,79 +0,0 @@
{{!
modpage.mustache -- the moderator panel when logged in
template parameters:
- prefix ( the site's prefix )
}}
<!doctype html><html><head>
<link rel="stylesheet" href="{{prefix}}static/chen-chan.css">
<!-- yes it uses js -->
<script src="{{prefix}}static/mod.js"></script>
<title>nntpchan mod page</title>
</head><body>
<dl>
<dt>post actions</dt>
<dd>
<div>
<label for="nntpchan_mod_target">target</label>
<input id="nntpchan_mod_target" type="text">
</div>
<div>
<button onclick="nntpchan_ban()">ban (url)</button>
<button onclick="nntpchan_delete()">delete (url)</button>
<button onclick="nntpchan_unban()">unban (ip)</button>
</div>
</dd>
</dl>
<dl>
<dt>key actions</dt>
<dd>
<div>
<label for="nntpchan_board_target">pubkey:</label>
<input type="text" id="nntpchan_key_target" />
</div>
<div>
<button onclick="nntpchan_key_add()">add key</button>
<button onclick="nntpchan_key_del()">remove key</button>
</div>
</dd>
</dl>
<dl>
<dt>board actions</dt>
<dd>
<div>
<label for="nntpchan_board_target">board name:</label>
<input type="text" id="nntpchan_board_target">
</div>
<div>
<button onclick="nntpchan_admin_board('frontend.add')">add board</button>
<button onclick="nntpchan_admin_board('frontend.regen')">regenerate</button>
<button onclick="nntpchan_admin_board('frontend.ban')">ban</button>
<button onclick="nntpchan_admin_board('frontend.unban')">unban</button>
<button onclick="nntpchan_admin_board('frontend.nuke')">nuke</button>
</div>
</dd>
</dl>
<dl>
<dt>lightweight actions</dt>
<dd>
<button onclick="nntpchan_admin('template.reload')">reload all templates</button>
</dd>
</dl>
<dl>
<dt>very load heavy actions, use with care</dt>
<dd>
<div>
<button onclick="nntpchan_admin('frontend.regen')">regenerate all pages</button>
<button onclick="nntpchan_admin('thumbnail.regen')">regenerate all thumbnails</button>
</div>
</dd>
</dl>
<dl>
<dt>status</dt>
<dd>
<div id="nntpchan_mod_result"></div>
<noscript>
<b>enable js to use the mod panel kthx</b>
</noscript>
</dd>
</dl>
</body></html>

View File

@@ -1,12 +0,0 @@
{{!
navbar.mustache -- element on the top of each nod-mod page
TODO: make this not suck, have a board list, have board page list
template parameters:
- name ( the name of whatever resource we are on, board/thread )
- frontend ( the name of the frontend we are on )
- links ( a list of Link Models to display )
- prefix ( site prefix )
}}
<p>{{name}} on {{frontend}} | {{# links}} <a href="{{LinkURL}}">{{Text}}</a> {{/ links}} | <a href="{{prefix}}">front page</a> <a href="{{prefix}}ukko.html">overboard</a></p>

View File

@@ -1 +0,0 @@
no apparently.

View File

@@ -1,7 +0,0 @@
{{! overview.mustache
paramters:
- overview (list of PostModels in order of last posted)
}}
<table><tr><th>date</th><th>board</th><th>subject</th></tr>{{#overview}}<tr><td>{{Date}}</td><td><a href="{{Prefix}}{{Board}}-0.html">{{Board}}</a></td><td><a href="{{PostURL}}">{{Truncate.Subject}}</a></td></tr>{{/overview}}</table>

View File

@@ -1,15 +0,0 @@
<dt id="{{PostHash}}">
<a onclick="nntpchan_backlink('{{ShortHash}}');" title="{{MessageID}}">{{ShortHash}}</a>
{{Date}}
{{#IsI2P}}i2p{{/IsI2P}}{{#IsTor}}tor{{/IsTor}}{{#IsClearnet}}clr{{/IsClearnet}}
{{Subject}}
{{Name}}
{{{Pubkey}}}
<a href="{{PostURL}}">[reply]</a>
</dt>
<dd>
{{{RenderBody}}}
{{#Attachments}}
file: <a href="{{Source}}" target="_blank">{{Filename}}</a><br><br>
{{/Attachments}}
</dd>

View File

@@ -1,3 +0,0 @@
<!doctype html><html><head>
<meta http-equiv="refresh" content="1; {{redirect_url}}"></head>
<body><p>fail: {{reason}}</p></body></html>

View File

@@ -1,40 +0,0 @@
<html><head><link rel="stylesheet" href="/static/chen-chan.css"><title>try again</title></head><body>
<form enctype="multipart/form-data" name="post" method="post">
{{#attachment}}
<input type="hidden" name="attachment_data" value="{{attachment}}">
<input type="hidden" name="attachment_filename" value="{{attachment_filename}}">
<input type="hidden" name="attachment_mime" value="{{attachment_type}}">
{{/attachment}}
<input type="hidden" name="reference" value="{{reference}}">
<input type="hidden" name="name" value="{{name}}">
<input type="hidden" name="subject" value="{{subject}}">
<input type="hidden" name="captcha_id" value="{{captcha_id}}">
<input type="hidden" name="message" value="{{message}}">
<div id="postform-outer">
<div id="postform-inner">
<div>{{fail_message}}</div>
<table class="postform">
<tbody>
<tr>
<th>
Captcha
</th>
<td>
<img id="captcha_img" src="{{prefix}}captcha/{{captcha_id}}.png" alt="captcha" />
</td>
</tr>
<tr>
<th>
Solution
</th>
<td>
<input type="text" name="captcha" />
<input type="submit" value="Post" class="button" />
</td>
</tr>
</tbody>
</table>
</div>
</div>
</form>
</body></html>

View File

@@ -1,7 +0,0 @@
{{!
post_success.mustache -- shown when we do a successful post
template parameters:
- redirect_url ( the url of the next page we should redirect to )
- message_id ( the value of the Message-ID header in the post we made, the truncated sha1 of this is the >>posthash )
}}
<!doctype html><html><head><meta http-equiv="refresh" content="2; {{redirect_url}}"></head><body><pre>posted as {{message_id}}</pre></body></html>

View File

@@ -1,20 +0,0 @@
{{!
postform.mustache -- the form for posting
template parameters:
- post_url ( the url of the post form )
- reference ( the post we are replying to, or empty string if it's an op )
- button ( the text for the reply button )
}}
<form action="{{post_url}}" enctype="multipart/form-data" name="post" method="post" style="overflow:auto">
<input type="hidden" name="reference" value="{{reference}}">
<textarea id="postform_message" name="message" placeholder="text" cols="40" rows="10" style="float:left" ></textarea><br>
<div style="float:left">
<input type="file" name="attachment"><br>
<input type="text" name="subject" placeholder="subject"><br>
<input type="text" name="name" placeholder="name"><br>
<input type="text" name="captcha" placeholder="captcha"><br>
<input type="submit" value="{{button}}" class="button"><br>
</div>
<img id="captcha_img" src="{{prefix}}captcha/img" alt="captcha" style="float:left">
</form>

View File

@@ -1,7 +0,0 @@
{{!
posts graph.mustache -- post frequence graph
parameters:
* graph - a postsGraph instance (see srnd/model.go)
}}
<table><tr><th>date</th><th>posts</th><th>cock</th></tr>{{#graph.Scale}}<tr><td>{{Day}}</td><td>{{Num}}</td><td>{{OvercockGraph}}</td></tr>{{/graph.Scale}}</table>

View File

@@ -1,23 +0,0 @@
{{!
thread.mustache -- renders to a thread-*.html page, shows the entire thread
template parameters:
- board ( the Board Model of the board this thread was posted in )
- thread ( the Thread Model of the current thread being rendered )
- form ( the post form markup )
Thread Model attributes:
- OP , the Post Model of the original poster
- Replies , a list of all the replies or empty if none
- Board , the name of the Board this thread is on
- BoardURL , the url that points to the board index page
}}
<!doctype html><html><head><link rel="stylesheet" href="/static/chen-chan.css"><title>{{thread.OP.Subject}}</title><script src="{{thread.Prefix}}static/nntpchan.js"></script></head><body>
{{{thread.Navbar}}}
{{{form}}}
<dl>
{{{thread.OP.RenderPost}}}
{{# thread.Replies}}
{{{RenderPost}}}
{{/ thread.Replies}}
</dl>
</body></html>

View File

@@ -1,19 +0,0 @@
{{!
ukko.mustache -- overboard, contains the last threads posted regardless of newsgroup
template parameters:
- prefix ( site prefix )
- threads ( a list of Thread Models that represent the latest threads )
}}
<!doctype html><html><head><link rel="stylesheet" href="/static/chen-chan.css"><title>ukko/overboard</title></head><body>
<a href="/">frontpage</a>
{{#threads}}
<hr>
<p>posted on <a href="{{{BoardURL}}}">{{OP.Board}}</a></p>
<dl>
{{{OP.Truncate.RenderPost}}}
{{#Truncate.Replies}}
{{{Truncate.RenderPost}}}
{{/Truncate.Replies}}
</dl>
{{/threads}}
</body></html>

View File

@@ -1,25 +0,0 @@
{{!
404.mustache -- 404 page
template parameters:
- prefix (the site prefix)
}}
<html>
<head>
<title> 404 </title>
<meta charset="utf-8" />
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
</head>
<body>
<center>
<div class="not_found">
<img src="{{prefix}}static/404.png"></img>
<div class="not_found_message">
{{#i18n.Translations}}{{resource_not_found}}{{/i18n.Translations}}
</div>
</div>
</center>
</body>
</html>

View File

@@ -11,13 +11,10 @@
- Navbar ( a function that renders the navbar, should not be escaped )
- Threads ( a list of Thread Models with the last few replies )
}}
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link id="current_theme" rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css"></link>
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{board.Board}}</title>
</head>
@@ -25,12 +22,7 @@
<!-- begin navbar -->
{{{board.Navbar}}}
<!-- end navbar -->
<center>
<div id="nntpchan_banner">
</div>
</center>
<div class="board_header">{{board.Board}}</div>
<div class="board_header_catalog"><a href="{{board.Prefix}}catalog-{{board.Name}}.html"> {{#i18n.Translations}}{{catalog_label}}{{/i18n.Translations}} </a></div>
<!-- postform -->
<div id="postform_container">
{{{form}}}
@@ -39,33 +31,17 @@
<div id="threads_container">
{{#board.Threads}}
<div class="thread" id="thread_{{OP.PostHash}}">
<div class="thread_header">
<div clsss="thread_header">
</div>
{{{OP.Truncate.RenderPost}}}
{{#Truncate.Replies}}
{{{Truncate.RenderPost}}}
<br />
{{/Truncate.Replies}}
<hr />
</div>
<br/>
<hr/>
{{/board.Threads}}
</div>
<div class="pagelist">{{# board.PageList }}[<a href="{{LinkURL}}"> {{Text}} </a>] {{/ board.PageList }}
<span class="navbar-sep">
|
</span>
<a href="{{board.Prefix}}catalog-{{board.Name}}.html"> {{#i18n.Translations}}{{catalog_label}}{{/i18n.Translations}} </a>
</div>
<script type="text/javascript">
var prefix = "{{board.Prefix}}";
var e = document.getElementById("nntpchan_banner");
nntpchan_inject_banners(e, prefix);
init(prefix);
ready();
</script>
<hr/>
<footer>
<p class="legal">All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p class="legal">To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</body>
</html>

View File

@@ -1,51 +0,0 @@
{{!
boardlist.mustache -- full list of every board
template parameters:
- graph ( a list of 4 string tuples: (board, posts_per_hour, posts_per_day, total_posts) )
- frontend ( the name of the frontend )
- prefix ( the site's prefix )
}}
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="{{prefix}}static/site.css"></link>
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css"></link>
<title> {{frontend}} {{#i18n.Translations}}{{board_list_title}}{{/i18n.Translations}} </title>
</head>
<body>
<center>
<div class="index-outer">
<div class="index-inner">
<table id="board_graph">
<thead>
<tr>
<th> {{#i18n.Translations}}{{board_label}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{pph_label}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{ppd_label}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{total}}{{/i18n.Translations}} </th>
</tr>
</thead>
<tbody>
{{# graph}}
<tr>
<td>
<a href="{{prefix}}{{Board}}-0.html">{{Board}}</a>
</td>
<td>
{{Hour}}
</td>
<td>
{{Day}}
</td>
<td>
{{All}}
</td>
</tr>
{{/ graph}}
</tbody>
</table>
</div>
</div>
</center>
</body>
</html>

View File

@@ -1,47 +0,0 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<link id="current_theme" rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{catalog_title}}{{/i18n.Translations}} {{board.Name}}</title>
</head>
<body>
<!-- begin navbar -->
{{{board.Navbar}}}
<!-- end navbar -->
<center>
<div id="nntpchan_banner">
</div>
</center>
<div class="board_header">{{#i18n.Translations}}{{catalog_title}}{{/i18n.Translations}} {{board.Name}}</div>
<!-- postform -->
<hr />
<div id="catalog_container">
{{#board.Threads}}
<div class="catalog_thread">
<a class="linkThumb" href="{{OP.PostURL}}"><img src="{{OP.RepresentativeThumb}}"></a>
<div class="catalog_header">{{#i18n.Translations}}{{replies_short_label}}{{/i18n.Translations}}: {{ReplyCount}} / {{#i18n.Translations}}{{pictures_short_label}}{{/i18n.Translations}}: {{Page}}</div>
<div class="catalog_subject">{{OP.Subject}}</div>
<div class="catalog_body">
{{{OP.RenderBody}}}
</div>
</div>
{{/board.Threads}}
</div>
<script type="text/javascript">
var e = document.getElementById("nntpchan_banner");
nntpchan_inject_banners(e, "{{board.Prefix}}");
</script>
<hr/>
<footer>
<p class="legal">All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p class="legal">To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</body>
</html>

View File

@@ -1,9 +1,7 @@
{{!
frontpage.mustache -- template for index.html
template parameters:
- boardgraph ( markup of boardPageRows instance, see srnd/model.go )
- postgraph ( markup of postsGraph instance , see srnd/model.go )
- overview ( markup of overviewModel instance , see srnd/model.go )
- graph ( a list of 4 string tuples: (board, posts_per_hour, posts_per_day, total_posts) )
- totalposts ( the number of total posts we have ever seen )
- frontend ( the name of the frontend )
- prefix ( the site's prefix )
@@ -11,78 +9,50 @@
<html>
<head>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script src="{{prefix}}static/nntpchan.js" type="text/javascript"></script>
<link rel="stylesheet" href="{{prefix}}static/site.css"></link>
<title> {{frontend}} on nntpchan </title>
</head>
<body>
{{{navbar}}}
<center>
<div class="index-outer">
<div id="nntpchan_banner">
</div>
<div class="index-inner">
<div>{{frontend}} on nntpchan</div>
<div>View the <a href="{{prefix}}ukko.html">overboard</a></div>
<div>Join the IRC on <a href="https://qchat.rizon.net/?channels=#nntpchan">rizon</a> or <a href="irc://127.0.0.1:6668/overchan">irc2p</a></div>
<div>Check out the <a href="{{prefix}}boards.html">board list</a></div>
<div>Fork on github: <a href="https://github.com/majestrate/nntpchan/">frontend</a> and <a href="https://github.com/majestrate/srndv2/">core</a></div>
<div>We've Had {{totalposts}} Posts Since August 01 2015 </div>
</div>
<div class="index-inner">
<table>
<tbody>
<tr>
<td class="posts_td">
{{{postsgraph}}}
</td>
<td class="board_td">
{{! todo: move boardgraph into its own file like postsgraph }}
<table id="board_graph">
<thead>
<tr>
<th> {{#i18n.Translations}}{{board_label}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{posts_hour}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{posts_today}}{{/i18n.Translations}} </th>
<th> {{#i18n.Translations}}{{total}}{{/i18n.Translations}} </th>
</tr>
</thead>
<tbody>
{{# boardgraph}}
<tr>
<td>
<a href="{{prefix}}{{Board}}-0.html">{{Board}}</a>
</td>
<td>
{{Hour}}
</td>
<td>
{{Day}}
</td>
<td>
{{All}}
</td>
</tr>
{{/ boardgraph}}
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
{{{overview}}}
</div>
<div class="index-outer">
<div class="index-inner">
<h1> {{frontend}} on nntpchan </h1>
<h2>View the <a href="ukko.html">overboard</a></h2>
<h3>Read the <a href="{{prefix}}static/faq.html">FAQ</a></h3>
<h3>Join the <a href="https://qchat.rizon.net/?channels=#nntpchan">IRC</a></h3>
<h3>Create a <a href="{{prefix}}new/">new board</a></h3>
<h3>Fork on github: <a href="https://github.com/majestrate/nntpchan/">frontend</a> and <a href="https://github.com/majestrate/srndv2/">core</a></h3>
<h4>We've Had {{totalposts}} Posts Since August 01 2015 </h4>
</div>
<div class="index-inner">
<table id="board_graph">
<tbody>
<tr>
<th> Board </th>
<th> Post per Hour </th>
<th> Post per Day </th>
<th> Total </th>
</tr>
{{# graph}}
<tr>
<td>
<a href="{{prefix}}{{Board}}-0.html">{{Board}}</a>
</td>
<td>
{{Hour}}
</td>
<td>
{{Day}}
</td>
<td>
{{All}}
</td>
</tr>
{{/ graph}}
</tbody>
</table>
</div>
</div>
</center>
<script type="text/javascript">
var e = document.getElementById("nntpchan_banner");
nntpchan_inject_banners(e, "{{prefix}}");
</script>
<hr/>
<footer>
<p class="legal">All posts on this site are the responsibility of the individual poster and not the administration, pursuant to 47 U.S.C. § 230.</p>
<p class="legal">To make a DMCA request or report illegal content, please contact the administration</p>
</footer>
</body>
</html>
</html>

View File

@@ -1,37 +0,0 @@
{{!
graph_history.mustache
template parameters:
- history ( a list of PostEntry instances, see srnd/model.go )
}}
<!doctype html>
<html>
<head>
<meta charset="utf-8"></meta>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link rel="stylesheet" href="{{prefix}}static/user.css" />
<title>{{#i18n.Translations}}{{post_history_title}}{{/i18n.Translations}}</title>
</head>
<body>
<td>
<table id="history_graph">
<thead>
<tr>
<th>{{#i18n.Translations}}{{month}}{{/i18n.Translations}}</th>
<th>{{#i18n.Translations}}{{posts}}{{/i18n.Translations}}</th>
<th></th>
</tr>
</thead>
<tbody>
{{#history.Scale}}
<tr>
<td>{{Date}}</td>
<td class="history_num">{{Num}}</td>
<td>{{OvercockGraph}}</td>
</tr>
{{/history.Scale}}
</tbody>
</table>
</td>
</body>
</html>

View File

@@ -1,64 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{api_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{api_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{api_username}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="user" value="{{dialog.User}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{api_password_name}}{{/i18n.Translations}}
</th>
<td>
<input type="password" name="pass" value="">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{api_secret_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="secret" value="{{dialog.Secret}}">
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,64 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{binary_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{binary_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{convert_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="convert" value="{{dialog.Convert}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{ffmpeg_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="ffmpeg" value="{{dialog.FFmpeg}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{sox_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="sox" value="{{dialog.Sox}}">
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,64 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{cache_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{cache_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
</th>
<td>
<input type="radio" name="cache" value="file" checked> {{#i18n.Translations}}{{file_cache_name}}{{/i18n.Translations}}
</td>
</tr>
<tr>
<th>
</th>
<td>
<input type="radio" name="cache" value="null"> {{#i18n.Translations}}{{null_cache_name}}{{/i18n.Translations}}
</td>
</tr>
<tr>
<th>
</th>
<td>
<input type="radio" name="cache" value="redis"> {{#i18n.Translations}}{{redis_cache_name}}{{/i18n.Translations}}
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,56 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{crypto_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{crypto_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{host_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="host" value="{{dialog.Host}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{key_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="key" value="{{dialog.Key}}">
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,56 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{db_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{db_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
</th>
<td>
<input type="radio" name="db" value="postgres"> {{#i18n.Translations}}{{postgres_name}}{{/i18n.Translations}}
</td>
</tr>
<tr>
<th>
</th>
<td>
<input type="radio" name="db" value="redis" checked> {{#i18n.Translations}}{{redis_name}}{{/i18n.Translations}}
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,72 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{frontend_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{frontend_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{frontend_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="name" value="{{dialog.Name}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{locale_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="locale" value="{{dialog.Locale}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{allow_files_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="checkbox" name="allow_files" value="1" checked>
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{enable_json_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="checkbox" name="json" value="1">
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,56 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{key_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{key_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{pubkey_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="public" value="{{dialog.Public}}" size="65" readonly>
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{secretkey_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="secret" value="{{dialog.Secret}}" size="65" readonly>
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,80 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{nntp_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{nntp_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{nntp_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="nntp_name" value="{{dialog.Name}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{allow_attachments_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="checkbox" name="allow_attachments" value="1" checked>
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{allow_anon_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="checkbox" name="allow_anon" value="1" checked>
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{allow_anon_attachments_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="checkbox" name="allow_anon_attachments" value="1">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{require_tls_prompt}}{{/i18n.Translations}}
</th>
<td>
<input type="checkbox" name="require_tls" value="1" checked>
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,72 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{postgres_db_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{postgres_db_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{host_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="host" value="{{dialog.Host}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{port_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="port" value="{{dialog.Port}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{username_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="user" value="{{dialog.Username}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{password_name}}{{/i18n.Translations}}
</th>
<td>
<input type="password" name="password" value="">
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,64 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{redis_db_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{redis_db_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{host_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="host" value="{{dialog.Host}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{port_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="port" value="{{dialog.Port}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{password_name}}{{/i18n.Translations}}
</th>
<td>
<input type="password" name="password" value="">
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -1,64 +0,0 @@
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale=1" />
<link rel="stylesheet" href="{{board.Prefix}}static/site.css" />
<link rel="stylesheet" href="{{board.Prefix}}static/user.css" />
<script type="text/javascript" src="{{board.Prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{redis_db_install_title}}{{/i18n.Translations}}</title>
</head>
<body>
<div class="installer_header">{{#i18n.Translations}}{{redis_db_install_title}}{{/i18n.Translations}}</div>
{{#dialog.HasError}}
<p class="installer_error">{{dialog.Error}}</p>
{{/dialog.HasError}}
<form action="" method="post" autocomplete="off">
<div id="install-outer">
<div id="installer-inner">
<table class="installer-tab">
<tbody>
<tr>
<th>
{{#i18n.Translations}}{{host_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="host" value="{{dialog.Host}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{port_name}}{{/i18n.Translations}}
</th>
<td>
<input type="text" name="port" value="{{dialog.Port}}">
</td>
</tr>
<tr>
<th>
{{#i18n.Translations}}{{password_name}}{{/i18n.Translations}}
</th>
<td>
<input type="password" name="password" value="">
</td>
</tr>
</tbody>
</table>
</div>
{{#dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{next_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
{{^dialog.HasNext}}
<input type="submit" value="{{#i18n.Translations}}{{finish_prompt}}{{/i18n.Translations}}" class="button" />
{{/dialog.HasNext}}
</div>
</form>
{{#dialog.HasPrevious}}
<div class="back_button">
<form action="" method="post">
<input type="hidden" name="back" value="true" />
<input type="submit" value="{{#i18n.Translations}}{{previous_prompt}}{{/i18n.Translations}}" class="button" />
</form>
</div>
{{/dialog.HasPrevious}}
</body>
</html>

View File

@@ -5,19 +5,16 @@
- public ( the public key in hex )
- secret ( the secret key in hex )
- tripcode ( html version of the public key )
}}
<!doctype html>
}}
<html>
<head>
<meta charset="utf-8"></meta>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
<title>{{#i18n.Translations}}{{new_tripcode_title}}{{/i18n.Translations}}</title>
<link rel="stylesheet" href="{{prefix}}static/site.css"></link>
<title> here is a new tripcode </title>
</head>
<body>
<pre>{{#i18n.Translations}}{{new_tripcode_label}}{{/i18n.Translations}}:
<div id="secret_key">{{#i18n.Translations}}{{secret_key}}{{/i18n.Translations}}: {{secret}}</div><div id="public_key">{{#i18n.Translations}}{{public_key}}{{/i18n.Translations}}: {{public}}</div><div>{{#i18n.Translations}}{{tripcode}}{{/i18n.Translations}}: <span class="tripcode" id="capcode_key">{{{tripcode}}}</span></div>
<pre>New Tripcode:
<div id="secret_key">secret: {{secret}}</div><div id="public_key">public: {{public}}</div><div>tripcode: <span class="tripcode" id="capcode_key">{{{tripcode}}}</span></div>
</pre>
</body>
</html>
</html>

View File

@@ -1,32 +0,0 @@
{{!
live.mustache -- live ui
template parameters:
- prefix ( site prefix )
}}
<!doctype html>
<html>
<head>
<title> {{#i18n.Translations}}{{overboard_title}}{{/i18n.Translations}} </title>
<meta charset="utf-8" />
<meta name="viewport" content="initial-scale = 1.0,maximum-scale = 1.0" />
<link rel="stylesheet" href="{{prefix}}static/livechan.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<style type="text/css" id="convo_filter">
</style>
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
</head>
<body>
<noscript>you need js for livechan mode</noscript>
<script type="text/javascript" >
var e = document.createElement("div");
e.setAttribute("id", "chat");
e.setAttribute("style", "position:fixed;left:0;right:0;top:0;bottom:0;width:100%;height:100%;");
document.body.appendChild(e);
var board = "";
if (location.hash != "" ) {
board = location.hash.substr(1);
}
new Chat(e, board, { prefix : "{{prefix}}" });
</script>
</body>
</html>

View File

@@ -1,49 +0,0 @@
{{!
modpage.mustache -- the moderator panel when logged in
template parameters:
- prefix ( the site's prefix )
}}
<!doctype html>
<html>
<head>
<link rel="stylesheet" href="{{prefix}}static/site.css" />
<link id="current_theme" rel="stylesheet" href="{{prefix}}static/user.css" />
<!-- yes it uses js -->
<script type="text/javascript" src="{{prefix}}static/nntpchan.js"></script>
<script type="text/javascript" src="{{prefix}}static/mod.js"></script>
<script type="text/javascript" src="{{prefix}}static/feed.js"></script>
<title> {{#i18n.Translations}}{{modpage_title}}{{/i18n.Translations}} </title>
</head>
<body onload="main()">
<div id="nntpchan_mod_result"></div>
<div class="nntpchan_feed_pane">
<pre> Add Feed </pre>
<label for="add_feed_host">Host</label>
<input id="add_feed_host" />
<label for="add_feed_host">Port</label>
<input id="add_feed_port" />
<label for="add_feed_name">Name</label>
<input id="add_feed_name" />
<button onclick="nntp_feed_add()">Add</button>
</div>
<div class="nntpchan_feed_pane">
<pre> Remove Feed </pre>
<label for="del_feed_name">Name</label>
<input id="del_feed_name" />
<button onclick="nntp_feed_del()">Remove</button>
</div>
<div class="nntpchan_feed_pane">
<pre> Sync Feeds </pre>
<button onclick="nntpchan_admin('feed.sync')">Sync All</button>
</div>
<div id="nntpchan_feed_result"></div>
<div id="nntpchan_feeds"></div>
<script>
nntp_feed_update();
</script>
<noscript>
<b>{{#i18n.Translations}}{{nojs_info}}{{/i18n.Translations}}</b>
</noscript>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More