git.s-ol.nu mmm / 91546d1
add WebStore, fix Browser s-ol 3 years ago
7 changed file(s) with 123 addition(s) and 3171 deletion(s). Raw diff Collapse all Expand all
00 db.sqlite3
1 root/static/style/text$css.css
2 root/static/mmm/application$lua.lua
13 ##### TUP GITIGNORE #####
24 ##### Lines below automatically generated by Tup.
35 ##### Do not edit.
5757 table.insert(on_load, function()
5858 local path = #{string.format '%q', path}
5959 local browser = require 'mmm.mmmfs.browser'
60 local root = dofile '/$bundle.lua'
61 root:mount('', true)
60 local fileder = require 'mmm.mmmfs.fileder'
61 local web = require 'mmm.mmmfs.stores.web'
62 local root = fileder.Fileder(web.WebStore({ verbose = true }), path)
6263
6364 BROWSER = browser.Browser(root, path, true)
6465 end)
1414 {
1515 inp: "text/#{lang}.*",
1616 out: 'mmm/dom',
17 transform: (val) -> languages[lang] val
17 transform: (val) => languages[lang] val
1818 }
1919
2020 casts = {
2626 {
2727 inp: 'text/plain'
2828 out: 'mmm/dom'
29 transform: (val) -> text val
29 transform: (val) => text val
3030 },
3131 {
3232 inp: 'URL.*'
3333 out: 'mmm/dom'
34 transform: (href) -> span a (code href), :href
34 transform: (href) => span a (code href), :href
3535 },
3636 }
3737
4848
4949 -- update URL bar
5050 if MODE == 'CLIENT'
51 logo = document\querySelector 'header h1 > svg'
51 logo = document\querySelector 'header h1 > a > svg'
5252 spin = ->
5353 logo.classList\add 'spin'
5454 logo.parentElement.offsetWidth
114114
115115 \append span 'view facet:', style: { 'margin-right': '0' }
116116 \append @active\map (fileder) ->
117 onchange = (_, e) ->
118 { :type } = @facet\get!
119 @facet\set Key name: e.target.value, :type
120
121 current = @facet\get!
122 current = current and current.name
123 with select :onchange, disabled: not fileder
124 has_main = fileder and fileder\find current.name, '.*'
125 \append option '(main)', value: '', disabled: not has_main, selected: current == ''
126 if fileder
127 for i, value in ipairs fileder\get_facets!
128 continue if value == ''
129 \append option value, :value, selected: value == current
117 onchange = (_, e) ->
118 { :type } = @facet\get!
119 @facet\set Key name: e.target.value, :type
120
121 current = @facet\get!
122 current = current and current.name
123 with select :onchange, disabled: not fileder
124 has_main = fileder and fileder\has_facet ''
125 \append option '(main)', value: '', disabled: not has_main, selected: current == ''
126 if fileder
127 for i, value in ipairs fileder\get_facets!
128 continue if value == ''
129 \append option value, :value, selected: value == current
130130 \append @inspect\map (enabled) ->
131131 if not enabled
132132 button 'inspect', onclick: (_, e) -> @inspect\set true
214214 with select :onchange
215215 \append option '(none)', value: '', disabled: true, selected: not value
216216 if fileder
217 for i, key in ipairs fileder\get_facets!
218 value = key\tostring!
217 for value in pairs fileder.facet_keys
219218 \append option value, :value, selected: value == current
220219 @inspect\map (enabled) ->
221220 if enabled
121121 for k, v in pairs @facet_keys
122122 t[v]
123123
124 pairs @facets
124 next, t
125125
126126 __newindex: (t, k, v) ->
127127 -- get canonical Key instance
130130 rawset t, k, v
131131
132132 v = k unless v == nil
133 rawset @facet_keys, k, v
133 @facet_keys[k] = v
134134 }
135135
136136 load: =>
145145
146146 _, name = dir_base @path
147147 @facets['name: alpha'] = name
148
149 -- -- instantiate from facets and children tables
150 -- -- or mix in one table (numeric keys are children, remainder facets)
151 -- -- facet-keys are passed to Key constructor
152 -- new: (facets, children) =>
153 -- if not children
154 -- children = for i, child in ipairs facets
155 -- facets[i] = nil
156 -- child
157
158 -- -- automatically mount children on insert
159 -- @children = setmetatable {}, {
160 -- __index: (t, k) ->
161 -- return rawget t, k unless 'string' == type k
162
163 -- @walk "#{@path}/#{k}"
164
165 -- __newindex: (t, k, child) ->
166 -- rawset t, k, child
167 -- if @path == '/'
168 -- child\mount '/'
169 -- elseif @path
170 -- child\mount @path .. '/'
171 -- }
172
173 -- -- copy children
174 -- for i, child in ipairs children
175 -- @children[i] = child
176
177 -- -- automatically reify string keys on insert
178 -- @facets = setmetatable {}, __newindex: (t, key, v) ->
179 -- rawset t, (Key key), v
180
181 -- -- copy facets
182 -- for k, v in pairs facets
183 -- @facets[k] = v
184148
185149 -- recursively walk to and return the fileder with @path == path
186150 -- * path - the path to walk to
0 -- split filename into dirname + basename
1 dir_base = (path) ->
2 dir, base = path\match '(.-)([^/]-)$'
3 if dir and #dir > 0
4 dir = dir\sub 1, #dir - 1
5
6 dir, base
7
8 { :location, :XMLHttpRequest, :JSON } = js.global
9 fetch = (url) ->
10 request = js.new XMLHttpRequest
11 request\open 'GET', url, false
12 request\send js.null
13
14 assert request.status == 200, "unexpected status code: #{request.status}"
15 request.responseText
16
17 class WebStore
18 new: (opts = {}) =>
19 if MODE == 'CLIENT'
20 origin = location.origin
21 opts.host or= origin
22 opts.verbose or= false
23
24 if not opts.verbose
25 @log = ->
26
27 -- ensure host ends without a slash
28 @host = opts.host\match '^(.-)/?$'
29 @log "connecting to '#{@host}'..."
30
31 log: (...) =>
32 print "[DB]", ...
33
34 -- fileders
35 list_fileders_in: (path='') =>
36 coroutine.wrap ->
37 json = fetch "#{@host .. path}/?index: application/json"
38 index = JSON\parse json
39 for child in js.of index.children
40 coroutine.yield child.path
41
42 list_all_fileders: (path='') =>
43 coroutine.wrap ->
44 for path in @list_fileders_in path
45 coroutine.yield path
46 for p in @list_all_fileders path
47 coroutine.yield p
48
49 create_fileder: (parent, name) =>
50 @log "creating fileder #{path}"
51 error "not implemented"
52
53 remove_fileder: (path) =>
54 @log "removing fileder #{path}"
55 error "not implemented"
56
57 rename_fileder: (path, next_name) =>
58 @log "renaming fileder #{path} -> '#{next_name}'"
59 error "not implemented"
60
61 move_fileder: (path, next_parent) =>
62 @log "moving fileder #{path} -> #{next_parent}/"
63 error "not implemented"
64
65 -- facets
66 list_facets: (path) =>
67 coroutine.wrap ->
68 json = fetch "#{@host .. path}/?index: application/json"
69 index = JSON\parse json
70 for facet in js.of index.facets
71 coroutine.yield facet.name, facet.type
72 -- @TODO: this doesn't belong here!
73 if facet.type\match 'text/moonscript'
74 coroutine.yield facet.name, facet.type\gsub 'text/moonscript', 'text/lua'
75
76 load_facet: (path, name, type) =>
77 fetch "#{@host .. path}/#{name}: #{type}"
78
79 create_facet: (path, name, type, blob) =>
80 @log "creating facet #{path} | #{name}: #{type}"
81 error "not implemented"
82
83 remove_facet: (path, name, type) =>
84 @log "removing facet #{path} | #{name}: #{type}"
85 error "not implemented"
86
87 rename_facet: (path, name, type, next_name) =>
88 @log "renaming facet #{path} | #{name}: #{type} -> #{next_name}"
89 error "not implemented"
90
91 update_facet: (path, name, type, blob) =>
92 @log "updating facet #{path} | #{name}: #{type}"
93 error "not implemented"
94
95 {
96 :WebStore
97 }
+0
-2701
root/static/mmm/application$lua.lua less more
0 local p = package.preload
1 if not p["mmm.canvasapp"] then p["mmm.canvasapp"] = load("local window = js.global\
2 local js = require('js')\
3 local a, canvas, div, button, script\
4 do\
5 local _obj_0 = require('mmm.dom')\
6 a, canvas, div, button, script = _obj_0.a, _obj_0.canvas, _obj_0.div, _obj_0.button, _obj_0.script\
7 end\
8 local CanvasApp\
9 do\
10 local _class_0\
11 local _base_0 = {\
12 width = 500,\
13 height = 400,\
14 update = function(self, dt)\
15 self.time = self.time + dt\
16 if self.length and self.time > self.length then\
17 self.time = self.time - self.length\
18 return true\
19 end\
20 end,\
21 render = function(self, fps)\
22 if fps == nil then\
23 fps = 60\
24 end\
25 assert(self.length, 'cannot render CanvasApp without length set')\
26 self.paused = true\
27 local actual_render\
28 actual_render = function()\
29 local writer = js.new(window.Whammy.Video, fps)\
30 local doFrame\
31 doFrame = function()\
32 local done = self:update(1 / fps)\
33 self.ctx:resetTransform()\
34 self:draw()\
35 writer:add(self.canvas)\
36 if done or self.time >= self.length then\
37 local blob = writer:compile()\
38 local name = tostring(self.__class.__name) .. \"_\" .. tostring(fps) .. \"fps.webm\"\
39 return self.node.lastChild:appendChild(a(name, {\
40 download = name,\
41 href = window.URL:createObjectURL(blob)\
42 }))\
43 else\
44 return window:setTimeout(doFrame)\
45 end\
46 end\
47 self.time = 0\
48 return doFrame()\
49 end\
50 if window.Whammy then\
51 return actual_render()\
52 else\
53 window.global = window.global or window\
54 return document.body:appendChild(script({\
55 onload = actual_render,\
56 src = 'https://cdn.jsdelivr.net/npm/whammy@0.0.1/whammy.min.js'\
57 }))\
58 end\
59 end\
60 }\
61 _base_0.__index = _base_0\
62 _class_0 = setmetatable({\
63 __init = function(self, show_menu, paused)\
64 if show_menu == nil then\
65 show_menu = false\
66 end\
67 self.paused = paused\
68 self.canvas = canvas({\
69 width = self.width,\
70 height = self.height\
71 })\
72 self.ctx = self.canvas:getContext('2d')\
73 self.time = 0\
74 self.canvas.tabIndex = 0\
75 self.canvas:addEventListener('click', function(_, e)\
76 return self.click and self:click(e.offsetX, e.offsetY, e.button)\
77 end)\
78 self.canvas:addEventListener('keydown', function(_, e)\
79 return self.keydown and self:keydown(e.key, e.code)\
80 end)\
81 local lastMillis = window.performance:now()\
82 local animationFrame\
83 animationFrame = function(_, millis)\
84 self:update((millis - lastMillis) / 1000)\
85 self.ctx:resetTransform()\
86 self:draw()\
87 lastMillis = millis\
88 if not self.paused then\
89 return window:setTimeout((function()\
90 return window:requestAnimationFrame(animationFrame)\
91 end), 0)\
92 end\
93 end\
94 window:requestAnimationFrame(animationFrame)\
95 if show_menu then\
96 self.node = div({\
97 className = 'canvas_app',\
98 self.canvas,\
99 div({\
100 className = 'overlay',\
101 button('render 30fps', {\
102 onclick = function()\
103 return self:render(30)\
104 end\
105 }),\
106 button('render 60fps', {\
107 onclick = function()\
108 return self:render(60)\
109 end\
110 })\
111 })\
112 })\
113 else\
114 self.node = self.canvas\
115 end\
116 end,\
117 __base = _base_0,\
118 __name = \"CanvasApp\"\
119 }, {\
120 __index = _base_0,\
121 __call = function(cls, ...)\
122 local _self_0 = setmetatable({}, _base_0)\
123 cls.__init(_self_0, ...)\
124 return _self_0\
125 end\
126 })\
127 _base_0.__class = _class_0\
128 CanvasApp = _class_0\
129 end\
130 return {\
131 CanvasApp = CanvasApp\
132 }\
133 ", "mmm/canvasapp.client.lua") end
134 if not p["mmm.color"] then p["mmm.color"] = load("local rgb\
135 rgb = function(r, g, b)\
136 if 'table' == type(r) then\
137 r, g, b = table.unpack(r)\
138 end\
139 return \"rgb(\" .. tostring(r * 255) .. \", \" .. tostring(g * 255) .. \", \" .. tostring(b * 255) .. \")\"\
140 end\
141 local rgba\
142 rgba = function(r, g, b, a)\
143 if 'table' == type(r) then\
144 r, g, b, a = table.unpack(r)\
145 end\
146 return \"rgba(\" .. tostring(r * 255) .. \", \" .. tostring(g * 255) .. \", \" .. tostring(b * 255) .. \", \" .. tostring(a or 1) .. \")\"\
147 end\
148 local hsl\
149 hsl = function(h, s, l)\
150 if 'table' == type(h) then\
151 h, s, l = table.unpack(h)\
152 end\
153 return \"hsl(\" .. tostring(h * 360) .. \", \" .. tostring(s * 100) .. \"%, \" .. tostring(l * 100) .. \"%)\"\
154 end\
155 local hsla\
156 hsla = function(h, s, l, a)\
157 if 'table' == type(h) then\
158 h, s, l, a = table.unpack(h)\
159 end\
160 return \"hsla(\" .. tostring(h * 360) .. \", \" .. tostring(s * 100) .. \"%, \" .. tostring(l * 100) .. \"%, \" .. tostring(a or 1) .. \")\"\
161 end\
162 return {\
163 rgb = rgb,\
164 rgba = rgba,\
165 hsl = hsl,\
166 hsla = hsla\
167 }\
168 ", "mmm/color.lua") end
169 if not p["mmm.highlighting"] then p["mmm.highlighting"] = load("local code\
170 code = require('mmm.dom').code\
171 local highlight\
172 local trim\
173 trim = function(str)\
174 return str:match('^ *(..-) *$')\
175 end\
176 if MODE == 'SERVER' then\
177 highlight = function(lang, str)\
178 assert(str, 'no string to highlight')\
179 return code((trim(str)), {\
180 class = \"hljs lang-\" .. tostring(lang)\
181 })\
182 end\
183 else\
184 highlight = function(lang, str)\
185 assert(str, 'no string to highlight')\
186 local result = window.hljs:highlight(lang, (trim(str)), true)\
187 do\
188 local _with_0 = code({\
189 class = \"hljs lang-\" .. tostring(lang)\
190 })\
191 _with_0.innerHTML = result.value\
192 return _with_0\
193 end\
194 end\
195 end\
196 local languages = setmetatable({ }, {\
197 __index = function(self, name)\
198 do\
199 local val\
200 val = function(str)\
201 return highlight(name, str)\
202 end\
203 self[name] = val\
204 return val\
205 end\
206 end\
207 })\
208 return {\
209 highlight = highlight,\
210 languages = languages\
211 }\
212 ", "mmm/highlighting.lua") end
213 if not p["mmm"] then p["mmm"] = load("window = js.global\
214 local console\
215 document, console = window.document, window.console\
216 MODE = 'CLIENT'\
217 local deep_tostring\
218 deep_tostring = function(tbl, space)\
219 if space == nil then\
220 space = ''\
221 end\
222 if 'userdata' == type(tbl) then\
223 return tbl\
224 end\
225 local buf = space .. tostring(tbl)\
226 if not ('table' == type(tbl)) then\
227 return buf\
228 end\
229 buf = buf .. ' {\\n'\
230 for k, v in pairs(tbl) do\
231 buf = buf .. tostring(space) .. \" [\" .. tostring(k) .. \"]: \" .. tostring(deep_tostring(v, space .. ' ')) .. \"\\n\"\
232 end\
233 buf = buf .. tostring(space) .. \"}\"\
234 return buf\
235 end\
236 print = function(...)\
237 local contents\
238 do\
239 local _accum_0 = { }\
240 local _len_0 = 1\
241 local _list_0 = {\
242 ...\
243 }\
244 for _index_0 = 1, #_list_0 do\
245 local v = _list_0[_index_0]\
246 _accum_0[_len_0] = deep_tostring(v)\
247 _len_0 = _len_0 + 1\
248 end\
249 contents = _accum_0\
250 end\
251 return console:log(table.unpack(contents))\
252 end\
253 warn = function(...)\
254 local contents\
255 do\
256 local _accum_0 = { }\
257 local _len_0 = 1\
258 local _list_0 = {\
259 ...\
260 }\
261 for _index_0 = 1, #_list_0 do\
262 local v = _list_0[_index_0]\
263 _accum_0[_len_0] = deep_tostring(v)\
264 _len_0 = _len_0 + 1\
265 end\
266 contents = _accum_0\
267 end\
268 return console:warn(table.unpack(contents))\
269 end\
270 package.path = '/?.lua;/?/init.lua'\
271 do\
272 local _require = require\
273 relative = function(base, sub)\
274 if not ('number' == type(sub)) then\
275 sub = 0\
276 end\
277 for i = 1, sub do\
278 base = base:match('^(.*)%.%w+$')\
279 end\
280 return function(name, x)\
281 if '.' == name:sub(1, 1) then\
282 name = base .. name\
283 end\
284 return _require(name)\
285 end\
286 end\
287 end\
288 if on_load then\
289 for _index_0 = 1, #on_load do\
290 local f = on_load[_index_0]\
291 f()\
292 end\
293 end\
294 on_load = setmetatable({ }, {\
295 __newindex = function(t, k, v)\
296 rawset(t, k, v)\
297 return v()\
298 end\
299 })\
300 ", "mmm/init.client.lua") end
301 if not p["mmm.ordered"] then p["mmm.ordered"] = load("local sort\
302 sort = function(t, order_fn, only_strings)\
303 do\
304 local index\
305 do\
306 local _accum_0 = { }\
307 local _len_0 = 1\
308 for k, v in pairs(t) do\
309 if (not only_strings) or 'string' == type(k) then\
310 _accum_0[_len_0] = k\
311 _len_0 = _len_0 + 1\
312 end\
313 end\
314 index = _accum_0\
315 end\
316 table.sort(index, order_fn)\
317 return index\
318 end\
319 end\
320 local onext\
321 onext = function(state, key)\
322 state.i = state.i + state.step\
323 local t, index, i\
324 t, index, i = state.t, state.index, state.i\
325 do\
326 key = index[i]\
327 if key then\
328 return key, t[key]\
329 end\
330 end\
331 end\
332 local opairs\
333 opairs = function(t, order_fn, only_strings)\
334 if only_strings == nil then\
335 only_strings = false\
336 end\
337 local state = {\
338 t = t,\
339 i = 0,\
340 step = 1,\
341 index = sort(t, order_fn, only_strings)\
342 }\
343 return onext, state, nil\
344 end\
345 local ropairs\
346 ropairs = function(t, order_fn, only_strings)\
347 if only_strings == nil then\
348 only_strings = false\
349 end\
350 local index = sort(t, order_fn, only_strings)\
351 local state = {\
352 t = t,\
353 index = index,\
354 i = #index + 1,\
355 step = -1\
356 }\
357 return onext, state, nil\
358 end\
359 return {\
360 onext = onext,\
361 opairs = opairs,\
362 ropairs = ropairs\
363 }\
364 ", "mmm/ordered.lua") end
365 if not p["mmm.mmmfs.browser"] then p["mmm.mmmfs.browser"] = load("local require = relative(..., 1)\
366 local Key\
367 Key = require('.fileder').Key\
368 local converts, get_conversions, apply_conversions\
369 do\
370 local _obj_0 = require('.conversion')\
371 converts, get_conversions, apply_conversions = _obj_0.converts, _obj_0.get_conversions, _obj_0.apply_conversions\
372 end\
373 local ReactiveVar, get_or_create, text, elements\
374 do\
375 local _obj_0 = require('mmm.component')\
376 ReactiveVar, get_or_create, text, elements = _obj_0.ReactiveVar, _obj_0.get_or_create, _obj_0.text, _obj_0.elements\
377 end\
378 local pre, div, nav, span, button, a, code, select, option\
379 pre, div, nav, span, button, a, code, select, option = elements.pre, elements.div, elements.nav, elements.span, elements.button, elements.a, elements.code, elements.select, elements.option\
380 local languages\
381 languages = require('mmm.highlighting').languages\
382 local keep\
383 keep = function(var)\
384 local last = var:get()\
385 return var:map(function(val)\
386 last = val or last\
387 return last\
388 end)\
389 end\
390 local code_cast\
391 code_cast = function(lang)\
392 return {\
393 inp = \"text/\" .. tostring(lang) .. \".*\",\
394 out = 'mmm/dom',\
395 transform = function(val)\
396 return languages[lang](val)\
397 end\
398 }\
399 end\
400 local casts = {\
401 code_cast('javascript'),\
402 code_cast('moonscript'),\
403 code_cast('lua'),\
404 code_cast('markdown'),\
405 code_cast('html'),\
406 {\
407 inp = 'text/plain',\
408 out = 'mmm/dom',\
409 transform = function(val)\
410 return text(val)\
411 end\
412 },\
413 {\
414 inp = 'URL.*',\
415 out = 'mmm/dom',\
416 transform = function(href)\
417 return span(a((code(href)), {\
418 href = href\
419 }))\
420 end\
421 }\
422 }\
423 for _index_0 = 1, #converts do\
424 local convert = converts[_index_0]\
425 table.insert(casts, convert)\
426 end\
427 local Browser\
428 do\
429 local _class_0\
430 local err_and_trace, default_convert\
431 local _base_0 = {\
432 get_content = function(self, prop, err, convert)\
433 if err == nil then\
434 err = self.error\
435 end\
436 if convert == nil then\
437 convert = default_convert\
438 end\
439 local clear_error\
440 clear_error = function()\
441 if MODE == 'CLIENT' then\
442 return err:set()\
443 end\
444 end\
445 local disp_error\
446 disp_error = function(msg)\
447 if MODE == 'CLIENT' then\
448 err:set(pre(msg))\
449 end\
450 warn(\"ERROR rendering content: \" .. tostring(msg))\
451 return nil\
452 end\
453 local active = self.active:get()\
454 if not (active) then\
455 return disp_error(\"fileder not found!\")\
456 end\
457 if not (prop) then\
458 return disp_error(\"facet not found!\")\
459 end\
460 local ok, res = xpcall(convert, err_and_trace, active, prop)\
461 if MODE == 'CLIENT' then\
462 document.body.classList:remove('loading')\
463 end\
464 if ok and res then\
465 clear_error()\
466 return res\
467 elseif ok then\
468 return div(\"[no conversion path to \" .. tostring(prop.type) .. \"]\")\
469 elseif res and res.msg.match and res.msg:match('%[nossr%]$') then\
470 return div(\"[this page could not be pre-rendered on the server]\")\
471 else\
472 res = tostring(res.msg) .. \"\\n\" .. tostring(res.trace)\
473 return disp_error(res)\
474 end\
475 end,\
476 get_inspector = function(self)\
477 self.inspect_prop = self.facet:map(function(prop)\
478 local active = self.active:get()\
479 local key = active and active:find(prop)\
480 if key and key.original then\
481 key = key.original\
482 end\
483 return key\
484 end)\
485 self.inspect_err = ReactiveVar()\
486 do\
487 local _with_0 = div({\
488 class = 'view inspector'\
489 })\
490 _with_0:append(nav({\
491 span('inspector'),\
492 self.inspect_prop:map(function(current)\
493 current = current and current:tostring()\
494 local fileder = self.active:get()\
495 local onchange\
496 onchange = function(_, e)\
497 if e.target.value == '' then\
498 return \
499 end\
500 local name\
501 name = self.facet:get().name\
502 return self.inspect_prop:set(Key(e.target.value))\
503 end\
504 do\
505 local _with_1 = select({\
506 onchange = onchange\
507 })\
508 _with_1:append(option('(none)', {\
509 value = '',\
510 disabled = true,\
511 selected = not value\
512 }))\
513 if fileder then\
514 for key, _ in pairs(fileder.facets) do\
515 local value = key:tostring()\
516 _with_1:append(option(value, {\
517 value = value,\
518 selected = value == current\
519 }))\
520 end\
521 end\
522 return _with_1\
523 end\
524 end),\
525 self.inspect:map(function(enabled)\
526 if enabled then\
527 return button('close', {\
528 onclick = function(_, e)\
529 return self.inspect:set(false)\
530 end\
531 })\
532 end\
533 end)\
534 }))\
535 _with_0:append((function()\
536 do\
537 local _with_1 = div({\
538 class = self.inspect_err:map(function(e)\
539 if e then\
540 return 'error-wrap'\
541 else\
542 return 'error-wrap empty'\
543 end\
544 end)\
545 })\
546 _with_1:append(span(\"an error occured while rendering this view:\"))\
547 _with_1:append(self.inspect_err)\
548 return _with_1\
549 end\
550 end)())\
551 _with_0:append((function()\
552 do\
553 local _with_1 = pre({\
554 class = 'content'\
555 })\
556 _with_1:append(keep(self.inspect_prop:map(function(prop, old)\
557 return self:get_content(prop, self.inspect_err, function(self, prop)\
558 local value, key = self:get(prop)\
559 assert(key, \"couldn't @get \" .. tostring(prop))\
560 local conversions = get_conversions('mmm/dom', key.type, casts)\
561 assert(conversions, \"cannot cast '\" .. tostring(key.type) .. \"'\")\
562 return apply_conversions(conversions, value, self, prop)\
563 end)\
564 end)))\
565 return _with_1\
566 end\
567 end)())\
568 return _with_0\
569 end\
570 end,\
571 navigate = function(self, new)\
572 return self.path:set(new)\
573 end\
574 }\
575 _base_0.__index = _base_0\
576 _class_0 = setmetatable({\
577 __init = function(self, root, path, rehydrate)\
578 if rehydrate == nil then\
579 rehydrate = false\
580 end\
581 self.root = root\
582 assert(self.root, 'root fileder is nil')\
583 self.path = ReactiveVar(path or '')\
584 if MODE == 'CLIENT' then\
585 local logo = document:querySelector('header h1 > svg')\
586 local spin\
587 spin = function()\
588 logo.classList:add('spin')\
589 local _ = logo.parentElement.offsetWidth\
590 return logo.classList:remove('spin')\
591 end\
592 self.path:subscribe(function(path)\
593 document.body.classList:add('loading')\
594 spin()\
595 if self.skip then\
596 return \
597 end\
598 local vis_path = path .. ((function()\
599 if '/' == path:sub(-1) then\
600 return ''\
601 else\
602 return '/'\
603 end\
604 end)())\
605 return window.history:pushState(path, '', vis_path)\
606 end)\
607 window.onpopstate = function(_, event)\
608 if event.state and not event.state == js.null then\
609 self.skip = true\
610 self.path:set(event.state)\
611 self.skip = nil\
612 end\
613 end\
614 end\
615 self.active = self.path:map((function()\
616 local _base_1 = self.root\
617 local _fn_0 = _base_1.walk\
618 return function(...)\
619 return _fn_0(_base_1, ...)\
620 end\
621 end)())\
622 self.facet = self.active:map(function(fileder)\
623 if not (fileder) then\
624 return \
625 end\
626 local last = self.facet and self.facet:get()\
627 return Key((function()\
628 if last then\
629 return last.type\
630 else\
631 return 'mmm/dom'\
632 end\
633 end)())\
634 end)\
635 self.inspect = ReactiveVar((MODE == 'CLIENT' and window.location.search:match('[?&]inspect')))\
636 local main = get_or_create('div', 'browser-root', {\
637 class = 'main view'\
638 })\
639 if MODE == 'SERVER' then\
640 main:append(nav({\
641 id = 'browser-navbar',\
642 span('please stand by... interactivity loading :)')\
643 }))\
644 else\
645 main:prepend((function()\
646 do\
647 local _with_0 = get_or_create('nav', 'browser-navbar')\
648 _with_0.node.innerHTML = ''\
649 _with_0:append(span('path: ', self.path:map(function(path)\
650 do\
651 local _with_1 = div({\
652 class = 'path',\
653 style = {\
654 display = 'inline-block'\
655 }\
656 })\
657 local path_segment\
658 path_segment = function(name, href)\
659 return a(name, {\
660 href = href,\
661 onclick = function(_, e)\
662 e:preventDefault()\
663 return self:navigate(href)\
664 end\
665 })\
666 end\
667 local href = ''\
668 path = path:match('^/(.*)')\
669 _with_1:append(path_segment('root', ''))\
670 while path do\
671 local name, rest = path:match('^([%w%-_%.]+)/(.*)')\
672 if not name then\
673 name = path\
674 end\
675 path = rest\
676 href = tostring(href) .. \"/\" .. tostring(name)\
677 _with_1:append('/')\
678 _with_1:append(path_segment(name, href))\
679 end\
680 return _with_1\
681 end\
682 end)))\
683 _with_0:append(span('view facet:', {\
684 style = {\
685 ['margin-right'] = '0'\
686 }\
687 }))\
688 _with_0:append(self.active:map(function(fileder)\
689 local onchange\
690 onchange = function(_, e)\
691 local type\
692 type = self.facet:get().type\
693 return self.facet:set(Key({\
694 name = e.target.value,\
695 type = type\
696 }))\
697 end\
698 local current = self.facet:get()\
699 current = current and current.name\
700 do\
701 local _with_1 = select({\
702 onchange = onchange,\
703 disabled = not fileder\
704 })\
705 local has_main = fileder and fileder:find(current.name, '.*')\
706 _with_1:append(option('(main)', {\
707 value = '',\
708 disabled = not has_main,\
709 selected = current == ''\
710 }))\
711 if fileder then\
712 for i, value in ipairs(fileder:get_facets()) do\
713 local _continue_0 = false\
714 repeat\
715 if value == '' then\
716 _continue_0 = true\
717 break\
718 end\
719 _with_1:append(option(value, {\
720 value = value,\
721 selected = value == current\
722 }))\
723 _continue_0 = true\
724 until true\
725 if not _continue_0 then\
726 break\
727 end\
728 end\
729 end\
730 return _with_1\
731 end\
732 end))\
733 _with_0:append(self.inspect:map(function(enabled)\
734 if not enabled then\
735 return button('inspect', {\
736 onclick = function(_, e)\
737 return self.inspect:set(true)\
738 end\
739 })\
740 end\
741 end))\
742 return _with_0\
743 end\
744 end)())\
745 end\
746 self.error = ReactiveVar()\
747 main:append((function()\
748 do\
749 local _with_0 = get_or_create('div', 'browser-error', {\
750 class = self.error:map(function(e)\
751 if e then\
752 return 'error-wrap'\
753 else\
754 return 'error-wrap empty'\
755 end\
756 end)\
757 })\
758 _with_0:append((span(\"an error occured while rendering this view:\")), (rehydrate and _with_0.node.firstChild))\
759 _with_0:append(self.error)\
760 return _with_0\
761 end\
762 end)())\
763 main:append((function()\
764 do\
765 local _with_0 = get_or_create('div', 'browser-content', {\
766 class = 'content'\
767 })\
768 local content = ReactiveVar((function()\
769 if rehydrate then\
770 return _with_0.node.lastChild\
771 else\
772 return self:get_content(self.facet:get())\
773 end\
774 end)())\
775 _with_0:append(keep(content))\
776 if MODE == 'CLIENT' then\
777 self.facet:subscribe(function(p)\
778 return window:setTimeout((function()\
779 return content:set(self:get_content(p))\
780 end), 150)\
781 end)\
782 end\
783 return _with_0\
784 end\
785 end)())\
786 if rehydrate then\
787 self.facet:set(self.facet:get())\
788 end\
789 local inspector = self.inspect:map(function(enabled)\
790 if enabled then\
791 return self:get_inspector()\
792 end\
793 end)\
794 local wrapper = get_or_create('div', 'browser-wrapper', main, inspector, {\
795 class = 'browser'\
796 })\
797 self.node = wrapper.node\
798 do\
799 local _base_1 = wrapper\
800 local _fn_0 = _base_1.render\
801 self.render = function(...)\
802 return _fn_0(_base_1, ...)\
803 end\
804 end\
805 end,\
806 __base = _base_0,\
807 __name = \"Browser\"\
808 }, {\
809 __index = _base_0,\
810 __call = function(cls, ...)\
811 local _self_0 = setmetatable({}, _base_0)\
812 cls.__init(_self_0, ...)\
813 return _self_0\
814 end\
815 })\
816 _base_0.__class = _class_0\
817 local self = _class_0\
818 err_and_trace = function(msg)\
819 return {\
820 msg = msg,\
821 trace = debug.traceback()\
822 }\
823 end\
824 default_convert = function(self, key)\
825 return self:get(key.name, 'mmm/dom')\
826 end\
827 default_convert = function(self, key)\
828 return self:get(key.name, 'mmm/dom')\
829 end\
830 Browser = _class_0\
831 end\
832 return {\
833 Browser = Browser\
834 }\
835 ", "mmm/mmmfs/browser.lua") end
836 if not p["mmm.mmmfs.conversion"] then p["mmm.mmmfs.conversion"] = load("local require = relative(..., 1)\
837 local converts = require('.converts')\
838 local count\
839 count = function(base, pattern)\
840 if pattern == nil then\
841 pattern = '->'\
842 end\
843 return select(2, base:gsub(pattern, ''))\
844 end\
845 local escape_pattern\
846 escape_pattern = function(inp)\
847 return \"^\" .. tostring(inp:gsub('([^%w])', '%%%1')) .. \"$\"\
848 end\
849 local escape_inp\
850 escape_inp = function(inp)\
851 return \"^\" .. tostring(inp:gsub('([-/])', '%%%1')) .. \"$\"\
852 end\
853 local get_conversions\
854 get_conversions = function(want, have, _converts, limit)\
855 if _converts == nil then\
856 _converts = converts\
857 end\
858 if limit == nil then\
859 limit = 5\
860 end\
861 assert(have, 'need starting type(s)')\
862 if 'string' == type(have) then\
863 have = {\
864 have\
865 }\
866 end\
867 assert(#have > 0, 'need starting type(s) (list was empty)')\
868 want = escape_pattern(want)\
869 local iterations = limit + math.max(table.unpack((function()\
870 local _accum_0 = { }\
871 local _len_0 = 1\
872 for _index_0 = 1, #have do\
873 local type = have[_index_0]\
874 _accum_0[_len_0] = count(type)\
875 _len_0 = _len_0 + 1\
876 end\
877 return _accum_0\
878 end)()))\
879 do\
880 local _accum_0 = { }\
881 local _len_0 = 1\
882 for _index_0 = 1, #have do\
883 local start = have[_index_0]\
884 _accum_0[_len_0] = {\
885 start = start,\
886 rest = start,\
887 conversions = { }\
888 }\
889 _len_0 = _len_0 + 1\
890 end\
891 have = _accum_0\
892 end\
893 for i = 1, iterations do\
894 local next_have, c = { }, 1\
895 for _index_0 = 1, #have do\
896 local _des_0 = have[_index_0]\
897 local start, rest, conversions\
898 start, rest, conversions = _des_0.start, _des_0.rest, _des_0.conversions\
899 if rest:match(want) then\
900 return conversions, start\
901 else\
902 for _index_1 = 1, #_converts do\
903 local _continue_0 = false\
904 repeat\
905 local convert = _converts[_index_1]\
906 local inp = escape_inp(convert.inp)\
907 if not (rest:match(inp)) then\
908 _continue_0 = true\
909 break\
910 end\
911 local result = rest:gsub(inp, convert.out)\
912 if result then\
913 next_have[c] = {\
914 start = start,\
915 rest = result,\
916 conversions = {\
917 {\
918 convert = convert,\
919 from = rest,\
920 to = result\
921 },\
922 table.unpack(conversions)\
923 }\
924 }\
925 c = c + 1\
926 end\
927 _continue_0 = true\
928 until true\
929 if not _continue_0 then\
930 break\
931 end\
932 end\
933 end\
934 end\
935 have = next_have\
936 if not (#have > 0) then\
937 return \
938 end\
939 end\
940 end\
941 local apply_conversions\
942 apply_conversions = function(conversions, value, ...)\
943 for i = #conversions, 1, -1 do\
944 local step = conversions[i]\
945 value = step.convert.transform(step, value, ...)\
946 end\
947 return value\
948 end\
949 return {\
950 converts = converts,\
951 get_conversions = get_conversions,\
952 apply_conversions = apply_conversions\
953 }\
954 ", "mmm/mmmfs/conversion.lua") end
955 if not p["mmm.mmmfs.fileder"] then p["mmm.mmmfs.fileder"] = load("local require = relative(..., 1)\
956 local get_conversions, apply_conversions\
957 do\
958 local _obj_0 = require('.conversion')\
959 get_conversions, apply_conversions = _obj_0.get_conversions, _obj_0.apply_conversions\
960 end\
961 local Key\
962 do\
963 local _class_0\
964 local _base_0 = {\
965 tostring = function(self)\
966 if self.name == '' then\
967 return self.type\
968 else\
969 return tostring(self.name) .. \": \" .. tostring(self.type)\
970 end\
971 end,\
972 __tostring = function(self)\
973 return self:tostring()\
974 end\
975 }\
976 _base_0.__index = _base_0\
977 _class_0 = setmetatable({\
978 __init = function(self, opts, second)\
979 if 'string' == type(second) then\
980 self.name, self.type = (opts or ''), second\
981 elseif 'string' == type(opts) then\
982 self.name, self.type = opts:match('^([%w-_]+): *(.+)$')\
983 if not self.name then\
984 self.name = ''\
985 self.type = opts\
986 end\
987 elseif 'table' == type(opts) then\
988 self.name = opts.name\
989 self.type = opts.type\
990 self.original = opts.original\
991 self.filename = opts.filename\
992 else\
993 return error(\"wrong argument type: \" .. tostring(type(opts)) .. \", \" .. tostring(type(second)))\
994 end\
995 end,\
996 __base = _base_0,\
997 __name = \"Key\"\
998 }, {\
999 __index = _base_0,\
1000 __call = function(cls, ...)\
1001 local _self_0 = setmetatable({}, _base_0)\
1002 cls.__init(_self_0, ...)\
1003 return _self_0\
1004 end\
1005 })\
1006 _base_0.__class = _class_0\
1007 Key = _class_0\
1008 end\
1009 local Fileder\
1010 do\
1011 local _class_0\
1012 local _base_0 = {\
1013 walk = function(self, path)\
1014 if path == '' then\
1015 return self\
1016 end\
1017 if '/' ~= path:sub(1, 1) then\
1018 path = tostring(self.path) .. \"/\" .. tostring(path)\
1019 end\
1020 if not (self.path == path:sub(1, #self.path)) then\
1021 return \
1022 end\
1023 if #path == #self.path then\
1024 return self\
1025 end\
1026 local _list_0 = self.children\
1027 for _index_0 = 1, #_list_0 do\
1028 local child = _list_0[_index_0]\
1029 do\
1030 local match = child:walk(path)\
1031 if match then\
1032 return match\
1033 end\
1034 end\
1035 end\
1036 end,\
1037 mount = function(self, path, mount_as)\
1038 if not mount_as then\
1039 path = path .. self:gett('name: alpha')\
1040 end\
1041 assert(not self.path or self.path == path, \"mounted twice: \" .. tostring(self.path) .. \" and now \" .. tostring(path))\
1042 self.path = path\
1043 local _list_0 = self.children\
1044 for _index_0 = 1, #_list_0 do\
1045 local child = _list_0[_index_0]\
1046 child:mount(self.path .. '/')\
1047 end\
1048 end,\
1049 iterate = function(self, depth)\
1050 if depth == nil then\
1051 depth = 0\
1052 end\
1053 coroutine.yield(self)\
1054 if depth == 1 then\
1055 return \
1056 end\
1057 local _list_0 = self.children\
1058 for _index_0 = 1, #_list_0 do\
1059 local child = _list_0[_index_0]\
1060 child:iterate(depth - 1)\
1061 end\
1062 end,\
1063 get_facets = function(self)\
1064 local names = { }\
1065 for key in pairs(self.facets) do\
1066 names[key.name] = true\
1067 end\
1068 local _accum_0 = { }\
1069 local _len_0 = 1\
1070 for name in pairs(names) do\
1071 _accum_0[_len_0] = name\
1072 _len_0 = _len_0 + 1\
1073 end\
1074 return _accum_0\
1075 end,\
1076 has = function(self, ...)\
1077 local want = Key(...)\
1078 for key in pairs(self.facets) do\
1079 local _continue_0 = false\
1080 repeat\
1081 if key.original then\
1082 _continue_0 = true\
1083 break\
1084 end\
1085 if key.name == want.name and key.type == want.type then\
1086 return key\
1087 end\
1088 _continue_0 = true\
1089 until true\
1090 if not _continue_0 then\
1091 break\
1092 end\
1093 end\
1094 end,\
1095 has_facet = function(self, want)\
1096 for key in pairs(self.facets) do\
1097 local _continue_0 = false\
1098 repeat\
1099 if key.original then\
1100 _continue_0 = true\
1101 break\
1102 end\
1103 if key.name == want then\
1104 return key\
1105 end\
1106 _continue_0 = true\
1107 until true\
1108 if not _continue_0 then\
1109 break\
1110 end\
1111 end\
1112 end,\
1113 find = function(self, ...)\
1114 local want = Key(...)\
1115 local matching\
1116 do\
1117 local _accum_0 = { }\
1118 local _len_0 = 1\
1119 for key in pairs(self.facets) do\
1120 if key.name == want.name then\
1121 _accum_0[_len_0] = key\
1122 _len_0 = _len_0 + 1\
1123 end\
1124 end\
1125 matching = _accum_0\
1126 end\
1127 if not (#matching > 0) then\
1128 return \
1129 end\
1130 local shortest_path, start = get_conversions(want.type, (function()\
1131 local _accum_0 = { }\
1132 local _len_0 = 1\
1133 for _index_0 = 1, #matching do\
1134 local key = matching[_index_0]\
1135 _accum_0[_len_0] = key.type\
1136 _len_0 = _len_0 + 1\
1137 end\
1138 return _accum_0\
1139 end)())\
1140 if start then\
1141 for _index_0 = 1, #matching do\
1142 local key = matching[_index_0]\
1143 if key.type == start then\
1144 return key, shortest_path\
1145 end\
1146 end\
1147 return error(\"couldn't find key after resolution?\")\
1148 end\
1149 end,\
1150 get = function(self, ...)\
1151 local want = Key(...)\
1152 local key, conversions = self:find(want)\
1153 if key then\
1154 local value = apply_conversions(conversions, self.facets[key], self, key)\
1155 return value, key\
1156 end\
1157 end,\
1158 gett = function(self, ...)\
1159 local want = Key(...)\
1160 local value, key = self:get(want)\
1161 assert(value, tostring(self) .. \" doesn't have value for '\" .. tostring(want) .. \"'\")\
1162 return value, key\
1163 end,\
1164 __tostring = function(self)\
1165 return \"Fileder:\" .. tostring(self.path)\
1166 end\
1167 }\
1168 _base_0.__index = _base_0\
1169 _class_0 = setmetatable({\
1170 __init = function(self, facets, children)\
1171 if not children then\
1172 do\
1173 local _accum_0 = { }\
1174 local _len_0 = 1\
1175 for i, child in ipairs(facets) do\
1176 facets[i] = nil\
1177 local _value_0 = child\
1178 _accum_0[_len_0] = _value_0\
1179 _len_0 = _len_0 + 1\
1180 end\
1181 children = _accum_0\
1182 end\
1183 end\
1184 self.children = setmetatable({ }, {\
1185 __index = function(t, k)\
1186 if not ('string' == type(k)) then\
1187 return rawget(t, k)\
1188 end\
1189 return self:walk(tostring(self.path) .. \"/\" .. tostring(k))\
1190 end,\
1191 __newindex = function(t, k, child)\
1192 rawset(t, k, child)\
1193 if self.path == '/' then\
1194 return child:mount('/')\
1195 elseif self.path then\
1196 return child:mount(self.path .. '/')\
1197 end\
1198 end\
1199 })\
1200 for i, child in ipairs(children) do\
1201 self.children[i] = child\
1202 end\
1203 self.facets = setmetatable({ }, {\
1204 __newindex = function(t, key, v)\
1205 return rawset(t, (Key(key)), v)\
1206 end\
1207 })\
1208 for k, v in pairs(facets) do\
1209 self.facets[k] = v\
1210 end\
1211 end,\
1212 __base = _base_0,\
1213 __name = \"Fileder\"\
1214 }, {\
1215 __index = _base_0,\
1216 __call = function(cls, ...)\
1217 local _self_0 = setmetatable({}, _base_0)\
1218 cls.__init(_self_0, ...)\
1219 return _self_0\
1220 end\
1221 })\
1222 _base_0.__class = _class_0\
1223 Fileder = _class_0\
1224 end\
1225 local dir_base\
1226 dir_base = function(path)\
1227 local dir, base = path:match('(.-)([^/]-)$')\
1228 if dir and #dir > 0 then\
1229 dir = dir:sub(1, #dir - 1)\
1230 end\
1231 return dir, base\
1232 end\
1233 local load_tree\
1234 load_tree = function(store, root)\
1235 if root == nil then\
1236 root = ''\
1237 end\
1238 local fileders = setmetatable({ }, {\
1239 __index = function(self, path)\
1240 do\
1241 local val = Fileder({ })\
1242 val.path = path\
1243 rawset(self, path, val)\
1244 return val\
1245 end\
1246 end\
1247 })\
1248 root = fileders[root]\
1249 root.facets['name: alpha'] = ''\
1250 for fn, ft in store:list_facets(root.path) do\
1251 local val = store:load_facet(root.path, fn, ft)\
1252 root.facets[Key(fn, ft)] = val\
1253 end\
1254 for path in store:list_all_fileders(root.path) do\
1255 local fileder = fileders[path]\
1256 local parent, name = dir_base(path)\
1257 fileder.facets['name: alpha'] = name\
1258 table.insert(fileders[parent].children, fileder)\
1259 for fn, ft in store:list_facets(path) do\
1260 local val = store:load_facet(path, fn, ft)\
1261 fileder.facets[Key(fn, ft)] = val\
1262 end\
1263 end\
1264 return root\
1265 end\
1266 return {\
1267 Key = Key,\
1268 Fileder = Fileder,\
1269 dir_base = dir_base,\
1270 load_tree = load_tree\
1271 }\
1272 ", "mmm/mmmfs/fileder.lua") end
1273 if not p["mmm.mmmfs"] then p["mmm.mmmfs"] = load("local require = relative(...)\
1274 local Key, Fileder\
1275 do\
1276 local _obj_0 = require('.fileder')\
1277 Key, Fileder = _obj_0.Key, _obj_0.Fileder\
1278 end\
1279 local Browser\
1280 Browser = require('.browser').Browser\
1281 return {\
1282 Key = Key,\
1283 Fileder = Fileder,\
1284 Browser = Browser\
1285 }\
1286 ", "mmm/mmmfs/init.lua") end
1287 if not p["mmm.mmmfs.util"] then p["mmm.mmmfs.util"] = load("local merge\
1288 merge = function(orig, extra)\
1289 if orig == nil then\
1290 orig = { }\
1291 end\
1292 do\
1293 local attr\
1294 do\
1295 local _tbl_0 = { }\
1296 for k, v in pairs(orig) do\
1297 _tbl_0[k] = v\
1298 end\
1299 attr = _tbl_0\
1300 end\
1301 for k, v in pairs(extra) do\
1302 attr[k] = v\
1303 end\
1304 return attr\
1305 end\
1306 end\
1307 local tourl\
1308 tourl = function(path)\
1309 if STATIC then\
1310 return path .. '/'\
1311 else\
1312 return path .. '/'\
1313 end\
1314 end\
1315 return function(elements)\
1316 local a, div, pre\
1317 a, div, pre = elements.a, elements.div, elements.pre\
1318 local find_fileder\
1319 find_fileder = function(fileder, origin)\
1320 if 'string' == type(fileder) then\
1321 assert(origin, \"cannot resolve path '\" .. tostring(fileder) .. \"' without origin!\")\
1322 return assert((origin:walk(fileder)), \"couldn't resolve path '\" .. tostring(fileder) .. \"' from \" .. tostring(origin))\
1323 else\
1324 return assert(fileder, \"no fileder passed.\")\
1325 end\
1326 end\
1327 local navigate_to\
1328 navigate_to = function(path, name, opts)\
1329 if opts == nil then\
1330 opts = { }\
1331 end\
1332 opts.href = tourl(path)\
1333 if MODE == 'CLIENT' then\
1334 opts.onclick = function(self, e)\
1335 e:preventDefault()\
1336 return BROWSER:navigate(path)\
1337 end\
1338 end\
1339 return a(name, opts)\
1340 end\
1341 local link_to\
1342 link_to = function(fileder, name, origin, attr)\
1343 fileder = find_fileder(fileder, origin)\
1344 name = name or fileder:get('title: mmm/dom')\
1345 name = name or fileder:gett('name: alpha')\
1346 do\
1347 local href = fileder:get('link: URL.*')\
1348 if href then\
1349 return a(name, merge(attr, {\
1350 href = href,\
1351 target = '_blank'\
1352 }))\
1353 else\
1354 return a(name, merge(attr, {\
1355 href = tourl(fileder.path),\
1356 onclick = (function()\
1357 if MODE == 'CLIENT' then\
1358 return function(self, e)\
1359 e:preventDefault()\
1360 return BROWSER:navigate(fileder.path)\
1361 end\
1362 end\
1363 end)()\
1364 }))\
1365 end\
1366 end\
1367 end\
1368 local embed\
1369 embed = function(fileder, name, origin, opts)\
1370 if name == nil then\
1371 name = ''\
1372 end\
1373 if opts == nil then\
1374 opts = { }\
1375 end\
1376 fileder = find_fileder(fileder, origin)\
1377 local ok, node = pcall(fileder.gett, fileder, name, 'mmm/dom')\
1378 if not ok then\
1379 return div(\"couldn't embed \" .. tostring(fileder) .. \" \" .. tostring(name), (pre(node)), {\
1380 style = {\
1381 background = 'var(--gray-fail)',\
1382 padding = '1em'\
1383 }\
1384 })\
1385 end\
1386 local klass = 'embed'\
1387 if opts.desc then\
1388 klass = klass .. ' desc'\
1389 end\
1390 if opts.inline then\
1391 klass = klass .. ' inline'\
1392 end\
1393 node = div({\
1394 class = klass,\
1395 node,\
1396 (function()\
1397 if opts.desc then\
1398 return div(opts.desc, {\
1399 class = 'description'\
1400 })\
1401 end\
1402 end)()\
1403 })\
1404 if opts.nolink then\
1405 return node\
1406 end\
1407 return link_to(fileder, node, nil, opts.attr)\
1408 end\
1409 return {\
1410 find_fileder = find_fileder,\
1411 link_to = link_to,\
1412 navigate_to = navigate_to,\
1413 embed = embed\
1414 }\
1415 end\
1416 ", "mmm/mmmfs/util.lua") end
1417 if not p["mmm.mmmfs.converts"] then p["mmm.mmmfs.converts"] = load("local require = relative(..., 1)\
1418 local div, code, img, video, blockquote, a, span, source, iframe\
1419 do\
1420 local _obj_0 = require('mmm.dom')\
1421 div, code, img, video, blockquote, a, span, source, iframe = _obj_0.div, _obj_0.code, _obj_0.img, _obj_0.video, _obj_0.blockquote, _obj_0.a, _obj_0.span, _obj_0.source, _obj_0.iframe\
1422 end\
1423 local find_fileder, link_to, embed\
1424 do\
1425 local _obj_0 = (require('mmm.mmmfs.util'))(require('mmm.dom'))\
1426 find_fileder, link_to, embed = _obj_0.find_fileder, _obj_0.link_to, _obj_0.embed\
1427 end\
1428 local render\
1429 render = require('.layout').render\
1430 local tohtml\
1431 tohtml = require('mmm.component').tohtml\
1432 local js_fix\
1433 if MODE == 'CLIENT' then\
1434 js_fix = function(arg)\
1435 if arg == js.null then\
1436 return \
1437 end\
1438 return arg\
1439 end\
1440 end\
1441 local single\
1442 single = function(func)\
1443 return function(self, val)\
1444 return func(val)\
1445 end\
1446 end\
1447 local loadwith\
1448 loadwith = function(_load)\
1449 return function(self, val, fileder, key)\
1450 local func = assert(_load(val, tostring(fileder) .. \"#\" .. tostring(key)))\
1451 return func()\
1452 end\
1453 end\
1454 local converts = {\
1455 {\
1456 inp = 'fn -> (.+)',\
1457 out = '%1',\
1458 transform = function(self, val, fileder)\
1459 return val(fileder)\
1460 end\
1461 },\
1462 {\
1463 inp = 'mmm/component',\
1464 out = 'mmm/dom',\
1465 transform = single(tohtml)\
1466 },\
1467 {\
1468 inp = 'mmm/dom',\
1469 out = 'text/html+frag',\
1470 transform = function(self, node)\
1471 if MODE == 'SERVER' then\
1472 return node\
1473 else\
1474 return node.outerHTML\
1475 end\
1476 end\
1477 },\
1478 {\
1479 inp = 'mmm/dom',\
1480 out = 'text/html',\
1481 transform = function(self, html, fileder)\
1482 return render(html, fileder)\
1483 end\
1484 },\
1485 {\
1486 inp = 'text/html%+frag',\
1487 out = 'mmm/dom',\
1488 transform = (function()\
1489 if MODE == 'SERVER' then\
1490 return function(self, html, fileder)\
1491 html = html:gsub('<mmm%-link%s+(.-)>(.-)</mmm%-link>', function(attrs, text)\
1492 if #text == 0 then\
1493 text = nil\
1494 end\
1495 local path = ''\
1496 while attrs and attrs ~= '' do\
1497 local key, val, _attrs = attrs:match('^(%w+)=\"([^\"]-)\"%s*(.*)')\
1498 if not key then\
1499 key, _attrs = attrs:match('^(%w+)%s*(.*)$')\
1500 val = true\
1501 end\
1502 attrs = _attrs\
1503 local _exp_0 = key\
1504 if 'path' == _exp_0 then\
1505 path = val\
1506 else\
1507 warn(\"unkown attribute '\" .. tostring(key) .. \"=\\\"\" .. tostring(val) .. \"\\\"' in <mmm-link>\")\
1508 end\
1509 end\
1510 return link_to(path, text, fileder)\
1511 end)\
1512 html = html:gsub('<mmm%-embed%s+(.-)>(.-)</mmm%-embed>', function(attrs, desc)\
1513 local path, facet = '', ''\
1514 local opts = { }\
1515 if #desc ~= 0 then\
1516 opts.desc = desc\
1517 end\
1518 while attrs and attrs ~= '' do\
1519 local key, val, _attrs = attrs:match('^(%w+)=\"([^\"]-)\"%s*(.*)')\
1520 if not key then\
1521 key, _attrs = attrs:match('^(%w+)%s*(.*)$')\
1522 val = true\
1523 end\
1524 attrs = _attrs\
1525 local _exp_0 = key\
1526 if 'path' == _exp_0 then\
1527 path = val\
1528 elseif 'facet' == _exp_0 then\
1529 facet = val\
1530 elseif 'nolink' == _exp_0 then\
1531 opts.nolink = true\
1532 elseif 'inline' == _exp_0 then\
1533 opts.inline = true\
1534 else\
1535 warn(\"unkown attribute '\" .. tostring(key) .. \"=\\\"\" .. tostring(val) .. \"\\\"' in <mmm-embed>\")\
1536 end\
1537 end\
1538 return embed(path, facet, fileder, opts)\
1539 end)\
1540 return html\
1541 end\
1542 else\
1543 return function(self, html, fileder)\
1544 local parent\
1545 do\
1546 local _with_0 = document:createElement('div')\
1547 _with_0.innerHTML = html\
1548 local embeds = _with_0:getElementsByTagName('mmm-embed')\
1549 do\
1550 local _accum_0 = { }\
1551 local _len_0 = 1\
1552 for i = 0, embeds.length - 1 do\
1553 _accum_0[_len_0] = embeds[i]\
1554 _len_0 = _len_0 + 1\
1555 end\
1556 embeds = _accum_0\
1557 end\
1558 for _index_0 = 1, #embeds do\
1559 local element = embeds[_index_0]\
1560 local path = js_fix(element:getAttribute('path'))\
1561 local facet = js_fix(element:getAttribute('facet'))\
1562 local nolink = js_fix(element:getAttribute('nolink'))\
1563 local inline = js_fix(element:getAttribute('inline'))\
1564 local desc = js_fix(element.innerText)\
1565 element:replaceWith(embed(path or '', facet or '', fileder, {\
1566 nolink = nolink,\
1567 inline = inline,\
1568 desc = desc\
1569 }))\
1570 end\
1571 embeds = _with_0:getElementsByTagName('mmm-link')\
1572 do\
1573 local _accum_0 = { }\
1574 local _len_0 = 1\
1575 for i = 0, embeds.length - 1 do\
1576 _accum_0[_len_0] = embeds[i]\
1577 _len_0 = _len_0 + 1\
1578 end\
1579 embeds = _accum_0\
1580 end\
1581 for _index_0 = 1, #embeds do\
1582 local element = embeds[_index_0]\
1583 local text = js_fix(element.innerText)\
1584 local path = js_fix(element:getAttribute('path'))\
1585 element:replaceWith(link_to(path or '', text, fileder))\
1586 end\
1587 parent = _with_0\
1588 end\
1589 assert(1 == parent.childElementCount, \"text/html with more than one child!\")\
1590 return parent.firstElementChild\
1591 end\
1592 end\
1593 end)()\
1594 },\
1595 {\
1596 inp = 'text/lua -> (.+)',\
1597 out = '%1',\
1598 transform = loadwith(load or loadstring)\
1599 },\
1600 {\
1601 inp = 'mmm/tpl -> (.+)',\
1602 out = '%1',\
1603 transform = function(self, source, fileder)\
1604 return source:gsub('{{(.-)}}', function(expr)\
1605 local path, facet = expr:match('^([%w%-_%./]*)%+(.*)')\
1606 assert(path, \"couldn't match TPL expression '\" .. tostring(expr) .. \"'\")\
1607 return (find_fileder(path, fileder)):gett(facet)\
1608 end)\
1609 end\
1610 },\
1611 {\
1612 inp = 'time/iso8601-date',\
1613 out = 'time/unix',\
1614 transform = function(self, val)\
1615 local year, _, month, day = val:match('^%s*(%d%d%d%d)(%-?)([01]%d)%2([0-3]%d)%s*$')\
1616 assert(year, \"failed to parse ISO 8601 date: '\" .. tostring(val) .. \"'\")\
1617 return os.time({\
1618 year = year,\
1619 month = month,\
1620 day = day\
1621 })\
1622 end\
1623 },\
1624 {\
1625 inp = 'URL -> twitter/tweet',\
1626 out = 'mmm/dom',\
1627 transform = function(self, href)\
1628 local id = assert((href:match('twitter.com/[^/]-/status/(%d*)')), \"couldn't parse twitter/tweet URL: '\" .. tostring(href) .. \"'\")\
1629 if MODE == 'CLIENT' then\
1630 do\
1631 local parent = div()\
1632 window.twttr.widgets:createTweet(id, parent)\
1633 return parent\
1634 end\
1635 else\
1636 return div(blockquote({\
1637 class = 'twitter-tweet',\
1638 ['data-lang'] = 'en',\
1639 a('(linked tweet)', {\
1640 href = href\
1641 })\
1642 }))\
1643 end\
1644 end\
1645 },\
1646 {\
1647 inp = 'URL -> youtube/video',\
1648 out = 'mmm/dom',\
1649 transform = function(self, link)\
1650 local id = link:match('youtu%.be/([^/]+)')\
1651 id = id or link:match('youtube.com/watch.*[?&]v=([^&]+)')\
1652 id = id or link:match('youtube.com/[ev]/([^/]+)')\
1653 id = id or link:match('youtube.com/embed/([^/]+)')\
1654 assert(id, \"couldn't parse youtube URL: '\" .. tostring(link) .. \"'\")\
1655 return iframe({\
1656 width = 560,\
1657 height = 315,\
1658 frameborder = 0,\
1659 allowfullscreen = true,\
1660 frameBorder = 0,\
1661 src = \"//www.youtube.com/embed/\" .. tostring(id)\
1662 })\
1663 end\
1664 },\
1665 {\
1666 inp = 'URL -> image/.+',\
1667 out = 'mmm/dom',\
1668 transform = function(self, src, fileder)\
1669 return img({\
1670 src = src\
1671 })\
1672 end\
1673 },\
1674 {\
1675 inp = 'URL -> video/.+',\
1676 out = 'mmm/dom',\
1677 transform = function(self, src)\
1678 return video((source({\
1679 src = src\
1680 })), {\
1681 controls = true,\
1682 loop = true\
1683 })\
1684 end\
1685 },\
1686 {\
1687 inp = 'text/plain',\
1688 out = 'mmm/dom',\
1689 transform = function(self, val)\
1690 return span(val)\
1691 end\
1692 },\
1693 {\
1694 inp = 'alpha',\
1695 out = 'mmm/dom',\
1696 transform = single(code)\
1697 },\
1698 {\
1699 inp = '(.+)',\
1700 out = 'URL -> %1',\
1701 transform = function(self, _, fileder, key)\
1702 return tostring(fileder.path) .. \"/\" .. tostring(key.name) .. \":\" .. tostring(self.from)\
1703 end\
1704 }\
1705 }\
1706 if MODE == 'SERVER' then\
1707 local ok, moon = pcall(require, 'moonscript.base')\
1708 if ok then\
1709 local _load = moon.load or moon.loadstring\
1710 table.insert(converts, {\
1711 inp = 'text/moonscript -> (.+)',\
1712 out = '%1',\
1713 transform = loadwith(moon.load or moon.loadstring)\
1714 })\
1715 table.insert(converts, {\
1716 inp = 'text/moonscript -> (.+)',\
1717 out = 'text/lua -> %1',\
1718 transform = single(moon.to_lua)\
1719 })\
1720 end\
1721 else\
1722 table.insert(converts, {\
1723 inp = 'text/javascript -> (.+)',\
1724 out = '%1',\
1725 transform = function(self, source)\
1726 local f = js.new(window.Function, source)\
1727 return f()\
1728 end\
1729 })\
1730 end\
1731 do\
1732 local markdown\
1733 if MODE == 'SERVER' then\
1734 local success, discount = pcall(require, 'discount')\
1735 if not success then\
1736 warn(\"NO MARKDOWN SUPPORT!\", discount)\
1737 end\
1738 markdown = success and function(md)\
1739 local res = assert(discount.compile(md, 'githubtags'))\
1740 return res.body\
1741 end\
1742 else\
1743 markdown = window and window.marked and (function()\
1744 local _base_0 = window\
1745 local _fn_0 = _base_0.marked\
1746 return function(...)\
1747 return _fn_0(_base_0, ...)\
1748 end\
1749 end)()\
1750 end\
1751 if markdown then\
1752 table.insert(converts, {\
1753 inp = 'text/markdown',\
1754 out = 'text/html+frag',\
1755 transform = function(self, md)\
1756 return \"<div class=\\\"markdown\\\">\" .. tostring(markdown(md)) .. \"</div>\"\
1757 end\
1758 })\
1759 table.insert(converts, {\
1760 inp = 'text/markdown%+span',\
1761 out = 'mmm/dom',\
1762 transform = (function()\
1763 if MODE == 'SERVER' then\
1764 return function(self, source)\
1765 local html = markdown(source)\
1766 html = html:gsub('^<p', '<span')\
1767 return html:gsub('/p>$', '/span>')\
1768 end\
1769 else\
1770 return function(self, source)\
1771 local html = markdown(source)\
1772 html = html:gsub('^%s*<p>%s*', '')\
1773 html = html:gsub('%s*</p>%s*$', '')\
1774 do\
1775 local _with_0 = document:createElement('span')\
1776 _with_0.innerHTML = html\
1777 return _with_0\
1778 end\
1779 end\
1780 end\
1781 end)()\
1782 })\
1783 end\
1784 end\
1785 return converts\
1786 ", "mmm/mmmfs/converts.lua") end
1787 if not p["mmm.mmmfs.layout"] then p["mmm.mmmfs.layout"] = load("local require = relative(..., 1)\
1788 local header, aside, footer, div, svg, script, g, circle, h1, span, b, a, img\
1789 do\
1790 local _obj_0 = require('mmm.dom')\
1791 header, aside, footer, div, svg, script, g, circle, h1, span, b, a, img = _obj_0.header, _obj_0.aside, _obj_0.footer, _obj_0.div, _obj_0.svg, _obj_0.script, _obj_0.g, _obj_0.circle, _obj_0.h1, _obj_0.span, _obj_0.b, _obj_0.a, _obj_0.img\
1792 end\
1793 local navigate_to\
1794 navigate_to = (require('mmm.mmmfs.util'))(require('mmm.dom')).navigate_to\
1795 local pick\
1796 pick = function(...)\
1797 local num = select('#', ...)\
1798 local i = math.ceil(math.random() * num)\
1799 return (select(i, ...))\
1800 end\
1801 local iconlink\
1802 iconlink = function(href, src, alt, style)\
1803 return a({\
1804 class = 'iconlink',\
1805 target = '_blank',\
1806 rel = 'me',\
1807 href = href,\
1808 img({\
1809 src = src,\
1810 alt = alt,\
1811 style = style\
1812 })\
1813 })\
1814 end\
1815 local logo = svg({\
1816 class = 'sun',\
1817 viewBox = '-0.75 -1 1.5 2',\
1818 xmlns = 'http://www.w3.org/2000/svg',\
1819 baseProfile = 'full',\
1820 version = '1.1',\
1821 g({\
1822 transform = 'translate(0 .18)',\
1823 g({\
1824 class = 'circle out',\
1825 circle({\
1826 r = '.6',\
1827 fill = 'none',\
1828 ['stroke-width'] = '.12'\
1829 })\
1830 }),\
1831 g({\
1832 class = 'circle in',\
1833 circle({\
1834 r = '.2',\
1835 stroke = 'none'\
1836 })\
1837 })\
1838 })\
1839 })\
1840 local gen_header\
1841 gen_header = function()\
1842 return header({\
1843 div({\
1844 h1({\
1845 navigate_to('', logo),\
1846 span({\
1847 span('mmm', {\
1848 class = 'bold'\
1849 }),\
1850 '&#8203;',\
1851 '.s&#8209;ol.nu'\
1852 })\
1853 }),\
1854 table.concat({\
1855 pick('fun', 'cool', 'weird', 'interesting', 'new', 'pleasant'),\
1856 pick('stuff', 'things', 'projects', 'experiments', 'visuals', 'ideas'),\
1857 pick(\"with\", 'and'),\
1858 pick('mostly code', 'code and wires', 'silicon', 'electronics', 'shaders', 'oscilloscopes', 'interfaces', 'hardware', 'FPGAs')\
1859 }, ' ')\
1860 }),\
1861 aside({\
1862 navigate_to('/about', 'about me'),\
1863 navigate_to('/games', 'games'),\
1864 navigate_to('/projects', 'other'),\
1865 a({\
1866 href = 'mailto:s%20[removethis]%20[at]%20s-ol.nu',\
1867 'contact',\
1868 script(\"\\n var l = document.currentScript.parentElement;\\n l.href = l.href.replace('%20[at]%20', '@');\\n l.href = l.href.replace('%20[removethis]', '') + '?subject=Hey there :)';\\n \")\
1869 })\
1870 })\
1871 })\
1872 end\
1873 footer = footer({\
1874 span({\
1875 'made with \\xe2\\x98\\xbd by ',\
1876 a('s-ol', {\
1877 href = 'https://twitter.com/S0lll0s'\
1878 }),\
1879 \", \" .. tostring(os.date('%Y'))\
1880 }),\
1881 div({\
1882 class = 'icons',\
1883 iconlink('https://github.com/s-ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/github.svg', 'github'),\
1884 iconlink('https://merveilles.town/@s_ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/mastodon.svg', 'mastodon'),\
1885 iconlink('https://twitter.com/S0lll0s', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/twitter.svg', 'twitter'),\
1886 iconlink('https://webring.xxiivv.com/#random', 'https://webring.xxiivv.com/icon.black.svg', 'webring', {\
1887 height = '1.3em',\
1888 ['margin-left'] = '.3em',\
1889 ['margin-top'] = '-0.12em'\
1890 })\
1891 })\
1892 })\
1893 local get_meta\
1894 get_meta = function(self)\
1895 local title = (self:get('title: text/plain')) or self:gett('name: alpha')\
1896 local l\
1897 l = function(str)\
1898 str = str:gsub('[%s\\\\n]+$', '')\
1899 return str:gsub('\\\\n', ' ')\
1900 end\
1901 local e\
1902 e = function(str)\
1903 return string.format('%q', l(str))\
1904 end\
1905 local meta = \"\\n <meta charset=\\\"UTF-8\\\">\\n <title>\" .. tostring(l(title)) .. \"</title>\\n \"\
1906 do\
1907 local page_meta = self:get('_meta: mmm/dom')\
1908 if page_meta then\
1909 meta = meta .. page_meta\
1910 else\
1911 meta = meta .. \"\\n <meta name=\\\"viewport\\\" content=\\\"width=device-width, initial-scale=1\\\">\\n\\n <meta property=\\\"og:title\\\" content=\" .. tostring(e(title)) .. \" />\\n <meta property=\\\"og:type\\\" content=\\\"website\\\" />\\n <meta property=\\\"og:url\\\" content=\\\"https://mmm.s-ol.nu\" .. tostring(self.path) .. \"/\\\" />\\n <meta property=\\\"og:site_name\\\" content=\\\"mmm\\\" />\"\
1912 do\
1913 local desc = self:get('description: text/plain')\
1914 if desc then\
1915 meta = meta .. \"\\n <meta property=\\\"og:description\\\" content=\" .. tostring(e(desc)) .. \" />\"\
1916 end\
1917 end\
1918 end\
1919 end\
1920 return meta\
1921 end\
1922 local render\
1923 render = function(content, fileder, opts)\
1924 if opts == nil then\
1925 opts = { }\
1926 end\
1927 opts.meta = opts.meta or get_meta(fileder)\
1928 opts.scripts = opts.scripts or ''\
1929 if not (opts.noview) then\
1930 content = [[ <div class=\"view main\">\
1931 <div class=\"content\">\
1932 ]] .. content .. [[ </div>\
1933 </div>\
1934 ]]\
1935 end\
1936 local buf = [[<!DOCTYPE html>\
1937 <html>\
1938 <head>\
1939 <link rel=\"stylesheet\" type=\"text/css\" href=\"/static/style/:text/css\" />\
1940 <link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,400,600\" />\
1941 ]]\
1942 buf = buf .. \"\\n \" .. tostring(get_meta(fileder)) .. \"\\n </head>\\n <body>\\n \" .. tostring(gen_header()) .. \"\\n\\n \" .. tostring(content) .. \"\\n\\n \" .. tostring(footer) .. \"\\n \"\
1943 buf = buf .. [[ <script type=\"application/javascript\" src=\"/static/highlight-pack/:application/javascript\"></script>\
1944 <script type=\"application/javascript\" src=\"//cdnjs.cloudflare.com/ajax/libs/marked/0.5.1/marked.min.js\"></script>\
1945 <script type=\"application/javascript\" src=\"//cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.6/svg.min.js\"></script>\
1946 <script type=\"application/javascript\" src=\"//platform.twitter.com/widgets.js\" charset=\"utf-8\"></script>\
1947 <script type=\"application/javascript\" src=\"/static/fengari-web/:application/javascript\"></script>\
1948 <script type=\"application/lua\" src=\"/static/mmm/:application/lua\"></script>\
1949 <script type=\"application/lua\">require 'mmm'</script>\
1950 ]]\
1951 buf = buf .. opts.scripts\
1952 buf = buf .. \"\\n </body>\\n</html>\\n \"\
1953 return buf\
1954 end\
1955 return {\
1956 render = render\
1957 }\
1958 ", "mmm/mmmfs/layout.lua") end
1959 if not p["mmm.mmmfs.stores.fs"] then p["mmm.mmmfs.stores.fs"] = load("local lfs = require('lfs')\
1960 local dir_base\
1961 dir_base = function(path)\
1962 local dir, base = path:match('(.-)([^/]-)$')\
1963 if dir and #dir > 0 then\
1964 dir = dir:sub(1, #dir - 1)\
1965 end\
1966 return dir, base\
1967 end\
1968 local FSStore\
1969 do\
1970 local _class_0\
1971 local _base_0 = {\
1972 log = function(self, ...)\
1973 return print(\"[DB]\", ...)\
1974 end,\
1975 list_fileders_in = function(self, path)\
1976 if path == nil then\
1977 path = ''\
1978 end\
1979 return coroutine.wrap(function()\
1980 for entry_name in lfs.dir(self.root .. path) do\
1981 local _continue_0 = false\
1982 repeat\
1983 if '.' == entry_name:sub(1, 1) then\
1984 _continue_0 = true\
1985 break\
1986 end\
1987 local entry_path = self.root .. tostring(path) .. \"/\" .. tostring(entry_name)\
1988 if 'directory' == lfs.attributes(entry_path, 'mode') then\
1989 coroutine.yield(tostring(path) .. \"/\" .. tostring(entry_name))\
1990 end\
1991 _continue_0 = true\
1992 until true\
1993 if not _continue_0 then\
1994 break\
1995 end\
1996 end\
1997 end)\
1998 end,\
1999 list_all_fileders = function(self, path)\
2000 if path == nil then\
2001 path = ''\
2002 end\
2003 return coroutine.wrap(function()\
2004 for path in self:list_fileders_in(path) do\
2005 coroutine.yield(path)\
2006 for p in self:list_all_fileders(path) do\
2007 coroutine.yield(p)\
2008 end\
2009 end\
2010 end)\
2011 end,\
2012 create_fileder = function(self, parent, name)\
2013 self:log(\"creating fileder \" .. tostring(path))\
2014 local path = tostring(parent) .. \"/\" .. tostring(name)\
2015 assert(lfs.mkdir(self.root .. path))\
2016 return path\
2017 end,\
2018 remove_fileder = function(self, path)\
2019 self:log(\"removing fileder \" .. tostring(path))\
2020 local rmdir\
2021 rmdir = function(path)\
2022 for file in lfs.dir(path) do\
2023 local _continue_0 = false\
2024 repeat\
2025 if '.' == file:sub(1, 1) then\
2026 _continue_0 = true\
2027 break\
2028 end\
2029 local file_path = tostring(path) .. \"/\" .. tostring(file)\
2030 local _exp_0 = lfs.attributes(file_path, 'mode')\
2031 if 'file' == _exp_0 then\
2032 assert(os.remove(file_path))\
2033 elseif 'directory' == _exp_0 then\
2034 assert(rmdir(file_path))\
2035 end\
2036 _continue_0 = true\
2037 until true\
2038 if not _continue_0 then\
2039 break\
2040 end\
2041 end\
2042 return lfs.rmdir(path)\
2043 end\
2044 return rmdir(self.root .. path)\
2045 end,\
2046 rename_fileder = function(self, path, next_name)\
2047 self:log(\"renaming fileder \" .. tostring(path) .. \" -> '\" .. tostring(next_name) .. \"'\")\
2048 local parent, name = dir_base(path)\
2049 return assert(os.rename(path, self.root .. tostring(parent) .. \"/\" .. tostring(next_name)))\
2050 end,\
2051 move_fileder = function(self, path, next_parent)\
2052 self:log(\"moving fileder \" .. tostring(path) .. \" -> \" .. tostring(next_parent) .. \"/\")\
2053 local parent, name = dir_base(path)\
2054 return assert(os.rename(self.root .. path, self.root .. tostring(next_parent) .. \"/\" .. tostring(name)))\
2055 end,\
2056 list_facets = function(self, path)\
2057 return coroutine.wrap(function()\
2058 for entry_name in lfs.dir(self.root .. path) do\
2059 local entry_path = tostring(self.root .. path) .. \"/\" .. tostring(entry_name)\
2060 if 'file' == lfs.attributes(entry_path, 'mode') then\
2061 entry_name = (entry_name:match('(.*)%.%w+')) or entry_name\
2062 entry_name = entry_name:gsub('%$', '/')\
2063 local name, type = entry_name:match('([%w-_]+): *(.+)')\
2064 if not name then\
2065 name = ''\
2066 type = entry_name\
2067 end\
2068 coroutine.yield(name, type)\
2069 end\
2070 end\
2071 end)\
2072 end,\
2073 tofp = function(self, path, name, type)\
2074 if #name > 0 then\
2075 type = tostring(name) .. \": \" .. tostring(type)\
2076 end\
2077 type = type:gsub('%/', '$')\
2078 return self.root .. tostring(path) .. \"/\" .. tostring(type)\
2079 end,\
2080 locate = function(self, path, name, type)\
2081 if not (lfs.attributes(self.root .. path, 'mode')) then\
2082 return \
2083 end\
2084 type = type:gsub('%/', '$')\
2085 if #name > 0 then\
2086 name = tostring(name) .. \": \"\
2087 end\
2088 name = name .. type\
2089 name = name:gsub('([^%w])', '%%%1')\
2090 local file_name\
2091 for entry_name in lfs.dir(self.root .. path) do\
2092 if (entry_name:match(\"^\" .. tostring(name) .. \"$\")) or entry_name:match(\"^\" .. tostring(name) .. \"%.%w+$\") then\
2093 if file_name then\
2094 error(\"two files match \" .. tostring(name) .. \": \" .. tostring(file_name) .. \" and \" .. tostring(entry_name) .. \"!\")\
2095 end\
2096 file_name = entry_name\
2097 end\
2098 end\
2099 return file_name and self.root .. tostring(path) .. \"/\" .. tostring(file_name)\
2100 end,\
2101 load_facet = function(self, path, name, type)\
2102 local filepath = self:locate(path, name, type)\
2103 if not (filepath) then\
2104 return \
2105 end\
2106 local file = assert((io.open(filepath, 'rb')), \"couldn't open facet file '\" .. tostring(filepath) .. \"'\")\
2107 do\
2108 local _with_0 = file:read('*all')\
2109 file:close()\
2110 return _with_0\
2111 end\
2112 end,\
2113 create_facet = function(self, path, name, type, blob)\
2114 self:log(\"creating facet \" .. tostring(path) .. \" | \" .. tostring(name) .. \": \" .. tostring(type))\
2115 assert(blob, \"cant create facet without value!\")\
2116 local filepath = self:tofp(path, name, type)\
2117 if lfs.attributes(filepath, 'mode') then\
2118 error(\"facet file already exists!\")\
2119 end\
2120 local file = assert((io.open(filepath, 'wb')), \"couldn't open facet file '\" .. tostring(filepath) .. \"'\")\
2121 file:write(blob)\
2122 return file:close()\
2123 end,\
2124 remove_facet = function(self, path, name, type)\
2125 self:log(\"removing facet \" .. tostring(path) .. \" | \" .. tostring(name) .. \": \" .. tostring(type))\
2126 local filepath = self:locate(path, name, type)\
2127 assert(filepath, \"couldn't locate facet!\")\
2128 return assert(os.remove(filepath))\
2129 end,\
2130 rename_facet = function(self, path, name, type, next_name)\
2131 self:log(\"renaming facet \" .. tostring(path) .. \" | \" .. tostring(name) .. \": \" .. tostring(type) .. \" -> \" .. tostring(next_name))\
2132 local filepath = self:locate(path, name, type)\
2133 assert(filepath, \"couldn't locate facet!\")\
2134 return assert(os.rename(filepath, self:tofp(path, next_name, type)))\
2135 end,\
2136 update_facet = function(self, path, name, type, blob)\
2137 self:log(\"updating facet \" .. tostring(path) .. \" | \" .. tostring(name) .. \": \" .. tostring(type))\
2138 local filepath = self:locate(path, name, type)\
2139 assert(filepath, \"couldn't locate facet!\")\
2140 local file = assert((io.open(filepath, 'wb')), \"couldn't open facet file '\" .. tostring(filepath) .. \"'\")\
2141 file:write(blob)\
2142 return file:close()\
2143 end\
2144 }\
2145 _base_0.__index = _base_0\
2146 _class_0 = setmetatable({\
2147 __init = function(self, opts)\
2148 if opts == nil then\
2149 opts = { }\
2150 end\
2151 opts.root = opts.root or 'root'\
2152 opts.verbose = opts.verbose or false\
2153 if not opts.verbose then\
2154 self.log = function() end\
2155 end\
2156 self.root = opts.root:match('^(.-)/?$')\
2157 return self:log(\"opening '\" .. tostring(opts.root) .. \"'...\")\
2158 end,\
2159 __base = _base_0,\
2160 __name = \"FSStore\"\
2161 }, {\
2162 __index = _base_0,\
2163 __call = function(cls, ...)\
2164 local _self_0 = setmetatable({}, _base_0)\
2165 cls.__init(_self_0, ...)\
2166 return _self_0\
2167 end\
2168 })\
2169 _base_0.__class = _class_0\
2170 FSStore = _class_0\
2171 end\
2172 return {\
2173 FSStore = FSStore\
2174 }\
2175 ", "mmm/mmmfs/stores/fs.lua") end
2176 if not p["mmm.mmmfs.stores"] then p["mmm.mmmfs.stores"] = load("local require = relative(..., 0)\
2177 local get_store\
2178 get_store = function(args, opts)\
2179 if args == nil then\
2180 args = 'sql'\
2181 end\
2182 if opts == nil then\
2183 opts = {\
2184 verbose = true\
2185 }\
2186 end\
2187 local type, arg = args:match('(%w+):(.*)')\
2188 if not (type) then\
2189 type = args\
2190 end\
2191 local _exp_0 = type:lower()\
2192 if 'sql' == _exp_0 then\
2193 local SQLStore\
2194 SQLStore = require('.sql').SQLStore\
2195 if arg == 'MEMORY' then\
2196 opts.memory = true\
2197 else\
2198 opts.file = arg\
2199 end\
2200 return SQLStore(opts)\
2201 elseif 'fs' == _exp_0 then\
2202 local FSStore\
2203 FSStore = require('.fs').FSStore\
2204 opts.root = arg\
2205 return FSStore(opts)\
2206 else\
2207 warn(\"unknown or missing value for STORE: valid types values are sql, fs\")\
2208 return os.exit(1)\
2209 end\
2210 end\
2211 return {\
2212 get_store = get_store\
2213 }\
2214 ", "mmm/mmmfs/stores/init.lua") end
2215 if not p["mmm.mmmfs.stores.sql"] then p["mmm.mmmfs.stores.sql"] = load("local sqlite = require('sqlite3')\
2216 local root = os.tmpname()\
2217 local SQLStore\
2218 do\
2219 local _class_0\
2220 local _base_0 = {\
2221 log = function(self, ...)\
2222 return print(\"[DB]\", ...)\
2223 end,\
2224 close = function(self)\
2225 return self.db:close()\
2226 end,\
2227 fetch = function(self, q, ...)\
2228 local stmt = assert(self.db:prepare(q))\
2229 if 0 < select('#', ...) then\
2230 stmt:bind(...)\
2231 end\
2232 return stmt:irows()\
2233 end,\
2234 fetch_one = function(self, q, ...)\
2235 local stmt = assert(self.db:prepare(q))\
2236 if 0 < select('#', ...) then\
2237 stmt:bind(...)\
2238 end\
2239 return stmt:first_irow()\
2240 end,\
2241 exec = function(self, q, ...)\
2242 local stmt = assert(self.db:prepare(q))\
2243 if 0 < select('#', ...) then\
2244 stmt:bind(...)\
2245 end\
2246 local res = assert(stmt:exec())\
2247 end,\
2248 list_fileders_in = function(self, path)\
2249 if path == nil then\
2250 path = ''\
2251 end\
2252 return coroutine.wrap(function()\
2253 for _des_0 in self:fetch('SELECT path\\n FROM fileder WHERE parent IS ?', path) do\
2254 path = _des_0[1]\
2255 coroutine.yield(path)\
2256 end\
2257 end)\
2258 end,\
2259 list_all_fileders = function(self, path)\
2260 if path == nil then\
2261 path = ''\
2262 end\
2263 return coroutine.wrap(function()\
2264 for path in self:list_fileders_in(path) do\
2265 coroutine.yield(path)\
2266 for p in self:list_all_fileders(path) do\
2267 coroutine.yield(p)\
2268 end\
2269 end\
2270 end)\
2271 end,\
2272 create_fileder = function(self, parent, name)\
2273 local path = tostring(parent) .. \"/\" .. tostring(name)\
2274 self:log(\"creating fileder \" .. tostring(path))\
2275 self:exec('INSERT INTO fileder (path, parent)\\n VALUES (:path, :parent)', {\
2276 path = path,\
2277 parent = parent\
2278 })\
2279 local changes = self:fetch_one('SELECT changes()')\
2280 assert(changes[1] == 1, \"couldn't create fileder - parent missing?\")\
2281 return path\
2282 end,\
2283 remove_fileder = function(self, path)\
2284 self:log(\"removing fileder \" .. tostring(path))\
2285 return self:exec('DELETE FROM fileder\\n WHERE path LIKE :path || \"/%\"\\n OR path = :path', path)\
2286 end,\
2287 rename_fileder = function(self, path, next_name)\
2288 self:log(\"renaming fileder \" .. tostring(path) .. \" -> '\" .. tostring(next_name) .. \"'\")\
2289 error('not implemented')\
2290 return self:exec('UPDATE fileder\\n SET path = parent || \"/\" || :next_name\\n WHERE path = :path', {\
2291 path = path,\
2292 next_name = next_name\
2293 })\
2294 end,\
2295 move_fileder = function(self, path, next_parent)\
2296 self:log(\"moving fileder \" .. tostring(path) .. \" -> \" .. tostring(next_parent) .. \"/\")\
2297 return error('not implemented')\
2298 end,\
2299 list_facets = function(self, path)\
2300 return coroutine.wrap(function()\
2301 for _des_0 in self:fetch('SELECT facet.name, facet.type\\n FROM facet\\n INNER JOIN fileder ON facet.fileder_id = fileder.id\\n WHERE fileder.path = ?', path) do\
2302 local name, type\
2303 name, type = _des_0[1], _des_0[2]\
2304 coroutine.yield(name, type)\
2305 end\
2306 end)\
2307 end,\
2308 load_facet = function(self, path, name, type)\
2309 local v = self:fetch_one('SELECT facet.value\\n FROM facet\\n INNER JOIN fileder ON facet.fileder_id = fileder.id\\n WHERE fileder.path = :path\\n AND facet.name = :name\\n AND facet.type = :type', {\
2310 path = path,\
2311 name = name,\
2312 type = type\
2313 })\
2314 return v and v[1]\
2315 end,\
2316 create_facet = function(self, path, name, type, blob)\
2317 self:log(\"creating facet \" .. tostring(path) .. \" | \" .. tostring(name) .. \": \" .. tostring(type))\
2318 self:exec('INSERT INTO facet (fileder_id, name, type, value)\\n SELECT id, :name, :type, :blob\\n FROM fileder\\n WHERE fileder.path = :path', {\
2319 path = path,\
2320 name = name,\
2321 type = type,\
2322 blob = blob\
2323 })\
2324 local changes = self:fetch_one('SELECT changes()')\
2325 return assert(changes[1] == 1, \"couldn't create facet - fileder missing?\")\
2326 end,\
2327 remove_facet = function(self, path, name, type)\
2328 self:log(\"removing facet \" .. tostring(path) .. \" | \" .. tostring(name) .. \": \" .. tostring(type))\
2329 self:exec('DELETE FROM facet\\n WHERE name = :name\\n AND type = :type\\n AND fileder_id = (SELECT id FROM fileder WHERE path = :path)', {\
2330 path = path,\
2331 name = name,\
2332 type = type\
2333 })\
2334 local changes = self:fetch_one('SELECT changes()')\
2335 return assert(changes[1] == 1, \"no such facet\")\
2336 end,\
2337 rename_facet = function(self, path, name, type, next_name)\
2338 self:log(\"renaming facet \" .. tostring(path) .. \" | \" .. tostring(name) .. \": \" .. tostring(type) .. \" -> \" .. tostring(next_name))\
2339 self:exec('UPDATE facet\\n SET name = :next_name\\n WHERE name = :name\\n AND type = :type\\n AND fileder_id = (SELECT id FROM fileder WHERE path = :path)', {\
2340 path = path,\
2341 name = name,\
2342 next_name = next_name,\
2343 type = type\
2344 })\
2345 local changes = self:fetch_one('SELECT changes()')\
2346 return assert(changes[1] == 1, \"no such facet\")\
2347 end,\
2348 update_facet = function(self, path, name, type, blob)\
2349 self:log(\"updating facet \" .. tostring(path) .. \" | \" .. tostring(name) .. \": \" .. tostring(type))\
2350 self:exec('UPDATE facet\\n SET value = :blob\\n WHERE facet.name = :name\\n AND facet.type = :type\\n AND facet.fileder_id = (SELECT id FROM fileder WHERE path = :path)', {\
2351 path = path,\
2352 name = name,\
2353 type = type,\
2354 blob = blob\
2355 })\
2356 local changes = self:fetch_one('SELECT changes()')\
2357 return assert(changes[1] == 1, \"no such facet\")\
2358 end\
2359 }\
2360 _base_0.__index = _base_0\
2361 _class_0 = setmetatable({\
2362 __init = function(self, opts)\
2363 if opts == nil then\
2364 opts = { }\
2365 end\
2366 opts.file = opts.file or 'db.sqlite3'\
2367 opts.verbose = opts.verbose or false\
2368 opts.memory = opts.memory or false\
2369 if not opts.verbose then\
2370 self.log = function() end\
2371 end\
2372 if opts.memory then\
2373 self:log(\"opening in-memory DB...\")\
2374 self.db = sqlite.open_memory()\
2375 else\
2376 self:log(\"opening '\" .. tostring(opts.file) .. \"'...\")\
2377 self.db = sqlite.open(opts.file)\
2378 end\
2379 return assert(self.db:exec([[ PRAGMA foreign_keys = ON;\
2380 PRAGMA case_sensitive_like = ON;\
2381 CREATE TABLE IF NOT EXISTS fileder (\
2382 id INTEGER NOT NULL PRIMARY KEY,\
2383 path TEXT NOT NULL UNIQUE,\
2384 parent TEXT REFERENCES fileder(path)\
2385 ON DELETE CASCADE\
2386 ON UPDATE CASCADE\
2387 );\
2388 INSERT OR IGNORE INTO fileder (path, parent) VALUES (\"\", NULL);\
2389 \
2390 CREATE TABLE IF NOT EXISTS facet (\
2391 fileder_id INTEGER NOT NULL\
2392 REFERENCES fileder\
2393 ON UPDATE CASCADE\
2394 ON DELETE CASCADE,\
2395 name TEXT NOT NULL,\
2396 type TEXT NOT NULL,\
2397 value BLOB NOT NULL,\
2398 PRIMARY KEY (fileder_id, name, type)\
2399 );\
2400 CREATE INDEX IF NOT EXISTS facet_fileder_id ON facet(fileder_id);\
2401 CREATE INDEX IF NOT EXISTS facet_name ON facet(name);\
2402 ]]))\
2403 end,\
2404 __base = _base_0,\
2405 __name = \"SQLStore\"\
2406 }, {\
2407 __index = _base_0,\
2408 __call = function(cls, ...)\
2409 local _self_0 = setmetatable({}, _base_0)\
2410 cls.__init(_self_0, ...)\
2411 return _self_0\
2412 end\
2413 })\
2414 _base_0.__class = _class_0\
2415 SQLStore = _class_0\
2416 end\
2417 return {\
2418 SQLStore = SQLStore\
2419 }\
2420 ", "mmm/mmmfs/stores/sql.lua") end
2421 if not p["mmm.dom"] then p["mmm.dom"] = load("local element\
2422 element = function(element)\
2423 return function(...)\
2424 local children = {\
2425 ...\
2426 }\
2427 local attributes = children[#children]\
2428 if 'table' == (type(attributes)) and not attributes.node then\
2429 table.remove(children)\
2430 else\
2431 attributes = { }\
2432 end\
2433 do\
2434 local e = document:createElement(element)\
2435 for k, v in pairs(attributes) do\
2436 if k == 'class' then\
2437 k = 'className'\
2438 end\
2439 if k == 'style' and 'table' == type(v) then\
2440 for kk, vv in pairs(v) do\
2441 e.style[kk] = vv\
2442 end\
2443 elseif 'string' == type(k) then\
2444 e[k] = v\
2445 end\
2446 end\
2447 if #children == 0 then\
2448 children = attributes\
2449 end\
2450 for _index_0 = 1, #children do\
2451 local child = children[_index_0]\
2452 if 'string' == type(child) then\
2453 child = document:createTextNode(child)\
2454 end\
2455 e:appendChild(child)\
2456 end\
2457 return e\
2458 end\
2459 end\
2460 end\
2461 return setmetatable({ }, {\
2462 __index = function(self, name)\
2463 do\
2464 local val = element(name)\
2465 self[name] = val\
2466 return val\
2467 end\
2468 end\
2469 })\
2470 ", "mmm/dom/init.client.lua") end
2471 if not p["mmm.component"] then p["mmm.component"] = load("local tohtml\
2472 tohtml = function(val)\
2473 if 'string' == type(val) then\
2474 return document:createTextNode(val)\
2475 end\
2476 if 'table' == type(val) then\
2477 assert(val.node, \"Table doesn't have .node\")\
2478 val = val.node\
2479 end\
2480 if 'userdata' == type(val) then\
2481 assert((js.instanceof(val, js.global.Node)), \"userdata is not a Node\")\
2482 return val\
2483 else\
2484 return error(\"not a Node: \" .. tostring(val) .. \", \" .. tostring(type(val)))\
2485 end\
2486 end\
2487 local text\
2488 text = function(str)\
2489 return document:createTextNode(tostring(str))\
2490 end\
2491 local ReactiveVar\
2492 do\
2493 local _class_0\
2494 local _base_0 = {\
2495 set = function(self, value)\
2496 local old = self.value\
2497 self.value = value\
2498 for k, callback in pairs(self.listeners) do\
2499 callback(self.value, old)\
2500 end\
2501 end,\
2502 get = function(self)\
2503 return self.value\
2504 end,\
2505 transform = function(self, transform)\
2506 return self:set(transform(self:get()))\
2507 end,\
2508 subscribe = function(self, callback)\
2509 do\
2510 local _with_0\
2511 _with_0 = function()\
2512 self.listeners[callback] = nil\
2513 end\
2514 self.listeners[callback] = callback\
2515 return _with_0\
2516 end\
2517 end,\
2518 map = function(self, transform)\
2519 do\
2520 local _with_0 = ReactiveVar(transform(self.value))\
2521 _with_0.upstream = self:subscribe(function(...)\
2522 return _with_0:set(transform(...))\
2523 end)\
2524 return _with_0\
2525 end\
2526 end\
2527 }\
2528 _base_0.__index = _base_0\
2529 _class_0 = setmetatable({\
2530 __init = function(self, value)\
2531 self.value = value\
2532 self.listeners = setmetatable({ }, {\
2533 __mode = 'kv'\
2534 })\
2535 end,\
2536 __base = _base_0,\
2537 __name = \"ReactiveVar\"\
2538 }, {\
2539 __index = _base_0,\
2540 __call = function(cls, ...)\
2541 local _self_0 = setmetatable({}, _base_0)\
2542 cls.__init(_self_0, ...)\
2543 return _self_0\
2544 end\
2545 })\
2546 _base_0.__class = _class_0\
2547 local self = _class_0\
2548 self.isinstance = function(val)\
2549 return 'table' == (type(val)) and val.subscribe\
2550 end\
2551 ReactiveVar = _class_0\
2552 end\
2553 local ReactiveElement\
2554 do\
2555 local _class_0\
2556 local _base_0 = {\
2557 destroy = function(self)\
2558 local _list_0 = self._subscriptions\
2559 for _index_0 = 1, #_list_0 do\
2560 local unsub = _list_0[_index_0]\
2561 unsub()\
2562 end\
2563 end,\
2564 set = function(self, attr, value)\
2565 if attr == 'class' then\
2566 attr = 'className'\
2567 end\
2568 if 'table' == (type(value)) and ReactiveVar.isinstance(value) then\
2569 table.insert(self._subscriptions, value:subscribe(function(...)\
2570 return self:set(attr, ...)\
2571 end))\
2572 value = value:get()\
2573 end\
2574 if attr == 'style' and 'table' == type(value) then\
2575 for k, v in pairs(value) do\
2576 self.node.style[k] = v\
2577 end\
2578 return \
2579 end\
2580 self.node[attr] = value\
2581 end,\
2582 prepend = function(self, child, last)\
2583 return self:append(child, last, 'prepend')\
2584 end,\
2585 append = function(self, child, last, mode)\
2586 if mode == nil then\
2587 mode = 'append'\
2588 end\
2589 if ReactiveVar.isinstance(child) then\
2590 table.insert(self._subscriptions, child:subscribe(function(...)\
2591 return self:append(...)\
2592 end))\
2593 child = child:get()\
2594 end\
2595 if 'string' == type(last) then\
2596 error('cannot replace string node')\
2597 end\
2598 if child == nil then\
2599 if last then\
2600 self:remove(last)\
2601 end\
2602 return \
2603 end\
2604 child = tohtml(child)\
2605 if last then\
2606 return self.node:replaceChild(child, tohtml(last))\
2607 else\
2608 local _exp_0 = mode\
2609 if 'append' == _exp_0 then\
2610 return self.node:appendChild(child)\
2611 elseif 'prepend' == _exp_0 then\
2612 return self.node:insertBefore(child, self.node.firstChild)\
2613 end\
2614 end\
2615 end,\
2616 remove = function(self, child)\
2617 self.node:removeChild(tohtml(child))\
2618 if 'table' == (type(child)) and child.destroy then\
2619 return child:destroy()\
2620 end\
2621 end\
2622 }\
2623 _base_0.__index = _base_0\
2624 _class_0 = setmetatable({\
2625 __init = function(self, element, ...)\
2626 if 'userdata' == type(element) then\
2627 self.node = element\
2628 else\
2629 self.node = document:createElement(element)\
2630 end\
2631 self._subscriptions = { }\
2632 local children = {\
2633 ...\
2634 }\
2635 local attributes = children[#children]\
2636 if 'table' == (type(attributes)) and (not ReactiveElement.isinstance(attributes)) and (not ReactiveVar.isinstance(attributes)) then\
2637 table.remove(children)\
2638 else\
2639 attributes = { }\
2640 end\
2641 for k, v in pairs(attributes) do\
2642 if 'string' == type(k) then\
2643 self:set(k, v)\
2644 end\
2645 end\
2646 if #children == 0 then\
2647 children = attributes\
2648 end\
2649 for _index_0 = 1, #children do\
2650 local child = children[_index_0]\
2651 self:append(child)\
2652 end\
2653 end,\
2654 __base = _base_0,\
2655 __name = \"ReactiveElement\"\
2656 }, {\
2657 __index = _base_0,\
2658 __call = function(cls, ...)\
2659 local _self_0 = setmetatable({}, _base_0)\
2660 cls.__init(_self_0, ...)\
2661 return _self_0\
2662 end\
2663 })\
2664 _base_0.__class = _class_0\
2665 local self = _class_0\
2666 self.isinstance = function(val)\
2667 return 'table' == (type(val)) and val.node\
2668 end\
2669 ReactiveElement = _class_0\
2670 end\
2671 local get_or_create\
2672 get_or_create = function(elem, id, ...)\
2673 elem = (document:getElementById(id)) or elem\
2674 do\
2675 local _with_0 = ReactiveElement(elem, ...)\
2676 _with_0:set('id', id)\
2677 return _with_0\
2678 end\
2679 end\
2680 local elements = setmetatable({ }, {\
2681 __index = function(self, name)\
2682 do\
2683 local val\
2684 val = function(...)\
2685 return ReactiveElement(name, ...)\
2686 end\
2687 self[name] = val\
2688 return val\
2689 end\
2690 end\
2691 })\
2692 return {\
2693 ReactiveVar = ReactiveVar,\
2694 ReactiveElement = ReactiveElement,\
2695 get_or_create = get_or_create,\
2696 tohtml = tohtml,\
2697 text = text,\
2698 elements = elements\
2699 }\
2700 ", "mmm/component/init.client.lua") end
+0
-411
root/static/style/text$css.css less more
0 :root {
1 --gray-bright: #eeeeee;
2 --gray-dark: #303336;
3 --gray-darker: #1d1f21;
4 --gray-neutral: #b9bdc1;
5 --gray-success: #bdddc1;
6 --gray-fail: #ddbdc1;
7 --margin-wide: 2rem; }
8 @media only screen and (max-width: 425px) {
9 :root {
10 --margin-wide: 0.5rem; } }
11
12 * {
13 font-weight: inherit;
14 font-style: inherit;
15 font-family: inherit;
16 color: inherit;
17 -webkit-font-smoothing: antialiased;
18 -moz-osx-font-smoothing: grayscale; }
19
20 body, html, h1, h2, h3, h4, h5, h6 {
21 margin: 0;
22 padding: 0; }
23
24 h1, h2, h3, h4, h5, h6 {
25 font-weight: bold; }
26
27 input, select, button {
28 color: initial; }
29
30 tt, code, kbd, samp {
31 font-family: monospace; }
32
33 code {
34 font-size: 0.8em; }
35
36 b, strong {
37 font-weight: bold; }
38
39 em, i {
40 font-style: italic; }
41
42 a {
43 font-size: 1em;
44 cursor: pointer; }
45
46 hr {
47 clear: both; }
48
49 ul {
50 margin: 0; }
51
52 body {
53 display: flex;
54 flex-direction: column;
55 justify-content: space-between;
56 background: #ffffff;
57 font-family: sans-serif;
58 min-height: 100vh; }
59
60 a {
61 display: inline-block;
62 font-weight: bold;
63 text-decoration: underline;
64 text-decoration-color: transparent;
65 transition: filter 500ms, text-decoration-color 500ms; }
66 a:hover {
67 filter: invert(40%);
68 text-decoration-color: currentColor; }
69
70 ::selection {
71 background: #303336;
72 color: #eeeeee;
73 padding: 1em; }
74
75 /*
76
77 vim-hybrid theme by w0ng (https://github.com/w0ng/vim-hybrid)
78
79 */
80 /*background color*/
81 .hljs {
82 background: #1d1f21;
83 border-radius: 6px;
84 padding: 0.2em 0.5em;
85 white-space: nowrap; }
86
87 pre > .hljs {
88 display: block;
89 overflow-x: auto;
90 padding: 1em;
91 white-space: pre-wrap;
92 margin: 0 2em; }
93
94 /*selection color*/
95 .hljs::selection,
96 .hljs span::selection {
97 background: #373b41; }
98
99 .hljs::-moz-selection,
100 .hljs span::-moz-selection {
101 background: #373b41; }
102
103 /*foreground color*/
104 .hljs {
105 color: #c5c8c6; }
106
107 /*color: fg_yellow*/
108 .hljs-title,
109 .hljs-name {
110 color: #f0c674; }
111
112 /*color: fg_comment*/
113 .hljs-comment,
114 .hljs-meta,
115 .hljs-meta .hljs-keyword {
116 color: #707880; }
117
118 /*color: fg_red*/
119 .hljs-number,
120 .hljs-symbol,
121 .hljs-literal,
122 .hljs-deletion,
123 .hljs-link {
124 color: #cc6666; }
125
126 /*color: fg_green*/
127 .hljs-string,
128 .hljs-doctag,
129 .hljs-addition,
130 .hljs-regexp,
131 .hljs-selector-attr,
132 .hljs-selector-pseudo {
133 color: #b5bd68; }
134
135 /*color: fg_purple*/
136 .hljs-attribute,
137 .hljs-code,
138 .hljs-selector-id {
139 color: #b294bb; }
140
141 /*color: fg_blue*/
142 .hljs-keyword,
143 .hljs-selector-tag,
144 .hljs-bullet,
145 .hljs-tag {
146 color: #81a2be; }
147
148 /*color: fg_aqua*/
149 .hljs-subst,
150 .hljs-variable,
151 .hljs-template-tag,
152 .hljs-template-variable {
153 color: #8abeb7; }
154
155 /*color: fg_orange*/
156 .hljs-type,
157 .hljs-built_in,
158 .hljs-builtin-name,
159 .hljs-quote,
160 .hljs-section,
161 .hljs-selector-class {
162 color: #de935f; }
163
164 .hljs-emphasis {
165 font-style: italic; }
166
167 .hljs-strong {
168 font-weight: bold; }
169
170 header > div {
171 font-family: 'Source Sans Pro', sans-serif;
172 padding: 1rem 2rem;
173 text-align: right;
174 color: #eeeeee;
175 background: #303336; }
176 header > div h1 {
177 display: flex;
178 justify-content: flex-end;
179 align-items: center;
180 flex-wrap: wrap;
181 margin: 0;
182 line-height: 5rem;
183 font-size: 4rem;
184 font-weight: 200; }
185 header > div h1 > span {
186 margin-left: auto; }
187 header > div h1 .bold {
188 font-weight: 400; }
189 header > div h1 > a {
190 height: 5rem; }
191 header > div h1 .sun {
192 height: 100%;
193 stroke: currentColor;
194 fill: currentColor; }
195 header > div h1 .sun .out, header > div h1 .sun .in {
196 animation: spin 2s;
197 animation-iteration-count: 1;
198 transform: scale(1); }
199 header > div h1 .sun .in {
200 animation: size 2s; }
201 header > div h1 .sun.spin .circle {
202 animation: none; }
203
204 header aside {
205 margin: 0;
206 padding: 0 2rem;
207 color: #eeeeee;
208 background: #1d1f21;
209 line-height: 3em; }
210 header aside > a {
211 margin-right: 2em; }
212
213 @keyframes spin {
214 0% {
215 transform: rotate3d(0.5, 1, 0, 0turn); }
216 100% {
217 transform: rotate3d(0.5, 1, 0, 1turn); } }
218
219 @keyframes size {
220 0%, 100% {
221 transform: scale(1); }
222 40%, 60% {
223 transform: scale(1.8); } }
224
225 footer {
226 display: flex;
227 padding: 1rem 2rem;
228 position: sticky;
229 bottom: 0;
230 color: #eeeeee;
231 background: #303336; }
232 footer .icons {
233 flex: 1;
234 display: flex;
235 justify-content: flex-end; }
236 footer .icons .iconlink {
237 filter: invert(0.7); }
238 footer .icons .iconlink:hover {
239 filter: invert(1); }
240 footer .icons .iconlink > img {
241 height: 1em;
242 margin-left: 0.5em;
243 vertical-align: middle; }
244
245 .browser {
246 display: flex;
247 flex: 1;
248 border-top: 0.2rem solid #eeeeee; }
249
250 .view {
251 display: flex;
252 flex-direction: column;
253 flex: 1 1 0;
254 min-width: 0; }
255 .view.inspector {
256 top: 0;
257 position: sticky;
258 max-height: 100vh;
259 color: #c5c8c6;
260 border-style: solid;
261 border-width: 0 0 0 0.6rem;
262 border-radius: 0 6px 6px 0;
263 border-color: #303336;
264 background: #303336; }
265 .view.inspector nav {
266 background: inherit; }
267 .view.inspector .content {
268 margin: 0;
269 padding: 0;
270 display: flex;
271 background: #1d1f21; }
272 .view.inspector .content > * {
273 display: block;
274 margin: 0;
275 padding: 1em;
276 border-radius: 0;
277 border: 0;
278 flex: 1; }
279 .view nav {
280 position: sticky;
281 top: 0;
282 display: flex;
283 flex: 0 0 auto;
284 flex-wrap: wrap;
285 justify-content: space-between;
286 background: #eeeeee;
287 z-index: 1000; }
288 .view nav > span:first-of-type {
289 flex: 1 0 auto; }
290 .view nav > * {
291 margin: 1em;
292 white-space: nowrap; }
293 @media only screen and (max-width: 425px) {
294 .view nav > .inspect-btn {
295 display: none; } }
296 .view .error-wrap {
297 flex: 0 0 auto;
298 padding: 1em 2em;
299 overflow: hidden;
300 background: #ddbdc1; }
301 .view .error-wrap.empty {
302 display: none; }
303 .view .error-wrap > span {
304 display: inline-block;
305 margin-bottom: 0.5em;
306 font-weight: bold;
307 color: #f00; }
308 .view .error-wrap > pre {
309 margin: 0; }
310 .view .content {
311 flex: 1 1 auto;
312 overflow: auto;
313 padding: 1em 2em;
314 position: relative; }
315
316 .content {
317 opacity: 1;
318 transition: opacity 150ms; }
319 body.loading .content {
320 opacity: 0; }
321
322 .content img, .content video {
323 width: inherit;
324 height: inherit; }
325
326 .content .markdown > img, .content .markdown > video,
327 .content .markdown > p > img,
328 .content .markdown > p > video,
329 .content .markdown > p > a > img,
330 .content .markdown > p > a > video,
331 .content .markdown > a > img,
332 .content .markdown > a > video {
333 display: block;
334 max-width: 100%;
335 max-height: 50vh;
336 padding: 0 2em;
337 box-sizing: border-box; }
338
339 .content .embed {
340 width: inherit;
341 height: inherit; }
342 .content .embed .description {
343 text-align: center; }
344 .content .embed.inline {
345 display: inline-block; }
346 .content .embed.desc {
347 display: inline-block;
348 padding: 0.5em;
349 margin: 0.2em;
350 background: #eeeeee; }
351
352 .content pre > code {
353 border-style: solid;
354 border-width: 0 0 0 0.6rem;
355 border-radius: 0 6px 6px 0;
356 border-color: #303336;
357 display: block;
358 border-radius: 6px;
359 margin: 0 var(--margin-wide);
360 padding: 1em;
361 white-space: pre-wrap;
362 overflow-x: auto;
363 background: #1d1f21;
364 color: #c5c8c6; }
365
366 .content pre.dual-code {
367 display: flex;
368 justify-content: space-between;
369 padding: 0 2rem; }
370 .content pre.dual-code > code {
371 border-style: solid;
372 border-width: 0 0.3rem 0 0;
373 border-radius: 6px 0 0 6px;
374 flex: 1;
375 margin: 0; }
376 .content pre.dual-code > code + code {
377 border-style: solid;
378 border-width: 0 0 0 0.3rem;
379 border-radius: 0 6px 6px 0; }
380
381 .content .example, .content .well {
382 border-style: solid;
383 border-width: 0 0 0 0.6rem;
384 border-radius: 0 6px 6px 0;
385 margin: 1rem var(--margin-wide);
386 padding: 1rem;
387 background: #eeeeee;
388 border-color: #b9bdc1; }
389
390 .canvas_app {
391 position: relative;
392 display: inline-block; }
393 .canvas_app .overlay {
394 position: absolute;
395 padding: 1rem;
396 top: 0;
397 left: 0;
398 right: 0;
399 bottom: 0;
400 opacity: 0;
401 background: rgba(0, 0, 0, 0.7);
402 transition: opacity 300ms; }
403 .canvas_app .overlay:hover {
404 opacity: 1; }
405 .canvas_app .overlay > * {
406 margin: 0.5em; }
407 .canvas_app .overlay a {
408 display: block;
409 color: #eeeeee;
410 font-family: inherit; }