render.jsView |
---|
3 | 3 … | var marked = require("ssb-marked"); |
4 | 4 … | var htime = require("human-time"); |
5 | 5 … | var emojis = require("emoji-named-characters"); |
6 | 6 … | var cat = require("pull-cat"); |
| 7 … | +var h = require('hyperscript'); |
7 | 8 … | |
8 | 9 … | var emojiDir = path.join(require.resolve("emoji-named-characters"), "../pngs"); |
9 | 10 … | |
10 | 11 … | exports.wrapPage = wrapPage; |
38 | 39 … | return href; |
39 | 40 … | }; |
40 | 41 … | |
41 | 42 … | MdRenderer.prototype.image = function(href, title, text) { |
42 | | - return ( |
43 | | - '<img src="' + |
44 | | - this.opts.img_base + |
45 | | - escape(href) + |
46 | | - '"' + |
47 | | - ' alt="' + |
48 | | - text + |
49 | | - '"' + |
50 | | - (title ? ' title="' + title + '"' : "") + |
51 | | - (this.options.xhtml ? "/>" : ">") |
52 | | - ); |
| 43 … | + return h('img', |
| 44 … | + { src: this.opts.img_base + href, |
| 45 … | + alt: text, |
| 46 … | + title: title |
| 47 … | + }).outerHTML; |
53 | 48 … | }; |
54 | 49 … | |
55 | 50 … | function renderEmoji(emoji) { |
56 | 51 … | var opts = this.renderer.opts; |
58 | 53 … | var url = mentions[emoji] |
59 | 54 … | ? opts.blob_base + encodeURIComponent(mentions[emoji]) |
60 | 55 … | : emoji in emojis && opts.emoji_base + escape(emoji) + '.png'; |
61 | 56 … | return url |
62 | | - ? '<img src="' + |
63 | | - url + |
64 | | - '"' + |
65 | | - ' alt=":' + |
66 | | - escape(emoji) + |
67 | | - ':"' + |
68 | | - ' title=":' + |
69 | | - escape(emoji) + |
70 | | - ':"' + |
71 | | - ' class="ssb-emoji" height="16" width="16">' |
| 57 … | + ? h('img.ssb-emoji', |
| 58 … | + { src: url, |
| 59 … | + alt: ':' + escape(emoji) + ':', |
| 60 … | + title: ':' + escape(emoji) + ':', |
| 61 … | + height: 16, width: 16 |
| 62 … | + }).outerHTML |
72 | 63 … | : ":" + emoji + ":"; |
73 | 64 … | } |
74 | 65 … | |
75 | 66 … | function escape(str) { |
98 | 89 … | return cat([pull.once(before), read, pull.once(after)]); |
99 | 90 … | }; |
100 | 91 … | } |
101 | 92 … | |
| 93 … | +function callToAction() { |
| 94 … | + return h('a.call-to-action', |
| 95 … | + { href: 'https://www.scuttlebutt.nz' }, |
| 96 … | + 'Join Scuttlebutt now').outerHTML; |
| 97 … | +} |
| 98 … | + |
| 99 … | +function toolTipTop() { |
| 100 … | + return h('span.top-tip', |
| 101 … | + 'You are reading content from ', |
| 102 … | + h('a', { href: 'https://www.scuttlebutt.nz' }, |
| 103 … | + 'Scuttlebutt')).outerHTML; |
| 104 … | +} |
| 105 … | + |
102 | 106 … | function renderAbout(opts, about, showAllHTML = "") { |
| 107 … | + var figCaption = h('figcaption'); |
| 108 … | + figCaption.innerHTML = 'Feed of ' + about.name + '<br>' + |
| 109 … | + (about.description != undefined ? |
| 110 … | + marked(about.description, opts.marked) : ''); |
103 | 111 … | return pull( |
104 | 112 … | pull.map(renderMsg.bind(this, opts)), |
105 | | - wrap( |
106 | | - '<span class="top-tip">You are reading content from ' + |
107 | | - '<a href="https://www.scuttlebutt.nz">Scuttlebutt</a>' + |
108 | | - '</span>' + |
109 | | - '<main>' + |
110 | | - '<article><header><figure>' + |
111 | | - '<img src="' + opts.img_base + escape(about.image) + '" ' + |
112 | | - 'height="200" width="200"><figcaption>' + |
113 | | - 'Feed of ' + about.name + '<br/>' + |
114 | | - (about.description != undefined ? |
115 | | - marked(about.description, opts.marked) : '') + |
116 | | - '</figcaption></figure></header></article>', |
117 | | - |
118 | | - showAllHTML + '</main>' + |
119 | | - '<a class="call-to-action" href="https://www.scuttlebutt.nz">' + |
120 | | - 'Join Scuttlebutt now' + |
121 | | - '</a>' |
122 | | - ) |
| 113 … | + wrap(toolTipTop() + '<main>' + |
| 114 … | + h('article', |
| 115 … | + h('header', |
| 116 … | + h('figure', |
| 117 … | + h('img', |
| 118 … | + { src: opts.img_base + about.image, |
| 119 … | + height: 200, |
|
| 120 … | + width: 200 |
| 121 … | + }), |
| 122 … | + figCaption) |
| 123 … | + )).outerHTML, |
| 124 … | + showAllHTML + '</main>' + callToAction()) |
123 | 125 … | ); |
124 | 126 … | } |
125 | 127 … | |
126 | 128 … | function renderThread(opts, showAllHTML = "") { |
127 | 129 … | return pull( |
128 | 130 … | pull.map(renderMsg.bind(this, opts)), |
129 | | - wrap( |
130 | | - '<span class="top-tip">You are reading content from ' + |
131 | | - '<a href="https://www.scuttlebutt.nz">Scuttlebutt</a>' + |
132 | | - '</span>' + |
133 | | - '<main>', |
134 | | - |
135 | | - showAllHTML + '</main>' + |
136 | | - '<a class="call-to-action" href="https://www.scuttlebutt.nz">' + |
137 | | - 'Join Scuttlebutt now' + |
138 | | - '</a>' |
139 | | - ) |
| 131 … | + wrap(toolTipTop() + '<main>', |
| 132 … | + showAllHTML + '</main>' + callToAction()) |
140 | 133 … | ); |
141 | 134 … | } |
142 | 135 … | |
143 | 136 … | function wrapPage(id) { |
319 | 312 … | |
320 | 313 … | function renderMsg(opts, msg) { |
321 | 314 … | var c = msg.value.content || {}; |
322 | 315 … | var name = encodeURIComponent(msg.key); |
323 | | - return ( |
324 | | - '<article id="' + |
325 | | - name + |
326 | | - '">' + |
327 | | - '<header>' + |
328 | | - '<figure>' + |
329 | | - '<img alt="" ' + |
330 | | - 'src="' + opts.img_base + escape(msg.author.image) + '" ' + |
331 | | - 'height="50" width="50">' + |
332 | | - '<figcaption>' + |
333 | | - '<a class="ssb-avatar-name"' + |
334 | | - ' href="' + opts.base + |
335 | | - escape(msg.value.author) + |
336 | | - '"' + |
337 | | - ">" + msg.author.name + "</a>" + |
338 | | - msgTimestamp(msg, opts.base + name) + |
339 | | - '</figcaption>' + |
340 | | - '</figure>' + |
341 | | - '</header>' + |
342 | | - render(opts, c) + |
343 | | - "</article>" |
344 | | - ); |
| 316 … | + return h('article#' + name, |
| 317 … | + h('header', |
| 318 … | + h('figure', |
| 319 … | + h('img', { alt: '', |
| 320 … | + src: opts.img_base + msg.author.image, |
| 321 … | + height: 50, width: 50 }), |
| 322 … | + h('figcaption', |
| 323 … | + h('a.ssb-avatar-name', |
| 324 … | + { href: opts.base + escape(msg.value.author) }, |
| 325 … | + msg.author.name), |
| 326 … | + msgTimestamp(msg, opts.base + name)))), |
| 327 … | + render(opts, c)).outerHTML; |
345 | 328 … | } |
346 | 329 … | |
347 | 330 … | function msgTimestamp(msg, link) { |
348 | 331 … | var date = new Date(msg.value.timestamp); |
349 | 332 … | var isoStr = date.toISOString(); |
350 | | - return ( |
351 | | - '<time class="ssb-timestamp" datetime="' + isoStr + '">' + |
352 | | - '<a ' + |
353 | | - 'href="' + link + '" ' + |
354 | | - 'title="' + isoStr + '" ' + |
355 | | - '>' + formatDate(date) + '</a>' + |
356 | | - '</time>' |
357 | | - ); |
| 333 … | + return h('time.ssb-timestamp', |
| 334 … | + { datetime: isoStr }, |
| 335 … | + h('a', |
| 336 … | + { href: link, |
| 337 … | + title: isoStr }, |
| 338 … | + formatDate(date))); |
358 | 339 … | } |
359 | 340 … | |
360 | 341 … | function formatDate(date) { |
361 | | - |
362 | 342 … | return htime(date); |
363 | 343 … | } |
364 | 344 … | |
365 | 345 … | function render(opts, c) { |
366 | 346 … | var base = opts.base; |
367 | 347 … | if (c.type === "post") { |
368 | 348 … | var channel = c.channel |
369 | | - ? '<div class="top-right"><a href="' + base + 'channel/' + c.channel + '">#' + c.channel + "</a></div>" |
370 | | - : ""; |
371 | | - return channel + renderPost(opts, c); |
| 349 … | + ? h('div.top-right', |
| 350 … | + h('a', |
| 351 … | + { href: base + 'channel/' + c.channel }, |
| 352 … | + '#' + c.channel)) |
| 353 … | + : ""; |
| 354 … | + return [channel, renderPost(opts, c)]; |
372 | 355 … | } else if (c.type == "vote" && c.vote.expression == "Dig") { |
373 | 356 … | var channel = c.channel |
374 | | - ? ' in <a href="' + base + 'channel/' + c.channel + '">#' + c.channel + "</a>" |
375 | | - : ""; |
| 357 … | + ? [' in ', |
| 358 … | + h('a', |
| 359 … | + { href: base + 'channel/' + c.channel }, |
| 360 … | + '#' + c.channel)] |
| 361 … | + : ""; |
376 | 362 … | var linkedText = "this"; |
377 | 363 … | if (typeof c.vote.linkedText != "undefined") |
378 | | - linkedText = c.vote.linkedText.substring(0, 75); |
379 | | - return ('<span class="status">' + |
380 | | - 'Liked ' + |
381 | | - '<a href="' + base + |
382 | | - c.vote.link + |
383 | | - '">' + |
384 | | - linkedText + |
385 | | - "</a>" + |
386 | | - channel + |
387 | | - '</span>' |
388 | | - ); |
| 364 … | + linkedText = c.vote.linkedText.substring(0, 75); |
| 365 … | + return h('span.status', |
| 366 … | + ['Liked ', |
| 367 … | + h('a', { href: base + c.vote.link }, linkedText), |
| 368 … | + channel]); |
389 | 369 … | } else if (c.type == "vote") { |
390 | 370 … | var linkedText = "this"; |
391 | 371 … | if (typeof c.vote.linkedText != "undefined") |
392 | 372 … | linkedText = c.vote.linkedText.substring(0, 75); |
393 | | - return '<span class="status">' + |
394 | | - 'Voted <a href="' + base + c.vote.link + '">' + linkedText + "</a>" + |
395 | | - '</span>'; |
| 373 … | + return h('span.status', |
| 374 … | + ['Voted ', |
| 375 … | + h('a', { href: base + c.vote.link }, linkedText)]); |
396 | 376 … | } else if (c.type == "contact" && c.following) { |
397 | 377 … | var name = c.contact; |
398 | | - if (typeof c.contactAbout != "undefined") name = c.contactAbout.name; |
399 | | - return '<span class="status">' + |
400 | | - 'Followed <a href="' + base + c.contact + '">' + name + "</a>" + |
401 | | - '</span>'; |
| 378 … | + if (typeof c.contactAbout != "undefined") |
| 379 … | + name = c.contactAbout.name; |
| 380 … | + return h('span.status', |
| 381 … | + ['Followed ', |
| 382 … | + h('a', { href: base + c.contact }, name)]); |
402 | 383 … | } else if (c.type == "contact" && !c.following) { |
403 | 384 … | var name = c.contact; |
404 | | - if (typeof c.contactAbout != "undefined") name = c.contactAbout.name; |
405 | | - return '<span class="status">' + |
406 | | - 'Unfollowed <a href="' + base + c.contact + '">' + name + "</a>" + |
407 | | - '</span>'; |
| 385 … | + if (typeof c.contactAbout != "undefined") |
| 386 … | + name = c.contactAbout.name; |
| 387 … | + return h('span.status', |
| 388 … | + ['Unfollowed ', |
| 389 … | + h('a', { href: base + c.contact }, name)]); |
408 | 390 … | } else if (typeof c == "string") { |
409 | | - return '<span class="status">' + |
410 | | - "Wrote something private" + |
411 | | - '</span>'; |
| 391 … | + return h('span.status', 'Wrote something private') |
412 | 392 … | } |
413 | 393 … | else if (c.type == "about") { |
414 | | - return '<span class="status">' + |
415 | | - "Changed something in about" + |
416 | | - '</span>' + |
417 | | - renderDefault(c); |
| 394 … | + return [h('span.status', 'Changed something in about'), |
| 395 … | + renderDefault(c)]; |
418 | 396 … | } |
419 | 397 … | else if (c.type == "issue") { |
420 | | - return '<span class="status">' + |
421 | | - "Created a git issue" + (c.repoName != undefined ? " in repo " + c.repoName : "") + renderPost(opts, c) + |
422 | | - '</span>'; |
| 398 … | + return [h('span.status', |
| 399 … | + "Created a git issue" + |
| 400 … | + (c.repoName != undefined ? " in repo " + c.repoName : ""), |
| 401 … | + renderPost(opts, c))]; |
423 | 402 … | } |
424 | 403 … | else if (c.type == "git-update") { |
425 | | - return '<span class="status">' + |
426 | | - "Did a git update " + (c.repoName != undefined ? " in repo " + c.repoName : "") + |
427 | | - "<br/>" + |
428 | | - (c.commits != undefined ? c.commits.map(com => { return "-" +com.title; }).join("<br/>") : "") + |
429 | | - '</span>' |
| 404 … | + return h('span.status', |
| 405 … | + "Did a git update " + |
| 406 … | + (c.repoName != undefined ? " in repo " + c.repoName : "") + |
| 407 … | + '<br>' + |
| 408 … | + (c.commits != undefined ? |
| 409 … | + c.commits.map(com => { return "-" +com.title; }).join('<br>') : "")); |
430 | 410 … | } |
431 | 411 … | else if (c.type == "ssb-dns") { |
432 | | - return '<span class="status">' + |
433 | | - "Updated DNS" + |
434 | | - '</span>' + |
435 | | - renderDefault(c); |
| 412 … | + return [h('span.status', 'Updated DNS'), renderDefault(c)]; |
436 | 413 … | } |
437 | 414 … | else if (c.type == "pub") { |
438 | | - return '<span class="status">' + |
439 | | - "Connected to the pub " + c.address.host + |
440 | | - '</span>' |
| 415 … | + return h('span.status', 'Connected to the pub ' + c.address.host); |
441 | 416 … | } |
442 | 417 … | else if (c.type == "channel" && c.subscribed) |
443 | | - return '<span class="status">' + |
444 | | - 'Subscribed to channel <a href="' + base + 'channel/' + |
445 | | - c.channel + |
446 | | - '">#' + |
447 | | - c.channel + |
448 | | - "</a>" + |
449 | | - '</span>'; |
| 418 … | + return h('span.status', |
| 419 … | + 'Subscribed to channel ', |
| 420 … | + h('a', |
| 421 … | + { href: base + 'channel/' + c.channel }, |
| 422 … | + '#' + c.channel)); |
450 | 423 … | else if (c.type == "channel" && !c.subscribed) |
451 | | - return '<span class="status">' + |
452 | | - 'Unsubscribed from channel <a href="' + base + 'channel/' + |
453 | | - c.channel + |
454 | | - '">#' + |
455 | | - c.channel + |
456 | | - "</a>" + |
457 | | - '</span>'; |
| 424 … | + return h('span.status', |
| 425 … | + 'Unsubscribed from channel ', |
| 426 … | + h('a', |
| 427 … | + { href: base + 'channel/' + c.channel }, |
| 428 … | + '#' + c.channel)) |
458 | 429 … | else return renderDefault(c); |
459 | 430 … | } |
460 | 431 … | |
461 | 432 … | function renderPost(opts, c) { |
462 | 433 … | opts.mentions = {}; |
463 | | - if (Array.isArray(c.mentions)) c.mentions.forEach(function (link) { |
464 | | - if (link && link.name && link.link) opts.mentions[link.name] = link.link; |
465 | | - }); |
466 | | - return '<section>' + marked(String(c.text), opts.marked) + "</section>"; |
| 434 … | + if (Array.isArray(c.mentions)) { |
| 435 … | + c.mentions.forEach(function (link) { |
| 436 … | + if (link && link.name && link.link) |
| 437 … | + opts.mentions[link.name] = link.link; |
| 438 … | + }); |
| 439 … | + } |
| 440 … | + var s = h('section'); |
| 441 … | + s.innerHTML = marked(String(c.text), opts.marked); |
| 442 … | + return s; |
467 | 443 … | } |
468 | 444 … | |
469 | 445 … | function renderDefault(c) { |
470 | | - return "<pre>" + JSON.stringify(c, 0, 2) + "</pre>"; |
| 446 … | + return h('pre', JSON.stringify(c, 0, 2)); |
471 | 447 … | } |