git.s-ol.nu mmm / abefbf8
move converts into plugins s-ol 3 years ago
11 changed file(s) with 427 addition(s) and 400 deletion(s). Raw diff Collapse all Expand all
77 add '?/init.server'
88
99 require 'mmm'
10 require 'mmm.mmmfs'
1011
1112 import dir_base, Key, Fileder from require 'mmm.mmmfs.fileder'
1213 import convert from require 'mmm.mmmfs.conversion'
6465 <script type="text/javascript" src="//unpkg.com/marked@0.7.0/marked.min.js"></script>
6566 <script type="text/javascript" src="/static/fengari-web/:text/javascript"></script>
6667 <script type="text/lua" src="/static/mmm/:text/lua"></script>
67 <script type="text/lua">require 'mmm'</script>
68 <script type="text/lua">require 'mmm'; require 'mmm.mmmfs'</script>
6869 ]]
6970
7071 render browser\todom!, fileder, noview: true, scripts: deps .. "
00 require = relative ..., 1
11 import Key from require '.fileder'
2 import converts from require '.plugins'
23 import get_conversions, apply_conversions from require '.conversion'
34 import ReactiveVar, get_or_create, text, elements, tohtml from require 'mmm.component'
45 import pre, div, nav, span, button, a, code, select, option from elements
56 import languages from require 'mmm.highlighting'
6 converts = require '.converts'
77
88 keep = (var) ->
99 last = var\get!
1010 var\map (val) ->
1111 last = val or last
1212 last
13
14 code_cast = (lang) ->
1513
1614 casts = {
1715 {
00 require = relative ..., 1
1 base_converts = require '.converts'
21 import Queue from require '.queue'
32
43 count = (base, pattern='->') -> select 2, base\gsub pattern, ''
1211 -- * want - stop type pattern
1312 -- * limit - limit conversion amount
1413 -- returns a list of conversion steps
15 get_conversions = (want, have, converts=base_converts, limit=5) ->
14 get_conversions = (want, have, converts=PLUGINS and PLUGINS.converts, limit=5) ->
1615 assert have, 'need starting type(s)'
16 assert converts, 'need to pass list of converts'
1717
1818 if 'string' == type have
1919 have = { have }
+0
-386
mmm/mmmfs/converts.moon less more
0 require = relative ..., 1
1 import div, pre, code, img, video, blockquote, a, span, source, iframe from require 'mmm.dom'
2 import find_fileder, link_to, embed from (require 'mmm.mmmfs.util') require 'mmm.dom'
3 import render from require '.layout'
4 import tohtml from require 'mmm.component'
5 import languages from require 'mmm.highlighting'
6
7 keep = (var) ->
8 last = var\get!
9 var\map (val) ->
10 last = val or last
11 last
12
13 -- fix JS null values
14 js_fix = if MODE == 'CLIENT'
15 (arg) ->
16 return if arg == js.null
17 arg
18
19 -- limit function to one argument
20 single = (func) -> (val) => func val
21
22 -- load a chunk using a specific 'load'er
23 loadwith = (_load) -> (val, fileder, key) =>
24 func = assert _load val, "#{fileder}##{key}"
25 func!
26
27 -- highlight code
28 code_hl = (lang) ->
29 {
30 inp: "text/#{lang}",
31 out: 'mmm/dom',
32 cost: 5
33 transform: (val) => pre languages[lang] val
34 }
35
36 -- list of converts
37 -- converts each have
38 -- * inp - input type. can capture subtypes using `(.+)`
39 -- * out - output type. can substitute subtypes from inp with %1, %2 etc.
40 -- * transform - function (val: inp, fileder) -> val: out
41 converts = {
42 {
43 inp: 'fn -> (.+)',
44 out: '%1',
45 cost: 1
46 transform: (val, fileder) => val fileder
47 }
48 {
49 inp: 'mmm/component',
50 out: 'mmm/dom',
51 cost: 3
52 transform: single tohtml
53 }
54 {
55 inp: 'mmm/dom',
56 out: 'text/html+frag',
57 cost: 3
58 transform: (node) => if MODE == 'SERVER' then node else node.outerHTML
59 }
60 {
61 -- inp: 'text/html%+frag',
62 -- @TODO: this doesn't feel right... maybe mmm/dom has to go?
63 inp: 'mmm/dom',
64 out: 'text/html',
65 cost: 3
66 transform: (html, fileder) => render html, fileder
67 }
68 {
69 inp: 'text/html%+frag',
70 out: 'mmm/dom',
71 cost: 1
72 transform: if MODE == 'SERVER'
73 (html, fileder) =>
74 html = html\gsub '<mmm%-link%s+(.-)>(.-)</mmm%-link>', (attrs, text) ->
75 text = nil if #text == 0
76 path = ''
77 while attrs and attrs != ''
78 key, val, _attrs = attrs\match '^(%w+)="([^"]-)"%s*(.*)'
79 if not key
80 key, _attrs = attrs\match '^(%w+)%s*(.*)$'
81 val = true
82
83 attrs = _attrs
84
85 switch key
86 when 'path' then path = val
87 else warn "unkown attribute '#{key}=\"#{val}\"' in <mmm-link>"
88
89 link_to path, text, fileder
90
91 html = html\gsub '<mmm%-embed%s+(.-)>(.-)</mmm%-embed>', (attrs, desc) ->
92 path, facet = '', ''
93 opts = {}
94 if #desc != 0
95 opts.desc = desc
96
97 while attrs and attrs != ''
98 key, val, _attrs = attrs\match '^(%w+)="([^"]-)"%s*(.*)'
99 if not key
100 key, _attrs = attrs\match '^(%w+)%s*(.*)$'
101 val = true
102
103 attrs = _attrs
104
105 switch key
106 when 'path' then path = val
107 when 'facet' then facet = val
108 when 'nolink' then opts.nolink = true
109 when 'inline' then opts.inline = true
110 else warn "unkown attribute '#{key}=\"#{val}\"' in <mmm-embed>"
111
112 embed path, facet, fileder, opts
113
114 html
115 else
116 (html, fileder) =>
117 parent = with document\createElement 'div'
118 .innerHTML = html
119
120 -- copy to iterate safely, HTMLCollections update when nodes are GC'ed
121 embeds = \getElementsByTagName 'mmm-embed'
122 embeds = [embeds[i] for i=0, embeds.length - 1]
123 for element in *embeds
124 path = js_fix element\getAttribute 'path'
125 facet = js_fix element\getAttribute 'facet'
126 nolink = js_fix element\getAttribute 'nolink'
127 inline = js_fix element\getAttribute 'inline'
128 desc = js_fix element.innerText
129 desc = nil if desc == ''
130
131 element\replaceWith embed path or '', facet or '', fileder, { :nolink, :inline, :desc }
132
133 embeds = \getElementsByTagName 'mmm-link'
134 embeds = [embeds[i] for i=0, embeds.length - 1]
135 for element in *embeds
136 text = js_fix element.innerText
137 path = js_fix element\getAttribute 'path'
138
139 element\replaceWith link_to path or '', text, fileder
140
141 assert 1 == parent.childElementCount, "text/html with more than one child!"
142 parent.firstElementChild
143 }
144 {
145 inp: 'text/lua -> (.+)',
146 out: '%1',
147 cost: 0.5
148 transform: loadwith load or loadstring
149 }
150 {
151 inp: 'mmm/tpl -> (.+)',
152 out: '%1',
153 cost: 1
154 transform: (source, fileder) =>
155 source\gsub '{{(.-)}}', (expr) ->
156 path, facet = expr\match '^([%w%-_%./]*)%+(.*)'
157 assert path, "couldn't match TPL expression '#{expr}'"
158
159 (find_fileder path, fileder)\gett facet
160 }
161 {
162 inp: 'time/iso8601-date',
163 out: 'time/unix',
164 cost: 0.5
165 transform: (val) =>
166 year, _, month, day = val\match '^%s*(%d%d%d%d)(%-?)([01]%d)%2([0-3]%d)%s*$'
167 assert year, "failed to parse ISO 8601 date: '#{val}'"
168 os.time :year, :month, :day
169 }
170 {
171 inp: 'URL -> twitter/tweet',
172 out: 'mmm/dom',
173 cost: 1
174 transform: (href) =>
175 id = assert (href\match 'twitter.com/[^/]-/status/(%d*)'), "couldn't parse twitter/tweet URL: '#{href}'"
176 if MODE == 'CLIENT'
177 with parent = div!
178 window.twttr.widgets\createTweet id, parent
179 else
180 div blockquote {
181 class: 'twitter-tweet'
182 'data-lang': 'en'
183 a '(linked tweet)', :href
184 }
185 }
186 {
187 inp: 'URL -> youtube/video',
188 out: 'mmm/dom',
189 cost: 1
190 transform: (link) =>
191 id = link\match 'youtu%.be/([^/]+)'
192 id or= link\match 'youtube.com/watch.*[?&]v=([^&]+)'
193 id or= link\match 'youtube.com/[ev]/([^/]+)'
194 id or= link\match 'youtube.com/embed/([^/]+)'
195
196 assert id, "couldn't parse youtube URL: '#{link}'"
197
198 iframe {
199 width: 560
200 height: 315
201 frameborder: 0
202 allowfullscreen: true
203 frameBorder: 0
204 src: "//www.youtube.com/embed/#{id}"
205 }
206 }
207 {
208 inp: 'URL -> image/.+',
209 out: 'mmm/dom',
210 cost: 1
211 transform: (src, fileder) => img :src
212 }
213 {
214 inp: 'URL -> video/.+',
215 out: 'mmm/dom',
216 cost: 1
217 transform: (src) =>
218 -- @TODO: add parsed MIME type
219 video (source :src), controls: true, loop: true
220 }
221 {
222 inp: 'text/plain',
223 out: 'mmm/dom',
224 cost: 2
225 transform: (val) => span val
226 }
227 {
228 inp: 'alpha',
229 out: 'mmm/dom',
230 cost: 2
231 transform: single code
232 }
233 -- this one needs a higher cost
234 -- {
235 -- inp: 'URL -> .+',
236 -- out: 'mmm/dom',
237 -- transform: single code
238 -- }
239 {
240 inp: '(.+)',
241 out: 'URL -> %1',
242 cost: 5
243 transform: (_, fileder, key) => "#{fileder.path}/#{key.name}:#{@from}"
244 }
245 {
246 inp: 'table',
247 out: 'text/json',
248 cost: 2
249 transform: do
250 tojson = (obj) ->
251 switch type obj
252 when 'string'
253 string.format '%q', obj
254 when 'table'
255 if obj[1] or not next obj
256 "[#{table.concat [tojson c for c in *obj], ','}]"
257 else
258 "{#{table.concat ["#{tojson k}: #{tojson v}" for k,v in pairs obj], ', '}}"
259 when 'number'
260 tostring obj
261 when 'boolean'
262 tostring obj
263 when 'nil'
264 'null'
265 else
266 error "unknown type '#{type obj}'"
267
268 (val) => tojson val
269 }
270 {
271 inp: 'table',
272 out: 'mmm/dom',
273 cost: 5
274 transform: do
275 deep_tostring = (tbl, space='') ->
276 buf = space .. tostring tbl
277
278 return buf unless 'table' == type tbl
279
280 buf = buf .. ' {\n'
281 for k,v in pairs tbl
282 buf = buf .. "#{space} [#{k}]: #{deep_tostring v, space .. ' '}\n"
283 buf = buf .. "#{space}}"
284 buf
285
286 (tbl) => pre code deep_tostring tbl
287 }
288 code_hl 'javascript'
289 code_hl 'moonscript'
290 code_hl 'lua'
291 code_hl 'markdown'
292 code_hl 'css'
293 }
294
295 if MODE == 'SERVER'
296 ok, moon = pcall require, 'moonscript.base'
297 if ok
298 _load = moon.load or moon.loadstring
299 table.insert converts, {
300 inp: 'text/moonscript -> (.+)',
301 out: '%1',
302 cost: 1
303 transform: loadwith moon.load or moon.loadstring
304 }
305
306 table.insert converts, {
307 inp: 'text/moonscript -> (.+)',
308 out: 'text/lua -> %1',
309 cost: 2
310 transform: single moon.to_lua
311 }
312 else
313 table.insert converts, {
314 inp: 'text/javascript -> (.+)',
315 out: '%1',
316 cost: 1
317 transform: (source) =>
318 f = js.new window.Function, source
319 f!
320 }
321
322 do
323 local markdown
324 if MODE == 'SERVER'
325 success, discount = pcall require, 'discount'
326 if not success
327 warn "NO MARKDOWN SUPPORT!", discount
328
329 markdown = success and (md) ->
330 res = assert discount.compile md, 'githubtags'
331 res.body
332 else
333 markdown = window and window.marked and window\marked
334
335 if markdown
336 table.insert converts, {
337 inp: 'text/markdown',
338 out: 'text/html+frag',
339 cost: 1
340 transform: (md) => "<div class=\"markdown\">#{markdown md}</div>"
341 }
342
343 table.insert converts, {
344 inp: 'text/markdown%+span',
345 out: 'mmm/dom',
346 cost: 1
347 transform: if MODE == 'SERVER'
348 (source) =>
349 html = markdown source
350 html = html\gsub '^<p', '<span'
351 html\gsub '/p>$', '/span>'
352 else
353 (source) =>
354 html = markdown source
355 html = html\gsub '^%s*<p>%s*', ''
356 html = html\gsub '%s*</p>%s*$', ''
357 with document\createElement 'span'
358 .innerHTML = html
359 }
360
361 if MODE == 'CLIENT' and window.mermaid
362 window.mermaid\initialize {
363 startOnLoad: false
364 fontFamily: 'monospace'
365 }
366
367 id_counter = 1
368 table.insert converts, {
369 inp: 'text/mermaid-graph'
370 out: 'mmm/dom'
371 cost: 1
372 transform: (source, fileder, key) =>
373 id_counter += 1
374 id = "mermaid-#{id_counter}"
375 with container = document\createElement 'div'
376 cb = (svg) =>
377 .innerHTML = svg
378 .firstElementChild.style.width = '100%'
379 .firstElementChild.style.height = 'auto'
380
381 window\setImmediate (_) ->
382 window.mermaid\render id, source, cb, container
383 }
384
385 converts
0 require = relative ...
1 import Key, Fileder from require '.fileder'
2 import Browser from require '.browser'
0 require = relative ..., 0
31
4 {
5 :Key
6 :Fileder
7 :Browser
8 }
2 export ^
3 PLUGINS = require '.plugins'
0 import pre from require 'mmm.dom'
1 import languages from require 'mmm.highlighting'
2
3 -- syntax-highlighted code
4 {
5 converts: {
6 {
7 inp: 'text/([^ ]*).*'
8 out: 'mmm/dom'
9 cost: 5
10 transform: (val) =>
11 lang = @from\match @convert.inp
12 pre languages[lang] val
13 }
14 }
15 }
0 require = relative ..., 1
1 import div, pre, code, img, video, span, source from require 'mmm.dom'
2 import find_fileder, link_to, embed from (require 'mmm.mmmfs.util') require 'mmm.dom'
3 import render from require '.layout'
4 import tohtml from require 'mmm.component'
5
6 keep = (var) ->
7 last = var\get!
8 var\map (val) ->
9 last = val or last
10 last
11
12 -- fix JS null values
13 js_fix = if MODE == 'CLIENT'
14 (arg) ->
15 return if arg == js.null
16 arg
17
18 -- limit function to one argument
19 single = (func) -> (val) => func val
20
21 -- load a chunk using a specific 'load'er
22 loadwith = (_load) -> (val, fileder, key) =>
23 func = assert _load val, "#{fileder}##{key}"
24 func!
25
26 -- list of converts
27 -- converts each have
28 -- * inp - input type. can capture subtypes using `(.+)`
29 -- * out - output type. can substitute subtypes from inp with %1, %2 etc.
30 -- * cost - conversion cost
31 -- * transform - function (val: inp, fileder) => val: out
32 -- @convert, @from, @to contain the convert and the concrete types
33 converts = {
34 {
35 inp: 'fn -> (.+)',
36 out: '%1',
37 cost: 1
38 transform: (val, fileder) => val fileder
39 }
40 {
41 inp: 'mmm/component',
42 out: 'mmm/dom',
43 cost: 3
44 transform: single tohtml
45 }
46 {
47 inp: 'mmm/dom',
48 out: 'text/html+frag',
49 cost: 3
50 transform: (node) => if MODE == 'SERVER' then node else node.outerHTML
51 }
52 {
53 -- inp: 'text/html%+frag',
54 -- @TODO: this doesn't feel right... maybe mmm/dom has to go?
55 inp: 'mmm/dom',
56 out: 'text/html',
57 cost: 3
58 transform: (html, fileder) => render html, fileder
59 }
60 {
61 inp: 'text/html%+frag',
62 out: 'mmm/dom',
63 cost: 1
64 transform: if MODE == 'SERVER'
65 (html, fileder) =>
66 html = html\gsub '<mmm%-link%s+(.-)>(.-)</mmm%-link>', (attrs, text) ->
67 text = nil if #text == 0
68 path = ''
69 while attrs and attrs != ''
70 key, val, _attrs = attrs\match '^(%w+)="([^"]-)"%s*(.*)'
71 if not key
72 key, _attrs = attrs\match '^(%w+)%s*(.*)$'
73 val = true
74
75 attrs = _attrs
76
77 switch key
78 when 'path' then path = val
79 else warn "unkown attribute '#{key}=\"#{val}\"' in <mmm-link>"
80
81 link_to path, text, fileder
82
83 html = html\gsub '<mmm%-embed%s+(.-)>(.-)</mmm%-embed>', (attrs, desc) ->
84 path, facet = '', ''
85 opts = {}
86 if #desc != 0
87 opts.desc = desc
88
89 while attrs and attrs != ''
90 key, val, _attrs = attrs\match '^(%w+)="([^"]-)"%s*(.*)'
91 if not key
92 key, _attrs = attrs\match '^(%w+)%s*(.*)$'
93 val = true
94
95 attrs = _attrs
96
97 switch key
98 when 'path' then path = val
99 when 'facet' then facet = val
100 when 'nolink' then opts.nolink = true
101 when 'inline' then opts.inline = true
102 else warn "unkown attribute '#{key}=\"#{val}\"' in <mmm-embed>"
103
104 embed path, facet, fileder, opts
105
106 html
107 else
108 (html, fileder) =>
109 parent = with document\createElement 'div'
110 .innerHTML = html
111
112 -- copy to iterate safely, HTMLCollections update when nodes are GC'ed
113 embeds = \getElementsByTagName 'mmm-embed'
114 embeds = [embeds[i] for i=0, embeds.length - 1]
115 for element in *embeds
116 path = js_fix element\getAttribute 'path'
117 facet = js_fix element\getAttribute 'facet'
118 nolink = js_fix element\getAttribute 'nolink'
119 inline = js_fix element\getAttribute 'inline'
120 desc = js_fix element.innerText
121 desc = nil if desc == ''
122
123 element\replaceWith embed path or '', facet or '', fileder, { :nolink, :inline, :desc }
124
125 embeds = \getElementsByTagName 'mmm-link'
126 embeds = [embeds[i] for i=0, embeds.length - 1]
127 for element in *embeds
128 text = js_fix element.innerText
129 path = js_fix element\getAttribute 'path'
130
131 element\replaceWith link_to path or '', text, fileder
132
133 assert 1 == parent.childElementCount, "text/html with more than one child!"
134 parent.firstElementChild
135 }
136 {
137 inp: 'text/lua -> (.+)',
138 out: '%1',
139 cost: 0.5
140 transform: loadwith load or loadstring
141 }
142 {
143 inp: 'mmm/tpl -> (.+)',
144 out: '%1',
145 cost: 1
146 transform: (source, fileder) =>
147 source\gsub '{{(.-)}}', (expr) ->
148 path, facet = expr\match '^([%w%-_%./]*)%+(.*)'
149 assert path, "couldn't match TPL expression '#{expr}'"
150
151 (find_fileder path, fileder)\gett facet
152 }
153 {
154 inp: 'time/iso8601-date',
155 out: 'time/unix',
156 cost: 0.5
157 transform: (val) =>
158 year, _, month, day = val\match '^%s*(%d%d%d%d)(%-?)([01]%d)%2([0-3]%d)%s*$'
159 assert year, "failed to parse ISO 8601 date: '#{val}'"
160 os.time :year, :month, :day
161 }
162 {
163 inp: 'URL -> image/.+',
164 out: 'mmm/dom',
165 cost: 1
166 transform: (src, fileder) => img :src
167 }
168 {
169 inp: 'URL -> video/.+',
170 out: 'mmm/dom',
171 cost: 1
172 transform: (src) =>
173 -- @TODO: add parsed MIME type
174 video (source :src), controls: true, loop: true
175 }
176 {
177 inp: 'text/plain',
178 out: 'mmm/dom',
179 cost: 2
180 transform: (val) => span val
181 }
182 {
183 inp: 'alpha',
184 out: 'mmm/dom',
185 cost: 2
186 transform: single code
187 }
188 -- this one needs a higher cost
189 -- {
190 -- inp: 'URL -> .+',
191 -- out: 'mmm/dom',
192 -- transform: single code
193 -- }
194 {
195 inp: '(.+)',
196 out: 'URL -> %1',
197 cost: 5
198 transform: (_, fileder, key) => "#{fileder.path}/#{key.name}:#{@from}"
199 }
200 {
201 inp: 'table',
202 out: 'text/json',
203 cost: 2
204 transform: do
205 tojson = (obj) ->
206 switch type obj
207 when 'string'
208 string.format '%q', obj
209 when 'table'
210 if obj[1] or not next obj
211 "[#{table.concat [tojson c for c in *obj], ','}]"
212 else
213 "{#{table.concat ["#{tojson k}: #{tojson v}" for k,v in pairs obj], ', '}}"
214 when 'number'
215 tostring obj
216 when 'boolean'
217 tostring obj
218 when 'nil'
219 'null'
220 else
221 error "unknown type '#{type obj}'"
222
223 (val) => tojson val
224 }
225 {
226 inp: 'table',
227 out: 'mmm/dom',
228 cost: 5
229 transform: do
230 deep_tostring = (tbl, space='') ->
231 buf = space .. tostring tbl
232
233 return buf unless 'table' == type tbl
234
235 buf = buf .. ' {\n'
236 for k,v in pairs tbl
237 buf = buf .. "#{space} [#{k}]: #{deep_tostring v, space .. ' '}\n"
238 buf = buf .. "#{space}}"
239 buf
240
241 (tbl) => pre code deep_tostring tbl
242 }
243 }
244
245 add_converts = (module) ->
246 ok, plugin = pcall require, ".plugins.#{module}"
247
248 if not ok
249 print "[Plugins] couldn't load plugins.#{module}: #{plugin}"
250 return
251
252 print "[Plugins] loaded plugins.#{module}"
253
254 if plugin.converts
255 for convert in *plugin.converts
256 table.insert converts, convert
257
258 add_converts 'code'
259 add_converts 'markdown'
260 add_converts 'mermaid'
261 add_converts 'twitter'
262 add_converts 'youtube'
263
264 if MODE == 'SERVER'
265 ok, moon = pcall require, 'moonscript.base'
266 if ok
267 _load = moon.load or moon.loadstring
268 table.insert converts, {
269 inp: 'text/moonscript -> (.+)',
270 out: '%1',
271 cost: 1
272 transform: loadwith moon.load or moon.loadstring
273 }
274
275 table.insert converts, {
276 inp: 'text/moonscript -> (.+)',
277 out: 'text/lua -> %1',
278 cost: 2
279 transform: single moon.to_lua
280 }
281 else
282 table.insert converts, {
283 inp: 'text/javascript -> (.+)',
284 out: '%1',
285 cost: 1
286 transform: (source) =>
287 f = js.new window.Function, source
288 f!
289 }
290
291 :converts
0 markdown = if MODE == 'SERVER'
1 success, discount = pcall require, 'discount'
2 assert success, "couldn't require 'discount'"
3
4 (md) ->
5 res = assert discount.compile md, 'githubtags'
6 res.body
7 else
8 assert window and window.marked, "marked.js not found"
9 window\marked
10
11 assert markdown, "no markdown implementation found"
12
13 {
14 converts: {
15 {
16 inp: 'text/markdown'
17 out: 'text/html+frag'
18 cost: 1
19 transform: (md) => "<div class=\"markdown\">#{markdown md}</div>"
20 }
21 {
22 inp: 'text/markdown%+span'
23 out: 'text/html+frag'
24 cost: 1
25 transform: (source) =>
26 html = markdown source
27 html = html\gsub '^%s*<p>%s*', '<span>'
28 html = html\gsub '%s*</p>%s*$', '</span>'
29 html
30 }
31 }
32 }
0 assert window and window.mermaid, "mermaid.js not found"
1
2 window.mermaid\initialize {
3 startOnLoad: false
4 fontFamily: 'monospace'
5 }
6
7 id_counter = 1
8
9 {
10 converts: {
11 {
12 inp: 'text/mermaid-graph'
13 out: 'mmm/dom'
14 cost: 1
15 transform: (source, fileder, key) =>
16 id_counter += 1
17 id = "mermaid-#{id_counter}"
18 with container = document\createElement 'div'
19 cb = (svg) =>
20 .innerHTML = svg
21 .firstElementChild.style.width = '100%'
22 .firstElementChild.style.height = 'auto'
23
24 window\setImmediate (_) ->
25 window.mermaid\render id, source, cb, container
26 }
27 }
28 }
0 import div, blockquote, a from require 'mmm.dom'
1
2 {
3 converts: {
4 {
5 inp: 'URL -> twitter/tweet'
6 out: 'mmm/dom'
7 cost: 1
8 transform: (href) =>
9 id = assert (href\match 'twitter.com/[^/]-/status/(%d*)'), "couldn't parse twitter/tweet URL: '#{href}'"
10 if MODE == 'CLIENT'
11 with parent = div!
12 window.twttr.widgets\createTweet id, parent
13 else
14 div blockquote {
15 class: 'twitter-tweet'
16 'data-lang': 'en'
17 a '(linked tweet)', :href
18 }
19 }
20 }
21 }
0 import iframe from require 'mmm.dom'
1
2 {
3 converts: {
4 {
5 inp: 'URL -> youtube/video'
6 out: 'mmm/dom'
7 cost: 1
8 transform: (link) =>
9 id = link\match 'youtu%.be/([^/]+)'
10 id or= link\match 'youtube.com/watch.*[?&]v=([^&]+)'
11 id or= link\match 'youtube.com/[ev]/([^/]+)'
12 id or= link\match 'youtube.com/embed/([^/]+)'
13
14 assert id, "couldn't parse youtube URL: '#{link}'"
15
16 iframe {
17 width: 560
18 height: 315
19 frameborder: 0
20 allowfullscreen: true
21 frameBorder: 0
22 src: "//www.youtube.com/embed/#{id}"
23 }
24 }
25 }
26 }