diff --git a/index.js b/index.js
index 0c7cced..34b5f8b 100644
--- a/index.js
+++ b/index.js
@@ -3,56 +3,29 @@ var http = require('http')
var qs = require('querystring')
var path = require('path')
var crypto = require('crypto')
-var cat = require('pull-cat')
var pull = require('pull-stream')
var paramap = require('pull-paramap')
-var marked = require('ssb-marked')
var sort = require('ssb-sort')
var toPull = require('stream-to-pull-stream')
var memo = require('asyncmemo')
var lru = require('lrucache')
-var htime = require('human-time')
-var emojis = require('emoji-named-characters')
var serveEmoji = require('emoji-server')()
+var {
+ MdRenderer,
+ renderEmoji,
+ formatMsgs,
+ wrapPage,
+ renderThread
+} = require('./render');
-var emojiDir = path.join(require.resolve('emoji-named-characters'), '../pngs')
var appHash = hash([fs.readFileSync(__filename)])
var urlIdRegex = /^(?:\/(([%&@]|%25|%26|%40)(?:[A-Za-z0-9\/+]|%2[Ff]|%2[Bb]){43}(?:=|%3[Dd])\.(?:sha256|ed25519))(?:\.([^?]*))?|(\/.*?))(?:\?(.*))?$/
-function MdRenderer(opts) {
- marked.Renderer.call(this, {})
- this.opts = opts
-}
-MdRenderer.prototype = new marked.Renderer()
-
-MdRenderer.prototype.urltransform = function (href) {
- if (!href) return false
- switch (href[0]) {
- case '#': return '/channel/' + href.slice(1)
- case '%': return this.opts.msg_base + encodeURIComponent(href)
- case '@': return this.opts.feed_base + encodeURIComponent(href)
- case '&': return this.opts.blob_base + encodeURIComponent(href)
- }
- if (href.indexOf('javascript:') === 0) return false
- return href
-}
-
-MdRenderer.prototype.image = function (href, title, text) {
- return '
' : '>')
-}
-
-function renderEmoji(emoji) {
- var opts = this.renderer.opts
- return emoji in emojis ?
- '
'
- : ':' + emoji + ':'
+function hash(arr) {
+ return arr.reduce(function (hash, item) {
+ return hash.update(String(item))
+ }, crypto.createHash('sha256')).digest('base64')
}
exports.name = 'viewer'
@@ -66,6 +39,7 @@ exports.init = function (sbot, config) {
var base = conf.base || '/'
var defaultOpts = {
+ base: base,
msg_base: conf.msg_base || base,
feed_base: conf.feed_base || base,
blob_base: conf.blob_base || base,
@@ -384,14 +358,6 @@ function getMsgWithValue(sbot, id, cb) {
})
}
-function escape(str) {
- return String(str)
- .replace(/&/g, '&')
- .replace(//g, '>')
- .replace(/"/g, '"')
-}
-
function respond(res, status, message) {
res.writeHead(status)
res.end(message)
@@ -461,178 +427,3 @@ function prepend(fn, arg) {
}
}
}
-
-function formatMsgs(id, ext, opts) {
- switch (ext || 'html') {
- case 'html': return pull(renderThread(opts), wrapPage(id))
- case 'js': return pull(renderThread(opts), wrapJSEmbed(opts))
- case 'json': return wrapJSON()
- default: return null
- }
-}
-
-function wrap(before, after) {
- return function (read) {
- return cat([pull.once(before), read, pull.once(after)])
- }
-}
-
-function renderThread(opts) {
- return pull(
- pull.map(renderMsg.bind(this, opts)),
- wrap('
', '
')
- )
-}
-
-function wrapPage(id) {
- return wrap(''
- + ''
- + '' + id + ' | ssb-viewer'
- + ''
- + ''
- + ''
- + '',
- ''
- )
-}
-
-function wrapJSON() {
- var first = true
- return pull(
- pull.map(JSON.stringify),
- join(','),
- wrap('[', ']')
- )
-}
-
-function wrapJSEmbed(opts) {
- return pull(
- wrap('', ''),
- pull.map(docWrite),
- opts.base_token && rewriteBase(new RegExp(opts.base_token, 'g'))
- )
-}
-
-
-function rewriteBase(token) {
- // detect the origin of the script and rewrite the js/html to use it
- return pull(
- replace(token, '" + SSB_VIEWER_ORIGIN + "/'),
- wrap('var SSB_VIEWER_ORIGIN = (function () {'
- + 'var scripts = document.getElementsByTagName("script")\n'
- + 'var script = scripts[scripts.length-1]\n'
- + 'if (!script) return location.origin\n'
- + 'return script.src.replace(/\\/%.*$/, "")\n'
- + '}())\n', '')
- )
-}
-
-function join(delim) {
- var first = true
- return pull.map(function (val) {
- if (!first) return delim + String(val)
- first = false
- return val
- })
-}
-
-function replace(re, rep) {
- return pull.map(function (val) {
- return String(val).replace(re, rep)
- })
-}
-
-function docWrite(str) {
- return 'document.write(' + JSON.stringify(str) + ')\n'
-}
-
-function hash(arr) {
- return arr.reduce(function (hash, item) {
- return hash.update(String(item))
- }, crypto.createHash('sha256')).digest('base64')
-}
-
-function renderMsg(opts, msg) {
- var c = msg.value.content || {}
- var name = encodeURIComponent(msg.key)
- return ''
-}
-
-function msgTimestamp(msg, name) {
- var date = new Date(msg.value.timestamp)
- return ''
-}
-
-function formatDate(date) {
- // return date.toISOString().replace('T', ' ')
- return htime(date)
-}
-
-function render(opts, c)
-{
- if (c.type === 'post') {
- var channel = c.channel ? ' in #' + c.channel + '' : ''
- return channel + renderPost(opts, c)
- } else if (c.type == 'vote' && c.vote.expression == 'Dig') {
- var channel = c.channel ? ' in #' + c.channel + '' : ''
- var linkedText = 'this'
- if (typeof c.vote.linkedText != 'undefined')
- linkedText = c.vote.linkedText.substring(0, 75)
- return ' dug ' + '' + linkedText + '' + channel
- }
- else if (c.type == 'vote') {
- var linkedText = 'this'
- if (typeof c.vote.linkedText != 'undefined')
- linkedText = c.vote.linkedText.substring(0, 75)
- return ' voted ' + linkedText + ''
- }
- else if (c.type == 'contact' && c.following) {
- var name = c.contact
- if (typeof c.contactAbout != 'undefined')
- name = c.contactAbout.name
- return ' followed ' + name + ""
- }
- else if (c.type == 'contact' && !c.following) {
- var name = c.contact
- if (typeof c.contactAbout != 'undefined')
- name = c.contactAbout.name
- return ' unfollowed ' + name + ""
- }
- else if (typeof c == 'string')
- return ' wrote something private '
- else if (c.type == 'about')
- return ' changed something in about'
- else if (c.type == 'issue')
- return ' created an issue'
- else if (c.type == 'git-update')
- return ' did a git update'
- else if (c.type == 'ssb-dns')
- return ' updated dns'
- else if (c.type == 'pub')
- return ' connected to a pub'
- else if (c.type == 'channel' && c.subscribed)
- return ' subscribed to channel #' + c.channel + ""
- else if (c.type == 'channel' && !c.subscribed)
- return ' unsubscribed from channel #' + c.channel + ""
- else
- return renderDefault(c)
-}
-
-function renderPost(opts, c) {
- return '' + marked(c.text, opts.marked) + '
'
-}
-
-function renderDefault(c) {
- return '' + JSON.stringify(c, 0, 2) + '
'
-}
diff --git a/render.js b/render.js
new file mode 100644
index 0000000..e9aac7c
--- /dev/null
+++ b/render.js
@@ -0,0 +1,439 @@
+var path = require('path');
+var pull = require("pull-stream");
+var marked = require("ssb-marked");
+var htime = require("human-time");
+var emojis = require("emoji-named-characters");
+var cat = require("pull-cat");
+
+var emojiDir = path.join(require.resolve("emoji-named-characters"), "../pngs");
+
+exports.wrapPage = wrapPage;
+exports.MdRenderer = MdRenderer;
+exports.renderEmoji = renderEmoji;
+exports.formatMsgs = formatMsgs;
+exports.renderThread = renderThread;
+
+function MdRenderer(opts) {
+ marked.Renderer.call(this, {});
+ this.opts = opts;
+}
+
+MdRenderer.prototype = new marked.Renderer();
+
+MdRenderer.prototype.urltransform = function(href) {
+ if (!href) return false;
+ switch (href[0]) {
+ case "#":
+ return this.opts.base + "channel/" + href.slice(1);
+ case "%":
+ return this.opts.msg_base + encodeURIComponent(href);
+ case "@":
+ return this.opts.feed_base + encodeURIComponent(href);
+ case "&":
+ return this.opts.blob_base + encodeURIComponent(href);
+ }
+ if (href.indexOf("javascript:") === 0) return false;
+ return href;
+};
+
+MdRenderer.prototype.image = function(href, title, text) {
+ return (
+ '
" : ">")
+ );
+};
+
+function renderEmoji(emoji) {
+ var opts = this.renderer.opts;
+ return emoji in emojis
+ ? '
'
+ : ":" + emoji + ":";
+}
+
+function escape(str) {
+ return String(str)
+ .replace(/&/g, "&")
+ .replace(//g, ">")
+ .replace(/"/g, """);
+}
+
+function formatMsgs(id, ext, opts) {
+ switch (ext || "html") {
+ case "html":
+ return pull(renderThread(opts), wrapPage(id));
+ case "js":
+ return pull(renderThread(opts), wrapJSEmbed(opts));
+ case "json":
+ return wrapJSON();
+ default:
+ return null;
+ }
+}
+
+function wrap(before, after) {
+ return function(read) {
+ return cat([pull.once(before), read, pull.once(after)]);
+ };
+}
+
+function renderThread(opts) {
+ return pull(
+ pull.map(renderMsg.bind(this, opts)),
+ wrap(
+ 'You are reading content from ' +
+ 'Scuttlebutt' +
+ '' +
+ '',
+
+ '' +
+ '' +
+ 'Join Scuttlebutt now' +
+ ''
+ )
+ );
+}
+
+function wrapPage(id) {
+ return wrap(
+ "" +
+ "" +
+ "" +
+ id + " | ssb-viewer" +
+ "" +
+ '' +
+ styles +
+ "",
+ ""
+ );
+}
+
+var styles = `
+
+`;
+
+function wrapJSON() {
+ var first = true;
+ return pull(pull.map(JSON.stringify), join(","), wrap("[", "]"));
+}
+
+function wrapJSEmbed(opts) {
+ return pull(
+ wrap('', ""),
+ pull.map(docWrite),
+ opts.base_token && rewriteBase(new RegExp(opts.base_token, "g"))
+ );
+}
+
+function rewriteBase(token) {
+ // detect the origin of the script and rewrite the js/html to use it
+ return pull(
+ replace(token, '" + SSB_VIEWER_ORIGIN + "/'),
+ wrap(
+ "var SSB_VIEWER_ORIGIN = (function () {" +
+ 'var scripts = document.getElementsByTagName("script")\n' +
+ "var script = scripts[scripts.length-1]\n" +
+ "if (!script) return location.origin\n" +
+ 'return script.src.replace(/\\/%.*$/, "")\n' +
+ "}())\n",
+ ""
+ )
+ );
+}
+
+function join(delim) {
+ var first = true;
+ return pull.map(function(val) {
+ if (!first) return delim + String(val);
+ first = false;
+ return val;
+ });
+}
+
+function replace(re, rep) {
+ return pull.map(function(val) {
+ return String(val).replace(re, rep);
+ });
+}
+
+function docWrite(str) {
+ return "document.write(" + JSON.stringify(str) + ")\n";
+}
+
+function renderMsg(opts, msg) {
+ var c = msg.value.content || {};
+ var name = encodeURIComponent(msg.key);
+ return (
+ '' +
+ '' +
+ render(opts, c) +
+ ""
+ );
+}
+
+function msgTimestamp(msg, name) {
+ var date = new Date(msg.value.timestamp);
+ var isoStr = date.toISOString();
+ return (
+ ''
+ );
+}
+
+function formatDate(date) {
+ // return date.toISOString().replace('T', ' ')
+ return htime(date);
+}
+
+function render(opts, c) {
+ var base = opts.base;
+ if (c.type === "post") {
+ var channel = c.channel
+ ? '"
+ : "";
+ return channel + renderPost(opts, c);
+ } else if (c.type == "vote" && c.vote.expression == "Dig") {
+ var channel = c.channel
+ ? ' in #' + c.channel + ""
+ : "";
+ var linkedText = "this";
+ if (typeof c.vote.linkedText != "undefined")
+ linkedText = c.vote.linkedText.substring(0, 75);
+ return ('' +
+ 'Liked ' +
+ '' +
+ linkedText +
+ "" +
+ channel +
+ ''
+ );
+ } else if (c.type == "vote") {
+ var linkedText = "this";
+ if (typeof c.vote.linkedText != "undefined")
+ linkedText = c.vote.linkedText.substring(0, 75);
+ return '' +
+ 'Voted ' + linkedText + "" +
+ '';
+ } else if (c.type == "contact" && c.following) {
+ var name = c.contact;
+ if (typeof c.contactAbout != "undefined") name = c.contactAbout.name;
+ return '' +
+ 'Followed ' + name + "" +
+ '';
+ } else if (c.type == "contact" && !c.following) {
+ var name = c.contact;
+ if (typeof c.contactAbout != "undefined") name = c.contactAbout.name;
+ return '' +
+ 'Unfollowed ' + name + "" +
+ '';
+ } else if (typeof c == "string") {
+ return '' +
+ "Wrote something private" +
+ '';
+ }
+ else if (c.type == "about") {
+ return '' +
+ "Changed something in about" +
+ '' +
+ renderDefault(c);
+ }
+ else if (c.type == "issue") {
+ return '' +
+ "Created a git issue" +
+ '' +
+ renderDefault(c);
+ }
+ else if (c.type == "git-update") {
+ return '' +
+ "Did a git update" +
+ '' +
+ renderDefault(c);
+ }
+ else if (c.type == "ssb-dns") {
+ return '' +
+ "Updated DNS" +
+ '' +
+ renderDefault(c);
+ }
+ else if (c.type == "pub") {
+ return '' +
+ "Connected to a pub" +
+ '' +
+ renderDefault(c);
+ }
+ else if (c.type == "channel" && c.subscribed)
+ return '' +
+ 'Subscribed to channel #' +
+ c.channel +
+ "" +
+ '';
+ else if (c.type == "channel" && !c.subscribed)
+ return '' +
+ 'Unsubscribed from channel #' +
+ c.channel +
+ "" +
+ '';
+ else return renderDefault(c);
+}
+
+function renderPost(opts, c) {
+ return '' + marked(c.text, opts.marked) + "";
+}
+
+function renderDefault(c) {
+ return "" + JSON.stringify(c, 0, 2) + "
";
+}