git ssb

16+

cel / patchfoo



Tree: a3dca9e424477c2745c59f482342a1580ed807f0

Files: a3dca9e424477c2745c59f482342a1580ed807f0 / lib / render.js

9836 bytesRaw
1var fs = require('fs')
2var path = require('path')
3var pull = require('pull-stream')
4var cat = require('pull-cat')
5var paramap = require('pull-paramap')
6var h = require('hyperscript')
7var marked = require('ssb-marked')
8var emojis = require('emoji-named-characters')
9var qs = require('querystring')
10var u = require('./util')
11var multicb = require('multicb')
12var RenderMsg = require('./render-msg')
13
14module.exports = Render
15
16function MdRenderer(render) {
17 marked.Renderer.call(this, {})
18 this.render = render
19}
20MdRenderer.prototype = new marked.Renderer()
21
22MdRenderer.prototype.urltransform = function (href) {
23 return this.render.toUrl(href)
24}
25
26MdRenderer.prototype.image = function (ref, title, text) {
27 var href = this.render.imageUrl(ref)
28 var name = text || title
29 if (name) href += '?name=' + encodeURIComponent(name)
30 return h('img', {
31 src: href,
32 alt: this.render.getImageAlt(ref, text),
33 title: title || undefined
34 }).outerHTML
35}
36
37MdRenderer.prototype.link = function (ref, title, text) {
38 var href = this.urltransform(ref)
39 var name = href && /^\/(&|%26)/.test(href) && (title || text)
40 if (u.isRef(ref)) {
41 var myName = this.render.app.getNameSync(ref)
42 if (myName) title = title ? title + ' (' + myName + ')' : myName
43 }
44 var a = h('a', {
45 class: href === false ? 'bad' : undefined,
46 href: href !== false ? href : undefined,
47 title: title || undefined,
48 download: name ? encodeURIComponent(name) : undefined
49 })
50 // text is already html-escaped
51 a.innerHTML = text
52 return a.outerHTML
53}
54
55MdRenderer.prototype.mention = function (preceding, id) {
56 var href = this.urltransform(id)
57 var myName = this.render.app.getNameSync(id)
58 if (id.length > 50) id = id.slice(0, 8) + '…'
59 return (preceding||'') + h('a', {
60 class: href === false ? 'bad' : undefined,
61 href: href !== false ? href : undefined,
62 title: myName || undefined,
63 }, id).outerHTML
64}
65
66function lexerRenderEmoji(emoji) {
67 var el = this.renderer.render.emoji(emoji)
68 return el && el.outerHTML || el
69}
70
71function Render(app, opts) {
72 this.app = app
73 this.opts = opts
74
75 this.markedOpts = {
76 gfm: true,
77 mentions: true,
78 tables: true,
79 breaks: true,
80 pedantic: false,
81 sanitize: true,
82 smartLists: true,
83 smartypants: false,
84 emoji: lexerRenderEmoji,
85 renderer: new MdRenderer(this),
86 }
87}
88
89Render.prototype.emoji = function (emoji) {
90 var name = ':' + emoji + ':'
91 var link = this._mentions && this._mentions[name]
92 if (link && link.link) {
93 this.app.reverseEmojiNameCache.set(emoji, link.link)
94 return h('img.ssb-emoji', {
95 src: this.opts.img_base + link.link,
96 alt: name
97 + (link.size != null ? ' (' + this.formatSize(link.size) + ')' : ''),
98 height: 17,
99 title: name,
100 })
101 }
102 if (emoji in emojis) {
103 return h('img.ssb-emoji', {
104 src: this.opts.emoji_base + emoji + '.png',
105 alt: name,
106 height: 17,
107 align: 'absmiddle',
108 title: name,
109 })
110 }
111 return name
112}
113
114/* disabled until it can be done safely without breaking html
115function fixSymbols(str) {
116 // Dillo doesn't do fallback fonts, so specifically render fancy characters
117 // with Symbola
118 return str.replace(/[^\u0000-\u00ff]+/, function ($0) {
119 return '<span class="symbol">' + $0 + '</span>'
120 })
121}
122*/
123
124Render.prototype.markdown = function (text, mentions) {
125 if (!text) return ''
126 var mentionsObj = this._mentions = {}
127 var mentionsByLink = this._mentionsByLink = {}
128 if (Array.isArray(mentions)) mentions.forEach(function (link) {
129 if (!link) return
130 else if (link.emoji)
131 mentionsObj[':' + link.name + ':'] = link
132 else if (link.name)
133 mentionsObj['@' + link.name] = link.link
134 else if (link.host === 'http://localhost:7777')
135 mentionsObj[link.href] = link.link
136 if (link.link)
137 mentionsByLink[link.link] = link
138 })
139 var out = marked(String(text), this.markedOpts)
140 delete this._mentions
141 delete this._mentionsByLink
142 return out //fixSymbols(out)
143}
144
145Render.prototype.imageUrl = function (ref) {
146 var m = /^blobstore:(.*)/.exec(ref)
147 if (m) ref = m[1]
148 return this.opts.img_base + ref
149}
150
151Render.prototype.getImageAlt = function (id, fallback) {
152 var link = this._mentionsByLink[id]
153 if (!link) return fallback
154 var name = link.name || fallback
155 return name
156 + (link.size != null ? ' (' + this.formatSize(link.size) + ')' : '')
157}
158
159Render.prototype.formatSize = function (size) {
160 if (size < 1024) return size + ' B'
161 size /= 1024
162 if (size < 1024) return size.toFixed(2) + ' KB'
163 size /= 1024
164 return size.toFixed(2) + ' MB'
165}
166
167Render.prototype.linkify = function (text) {
168 var arr = text.split(u.ssbRefEncRegex)
169 for (var i = 1; i < arr.length; i += 2) {
170 arr[i] = h('a', {href: this.toUrlEnc(arr[i])}, arr[i])
171 }
172 return arr
173}
174
175Render.prototype.toUrlEnc = function (href) {
176 var url = this.toUrl(href)
177 if (url) return url
178 try { href = decodeURIComponent(href) }
179 catch (e) { return false }
180 return this.toUrl(href)
181}
182
183Render.prototype.toUrl = function (href) {
184 if (!href) return href
185 var mentions = this._mentions
186 if (mentions && href in this._mentions) href = this._mentions[href]
187 if (/^ssb:\/\//.test(href)) href = href.substr(6)
188 switch (href[0]) {
189 case '%':
190 if (!u.isRef(href)) return false
191 return this.opts.base + encodeURIComponent(href)
192 case '@':
193 if (!u.isRef(href)) return false
194 return this.opts.base + href
195 case '&':
196 if (!u.isRef(href)) return false
197 return this.opts.blob_base + href
198 case '#': return this.opts.base + 'channel/' +
199 encodeURIComponent(href.substr(1))
200 case '/': return this.opts.base + href.substr(1)
201 case '?': return this.opts.base + 'search?q=' + encodeURIComponent(href)
202 }
203 var m = /^blobstore:(.*)/.exec(href)
204 if (m) return this.opts.blob_base + m[1]
205 if (/^javascript:/.test(href)) return false
206 return href
207}
208
209Render.prototype.lockIcon = function () {
210 return this.emoji('lock')
211}
212
213Render.prototype.avatarImage = function (link, cb) {
214 var self = this
215 if (!link) return cb(), ''
216 if (typeof link === 'string') link = {link: link}
217 var img = h('img.ssb-avatar-image', {
218 width: 72,
219 alt: ' '
220 })
221 if (link.image) gotAbout(null, link)
222 else self.app.getAbout(link.link, gotAbout)
223 function gotAbout(err, about) {
224 if (err) return cb(err)
225 if (!about.image) img.src = self.toUrl('/static/fallback.png')
226 else img.src = self.imageUrl(about.image)
227 cb()
228 }
229 return img
230}
231
232Render.prototype.prepareLink = function (link, cb) {
233 if (typeof link === 'string') link = {link: link}
234 if (link.name || !link.link) cb(null, link)
235 else this.app.getAbout(link.link, function (err, about) {
236 if (err) return cb(null, link)
237 link.name = about.name || about.title || (link.link.substr(0, 8) + '…')
238 if (link.name && link.name[0] === link.link[0]) {
239 link.name = link.name.substr(1)
240 }
241 cb(null, link)
242 })
243}
244
245Render.prototype.prepareLinks = function (links, cb) {
246 var self = this
247 if (!links) return cb()
248 var done = multicb({pluck: 1})
249 if (Array.isArray(links)) links.forEach(function (link) {
250 self.prepareLink(link, done())
251 })
252 done(cb)
253}
254
255Render.prototype.idLink = function (link, cb) {
256 var self = this
257 if (!link) return cb(), ''
258 var a = h('a', ' ')
259 self.prepareLink(link, function (err, link) {
260 if (err) return cb(err)
261 a.href = self.toUrl(link.link)
262 var sigil = link.link && link.link[0] || '@'
263 a.childNodes[0].textContent = sigil + link.name
264 cb()
265 })
266 return a
267}
268
269Render.prototype.privateLine = function (recps, cb) {
270 var done = multicb({pluck: 1, spread: true})
271 var self = this
272 var el = h('div.recps',
273 self.lockIcon(),
274 Array.isArray(recps)
275 ? recps.map(function (recp) {
276 return [' ', self.idLink(recp, done())]
277 }) : '')
278 done(cb)
279 return el
280}
281
282Render.prototype.msgLink = function (msg, cb) {
283 var self = this
284 var el = h('span')
285 var a = h('a', {href: self.toUrl(msg.key)}, msg.key)
286 self.app.unboxMsg(msg, function (err, msg) {
287 if (err) return el.appendChild(u.renderError(err)), cb()
288 var renderMsg = new RenderMsg(self, self.app, msg, {wrap: false})
289 renderMsg.title(function (err, title) {
290 if (err) return el.appendChild(u.renderError(err)), cb()
291 a.childNodes[0].textContent = title
292 cb()
293 })
294 })
295 return a
296}
297
298Render.prototype.renderMsg = function (msg, opts, cb) {
299 new RenderMsg(this, this.app, msg, opts).message(cb)
300}
301
302Render.prototype.renderFeeds = function (opts) {
303 var self = this
304 return paramap(function (msg, cb) {
305 self.renderMsg(msg, opts, cb)
306 }, 4)
307}
308
309Render.prototype.gitCommitBody = function (body) {
310 if (!body) return ''
311 var isMarkdown = !/^# Conflicts:$/m.test(body)
312 return isMarkdown
313 ? h('div', {innerHTML: this.markdown('\n' + body)})
314 : h('pre', this.linkify('\n' + body))
315}
316
317Render.prototype.getName = function (id, cb) {
318 // TODO: consolidate the get name/link functions
319 var self = this
320 switch (id && id[0]) {
321 case '%':
322 return self.app.getMsgDecrypted(id, function (err, msg) {
323 if (err && err.name == 'NotFoundError')
324 return cb(null, String(id).substring(0, 8) + '…(missing)')
325 if (err) return fallback()
326 new RenderMsg(self, self.app, msg, {wrap: false}).title(cb)
327 })
328 case '@': // fallthrough
329 case '&':
330 return self.app.getAbout(id, function (err, about) {
331 if (err || !about || !about.name) return fallback()
332 cb(null, about.name)
333 })
334 default:
335 return cb(null, String(id))
336 }
337 function fallback() {
338 cb(null, String(id).substr(0, 8) + '…')
339 }
340}
341
342Render.prototype.getNameLink = function (id, cb) {
343 var self = this
344 self.getName(id, function (err, name) {
345 if (err) return cb(err)
346 cb(null, h('a', {href: self.toUrl(id)}, name))
347 })
348}
349

Built with git-ssb-web