From c86f573440ec291ea16903bb97e895d15c4ca9c7 Mon Sep 17 00:00:00 2001 From: "Tristan B. Kildaire" Date: Sat, 2 Jul 2016 14:15:59 +0200 Subject: [PATCH 01/90] Update README.md --- doc/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/README.md b/doc/README.md index 9ae1c77..f78915b 100644 --- a/doc/README.md +++ b/doc/README.md @@ -9,7 +9,7 @@ Hey, welcome to the documentation. This will help you use and develop with NNTPC 2. [Setting up NNTPChan](setting-up.md) - Configuring the node 3. [Running NNTPChan](running.md) - Running the node for the first time 4. [Managing your NNTPChan node with the CLI](cli.md) - Manage many aspects of your node via the command-line interface -5. [Configuring your news reader for NNTPChan](extras/configure-newsreader.md) - Setup **Mozilla Thunderbird** or **Pan** to send and receive articles from your node. +5. [Configuring your news reader for NNTPChan](extras/configure-newsreader.md) - Setup **Mozilla Thunderbird** or **Pan** to send and receive articles from your NNTPChan node of choice. ##Developer related From 40eeb116de3f9cb29129f72126817c8253092db1 Mon Sep 17 00:00:00 2001 From: wzeth Date: Mon, 4 Jul 2016 02:27:56 -0400 Subject: [PATCH 02/90] document archive in [nntp] --- doc/srnd.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/srnd.md b/doc/srnd.md index b394243..5babfba 100644 --- a/doc/srnd.md +++ b/doc/srnd.md @@ -16,6 +16,7 @@ allow_attachments=1 require_tls=1 anon_nntp=0 feeds=/etc/nntpchan/feeds.d +archive=0 [pprof] enable=0 @@ -112,6 +113,9 @@ This is where you put the address and port that you would like the NNTP server t ####feeds * Feeds configurations can optionally be stored in a directory of your choosing (the default is `feeds.d` in the working directory). Any ini files located in this directory will be loaded. +####archive +* When this is set to `1`, the daemon will never expire posts. +* When this is set to `0`, the daemon will delete old posts. FIXME: under what conditions? ##`[pprof]` From 28b1864841a34be51ec2dc2ff29bc775e1163da2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 4 Jul 2016 10:12:03 -0400 Subject: [PATCH 03/90] add new livechan ui --- build-js.sh | 10 +- contrib/js/livechan.js | 1292 ++++++++++++++++++++++- contrib/js/old-livechan.js | 65 ++ contrib/static/livechan.css | 239 +++++ contrib/templates/default/live.mustache | 30 +- 5 files changed, 1562 insertions(+), 74 deletions(-) create mode 100644 contrib/js/old-livechan.js create mode 100644 contrib/static/livechan.css diff --git a/build-js.sh b/build-js.sh index f463106..c5fccb6 100755 --- a/build-js.sh +++ b/build-js.sh @@ -21,11 +21,11 @@ if [ ! -f $GOPATH/bin/gopherjs ]; then fi # build cuckoo miner -echo "Building cuckoo miner" -go get -v -u github.com/ZiRo-/cuckgo/miner_js -$GOPATH/bin/gopherjs -m -v build github.com/ZiRo-/cuckgo/miner_js -mv ./miner_js.js ./contrib/static/miner-js.js -rm ./miner_js.js.map +#echo "Building cuckoo miner" +#go get -v -u github.com/ZiRo-/cuckgo/miner_js +#$GOPATH/bin/gopherjs -m -v build github.com/ZiRo-/cuckgo/miner_js +#mv ./miner_js.js ./contrib/static/miner-js.js +#rm ./miner_js.js.map outfile=$PWD/contrib/static/nntpchan.js diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 790f5e5..e2194df 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -1,65 +1,1255 @@ -function livechan_got_post(widget, j) { - // do scroll - while (widget.children.length > 5) { - // remove top element - widget.removeChild(widget.children[0]); + + +function buildCaptcha(domElem, prefix) { + var captcha_widget = document.createElement("div"); + + captcha_widget.className = "livechan_captcha_inner"; + + var outer = document.createElement("div"); + + outer.className = "livechan_captcha"; + + var text = document.createElement("div"); + text.textContent = "solve the captcha"; + captcha_widget.appendChild(text); + + var captcha_image = document.createElement("img"); + captcha_image.className = "livechan_captcha_image"; + var div = document.createElement("div"); + div.appendChild(captcha_image); + captcha_widget.appendChild(div); + + var captcha_entry = document.createElement("input"); + captcha_entry.className = "livechan_captcha_input"; + var div = document.createElement("div"); + div.appendChild(captcha_entry); + captcha_widget.appendChild(div); + + var captcha_submit = document.createElement("input"); + captcha_submit.setAttribute("type", "button"); + captcha_submit.value = "solve"; + var div = document.createElement("div"); + div.appendChild(captcha_submit); + captcha_widget.appendChild(div); + + outer.appendChild(captcha_widget); + domElem.appendChild(outer); + + return { + widget: outer, + button: captcha_submit, + image: captcha_image, + entry: captcha_entry, + prefix: prefix, } - nntpchan_buildpost(widget, j); - // scroll to bottom - widget.scrollTop = widget.scrollHeight; } -// inject post form into an element -function inject_postform(prefix, parent) { +function Captcha(domElem, options, callback) { + if (options) { + this.options = options; + } else { + this.options = {}; + } + + this.prefix = options.prefix || "/"; + this.widget = buildCaptcha(domElem, this.prefix); + var self = this; + this.widget.button.addEventListener("click", function() { self.process(callback); }); +} + +Captcha.prototype.load = function() { + var self = this; + var xhr = new XMLHttpRequest(); + + var url = location.protocol + "//" + location.host + this.prefix ; + + xhr.open('get', url +"captcha/new"); + xhr.onreadystatechange = function () { + if (xhr.readyState == 4 && xhr.status == 200) { + var jdata = JSON.parse(xhr.responseText); + if ( jdata ) { + self.setCaptchaId(jdata); + } + } + } + + xhr.send(); +} + +/** + * @brief set captcha id + */ +Captcha.prototype.setCaptchaId = function(data) { + this.captcha_id = data.id; + this.setImageUrl(data.url); +} + +Captcha.prototype.setImageUrl = function(url) { + this.widget.image.setAttribute("src", url); +} + +/** + * @brief process captcha form + */ +Captcha.prototype.process = function(callback) { + console.log("process"); + console.log(this); + if (this.captcha_id) { + var solution = this.widget.entry.value; + var self = this; + callback(this.captcha_id, solution , function(solved) { + if (solved) { + // woot we solved it + self.hide(); + } else { + // TODO: inform user of bad solution + self.load(); + } + }); + } else { + // TODO: inform user of no captcha entred + self.load(); + } +} + +/** + * @brief show the captcha pane + */ +Captcha.prototype.show = function () { + console.log("hide captcha"); + var widget = this.widget.widget; + if ( widget.style ) { + widget.style.zIndex = 5; + } else { + widget.style = {zIndex: 5}; + } +} +/** + * @brief hide the captcha pane + */ +Captcha.prototype.hide = function () { + console.log("hide captcha"); + var widget = this.widget.widget; + if ( widget.style ) { + widget.style.zIndex = -1; + } else { + widget.style = {zIndex: -1}; + } +} + +/** + * build login widget + * for captcha / mod login + */ +function buildLogin(domElem) { + var widget = document.createElement("div"); + widget.className = "livechan_login_widget"; + widget.style.zIndex = -1; + + + var mod_div = document.createElement("div"); + mod_div.className = "livechan_login"; + + var mod_form = document.createElement("form"); + + var mod_username = document.createElement("input"); + mod_form.appendChild(mod_username); + mod_username.className = "livechan_login_username"; + + var mod_password = document.createElement("input"); + mod_password.className = "livechan_login_password"; + mod_password.setAttribute("type", "password"); + mod_form.appendChild(mod_password); + + var mod_submit = document.createElement("input"); + mod_password.className = "livechan_login_submit"; + mod_submit.setAttribute("type", "submit"); + mod_submit.setAttribute("value", "login"); + mod_form.appendChild(mod_submit); + mod_div.appendChild(mod_form); + widget.appendChild(mod_div); + domElem.appendChild(widget); + return { + widget: widget, + mod: { + form: mod_form, + username: mod_username, + password: mod_password, + submit: mod_submit + } + } +} + + +function Login(domElem) { + this._login = buildLogin(domElem); +} + +Login.prototype.show = function() { + var self = this; + self._login.widget.style.zIndex = 5; + console.log("show login widget"); +} + + + +/* + * @brief build livechan navbar + * @param domElem the root element to put the navbar in + */ +function LivechanNavbar(domElem) { + + this.navbar = document.createElement("div"); + this.navbar.className = 'livechan_navbar'; + + var container = document.createElement("div"); + // channel name label + var channelLabel = document.createElement("span"); + channelLabel.className = 'livechan_navbar_channel_label'; + + this.channel = channelLabel; + + // mod indicator + this.mod = document.createElement("span"); + this.mod.className = 'livechan_navbar_mod_indicator_inactive'; + + // TODO: don't hardcode + this.mod.textContent = "Anon"; + + // usercounter + this.status = document.createElement("span"); + this.status.className = 'livechan_navbar_status'; + + container.appendChild(this.mod); + container.appendChild(this.channel); + container.appendChild(this.status); + + this.navbar.appendChild(container); + + domElem.appendChild(this.navbar); + +} + + +/* @brief called when there is an "event" for the navbar */ +LivechanNavbar.prototype.onLivechanEvent = function (evstr) { + if ( evstr === "login:mod" ) { + // set indicator + this.mod.className = "livechan_mod_indicator_active"; + this.mod.textContent = "Moderator"; + } else if ( evstr === "login:admin" ) { + this.mod.className = "livechan_mod_indicator_admin"; + this.mod.textContent = "Admin"; + } +} + +/* @brief called when there is a notification for us */ +LivechanNavbar.prototype.onLivechanNotify = function(evstr) { + // do nothing for now + // maybe have some indicator that shows number of messages unread? +} + +/* @brief update online user counter */ +LivechanNavbar.prototype.updateUsers = function(count) { + this.updateStatus("Online: "+count); +} + +/* @brief update status label */ +LivechanNavbar.prototype.updateStatus = function(str) { + this.status.textContent = str; +} + +/* @brief set channel name */ +LivechanNavbar.prototype.setChannel = function(str) { + this.channel.textContent = str; +} + +var modCommands = [ + // login command + [/l(login)? (.*)/, function(m) { + var chat = this; + // mod login + chat.modLogin(m[2]); + }, + "login as user", "/l user:password", + ], + [/cp (\d+)/, function(m) { + var chat = this; + // permaban the fucker + chat.modAction(3, 4, m[1], "CP", -1); + }, + "handle illegal content", "/cp postnum", + ], + [/cnuke (\d+) (.*)/, function(m) { + var chat = this; + // channel ban + nuke files + chat.modAction(2, 4, m[1], m[2], -1); + }, + "channel level ban+nuke", "/cnuke postnum reason goes here", + ], + [/purge (\d+) (.*)/, function(m) { + var chat = this; + // channel ban + nuke files + chat.modAction(2, 9, m[1], m[2], -1); + }, + "channel level ban+nuke", "/cnuke postnum reason goes here", + ], + [/gnuke (\d+) (.*)/, function(m) { + var chat = this; + // global ban + nuke with reason + chat.modAction(3, 4, m[1], m[2], -1); + }, + "global ban+nuke", "/gnuke postnum reason goes here", + ], + [/gban (\d+) (.*)/, function(m) { + var chat = this; + // global ban with reason + chat.modAction(3, 3, m[1], m[2], -1); + }, + "global ban (no nuke)", "/gban postnum reason goes here", + ], + [/cban (\d+) (.*)/, function(m) { + var chat = this; + // channel ban with reason + chat.modAction(2, 3, m[1], m[2], -1); + }, + "channel level ban (no nuke)", "/cban postnum reason goes here", + ], + [/dpost (\d+)/, function(m) { + var chat = this; + // channel level delete post + chat.modAction(1, 2, m[1]); + }, + "delete post and file", "/dpost postnum", + ], + [/dfile (\d+)/, function(m) { + var chat = this; + // channel level delete file + chat.modAction(1, 1, m[1]); + }, + "delete just file", "/dpost postnum", + ] +] + + +/* + * @brief Build Notification widget + * @param domElem root element to put widget in + */ +function buildNotifyPane(domElem) { + var pane = document.createElement("div"); + pane.className = "livechan_notify_pane"; + domElem.appendChild(pane); + return pane; +} + +/* + * @brief Livechan Notification system + * @param domElem root element to put Notification Pane in. + */ +function LivechanNotify(domElem) { + this.pane = buildNotifyPane(domElem); +} + +/* @brief inform the user with a message */ +LivechanNotify.prototype.inform = function(str) { + // new Notify("livechan", {body: str}).show(); + var elem = document.createElement("div"); + elem.className = "livechan_notify_node"; + elem.textContent = Date.now() + ": " + str; + this.pane.appendChild(elem); + this.rollover(); +} + + +/* @brief roll over old messages */ +LivechanNotify.prototype.rollover = function() { + while ( this.pane.childNodes.length > this.scrollback ) { + this.pane.childNodes.removeChild(this.pane.childNodes[0]); + } +} + + + + +/* @brief Creates a structure of html elements for the + * chat. + * + * @param domElem The element to be populated with the + * chat structure. + * @param chatName The name of this chat + * @return An object of references to the structure + * created. + */ +function buildChat(chat, domElem, channel) { + channel = channel.toLowerCase(); + // build the navbar + // see nav.js + var navbar = new LivechanNavbar(domElem); + + // build the notification system + // see notify.js + var notify = new LivechanNotify(domElem); + + var output = document.createElement('div'); + output.className = 'livechan_chat_output'; + + var input_left = document.createElement('div'); + input_left.className = 'livechan_chat_input_left'; + + var input = document.createElement('form'); + input.className = 'livechan_chat_input'; + + var name = document.createElement('input'); + name.className = 'livechan_chat_input_name'; + name.setAttribute('placeholder', 'Anonymous'); + + var file = document.createElement('input'); + file.className = 'livechan_chat_input_file'; + file.setAttribute('type', 'file'); + file.setAttribute('value', 'upload'); + file.setAttribute('id', channel+'_input_file'); + + + var messageDiv = document.createElement('div'); + messageDiv.className = 'livechan_chat_input_message_div'; + + var message = document.createElement('textarea'); + message.className = 'livechan_chat_input_message'; + + var submit = document.createElement('input'); + submit.className = 'livechan_chat_input_submit'; + submit.setAttribute('type', 'submit'); + submit.setAttribute('value', 'send'); + var convobar = new ConvoBar(chat, domElem); + input_left.appendChild(name); + input_left.appendChild(convobar.elem); + input_left.appendChild(file); + input.appendChild(input_left); + messageDiv.appendChild(message); + input.appendChild(messageDiv); + input.appendChild(submit); + domElem.appendChild(output); + domElem.appendChild(input); + // inject convobar + + + return { + convobar : convobar, + notify: notify, + navbar: navbar, + output: output, + input: { + convo: convobar.elem, + form: input, + message: message, + name: name, + submit: submit, + file: file + } + }; +} + +function Connection(ws, channel) { + this.ws = ws; + this.channel = channel; +} + +Connection.prototype.ban = function(reason) { + if (this.ws) { + this.ws.close = null; + this.ws.close(); + alert("You have been banned for the following reason: "+reason); + } +} + +Connection.prototype.send = function(obj) { + /* Jsonify the object and send as string. */ + this.sendBinary(JSON.stringify(obj)); +} + +Connection.prototype.sendBinary = function(obj) { + if (this.ws) { + this.ws.send(obj); + } +} + +Connection.prototype.onmessage = function(callback) { + this.ws.onmessage = function(event) { + var data = JSON.parse(event.data); + callback(data); + } +} + +Connection.prototype.onclose = function(callback) { + this.ws.onclose = callback; +} + +/* @brief Initializes the websocket connection. + * + * @param channel The channel to open a connection to. + * @return A connection the the websocket. + */ +function initWebSocket(prefix, channel, connection) { + var ws = null; + if (window['WebSocket']) { + try { + var scheme = 'wss://'; + if ( location.protocol == "http:" ) scheme = "ws://"; + var ws_url = scheme+location.host+prefix+"live?"+channel; + console.log(ws_url); + ws = new WebSocket(ws_url); + } catch(e) { + ws = null; + } + } + if (ws !== null) { + ws.onerror = function() { + if (connection) { + connection.ws = null; + } + }; + if (connection) { + console.log("reconnected."); + connection.ws = ws; + return connection; + } else { + return new Connection(ws, channel); + } + } else { + return null; + } +} + +/* @brief Parses and returns a message div. + * + * @param data The message data to be parsed. + * @return A dom element containing the message. + */ +function parse(text, rules, end_tag) { + var output = document.createElement('div'); + var position = 0; + var end_matched = false; + if (end_tag) { + var end_handler = function(m) { + end_matched = true; + } + rules = [[end_tag, end_handler]].concat(rules); + } + do { + var match = null; + var match_pos = text.length; + var handler = null; + for (var i = 0; i < rules.length; i++) { + rules[i][0].lastIndex = position; + var result = rules[i][0].exec(text); + if (result !== null && position <= result.index && result.index < match_pos) { + match = result; + match_pos = result.index; + handler = rules[i][1]; + } + } + var unmatched_text = text.substring(position, match_pos); + output.appendChild(document.createTextNode(unmatched_text)); + position = match_pos; + if (match !== null) { + position += match[0].length; + output.appendChild(handler(match)); + } + } while (match !== null && !end_matched); + return output; +} + +var messageRules = [ + [/>>([0-9a-f]+)/g, function(m) { + var out = document.createElement('span'); + out.className = 'livechan_internallink'; + out.addEventListener('click', function() { + var selected = document.getElementById('livechan_chat_'+m[1]); + selected.scrollIntoView(true); + }); + out.appendChild(document.createTextNode('>>'+m[1])); + return out; + }], + [/^>.+/mg, function(m) { + var out = document.createElement('span'); + out.className = 'livechan_greentext'; + out.appendChild(document.createTextNode(m)); + return out; + }], + [/\[code\]\n?([\s\S]+)\[\/code\]/g, function(m) { + var out; + if (m.length >= 2 && m[1].trim !== '') { + out = document.createElement('pre'); + out.textContent = m[1]; + } else { + out = document.createTextNode(m); + } + return out; + }], + [/\[b\]\n?([\s\S]+)\[\/b\]/g, function(m) { + var out; + if (m.length >= 2 && m[1].trim !== '') { + out = document.createElement('span'); + out.className = 'livechan_boldtext'; + out.textContent = m[1]; + } else { + out = document.createTextNode(m); + } + return out; + }], + [/\[spoiler\]\n?([\s\S]+)\[\/spoiler\]/g, function(m) { + var out; + if ( m.length >= 2 && m[1].trim !== '') { + out = document.createElement('span'); + out.className = 'livechan_spoiler'; + out.textContent = m[1]; + } else { + out = document.createTextNode(m); + } + return out; + }], + [/\r?\n/g, function(m) { + return document.createElement('br'); + }], + [/==(.*)==/g, function(m) { + var out; + out = document.createElement("span"); + out.className = "livechan_redtext"; + out.textContent = m[1]; + return out; + }], + [/((([A-Za-z]{3,9}:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)/g, function(m) { + var out = document.createElement("a"); + out.href = m[1]; + out.textContent = m[1]; + return out; + }], +] + +/* @brief build the convorsation bar's elements +*/ +function buildConvoBar(domElem) { + var elem = document.createElement("div"); + elem.className = "livechan_convobar_root"; + + var convo = document.createElement('input'); + convo.className = 'livechan_chat_input_convo'; + convo.setAttribute("value", "General"); + elem.appendChild(convo); + domElem.appendChild(elem); + return { + widget: elem, + input: convo, + } +} + +/* @brief create the chat's convorsation bar + * @param domElem the element to place everything in + */ +function ConvoBar(chat, domElem) { + this.parent = chat; + this.holder = {}; + this.domElem = domElem; + var convo = buildConvoBar(domElem); + this.elem = convo.input; + this.widget = convo.widget; + this.active = null; + this.convoPosts = {}; +} + + +/* @brief update the convo bar + * @param convoId the name of this covnorsattion + */ +ConvoBar.prototype.update = function(convo, chat) { + var self = this; + if ( self.holder[convo] === undefined ) { + // new convo + // register convo + self.registerConvo(convo); + } + // bump existing convo + var convoId = self.holder[convo]; + var convoElem = document.getElementById("livechan_convobar_item_"+convoId); + var convoParent = convoElem.parentElement; + if ( convoParent.children.length > 1 ) { + convoParent.removeChild(convoElem); + convoParent.insertBefore(convoElem, convoParent.childNodes[0]); + } + // begin tracking a convo's posts if not already + if ( self.convoPosts[convo] === undefined ) { + self.convoPosts[convo] = []; + } + // add post to convo + self.convoPosts[convo].push(chat); + // do roll over + var scrollback = self.parent.options.scrollback || 30; + while(self.convoPosts[convo].length > scrollback) { + // remove oldest from convo tracker + var child_data = self.convoPosts[convo].shift(); + var child = document.getElementById("livechan_chat_"+child_data.Count); + // remove element from main chat element + self.parent.chatElems.output.removeChild(child.parentNode.parentElement); + } } -// 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 = "
livechan: "+str+"
"; - }; - 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); + + +/** @brief register a new convorsation + * @param convo the name of the convo + */ +ConvoBar.prototype.registerConvo = function(convo) { + var self = this; + var max_id = 0; + // get the highest convo id + for ( c in self.holder ) { + var id = self.holder[c]; + if (id > max_id ) { + max_id = id } + } + // put it in the holder + self.holder[convo] = max_id + 1; + // make a new entry in the convo bar + var elem = document.createElement("div"); + elem.className = "livechan_convobar_item"; + elem.setAttribute("id", "livechan_convobar_item_"+ self.holder[convo]); + var link = document.createElement("span"); + elem.addEventListener("click", function() { self.show(convo); }); + link.appendChild(document.createTextNode(convo)); + elem.appendChild(link); + // prepend the element + if (self.widget.children.length > 0 ) { + self.widget.insertBefore(elem, self.widget.childNodes[0]); } else { - parent.innerHTML = "
livechan mode requires websocket support
"; - setTimeout(function() { - parent.innerHTML = ""; - }, 5000); + self.widget.appendChild(elem); } } -function ukko_livechan(prefix) { - var ukko = document.getElementById("ukko_threads"); - if (ukko) { - // remove children - ukko.innerHTML = ""; - inject_livechan_widget(prefix, ukko); + +/* + * @brief load the converstation list from server + */ +ConvoBar.prototype.load = function() { + var self = this; + var prefix = self.parent.options.prefix || "/"; + var ajax = new XMLHttpRequest(); + // prepare ajax + ajax.onreadystatechange = function() { + if (ajax.status == 200 && ajax.readyState == XMLHttpRequest.DONE ) { + // clear state + self.holder = {}; + // clear widget + while(self.widget.firstChild) { + self.widget.removeChild(self.widget.firstChild); + } + // register all convos + var convos = json.parse(ajax.responseText); + for ( var idx = 0; idx < convos.length ; idx ++ ) { + self.registerConvo(convos[idx]); + } + } + } + // send ajax + ajax.open(prefix+"convos/"+self.parent.name); + ajax.send(); +} + +/* @brief Only Show chats from a convorsation + * @param convo the name of the convorsation or null for all + */ +ConvoBar.prototype.show = function(convo) { + var self = this; + var sheet = null; + for(var idx = 0; idx < document.styleSheets.length; idx++ ) { + var s = document.styleSheets[idx]; + if (s.ownerNode && s.ownerNode.id === "convo_filter") { + sheet = s; + break; + } + } + + // delete all filtering rules + while ( sheet.rules.length > 0 ) { + if (sheet.deleteRule) { + sheet.deleteRule(0); + } else if (sheet.removeRule) { + sheet.removeRule(0); + } else { + break; + } + } + if ( convo === self.active) { + // this is resetting the view + if (sheet.insertRule) { // firefox + sheet.insertRule(".livechan_chat_output_chat { display: block; }", 0); + } else if (sheet.addRule) { // not firefox + sheet.addRule(".livechan_chat_output_chat", "display: block"); + } + // unset active highlight + var convoId = self.holder[self.active]; + var itemElem = document.getElementById("livechan_convobar_item_"+convoId); + itemElem.style.background = null; + self.active = null; + } else { + // unset active highlight if it's there + if (self.active) { + var convoId = self.holder[self.active]; + var itemElem = document.getElementById("livechan_convobar_item_"+convoId); + itemElem.style.background = null; + } + // set active highlight to new element + convoId = self.holder[convo]; + itemElem = document.getElementById("livechan_convobar_item_"+convoId); + itemElem.style.background = "red"; + var elemClass = ".livechan_chat_convo_" + convoId; + if (sheet.insertRule) { // firefox + sheet.insertRule(elemClass+ " { display: block; }", 0); + sheet.insertRule(".livechan_chat_output_chat { display: none; }", 0); + } else if (sheet.addRule) { // not firefox + sheet.addRule(".livechan_chat_output_chat", "display: none"); + sheet.addRule(elemClass, "display: block"); + } + // this convo is now active + self.active = convo; + } + // set the convobar value + self.elem.value = self.active || "General"; + + // scroll view + self.parent.scroll(); +} + +/* @brief Creates a chat. + * + * @param domElem The element to populate with chat + * output div and input form. + * @param channel The channel to bind the chat to. + * + * @param options Channel Specific options + */ +function Chat(domElem, channel, options) { + var self = this; + this.name = channel.toLowerCase(); + this.domElem = domElem; + if (options) { + this.options = options; + } else { + this.options = {}; + } + + + this.chatElems = buildChat(this, this.domElem, this.name); + var prefix = this.options.prefix || "/"; + this.connection = initWebSocket(prefix, this.name); + this.initOutput(); + this.initInput(); + // set navbar channel name + this.chatElems.navbar.setChannel(this.name); + // prepare captcha callback + this.captcha_callback = null; + // create captcha + this.captcha = new Captcha(this.domElem, this.options, function(id, solution, callback) { + // set callback to handle captcha success + self.captcha_callback = callback; + // send captcha solution + self.connection.send({Captcha: { ID: id, Solution: solution}}); + }); + // begin login sequence + this.login(); +} + +/** + * @brief begin login sequence + */ +Chat.prototype.login = function() { + this.captcha.show(); + this.captcha.load(); +} + +/** + * @brief do mod login + */ +Chat.prototype.modLogin = function(str) { + var self = this; + self.connection.send({ModLogin: str}); +} + +Chat.prototype.modAction = function(scope, action, postID, reason, expire) { + var self = this; + self.connection.send({ + ModReason: reason, + ModScope: parseInt(scope), + ModAction: parseInt(action), + ModPostID: parseInt(postID), + ModExpire: parseInt(expire), + }); +} + +/* @brief called when our post got mentioned + * + * @param event the event that has this mention + */ +Chat.prototype.Mentioned = function(event, chat) { + var self = this; + self.notify("mentioned: "+chat); +} + +Chat.prototype.onNotifyShow = function () { + +} + + +Chat.prototype.readImage = function (elem, callback) { + var self = this; + + var reader = new FileReader(); + if (elem.files.length > 0 ) { + var file = elem.files[0]; + callback(file, file.name); + } else { + callback(null, null); } } +/* @brief Sends the message in the form. + * + * @param event The event causing a message to be sent. + */ +Chat.prototype.sendInput = function(event) { + var inputElem = this.chatElems.input; + var connection = this.connection; + var self = this; + + if (inputElem.message.value[0] == '/') { + var inp = inputElem.message.value; + var helpRegex = /(help)? (.*)/; + var helpMatch = helpRegex.exec(inp.slice(1)); + if (helpMatch) { + + } + if ( self.options.customCommands ) { + for (var i in self.options.customCommands) { + var regexPair = self.options.customCommands[i]; + var match = regexPair[0].exec(inp.slice(1)); + if (match) { + (regexPair[1]).call(self, match); + inputElem.message.value = ''; + } + } + } + // modCommands is defined in mod.js + for ( var i in modCommands ) { + var command = modCommands[i]; + var match = command[0].exec(inp.slice(1)); + if (match) { + (command[1]).call(self, match); + // don't clear input for mod command + } + } + event.preventDefault(); + return false; + } + if (inputElem.submit.disabled == false) { + var message = inputElem.message.value; + var name = inputElem.name.value; + var convo = inputElem.convo.value; + connection.send({Type: "post", Post: { + Message: message, + Name: name, + }}); + for (var idx = 0 ; idx < inputElem.file.files.length; idx ++ ) { + console.log("send file "+ idx); + connection.send(inputElem.file.files[idx]); + } + inputElem.file.value = ""; + //TODO: don't clear this when doing captcha + inputElem.message.value = ''; + inputElem.submit.disabled = true; + var i = parseInt(self.options.cooldown); + // fallback + if ( i == NaN ) { i = 4; } + inputElem.submit.setAttribute('value', i); + var countDown = setInterval(function(){ + inputElem.submit.setAttribute('value', --i); + }, 1000); + setTimeout(function(){ + clearInterval(countDown); + inputElem.submit.disabled = false; + inputElem.submit.setAttribute('value', 'send'); + }, i * 1000); + event.preventDefault(); + return false; + } +} + +/* @brief Binds the form submission to websockets. + */ +Chat.prototype.initInput = function() { + var inputElem = this.chatElems.input; + var connection = this.connection; + var self = this; + inputElem.form.addEventListener('submit', function(event) { + self.sendInput(event); + }); + + inputElem.message.addEventListener('keydown', function(event) { + /* If enter key. */ + if (event.keyCode === 13 && !event.shiftKey) { + self.sendInput(event); + } + }); + inputElem.message.focus(); +} + + +/* @brief show a notification to the user */ +Chat.prototype.notify = function(message) { + // show notification pane + this.showNotifyPane(); + + var notifyPane = this.chatElems.notify; + + notifyPane.inform(message); +} + +/* @brief show the notification pane */ +Chat.prototype.showNotifyPane = function () { + var pane = this.chatElems.notify.pane; + pane.style.zIndex = 5; +} + +/* @brief hide the notification pane */ +Chat.prototype.showNotifyPane = function () { + var pane = this.chatElems.notify.pane; + pane.style.zIndex = -1; +} + +Chat.prototype.error = function(message) { + var self = this; + console.log("error: "+message); + self.notify("an error has occured: "+message); +} + +/** handle inbound websocket message */ +Chat.prototype.handleMessage = function (data) { + var self = this; + + var mtype = data.Type.toLowerCase(); + + if (mtype == "captcha" ) { + if (self.captcha_callback) { + // captcha reply + self.captcha_callback(data.Success); + // reset + self.captcha_callback = null; + } else { + // captcha challenge + self.login(); + } + } else if (mtype == "post" ) { + self.insertChat(self.generateChat(data), data); + } else if (mtype == "count" ) { + self.chatElems.navbar.updateUsers(data.UserCount); + } else if (mtype == "ban" ) { + self.connection.ban(data.Reason); + } else if (mtype == "error") { + self.insertChat(self.generateChat({PostMessage: data.Error, PostSubject: "Server Error", PostName: "Server"})) + console.log("server error: "+data.Error); + } else { + console.log("unknown message type "+mtype); + } +} + +/* @brief Binds messages to be displayed to the output. + */ +Chat.prototype.initOutput = function() { + var outputElem = this.chatElems.output; + var connection = this.connection; + var self = this; + connection.onmessage(function(data) { + if( Object.prototype.toString.call(data) === '[object Array]' ) { + for (var i = 0; i < data.length; i++) { + self.handleMessage(data[i]); + } + } else { + self.handleMessage(data); + } + }); + connection.onclose(function() { + connection.ws = null; + var getConnection = setInterval(function() { + console.log("Attempting to reconnect."); + self.notify("disconnected"); + var prefix = self.options.prefix || "/"; + if (initWebSocket(prefix, connection.channel, connection) !== null + && connection.ws !== null) { + console.log("Success!"); + self.notify("connected to livechan"); + clearInterval(getConnection); + } + }, 1000); + }); +} + +/* @brief update the user counter for number of users online + */ +Chat.prototype.updateUserCount = function(count) { + var elem = this.chatElems.navbar.userCount; + elem.textContent = "Online: "+count; +} + +/* @brief Scrolls the chat to the bottom. + */ +Chat.prototype.scroll = function() { + this.chatElems.output.scrollTop = this.chatElems.output.scrollHeight; +} + +/** @brief roll over old posts, remove them from ui */ +Chat.prototype.rollover = function() { + var self = this; + var chatSize = self.options.scrollback || 50; + self.chatElems.convobar.rolloverAll(chatSize); +} + +/* @brief Inserts the chat into the DOM, overwriting if need be. + * + * @TODO: Actually scan and insert appropriately for varying numbers. + * + * @param outputElem The dom element to insert the chat into. + * @param chat The dom element to be inserted. + * @param number The number of the chat to keep it in order. + */ +Chat.prototype.insertChat = function(chat, data) { + //var number = data.Count; + //var convo = data.Convo; + //if (!number) { + // this.error("Error: invalid chat number."); + //} + var self = this; + // append to main output + var outputElem = this.chatElems.output; + outputElem.appendChild(chat); + // scroll to end + self.scroll(); +} + + +/* @brief Generates a chat div. + * + * @param data Data passed in via websocket. + * @return A dom element. + */ +Chat.prototype.generateChat = function(data) { + var self = this; + + var chat = document.createElement('div'); + conv = "General"; + self.chatElems.convobar.update(conv, data); + var convo = self.chatElems.convobar.holder[conv]; + chat.className = 'livechan_chat_output_chat livechan_chat_convo_' + convo; + chat.className = 'livechan_chat_output_chat'; + var convoLabel = document.createElement('span'); + convoLabel.className = 'livechan_convo_label'; + convoLabel.appendChild(document.createTextNode(conv)); + + var header = document.createElement('div'); + header.className = 'livechan_chat_output_header'; + var name = document.createElement('span'); + name.className = 'livechan_chat_output_name'; + var trip = document.createElement('span'); + trip.className = 'livechan_chat_output_trip'; + var date = document.createElement('span'); + date.className = 'livechan_chat_output_date'; + var count = document.createElement('span'); + count.className = 'livechan_chat_output_count'; + + var body = document.createElement('div'); + body.className = 'livechan_chat_output_body'; + var message = document.createElement('div'); + message.className = 'livechan_chat_output_message'; + + + if (data.Name) { + name.appendChild(document.createTextNode(data.PostName)); + } else { + name.appendChild(document.createTextNode('Anonymous')); + } + + if (data.Files) { + for (var idx = 0 ; idx < data.Files.length ; idx ++ ) { + var file = data.Files[idx]; + if(!file) continue; + var a = document.createElement('a'); + a.setAttribute('target', '_blank'); + // TODO: make these configurable + var thumb_url = self.options.prefix + 'thm/'+file.Path; + var src_url = self.options.prefix + 'img/'+file.Path; + + a.setAttribute('href',src_url); + var img = document.createElement('img'); + img.setAttribute('src', thumb_url); + img.className = 'livechan_image_thumb'; + a.appendChild(img); + message.appendChild(a); + img.onload = function() { self.scroll(); } + + img.addEventListener('mouseover', function () { + // load image + var i = document.createElement("img"); + i.src = src_url; + var e = document.createElement("div"); + e.setAttribute("id", "hover_"+data.Count); + e.setAttribute("class", "hover"); + e.appendChild(i); + chat.appendChild(e); + }); + img.addEventListener('mouseout', function () { + // unload image + var e = document.getElementById("hover_"+data.Count); + e.parentElement.removeChild(e); + }); + } + } + + /* Note that parse does everything here. If you want to change + * how things are rendered modify messageRules. */ + if (data.PostMessage) { + message.appendChild(parse(data.PostMessage, messageRules)); + } else { + message.appendChild(document.createTextNode('')); + } + + if (data.Posted) { + date.appendChild(document.createTextNode((new Date(data.Posted)).toLocaleString())); + } + + if (data.Tripcode) { + var et = document.createElement('span'); + et.innerHTML = data.Tripcode; + trip.appendChild(et); + } + + if (data.HashShort) { + var h = data.HashShort; + count.setAttribute('id', 'livechan_chat_'+h); + count.appendChild(document.createTextNode(h)); + count.addEventListener('click', function() { + self.chatElems.input.message.value += '>>'+h+'\n'; + self.chatElems.input.message.focus(); + }); + } + + header.appendChild(name); + header.appendChild(trip); + header.appendChild(date); + header.appendChild(convoLabel); + header.appendChild(count); + body.appendChild(message); + + chat.appendChild(header); + chat.appendChild(body); + return chat; +} diff --git a/contrib/js/old-livechan.js b/contrib/js/old-livechan.js new file mode 100644 index 0000000..790f5e5 --- /dev/null +++ b/contrib/js/old-livechan.js @@ -0,0 +1,65 @@ +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 = "
livechan: "+str+"
"; + }; + 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 = "
livechan mode requires websocket support
"; + 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); + } +} + diff --git a/contrib/static/livechan.css b/contrib/static/livechan.css new file mode 100644 index 0000000..071fea7 --- /dev/null +++ b/contrib/static/livechan.css @@ -0,0 +1,239 @@ +input, textarea, select { + -moz-border-radius: 0px; + -webkit-border-radius: 0px; + border-radius: 0px; +} + +.livechan_chat_input { + padding: 0; + margin: 0; + position: absolute; + width: 100%; + bottom: 0; + height: 60px; + left: 0; + right: 0; +} + +.livechan_chat_input_name, .livechan_chat_input_convo { + padding: 0; + padding-left: 2px; + margin: 0; + width: 50%; +} + +.livechan_chat_input_left { + width: 30%; +} + +.livechan_chat_input_file { + padding: 0; + margin: 0; + position: absolute; + left: 3px; + bottom: 3px; + border: none; +} + +.livechan_chat_input_message_div { + padding:0; + margin:0; + position: absolute; + width: 70%; + left: 20%; + top: 3px; + bottom: 3px; +} + +.livechan_chat_input_message { + padding: 0; + margin: 0; + border:none; + height: 100%; + width: 100%; + resize: none; +} + +.livechan_chat_input_submit { + position: absolute; + width: 47px; + top: 3px; + right: 3px; + bottom: 3px; + border: none; +} + +.livechan_chat_output { + position: absolute; + top: 20px; + left: 0; + right: 0; + bottom: 60px; + overflow: auto; + width: 90%; + -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: absolute; + opacity: 0.9; +} + +.livechan_captcha_inner { + padding: 200px; +} + +.livechan_captcha_image { +} + +.livechan_captcha_input { + float: down; +} + +.livechan_spoiler { + color: black; + background: black; +} + +.livechan_spoiler:hover { + color: white; +} + +.livechan_convo_label { + padding: 5px; +} + +.livechan_convobar_root { + position: absolute; + top: 20px; + right: 0; + width: 10%; + hieght: 90%; +} + +.livechan_convobar_item { + margin: 5px; + padding: 5px; +} + +.livechan_navbar { + z-index: 3; + position: absolute; + 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; +} + + +.hover , .hover > img { + z-index: 100; + position: absolute; + left: 10%; + max-width: 900px; + max-height: 900px; +} + +.livechan_chat_output, .livechan_captcha, .livechan_convobar_root, #chat { + background: #EEF2FF; +} + +.livechan_chat_output_chat, .livechan_navbar, .livechan_convobar_item { + background: #D6DAF0; +} + +.livechan_chat_output_chat { + font-family: sans-serif; + margin: 4px; + padding: 4px; +} + +.livechan_chat_output_name { + font-weight: bold; + color: green; +} + +.livechan_chat_output_count:hover { + color: red; +} + +.livechan_chat_input { + background: #98E; +} + +.livechan_greentext { + color: #789922; +} + +.livechan_boldtext { + font-weight: bold; +} + +.livechan_internallink { + color: blue; +} + +.livechan_internallink:hover { + 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; +} \ No newline at end of file diff --git a/contrib/templates/default/live.mustache b/contrib/templates/default/live.mustache index 01a97e7..86b825d 100644 --- a/contrib/templates/default/live.mustache +++ b/contrib/templates/default/live.mustache @@ -8,34 +8,28 @@ {{#i18n.Translations}}{{overboard_title}}{{/i18n.Translations}} - + + + -
-
-
-
- - {{{navbar}}} - -
-
+
-
+ From 63edd142430dce8889236ed73ac1e8870916a7c7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 4 Jul 2016 10:36:10 -0400 Subject: [PATCH 04/90] fix date error --- contrib/js/livechan.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index e2194df..9aecd85 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -1223,7 +1223,7 @@ Chat.prototype.generateChat = function(data) { } if (data.Posted) { - date.appendChild(document.createTextNode((new Date(data.Posted)).toLocaleString())); + date.appendChild(document.createTextNode((new Date(data.Posted * 1000)).toLocaleString())); } if (data.Tripcode) { From 8101e67b10da75322635a5218018393031b03102 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 4 Jul 2016 10:40:09 -0400 Subject: [PATCH 05/90] fix thumbnailing --- contrib/js/livechan.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 9aecd85..23124b4 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -1185,7 +1185,7 @@ Chat.prototype.generateChat = function(data) { var a = document.createElement('a'); a.setAttribute('target', '_blank'); // TODO: make these configurable - var thumb_url = self.options.prefix + 'thm/'+file.Path; + var thumb_url = self.options.prefix + 'thm/'+file.Path + ".jpeg"; var src_url = self.options.prefix + 'img/'+file.Path; a.setAttribute('href',src_url); From e461462bd67b7f62f7654be89569823789d0e7c6 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 4 Jul 2016 10:43:08 -0400 Subject: [PATCH 06/90] actually fix thumbnails --- contrib/js/livechan.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 23124b4..3f0ac4b 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -1185,7 +1185,7 @@ Chat.prototype.generateChat = function(data) { var a = document.createElement('a'); a.setAttribute('target', '_blank'); // TODO: make these configurable - var thumb_url = self.options.prefix + 'thm/'+file.Path + ".jpeg"; + var thumb_url = self.options.prefix + 'thm/'+file.Path + ".jpg"; var src_url = self.options.prefix + 'img/'+file.Path; a.setAttribute('href',src_url); From 13e0010419107290c3d138662891496c56450898 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 4 Jul 2016 11:28:10 -0400 Subject: [PATCH 07/90] update rollover in livechan ui --- contrib/js/livechan.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 3f0ac4b..f8951a1 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -365,8 +365,8 @@ LivechanNotify.prototype.inform = function(str) { /* @brief roll over old messages */ LivechanNotify.prototype.rollover = function() { while ( this.pane.childNodes.length > this.scrollback ) { - this.pane.childNodes.removeChild(this.pane.childNodes[0]); - } + this.pane.childNodes.removeChild(this.pane.childNodes[0]); + } } @@ -688,9 +688,9 @@ ConvoBar.prototype.update = function(convo, chat) { while(self.convoPosts[convo].length > scrollback) { // remove oldest from convo tracker var child_data = self.convoPosts[convo].shift(); - var child = document.getElementById("livechan_chat_"+child_data.Count); + //var child = document.getElementById("livechan_chat_"+child_data.Count); // remove element from main chat element - self.parent.chatElems.output.removeChild(child.parentNode.parentElement); + //self.parent.chatElems.output.removeChild(child.parentNode.parentElement); } } @@ -1111,7 +1111,9 @@ Chat.prototype.scroll = function() { Chat.prototype.rollover = function() { var self = this; var chatSize = self.options.scrollback || 50; - self.chatElems.convobar.rolloverAll(chatSize); + while ( this.chatElems.output.childNodes.length > chatSize ) { + this.chatElems.output.childNodes.removeChild(this.chatElems.output.childNodes[0]); + } } /* @brief Inserts the chat into the DOM, overwriting if need be. @@ -1134,6 +1136,7 @@ Chat.prototype.insertChat = function(chat, data) { outputElem.appendChild(chat); // scroll to end self.scroll(); + self.rollover(); } @@ -1201,14 +1204,14 @@ Chat.prototype.generateChat = function(data) { var i = document.createElement("img"); i.src = src_url; var e = document.createElement("div"); - e.setAttribute("id", "hover_"+data.Count); + e.setAttribute("id", "hover_"+data.ShortHash); e.setAttribute("class", "hover"); e.appendChild(i); chat.appendChild(e); }); img.addEventListener('mouseout', function () { // unload image - var e = document.getElementById("hover_"+data.Count); + var e = document.getElementById("hover_"+data.ShortHash); e.parentElement.removeChild(e); }); } From 4a8861761d288abf74ae3b9d62e8319a202078c2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 4 Jul 2016 11:52:43 -0400 Subject: [PATCH 08/90] update livechan to allow attachments --- contrib/js/livechan.js | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index f8951a1..b468319 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -905,12 +905,17 @@ Chat.prototype.onNotifyShow = function () { Chat.prototype.readImage = function (elem, callback) { var self = this; - var reader = new FileReader(); if (elem.files.length > 0 ) { + var reader = new FileReader(); var file = elem.files[0]; - callback(file, file.name); + reader.onloadend = function(ev) { + if (ev.target.readyState == FileReader.DONE) { + callback(window.btoa(ev.target.result), file.name, file.type); + } + } + reader.readAsBinaryString(file); } else { - callback(null, null); + callback(null, null, null); } } @@ -956,17 +961,21 @@ Chat.prototype.sendInput = function(event) { var message = inputElem.message.value; var name = inputElem.name.value; var convo = inputElem.convo.value; - connection.send({Type: "post", Post: { - Message: message, - Name: name, - }}); - for (var idx = 0 ; idx < inputElem.file.files.length; idx ++ ) { - console.log("send file "+ idx); - connection.send(inputElem.file.files[idx]); - } - inputElem.file.value = ""; - //TODO: don't clear this when doing captcha - inputElem.message.value = ''; + self.readImage(inputElem.file, function(fdata, fname, ftype) { + if (fdata) { + connection.send({Type: "post", Post: { + message: message, + name: name, + files: [{name: fname, data: fdata, type: ftype}], + }}); + } else { + connection.send({Type: "post", Post: { + message: message, + name: name, }}); + } + inputElem.file.value = ""; + inputElem.message.value = ''; + }); inputElem.submit.disabled = true; var i = parseInt(self.options.cooldown); // fallback From 9c038021b550c8382a806ee8967df988610a1cf2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 4 Jul 2016 12:50:00 -0400 Subject: [PATCH 09/90] update rollover in livechan --- contrib/js/livechan.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index b468319..77aab18 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -1120,8 +1120,9 @@ Chat.prototype.scroll = function() { Chat.prototype.rollover = function() { var self = this; var chatSize = self.options.scrollback || 50; - while ( this.chatElems.output.childNodes.length > chatSize ) { - this.chatElems.output.childNodes.removeChild(this.chatElems.output.childNodes[0]); + var e = self.chatElems.output; + while ( e.childNodes.length > chatSize ) { + e.childNodes[0].remove(); } } From 5cf12f37a5bc9329c5009f5dc9c8684c0f5c475a Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Mon, 4 Jul 2016 15:48:40 -0400 Subject: [PATCH 10/90] meh this should work for livechan ui --- contrib/js/livechan.js | 99 +++++++++++++++++++++++++----------------- 1 file changed, 59 insertions(+), 40 deletions(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 77aab18..2344c0f 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -450,15 +450,15 @@ function buildChat(chat, domElem, channel) { }; } -function Connection(ws, channel) { +function Connection(ws, url) { this.ws = ws; - this.channel = channel; + this.url = url; } Connection.prototype.ban = function(reason) { if (this.ws) { - this.ws.close = null; this.ws.close(); + this.ws.close = null; alert("You have been banned for the following reason: "+reason); } } @@ -475,14 +475,17 @@ Connection.prototype.sendBinary = function(obj) { } Connection.prototype.onmessage = function(callback) { - this.ws.onmessage = function(event) { - var data = JSON.parse(event.data); - callback(data); + if (this.ws) { + this.ws.onmessage = function(event) { + var data = JSON.parse(event.data); + callback(data); + } } } Connection.prototype.onclose = function(callback) { - this.ws.onclose = callback; + if(this.ws) + this.ws.onclose = callback; } /* @brief Initializes the websocket connection. @@ -490,21 +493,18 @@ Connection.prototype.onclose = function(callback) { * @param channel The channel to open a connection to. * @return A connection the the websocket. */ -function initWebSocket(prefix, channel, connection) { +function initWebSocket(url, connection) { var ws = null; if (window['WebSocket']) { try { - var scheme = 'wss://'; - if ( location.protocol == "http:" ) scheme = "ws://"; - var ws_url = scheme+location.host+prefix+"live?"+channel; - console.log(ws_url); - ws = new WebSocket(ws_url); - } catch(e) { + var w = new WebSocket(url); + ws = w; + } catch (ex) { ws = null; } } if (ws !== null) { - ws.onerror = function() { + ws.onclose = function() { if (connection) { connection.ws = null; } @@ -514,10 +514,8 @@ function initWebSocket(prefix, channel, connection) { connection.ws = ws; return connection; } else { - return new Connection(ws, channel); + return new Connection(ws, url); } - } else { - return null; } } @@ -567,6 +565,7 @@ var messageRules = [ out.addEventListener('click', function() { var selected = document.getElementById('livechan_chat_'+m[1]); selected.scrollIntoView(true); + // TODO: highlight }); out.appendChild(document.createTextNode('>>'+m[1])); return out; @@ -843,7 +842,10 @@ function Chat(domElem, channel, options) { this.chatElems = buildChat(this, this.domElem, this.name); var prefix = this.options.prefix || "/"; - this.connection = initWebSocket(prefix, this.name); + var scheme = "wss://"; + if (location.protocol == "http:") scheme = "ws://"; + var url = scheme + location.host + prefix + "live?"+ this.name; + this.connection = initWebSocket(url); this.initOutput(); this.initInput(); // set navbar channel name @@ -1087,20 +1089,19 @@ Chat.prototype.initOutput = function() { self.handleMessage(data); } }); - connection.onclose(function() { - connection.ws = null; - var getConnection = setInterval(function() { - console.log("Attempting to reconnect."); - self.notify("disconnected"); - var prefix = self.options.prefix || "/"; - if (initWebSocket(prefix, connection.channel, connection) !== null - && connection.ws !== null) { - console.log("Success!"); - self.notify("connected to livechan"); - clearInterval(getConnection); - } - }, 1000); - }); + var reconnect = function() { + connection.ws = null; + var getConnection = setInterval(function() { + console.log("Attempting to reconnect."); + if (initWebSocket(connection.url, connection) !== null + && connection.ws !== null) { + setTimeout(function() { + clearInterval(getConnection); + }, 100); + } + }, 5000); + }; + connection.onclose(reconnect); } /* @brief update the user counter for number of users online @@ -1198,10 +1199,12 @@ Chat.prototype.generateChat = function(data) { var a = document.createElement('a'); a.setAttribute('target', '_blank'); // TODO: make these configurable - var thumb_url = self.options.prefix + 'thm/'+file.Path + ".jpg"; - var src_url = self.options.prefix + 'img/'+file.Path; + var filepath = file.Path; + var thumb_url = self.options.prefix + 'thm/'+filepath + ".jpg"; + var src_url = self.options.prefix + 'img/'+filepath; a.setAttribute('href',src_url); + var fl = filepath.toLowerCase(); var img = document.createElement('img'); img.setAttribute('src', thumb_url); img.className = 'livechan_image_thumb'; @@ -1210,13 +1213,29 @@ Chat.prototype.generateChat = function(data) { img.onload = function() { self.scroll(); } img.addEventListener('mouseover', function () { - // load image - var i = document.createElement("img"); - i.src = src_url; + var e = document.createElement("div"); e.setAttribute("id", "hover_"+data.ShortHash); e.setAttribute("class", "hover"); - e.appendChild(i); + + if (fl.match(/\.(webm|mp4|mkv)$/)) { + // video + var v = document.createElement("video"); + v.src = src_url; + e.appendChild(v); + } else if (fl.match(/\.(mp3|ogg|oga|flac|opus)$/)) { + // audio + var a = document.createElement("audio"); + a.src = src_url; + e.appendChild(a); + } else if (fl.match(/\.txt$/)) { + // + } else { + // image + var i = document.createElement("img"); + i.src = src_url; + e.appendChild(i); + } chat.appendChild(e); }); img.addEventListener('mouseout', function () { @@ -1247,7 +1266,7 @@ Chat.prototype.generateChat = function(data) { if (data.HashShort) { var h = data.HashShort; - count.setAttribute('id', 'livechan_chat_'+h); + chat.setAttribute('id', 'livechan_chat_'+h); count.appendChild(document.createTextNode(h)); count.addEventListener('click', function() { self.chatElems.input.message.value += '>>'+h+'\n'; From 1c632666ab5cc515013632751178dedd76d28a35 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 5 Jul 2016 08:17:19 -0400 Subject: [PATCH 11/90] update livechan --- contrib/js/livechan.js | 4 +++- contrib/static/livechan.css | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 2344c0f..73bbdde 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -1030,12 +1030,14 @@ Chat.prototype.notify = function(message) { Chat.prototype.showNotifyPane = function () { var pane = this.chatElems.notify.pane; pane.style.zIndex = 5; + pane.style.visibility = 'visible'; } /* @brief hide the notification pane */ Chat.prototype.showNotifyPane = function () { var pane = this.chatElems.notify.pane; pane.style.zIndex = -1; + pane.style.visibility = 'hidden'; } Chat.prototype.error = function(message) { @@ -1186,7 +1188,7 @@ Chat.prototype.generateChat = function(data) { message.className = 'livechan_chat_output_message'; - if (data.Name) { + if (data.PostName) { name.appendChild(document.createTextNode(data.PostName)); } else { name.appendChild(document.createTextNode('Anonymous')); diff --git a/contrib/static/livechan.css b/contrib/static/livechan.css index 071fea7..97f58aa 100644 --- a/contrib/static/livechan.css +++ b/contrib/static/livechan.css @@ -110,7 +110,7 @@ input, textarea, select { } .livechan_captcha_inner { - padding: 200px; + padding: 20%; } .livechan_captcha_image { From 72947dbbccf619be4b8a6aa0aeb0536059503d65 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 5 Jul 2016 09:53:23 -0400 Subject: [PATCH 12/90] update livechan to be aware of convos --- contrib/js/livechan.js | 129 +++++++++++++++++++---------------------- 1 file changed, 59 insertions(+), 70 deletions(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 73bbdde..36330f1 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -431,8 +431,6 @@ function buildChat(chat, domElem, channel) { input.appendChild(submit); domElem.appendChild(output); domElem.appendChild(input); - // inject convobar - return { convobar : convobar, @@ -564,6 +562,8 @@ var messageRules = [ out.className = 'livechan_internallink'; out.addEventListener('click', function() { var selected = document.getElementById('livechan_chat_'+m[1]); + console.log(selected.convo); + selected.select(); selected.scrollIntoView(true); // TODO: highlight }); @@ -634,7 +634,8 @@ function buildConvoBar(domElem) { var convo = document.createElement('input'); convo.className = 'livechan_chat_input_convo'; - convo.setAttribute("value", "General"); + convo.setAttribute("value", ""); + convo.contentEditable = false; elem.appendChild(convo); domElem.appendChild(elem); return { @@ -654,42 +655,37 @@ function ConvoBar(chat, domElem) { this.elem = convo.input; this.widget = convo.widget; this.active = null; - this.convoPosts = {}; } /* @brief update the convo bar * @param convoId the name of this covnorsattion */ -ConvoBar.prototype.update = function(convo, chat) { +ConvoBar.prototype.update = function(msgid, data) { var self = this; - if ( self.holder[convo] === undefined ) { + if ( self.holder[msgid] === undefined ) { // new convo // register convo - self.registerConvo(convo); + self.registerConvo(msgid, data); } // bump existing convo - var convoId = self.holder[convo]; + var convoId = self.holder[msgid].id; var convoElem = document.getElementById("livechan_convobar_item_"+convoId); var convoParent = convoElem.parentElement; if ( convoParent.children.length > 1 ) { convoParent.removeChild(convoElem); convoParent.insertBefore(convoElem, convoParent.childNodes[0]); } - // begin tracking a convo's posts if not already - if ( self.convoPosts[convo] === undefined ) { - self.convoPosts[convo] = []; - } // add post to convo - self.convoPosts[convo].push(chat); + self.holder[msgid].posts.push(data); // do roll over var scrollback = self.parent.options.scrollback || 30; - while(self.convoPosts[convo].length > scrollback) { + while(self.holder[msgid].posts.length > scrollback) { // remove oldest from convo tracker - var child_data = self.convoPosts[convo].shift(); - //var child = document.getElementById("livechan_chat_"+child_data.Count); + var child_data = self.holder[msgid].posts.shift(); + var child = document.getElementById("livechan_chat_"+child_data.ShortHash); // remove element from main chat element - //self.parent.chatElems.output.removeChild(child.parentNode.parentElement); + self.parent.chatElems.output.removeChild(child.parentNode.parentElement); } } @@ -697,27 +693,37 @@ ConvoBar.prototype.update = function(convo, chat) { /** @brief register a new convorsation - * @param convo the name of the convo */ -ConvoBar.prototype.registerConvo = function(convo) { +ConvoBar.prototype.registerConvo = function(msgid, data) { var self = this; var max_id = 0; // get the highest convo id for ( c in self.holder ) { - var id = self.holder[c]; + var id = self.holder[c].id; if (id > max_id ) { max_id = id } } - // put it in the holder - self.holder[convo] = max_id + 1; + + self.holder[msgid] = { + subject: data.PostSubject, + msgid: data.Message_id, + id: max_id + 1, + posts: [], + select: function() { + console.log("selected convo "+msgid); + if ( self.active !== msgid ) { + self.show(msgid); + } + }, + } // make a new entry in the convo bar var elem = document.createElement("div"); elem.className = "livechan_convobar_item"; - elem.setAttribute("id", "livechan_convobar_item_"+ self.holder[convo]); + elem.setAttribute("id", "livechan_convobar_item_"+ self.holder[msgid].id); var link = document.createElement("span"); - elem.addEventListener("click", function() { self.show(convo); }); - link.appendChild(document.createTextNode(convo)); + elem.addEventListener("click", function() { self.show(msgid); }); + link.appendChild(document.createTextNode(data.PostSubject)); elem.appendChild(link); // prepend the element if (self.widget.children.length > 0 ) { @@ -727,39 +733,9 @@ ConvoBar.prototype.registerConvo = function(convo) { } } - -/* - * @brief load the converstation list from server - */ -ConvoBar.prototype.load = function() { - var self = this; - var prefix = self.parent.options.prefix || "/"; - var ajax = new XMLHttpRequest(); - // prepare ajax - ajax.onreadystatechange = function() { - if (ajax.status == 200 && ajax.readyState == XMLHttpRequest.DONE ) { - // clear state - self.holder = {}; - // clear widget - while(self.widget.firstChild) { - self.widget.removeChild(self.widget.firstChild); - } - // register all convos - var convos = json.parse(ajax.responseText); - for ( var idx = 0; idx < convos.length ; idx ++ ) { - self.registerConvo(convos[idx]); - } - } - } - // send ajax - ajax.open(prefix+"convos/"+self.parent.name); - ajax.send(); -} - /* @brief Only Show chats from a convorsation - * @param convo the name of the convorsation or null for all */ -ConvoBar.prototype.show = function(convo) { +ConvoBar.prototype.show = function(msgid) { var self = this; var sheet = null; for(var idx = 0; idx < document.styleSheets.length; idx++ ) { @@ -780,7 +756,7 @@ ConvoBar.prototype.show = function(convo) { break; } } - if ( convo === self.active) { + if (msgid === self.active) { // this is resetting the view if (sheet.insertRule) { // firefox sheet.insertRule(".livechan_chat_output_chat { display: block; }", 0); @@ -788,19 +764,21 @@ ConvoBar.prototype.show = function(convo) { sheet.addRule(".livechan_chat_output_chat", "display: block"); } // unset active highlight - var convoId = self.holder[self.active]; + var convoId = self.holder[self.active].id; var itemElem = document.getElementById("livechan_convobar_item_"+convoId); itemElem.style.background = null; self.active = null; + self.elem.value = ""; } else { // unset active highlight if it's there if (self.active) { - var convoId = self.holder[self.active]; + var convoId = self.holder[self.active].id; var itemElem = document.getElementById("livechan_convobar_item_"+convoId); itemElem.style.background = null; + self.elem.value = ""; } // set active highlight to new element - convoId = self.holder[convo]; + convoId = self.holder[msgid].id; itemElem = document.getElementById("livechan_convobar_item_"+convoId); itemElem.style.background = "red"; var elemClass = ".livechan_chat_convo_" + convoId; @@ -812,10 +790,15 @@ ConvoBar.prototype.show = function(convo) { sheet.addRule(elemClass, "display: block"); } // this convo is now active - self.active = convo; + self.active = msgid; } // set the convobar value - self.elem.value = self.active || "General"; + var a = self.holder[self.active]; + if(a) + self.elem.value = a.msgid; + else { + self.elem.value = ""; + } // scroll view self.parent.scroll(); @@ -962,18 +945,21 @@ Chat.prototype.sendInput = function(event) { if (inputElem.submit.disabled == false) { var message = inputElem.message.value; var name = inputElem.name.value; - var convo = inputElem.convo.value; + var convo = self.chatElems.convobar.active; self.readImage(inputElem.file, function(fdata, fname, ftype) { if (fdata) { connection.send({Type: "post", Post: { message: message, name: name, + reference: convo, files: [{name: fname, data: fdata, type: ftype}], }}); } else { connection.send({Type: "post", Post: { message: message, - name: name, }}); + reference: convo, + name: name, + }}); } inputElem.file.value = ""; inputElem.message.value = ''; @@ -1162,14 +1148,16 @@ Chat.prototype.generateChat = function(data) { var self = this; var chat = document.createElement('div'); - conv = "General"; - self.chatElems.convobar.update(conv, data); - var convo = self.chatElems.convobar.holder[conv]; - chat.className = 'livechan_chat_output_chat livechan_chat_convo_' + convo; - chat.className = 'livechan_chat_output_chat'; + self.chatElems.convobar.update(data.Parent, data); + var convo = self.chatElems.convobar.holder[data.Parent]; + chat.select = function() { + console.log("selecting..."); + convo.select(); + } + chat.className = 'livechan_chat_output_chat livechan_chat_convo_' + convo.id; var convoLabel = document.createElement('span'); convoLabel.className = 'livechan_convo_label'; - convoLabel.appendChild(document.createTextNode(conv)); + convoLabel.appendChild(document.createTextNode(convo.subject)); var header = document.createElement('div'); header.className = 'livechan_chat_output_header'; @@ -1273,6 +1261,7 @@ Chat.prototype.generateChat = function(data) { count.addEventListener('click', function() { self.chatElems.input.message.value += '>>'+h+'\n'; self.chatElems.input.message.focus(); + chat.select(); }); } From d5d30893e566a69408901900b3994b1a23724d9d Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 5 Jul 2016 10:11:58 -0400 Subject: [PATCH 13/90] livechan fixes --- contrib/js/livechan.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 36330f1..0b8601d 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -684,8 +684,9 @@ ConvoBar.prototype.update = function(msgid, data) { // remove oldest from convo tracker var child_data = self.holder[msgid].posts.shift(); var child = document.getElementById("livechan_chat_"+child_data.ShortHash); - // remove element from main chat element - self.parent.chatElems.output.removeChild(child.parentNode.parentElement); + if(child) { + child.remove(); + } } } @@ -710,6 +711,7 @@ ConvoBar.prototype.registerConvo = function(msgid, data) { msgid: data.Message_id, id: max_id + 1, posts: [], + ShortHash: data.ShortHash, select: function() { console.log("selected convo "+msgid); if ( self.active !== msgid ) { @@ -1107,12 +1109,14 @@ Chat.prototype.scroll = function() { /** @brief roll over old posts, remove them from ui */ Chat.prototype.rollover = function() { + /* var self = this; var chatSize = self.options.scrollback || 50; var e = self.chatElems.output; while ( e.childNodes.length > chatSize ) { e.childNodes[0].remove(); } +*/ } /* @brief Inserts the chat into the DOM, overwriting if need be. From e954c57da66eb0a8486bdd5a350b8210ecdae4af Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 5 Jul 2016 12:13:48 -0400 Subject: [PATCH 14/90] fix up livechan template --- contrib/templates/default/live.mustache | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/contrib/templates/default/live.mustache b/contrib/templates/default/live.mustache index 86b825d..225e5f4 100644 --- a/contrib/templates/default/live.mustache +++ b/contrib/templates/default/live.mustache @@ -16,20 +16,17 @@ -
+ - From 30b54c17fa273e17bc799ac4447e49376398804c Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 5 Jul 2016 12:14:52 -0400 Subject: [PATCH 15/90] document -> document.body --- contrib/templates/default/live.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/templates/default/live.mustache b/contrib/templates/default/live.mustache index 225e5f4..eb2b2de 100644 --- a/contrib/templates/default/live.mustache +++ b/contrib/templates/default/live.mustache @@ -21,7 +21,7 @@ var e = document.createElement("div"); e.setAttribute("id", "chat"); e.setAttribute("style", "position:absolute;left:0;right:0;top:0;bottom:0;"); - document.appendChild(e); + document.body.appendChild(e); var board = "live"; if (location.hash != "" ) { board = location.hash.substr(1); From c448777fe9751d0e1c8108c6c1d503299d1981db Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 5 Jul 2016 12:16:32 -0400 Subject: [PATCH 16/90] update livechan template --- contrib/templates/default/live.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/templates/default/live.mustache b/contrib/templates/default/live.mustache index eb2b2de..49aa880 100644 --- a/contrib/templates/default/live.mustache +++ b/contrib/templates/default/live.mustache @@ -20,7 +20,7 @@ {{frontend}} on nntpchan - + {{{navbar}}}
-
-
-

{{frontend}} on nntpchan

-

View the overboard

-

Read the FAQ

-

Join the IRC on rizon or irc2p

-

Lurk on URC

-

Check out the board list

-

Fork on github: frontend and core

-

We've Had {{totalposts}} Posts Since August 01 2015

-
-
-
-
- - - - - + {{# boardgraph}} + + + + + + + {{/ boardgraph}} + +
- {{{postsgraph}}} - - {{! todo: move boardgraph into its own file like postsgraph }} - - - - - - - - - - - {{# boardgraph}} +
+
+
+
+
{{frontend}} on nntpchan
+
View the overboard
+
Join the IRC on rizon or irc2p
+
Check out the board list
+
Fork on github: frontend and core
+
We've Had {{totalposts}} Posts Since August 01 2015
+
+
+
{{#i18n.Translations}}{{board_label}}{{/i18n.Translations}} {{#i18n.Translations}}{{posts_hour}}{{/i18n.Translations}} {{#i18n.Translations}}{{posts_today}}{{/i18n.Translations}} {{#i18n.Translations}}{{total}}{{/i18n.Translations}}
+ + + + - - -
+ {{{postsgraph}}} + + {{! todo: move boardgraph into its own file like postsgraph }} + + - - - - + + + + - {{/ boardgraph}} - -
- {{Board}} - - {{Hour}} - - {{Day}} - - {{All}} - {{#i18n.Translations}}{{board_label}}{{/i18n.Translations}} {{#i18n.Translations}}{{posts_hour}}{{/i18n.Translations}} {{#i18n.Translations}}{{posts_today}}{{/i18n.Translations}} {{#i18n.Translations}}{{total}}{{/i18n.Translations}}
-
- {{{overview}}} + +
+ {{Board}} + + {{Hour}} + + {{Day}} + + {{All}} +
+ + + + + {{{overview}}} +
-

- - + +
From 95864559fb399b8bb5245a2ea5a6cbcd954e144c Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 2 Aug 2016 20:03:45 -0400 Subject: [PATCH 72/90] wrap post in pre tag --- contrib/templates/default/post.mustache | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/contrib/templates/default/post.mustache b/contrib/templates/default/post.mustache index 63b996c..fdff77d 100644 --- a/contrib/templates/default/post.mustache +++ b/contrib/templates/default/post.mustache @@ -60,7 +60,9 @@ {{/post.Attachments}}
- {{{post.RenderBody}}} +
+      {{{post.RenderBody}}}
+    

From 74f07c3a6c3bd15181d396d698807bb2347ff560 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 14 Aug 2016 11:26:07 -0400 Subject: [PATCH 73/90] add test file --- contrib/frontends/php/vichan/post2nntp.php | 89 ++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 contrib/frontends/php/vichan/post2nntp.php diff --git a/contrib/frontends/php/vichan/post2nntp.php b/contrib/frontends/php/vichan/post2nntp.php new file mode 100644 index 0000000..2332926 --- /dev/null +++ b/contrib/frontends/php/vichan/post2nntp.php @@ -0,0 +1,89 @@ + $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://i2p.rocks: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 ", "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 ", "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 ", "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 ", "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>"); From f7eb634aa15d6f0a07cad5b0b9a3c6dadb270c53 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 14 Aug 2016 11:27:46 -0400 Subject: [PATCH 74/90] fix :p --- contrib/frontends/php/vichan/post2nntp.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/frontends/php/vichan/post2nntp.php b/contrib/frontends/php/vichan/post2nntp.php index 2332926..cb8e9b7 100644 --- a/contrib/frontends/php/vichan/post2nntp.php +++ b/contrib/frontends/php/vichan/post2nntp.php @@ -49,7 +49,7 @@ function gennntp($headers, $files) { } function shoveitup($msg, $id) { - $s = fsockopen("tcp://i2p.rocks:1119"); + $s = fsockopen("tcp://localhost:1119"); fgets($s); fputs($s, "MODE STREAM\r\n"); fgets($s); From c6c9c3f53b55afda3cde13efd3d2b5b08ad9f21f Mon Sep 17 00:00:00 2001 From: Jeff Date: Tue, 6 Sep 2016 19:05:52 -0400 Subject: [PATCH 75/90] apply mona's css patch --- contrib/static/site.css | 43 ++++++++++++++++++++++++++++------------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/contrib/static/site.css b/contrib/static/site.css index b96d805..7e1451f 100644 --- a/contrib/static/site.css +++ b/contrib/static/site.css @@ -90,22 +90,21 @@ textarea { } .post_body > pre { - font-size: 9pt; - background: #D6DAF0; + font-size: 10pt; font-weight: unset; } pre { white-space: pre-wrap; align: center; - font-size: 13pt; - background: #98E; + font-size: 12pt; color: black; display: inline-block; overflow-wrap: break-word; word-wrap: break-word; font-weight: bold; - padding: 20px 20px 20px 20px; + + font-family: sans; margin-left: auto; margin-right: auto; @@ -346,11 +345,26 @@ input, textarea { } .post { - max-width: 75%; - margin-bottom: 5px; + background-color: #D6DAF0; + + border: 1px solid #B7C5D9; + border-left: none; + border-top: none; + display: table; + padding: 2px; + margin: 2px; + + display: inline-block; + float: left; + clear: both; + min-width: 500px; } +.post.op { + background-color: #e4eeff; +} + .postheader { width: 100%; padding-top: 3px; @@ -450,10 +464,6 @@ input, textarea { padding-bottom: 0px; } -.post { - display: inline-block; -} - .pagelist { display: inline-block; overflow: hidden; @@ -560,7 +570,7 @@ textarea#reply-text { } .thread { - padding-left: 10%; + padding-left: 10px; padding-top: 10px; padding-bottom: 10px; width: 80%; @@ -569,4 +579,11 @@ textarea#reply-text { .hider { float: right; -} \ No newline at end of file +} +@keyframes rotate { + 0% { transform:rotate(0deg); } + 25% { transform:rotate(-1deg); } + 50% { transform:rotate(0deg); } + 75% { transform:rotate(1deg); } + +} From e57d4ea92152849468c0303e411e5e8e760c7ad1 Mon Sep 17 00:00:00 2001 From: wzeth Date: Sat, 10 Sep 2016 09:34:00 -0400 Subject: [PATCH 76/90] Use more succinct postgres role creation command It is far less error prone to use the tools that Postgres provides to create the role and the database. This amendment also ensures the created role password is encrypted. --- doc/database/postgres/configure-postgres.md | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/doc/database/postgres/configure-postgres.md b/doc/database/postgres/configure-postgres.md index c870c6a..ebe3675 100644 --- a/doc/database/postgres/configure-postgres.md +++ b/doc/database/postgres/configure-postgres.md @@ -4,23 +4,10 @@ Configuring Postgres database These are instructions for setting up NNTPChan with Postgres as the data-storage system. ##Configuring Postgres +A user with sufficient privileges to run su is required (hint: you can use root). This command switches to the Postgres user, creates a Postgres role called `srnd`, and prompts for a password. For illustrative purposes, we will use `srnd` as the password. -Setting up postgres (as root): - - # become postgres user - su postgres - # spawn postgres admin shell - psql - -You'll get a prompt, enter the following: - - CREATE ROLE srnd WITH LOGIN PASSWORD 'srnd'; - CREATE DATABASE srnd WITH ENCODING 'UTF8' OWNER srnd; - \q - -For demo purposes we'll use these credentials. -These are default values, please change them later. + # su - postgres -c "createuser --pwprompt --createdb --encrypted srnd" ###Important -These credentials assume you are going to run using a user called `srnd`, if your username you plan to run the daemon as is different please change `srnd` to your username. +It's easiest to connect to Postgres using role-based authentication. In this case, our Linux user `srnd` matches up with our Postgres role `srnd`, so role-based authentication can take place. If you're running SRNDv2 as a different user (e.g. `nntpchan`), you will need to create a role that matches that user using the command above. From a0a0a6feca63038070f45653adb3b308dbebd0f7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 2 Oct 2016 09:01:58 -0400 Subject: [PATCH 77/90] update readme --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 7eefe96..981de62 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,15 @@ Tor node list: 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 ##Support From db5b8ec309e028fa6803f5abd54bd73fc87619be Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 2 Oct 2016 20:36:59 -0400 Subject: [PATCH 78/90] try fixing liveui for chrome --- contrib/js/livechan.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index 46a6395..f39e8ef 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -1045,13 +1045,13 @@ Chat.prototype.sendInput = function(event) { } } var data = new FormData(); - data.set("name", name); - data.set("subject", subject); - data.set("message", message); + data.append("name", name); + data.append("subject", subject); + data.append("message", message); if (convo) - data.set("reference", convo); + data.append("reference", convo); if (inputElem.file.files[0]) - data.set("attachment_0", inputElem.file.files[0]); + data.append("attachment_0", inputElem.file.files[0]); ajax.send(data); /** self.readImage(inputElem.file, function(fdata, fname, ftype) { From 2355528b46228ac7180b6dec019707eab4fe4488 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Sun, 2 Oct 2016 20:42:45 -0400 Subject: [PATCH 79/90] fug --- contrib/js/livechan.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/js/livechan.js b/contrib/js/livechan.js index f39e8ef..9ca3096 100644 --- a/contrib/js/livechan.js +++ b/contrib/js/livechan.js @@ -1012,7 +1012,7 @@ Chat.prototype.sendInput = function(event) { console.log(board); var subject = self.chatElems.input.subject.value; var ajax = new XMLHttpRequest(); - ajax.open("POST", self.prefix+"livechan/api/post?newsgroup="+board); + ajax.open("POST", self.prefix+"livechan/api/post?newsgroup="+board, true); ajax.onreadystatechange = function() { if (ajax.readyState == 4) { console.log("post done"); From 61281190bb8af01b7409c1ebebd458fef12318d5 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 07:24:00 -0400 Subject: [PATCH 80/90] disable feature --- contrib/js/reply.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/contrib/js/reply.js b/contrib/js/reply.js index 1044a73..9223ba0 100644 --- a/contrib/js/reply.js +++ b/contrib/js/reply.js @@ -352,6 +352,8 @@ function inject_hover_for_element(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)) ) { From 6ea75236fa160a48ab6bf0225731a691c4d492ad Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 07:25:30 -0400 Subject: [PATCH 81/90] fix post.mustache --- contrib/templates/default/post.mustache | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/contrib/templates/default/post.mustache b/contrib/templates/default/post.mustache index fdff77d..b349cdc 100644 --- a/contrib/templates/default/post.mustache +++ b/contrib/templates/default/post.mustache @@ -60,9 +60,7 @@ {{/post.Attachments}}
-
-      {{{post.RenderBody}}}
-    
+
{{{post.RenderBody}}}

From 0e1e6201ca9002af264fa976a8aa40c68c6f2cd1 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 07:30:29 -0400 Subject: [PATCH 82/90] fix bloodgod.css --- contrib/static/bloodgod.css | 8 ++------ contrib/static/site.css | 4 ---- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/contrib/static/bloodgod.css b/contrib/static/bloodgod.css index 81a0019..fb9091e 100644 --- a/contrib/static/bloodgod.css +++ b/contrib/static/bloodgod.css @@ -2,10 +2,6 @@ bloodgod theme css override */ -/** - bloodgod theme css override -*/ - body { color: #666; background: #111; @@ -25,7 +21,7 @@ input[type="button"] { color: black; } -#captcha_img, pre { +#captcha_img { background: #D80000; } @@ -110,4 +106,4 @@ table thead th { .origin > img , .not_found > img { -webkit-filter: invert(1); filter: invert(1); -} \ No newline at end of file +} diff --git a/contrib/static/site.css b/contrib/static/site.css index 7e1451f..fd9faab 100644 --- a/contrib/static/site.css +++ b/contrib/static/site.css @@ -361,10 +361,6 @@ input, textarea { min-width: 500px; } -.post.op { - background-color: #e4eeff; -} - .postheader { width: 100%; padding-top: 3px; From a0deb532e302ec724406a5982a152f416c8ac267 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 07:33:03 -0400 Subject: [PATCH 83/90] fix bloodgod.css moar --- contrib/static/bloodgod.css | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contrib/static/bloodgod.css b/contrib/static/bloodgod.css index fb9091e..91b4e2d 100644 --- a/contrib/static/bloodgod.css +++ b/contrib/static/bloodgod.css @@ -7,6 +7,9 @@ body { background: #111; } +.post_body { + color: #666; +} input, textarea, button, input[type="text"], input[type="password"], input[type="checkbox"], input[type="file"], input[type="submit"], From 698ed1d42e272a0006397aea62cb697189909d02 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 07:34:08 -0400 Subject: [PATCH 84/90] previous commit --- contrib/static/bloodgod.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/static/bloodgod.css b/contrib/static/bloodgod.css index 91b4e2d..e8a1cd2 100644 --- a/contrib/static/bloodgod.css +++ b/contrib/static/bloodgod.css @@ -7,7 +7,7 @@ body { background: #111; } -.post_body { +.post_body > pre { color: #666; } From 287a49f1964bf6a987769297a83936aa9beceea2 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 07:35:06 -0400 Subject: [PATCH 85/90] fix tomorrow.css --- contrib/static/tomorrow.css | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/contrib/static/tomorrow.css b/contrib/static/tomorrow.css index b4e182d..9379b51 100644 --- a/contrib/static/tomorrow.css +++ b/contrib/static/tomorrow.css @@ -9,6 +9,11 @@ body padding-right: 4px; } +.post_body > pre { + color: #DADADA; +} + + main, aside, section @@ -640,4 +645,4 @@ img#nntpchan_banner { .post:target { background-color: #2c2d3e; box-shadow: 0px 0px 10px 2px; -} \ No newline at end of file +} From 5118ffb3f85dfd1949930ee8eb45b77276848cc7 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 07:36:33 -0400 Subject: [PATCH 86/90] fix tomorrow.css --- contrib/static/tomorrow.css | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/static/tomorrow.css b/contrib/static/tomorrow.css index 9379b51..a5b346f 100644 --- a/contrib/static/tomorrow.css +++ b/contrib/static/tomorrow.css @@ -11,6 +11,7 @@ body .post_body > pre { color: #DADADA; + text-align: left; } From 6996e3abc4f40404bc0b3e6e4f2965fbcecab020 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 08:26:50 -0400 Subject: [PATCH 87/90] remove cuckhold pow in postform --- contrib/templates/default/postform.mustache | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/templates/default/postform.mustache b/contrib/templates/default/postform.mustache index 36b252f..f135674 100644 --- a/contrib/templates/default/postform.mustache +++ b/contrib/templates/default/postform.mustache @@ -77,14 +77,14 @@ - + From 40ceb747aefde716fb9b9bc3f15bb39d36de97e0 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 10:06:25 -0400 Subject: [PATCH 88/90] refactor structure --- build-js.sh | 21 +++++++------------ contrib/js/contrib/readme.md | 1 + contrib/js/{main.js_ => entry.js} | 0 contrib/js/{ => nntpchan}/api.js | 0 contrib/js/{ => nntpchan}/banner.js | 0 contrib/js/{ => nntpchan}/captcha-reload.js | 0 contrib/js/{ => nntpchan}/expand-image.js | 0 contrib/js/{ => nntpchan}/expand-video.js | 0 contrib/js/{ => nntpchan}/hide-post.js | 0 contrib/js/{ => nntpchan}/livechan.js | 0 contrib/js/{ => nntpchan}/local_storage.js | 0 contrib/js/{ => nntpchan}/old-livechan.js | 0 contrib/js/nntpchan/readme.md | 1 + contrib/js/{ => nntpchan}/reply.js | 0 contrib/js/{ => nntpchan}/theme.js | 0 .../js/{ => nntpchan/unused}/cuckoo_miner.js | 0 contrib/js/nntpchan/unused/readme.md | 2 ++ contrib/js/readme.md | 2 ++ 18 files changed, 14 insertions(+), 13 deletions(-) create mode 100644 contrib/js/contrib/readme.md rename contrib/js/{main.js_ => entry.js} (100%) rename contrib/js/{ => nntpchan}/api.js (100%) rename contrib/js/{ => nntpchan}/banner.js (100%) rename contrib/js/{ => nntpchan}/captcha-reload.js (100%) rename contrib/js/{ => nntpchan}/expand-image.js (100%) rename contrib/js/{ => nntpchan}/expand-video.js (100%) rename contrib/js/{ => nntpchan}/hide-post.js (100%) rename contrib/js/{ => nntpchan}/livechan.js (100%) rename contrib/js/{ => nntpchan}/local_storage.js (100%) rename contrib/js/{ => nntpchan}/old-livechan.js (100%) create mode 100644 contrib/js/nntpchan/readme.md rename contrib/js/{ => nntpchan}/reply.js (100%) rename contrib/js/{ => nntpchan}/theme.js (100%) rename contrib/js/{ => nntpchan/unused}/cuckoo_miner.js (100%) create mode 100644 contrib/js/nntpchan/unused/readme.md create mode 100644 contrib/js/readme.md diff --git a/build-js.sh b/build-js.sh index c5fccb6..1828828 100755 --- a/build-js.sh +++ b/build-js.sh @@ -15,17 +15,6 @@ if [ ! -f $GOPATH/bin/minify ]; then echo "set up minifiy" go get -v github.com/tdewolff/minify/cmd/minify fi -if [ ! -f $GOPATH/bin/gopherjs ]; then - echo "set up gopherjs" - go get -v -u github.com/gopherjs/gopherjs -fi - -# build cuckoo miner -#echo "Building cuckoo miner" -#go get -v -u github.com/ZiRo-/cuckgo/miner_js -#$GOPATH/bin/gopherjs -m -v build github.com/ZiRo-/cuckgo/miner_js -#mv ./miner_js.js ./contrib/static/miner-js.js -#rm ./miner_js.js.map outfile=$PWD/contrib/static/nntpchan.js @@ -62,10 +51,16 @@ if [ -e ./contrib/js/contrib/*.js ] ; then done fi -mini ./contrib/js/main.js_ $outfile +mini ./contrib/js/entry.js $outfile # local js -for f in ./contrib/js/*.js ; do +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 + echo "ok" diff --git a/contrib/js/contrib/readme.md b/contrib/js/contrib/readme.md new file mode 100644 index 0000000..a0ea923 --- /dev/null +++ b/contrib/js/contrib/readme.md @@ -0,0 +1 @@ +3rd party javascript diff --git a/contrib/js/main.js_ b/contrib/js/entry.js similarity index 100% rename from contrib/js/main.js_ rename to contrib/js/entry.js diff --git a/contrib/js/api.js b/contrib/js/nntpchan/api.js similarity index 100% rename from contrib/js/api.js rename to contrib/js/nntpchan/api.js diff --git a/contrib/js/banner.js b/contrib/js/nntpchan/banner.js similarity index 100% rename from contrib/js/banner.js rename to contrib/js/nntpchan/banner.js diff --git a/contrib/js/captcha-reload.js b/contrib/js/nntpchan/captcha-reload.js similarity index 100% rename from contrib/js/captcha-reload.js rename to contrib/js/nntpchan/captcha-reload.js diff --git a/contrib/js/expand-image.js b/contrib/js/nntpchan/expand-image.js similarity index 100% rename from contrib/js/expand-image.js rename to contrib/js/nntpchan/expand-image.js diff --git a/contrib/js/expand-video.js b/contrib/js/nntpchan/expand-video.js similarity index 100% rename from contrib/js/expand-video.js rename to contrib/js/nntpchan/expand-video.js diff --git a/contrib/js/hide-post.js b/contrib/js/nntpchan/hide-post.js similarity index 100% rename from contrib/js/hide-post.js rename to contrib/js/nntpchan/hide-post.js diff --git a/contrib/js/livechan.js b/contrib/js/nntpchan/livechan.js similarity index 100% rename from contrib/js/livechan.js rename to contrib/js/nntpchan/livechan.js diff --git a/contrib/js/local_storage.js b/contrib/js/nntpchan/local_storage.js similarity index 100% rename from contrib/js/local_storage.js rename to contrib/js/nntpchan/local_storage.js diff --git a/contrib/js/old-livechan.js b/contrib/js/nntpchan/old-livechan.js similarity index 100% rename from contrib/js/old-livechan.js rename to contrib/js/nntpchan/old-livechan.js diff --git a/contrib/js/nntpchan/readme.md b/contrib/js/nntpchan/readme.md new file mode 100644 index 0000000..b08a229 --- /dev/null +++ b/contrib/js/nntpchan/readme.md @@ -0,0 +1 @@ +main nntpchan javascript files diff --git a/contrib/js/reply.js b/contrib/js/nntpchan/reply.js similarity index 100% rename from contrib/js/reply.js rename to contrib/js/nntpchan/reply.js diff --git a/contrib/js/theme.js b/contrib/js/nntpchan/theme.js similarity index 100% rename from contrib/js/theme.js rename to contrib/js/nntpchan/theme.js diff --git a/contrib/js/cuckoo_miner.js b/contrib/js/nntpchan/unused/cuckoo_miner.js similarity index 100% rename from contrib/js/cuckoo_miner.js rename to contrib/js/nntpchan/unused/cuckoo_miner.js diff --git a/contrib/js/nntpchan/unused/readme.md b/contrib/js/nntpchan/unused/readme.md new file mode 100644 index 0000000..2a6e625 --- /dev/null +++ b/contrib/js/nntpchan/unused/readme.md @@ -0,0 +1,2 @@ +this directory holds unused javascript files for nntpchan +don't delete files move them here diff --git a/contrib/js/readme.md b/contrib/js/readme.md new file mode 100644 index 0000000..406e4ab --- /dev/null +++ b/contrib/js/readme.md @@ -0,0 +1,2 @@ +javascript files for nntpchan + From dc75a3513e161ffca6b441630849e9e8bad66fc0 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Fri, 7 Oct 2016 10:17:29 -0400 Subject: [PATCH 89/90] add js branding --- build-js.sh | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/build-js.sh b/build-js.sh index 1828828..abe98c7 100755 --- a/build-js.sh +++ b/build-js.sh @@ -31,8 +31,9 @@ lint() { mini() { echo "minify $1" echo "" >> $2 - echo "/* local file: $1 */" >> $2 + echo "/* begin $1 */" >> $2 $GOPATH/bin/minify --mime=text/javascript >> $2 < $1 + echo "/* end $1 */" >> $2 } # do linting too @@ -43,7 +44,19 @@ if [ "x$1" == "xlint" ] ; then done fi -echo -e "//For source code and license information please check https://github.com/majestrate/nntpchan \n" > $outfile +rm -f $outfile + +echo '/*' >> $outfile +echo ' * For source code and license information please check https://github.com/majestrate/nntpchan' >> $outfile +brandingfile=./contrib/branding.txt +if [ -e $brandingfile ] ; then + echo ' *' >> $outfile + while read line; do + echo -n ' * ' >> $outfile; + echo $line >> $outfile; + done < $brandingfile; +fi +echo ' */' >> $outfile if [ -e ./contrib/js/contrib/*.js ] ; then for f in ./contrib/js/contrib/*.js ; do From 4ab90f31523d8cf2760e705fa2b77defe796a055 Mon Sep 17 00:00:00 2001 From: wzeth Date: Sat, 8 Oct 2016 05:08:46 -0400 Subject: [PATCH 90/90] Explicit notice of redis cache deprecation Users should know that redis cache is no longer valid as of SRNDv2 commit 96de42 --- doc/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/README.md b/doc/README.md index f78915b..66a5425 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,5 +1,6 @@ NNTPChan Documentation ====================== +**WARNING: Caching with redis was deprecated in [commit 96de42](https://github.com/majestrate/srndv2/commit/96de42bf5d689a54d27871c9f8bc4ef3d0cdbefc). Any reference to redis as a cache should be ignored. You should instead use null cache.** Hey, welcome to the documentation. This will help you use and develop with NNTPChan.