git.s-ol.nu mmm / ad26c7c
allow server to render with layout s-ol 3 years ago
6 changed file(s) with 449 addition(s) and 430 deletion(s). Raw diff Collapse all Expand all
+0
-108
build/layout.moon less more
0 import header, aside, footer, div, svg, script, g, circle, h1, span, b, a, img from require 'mmm.dom'
1 import navigate_to from (require 'mmm.mmmfs.util') require 'mmm.dom'
2
3 pick = (...) ->
4 num = select '#', ...
5 i = math.ceil math.random! * num
6 select i, ...
7
8 iconlink = (href, src, alt, style) -> a {
9 class: 'iconlink',
10 target: '_blank',
11 rel: 'me',
12 :href,
13 img :src, :alt, :style
14 }
15
16 logo = svg {
17 class: 'sun'
18 viewBox: '-0.75 -1 1.5 2'
19 xmlns: 'http://www.w3.org/2000/svg'
20 baseProfile: 'full'
21 version: '1.1'
22
23 g {
24 transform: 'translate(0 .18)'
25
26 g { class: 'circle out', circle r: '.6', fill: 'none', 'stroke-width': '.12' }
27 g { class: 'circle in', circle r: '.2', stroke: 'none' }
28 }
29 }
30
31 {
32 header: header {
33 div {
34 h1 {
35 logo
36 span {
37 span 'mmm', class: 'bold'
38 '​'
39 '.s‑ol.nu'
40 }
41 }
42 span "fun stuff with code and wires"
43 -- pick 'fun', 'cool', 'weird', 'interesting', 'new'
44 -- pick 'stuff', 'things', 'projects', 'experiments', 'news'
45 -- "with"
46 -- pick 'mostly code', 'code and wires', 'silicon', 'electronics'
47 }
48 aside {
49 navigate_to '/about', 'about me'
50 navigate_to '/games', 'games'
51 navigate_to '/projects', 'other'
52 a {
53 href: 'mailto:s%20[removethis]%20[at]%20s-ol.nu'
54 'contact'
55 script "
56 var l = document.currentScript.parentElement;
57 l.href = l.href.replace('%20[at]%20', '@');
58 l.href = l.href.replace('%20[removethis]', '') + '?subject=Hey there :)';
59 "
60 }
61 }
62 }
63 footer: footer {
64 span {
65 'made with \xe2\x98\xbd by '
66 a 's-ol', href: 'https://twitter.com/S0lll0s'
67 ", #{os.date '%Y'}"
68 }
69 div {
70 class: 'icons',
71 iconlink 'https://github.com/s-ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/github.svg', 'github'
72 iconlink 'https://merveilles.town/@s_ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/mastodon.svg', 'mastodon'
73 iconlink 'https://twitter.com/S0lll0s', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/twitter.svg', 'twitter'
74 iconlink 'https://webring.xxiivv.com/#random', 'https://webring.xxiivv.com/icon.black.svg', 'webring',
75 { height: '1.3em', 'margin-left': '.3em', 'margin-top': '-0.12em' }
76 }
77 }
78 get_meta: =>
79 title = (@get 'title: text/plain') or @gett 'name: alpha'
80
81 l = (str) ->
82 str = str\gsub '[%s\\n]+$', ''
83 str\gsub '\\n', ' '
84 e = (str) -> string.format '%q', l str
85
86 meta = "
87 <meta charset=\"UTF-8\">
88 <title>#{l title}</title>
89 "
90
91 if page_meta = @get '_meta: mmm/dom'
92 meta ..= page_meta
93 else
94 meta ..= "
95 <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
96
97 <meta property=\"og:title\" content=#{e title} />
98 <meta property=\"og:type\" content=\"website\" />
99 <meta property=\"og:url\" content=\"https://mmm.s-ol.nu#{@path}/\" />
100 <meta property=\"og:site_name\" content=\"mmm\" />"
101
102 if desc = @get 'description: text/plain'
103 meta ..= "
104 <meta property=\"og:description\" content=#{e desc} />"
105
106 meta
107 }
99 require 'mmm'
1010 import tohtml from require 'mmm.component'
1111 import Browser from require 'mmm.mmmfs.browser'
12 import get_meta, header, footer from require 'build.layout'
12 import render from require 'mmm.mmmfs.layout'
13 import SQLStore from require 'mmm.mmmfs.drivers.sql'
14 import load_tree from require 'build.util'
1315
1416 -- usage:
15 -- moon render_all.moon [db.sqlite3]
16 { file } = arg
17 -- moon render_all.moon [db.sqlite3] [startpath]
18 { file, startpath } = arg
1719
1820 export BROWSER
1921
20 render = (fileder, output) ->
21 BROWSER = Browser fileder
22
23 with io.open output, 'w'
24 \write [[
25 <!DOCTYPE html>
26 <html>
27 <head>
28 <link rel="stylesheet" type="text/css" href="/main.css" />
29 <!--
30 <link rel="preload" as="fetch" href="/mmm/dom/init.lua" />
31 <link rel="preload" as="fetch" href="/mmm/component/init.lua" />
32 <link rel="preload" as="fetch" href="/mmm/mmmfs/init.lua" />
33 <link rel="preload" as="fetch" href="/mmm/mmmfs/fileder.lua" />
34 <link rel="preload" as="fetch" href="/mmm/mmmfs/browser.lua" />
35 -->
36
37 <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,400" rel="stylesheet">
38 ]]
39 \write "
40 #{get_meta fileder}
41 </head>
42 <body>
43 #{header}
44
45 #{assert (tohtml BROWSER), "couldn't render BROWSER"}
46
47 #{footer}
48 "
49 \write [[
50 <script src="/highlight.pack.js"></script>
51 <script src="//cdnjs.cloudflare.com/ajax/libs/marked/0.5.1/marked.min.js"></script>
52 <script src="//cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.6/svg.min.js"></script>
53 <script src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
54 <script src="/fengari-web.js"></script>
55 <script type="application/lua" src="/mmm.bundle.lua"></script>
56 <script type="application/lua">require 'mmm'</script>
57 ]]
58 \write "
59 <script type=\"application/lua\">
60 on_load = on_load or {}
61 table.insert(on_load, function()
62 local path = #{string.format '%q', path}
63 local browser = require 'mmm.mmmfs.browser'
64 local root = dofile '/$bundle.lua'
65 root:mount('', true)
66
67 BROWSER = browser.Browser(root, path, true)
68 end)
69 </script>
70 </body>
71 </html>
72 "
73 \close!
74
75
7622 tree = load_tree SQLStore :name
23 tree = tree\walk startpath if startpath
7724
7825 for fileder in coroutine.wrap tree\iterate
7926 print "rendering '#{fileder.path}'..."
8027 os.execute "mkdir -p 'out/#{fileder.path}'"
81 render fileder, "out/#{fileder.path}/index.html"
28
29 BROWSER = Browser fileder
30 with io.open "out/#{fileder.path}/index.html", 'w'
31 \write render (tohtml BROWSER), fileder
32 \close!
8181 path = req\get ':path'
8282
8383 path, facet = dir_base path
84 print "'#{path}', '#{facet}'"
8584 facet = if #facet > 0
8685 facet = '' if facet == ':'
8786 accept = req\get 'mmm-accept'
9493 res = headers.new!
9594 response_type = if status > 299 then 'text/plain'
9695 else if facet then facet.type
97 else 'text/plain'
96 else 'text/json'
9897 res\append ':status', tostring status
9998 res\append 'content-type', response_type
10099
0 import div, code, img, video, blockquote, a, span, source, iframe from require 'mmm.dom'
1 import find_fileder, link_to, embed from (require 'mmm.mmmfs.util') require 'mmm.dom'
2 import tohtml from require 'mmm.component'
3
4 -- fix JS null values
5 js_fix = if MODE == 'CLIENT'
6 (arg) ->
7 return if arg == js.null
8 arg
9
10 -- limit function to one argument
11 single = (func) -> (val) -> func val
12
13 -- load a chunk using a specific 'load'er
14 loadwith = (_load) -> (val, fileder, key) ->
15 func = assert _load val, "#{fileder}##{key}"
16 func!
17
18 -- list of converts
19 -- converts each have
20 -- * inp - input type. can capture subtypes using `(.+)`
21 -- * out - output type. can substitute subtypes from inp with %1, %2 etc.
22 -- * transform - function (val: inp, fileder) -> val: out
23 converts = {
24 {
25 inp: 'fn -> (.+)',
26 out: '%1',
27 transform: (val, fileder) -> val fileder
28 },
29 {
30 inp: 'mmm/component',
31 out: 'mmm/dom',
32 transform: single tohtml
33 },
34 {
35 inp: 'mmm/dom',
36 out: 'text/html',
37 transform: (node) -> if MODE == 'SERVER' then node else node.outerHTML
38 },
39 {
40 inp: 'text/html',
41 out: 'mmm/dom',
42 transform: if MODE == 'SERVER'
43 (html, fileder) ->
44 html = html\gsub '<mmm%-link%s+(.-)>(.-)</mmm%-link>', (attrs, text) ->
45 text = nil if #text == 0
46 path = ''
47 while attrs and attrs != ''
48 key, val, _attrs = attrs\match '^(%w+)="([^"]-)"%s*(.*)'
49 if not key
50 key, _attrs = attrs\match '^(%w+)%s*(.*)$'
51 val = true
52
53 attrs = _attrs
54
55 switch key
56 when 'path' then path = val
57 else warn "unkown attribute '#{key}=\"#{val}\"' in <mmm-link>"
58
59 link_to path, text, fileder
60
61 html = html\gsub '<mmm%-embed%s+(.-)>(.-)</mmm%-embed>', (attrs, desc) ->
62 path, facet = '', ''
63 opts = {}
64 if #desc != 0
65 opts.desc = desc
66
67 while attrs and attrs != ''
68 key, val, _attrs = attrs\match '^(%w+)="([^"]-)"%s*(.*)'
69 if not key
70 key, _attrs = attrs\match '^(%w+)%s*(.*)$'
71 val = true
72
73 attrs = _attrs
74
75 switch key
76 when 'path' then path = val
77 when 'facet' then facet = val
78 when 'nolink' then opts.nolink = true
79 when 'inline' then opts.inline = true
80 else warn "unkown attribute '#{key}=\"#{val}\"' in <mmm-embed>"
81
82 embed path, facet, fileder, opts
83
84 html
85 else
86 (html, fileder) ->
87 parent = with document\createElement 'div'
88 .innerHTML = html
89
90 -- copy to iterate safely, HTMLCollections update when nodes are GC'ed
91 embeds = \getElementsByTagName 'mmm-embed'
92 embeds = [embeds[i] for i=0, embeds.length - 1]
93 for element in *embeds
94 path = js_fix element\getAttribute 'path'
95 facet = js_fix element\getAttribute 'facet'
96 nolink = js_fix element\getAttribute 'nolink'
97 inline = js_fix element\getAttribute 'inline'
98 desc = js_fix element.innerText
99
100 element\replaceWith embed path or '', facet or '', fileder, { :nolink, :inline, :desc }
101
102 embeds = \getElementsByTagName 'mmm-link'
103 embeds = [embeds[i] for i=0, embeds.length - 1]
104 for element in *embeds
105 text = js_fix element.innerText
106 path = js_fix element\getAttribute 'path'
107
108 element\replaceWith link_to path or '', text, fileder
109
110 assert 1 == parent.childElementCount, "text/html with more than one child!"
111 parent.firstElementChild
112 },
113 {
114 inp: 'text/lua -> (.+)',
115 out: '%1',
116 transform: loadwith load or loadstring
117 },
118 {
119 inp: 'mmm/tpl -> (.+)',
120 out: '%1',
121 transform: (source, fileder) ->
122 source\gsub '{{(.-)}}', (expr) ->
123 path, facet = expr\match '^([%w%-_%./]*)%+(.*)'
124 assert path, "couldn't match TPL expression '#{expr}'"
125
126 (find_fileder path, fileder)\gett facet
127 },
128 {
129 inp: 'time/iso8601-date',
130 out: 'time/unix',
131 transform: (val) ->
132 year, _, month, day = val\match '^%s*(%d%d%d%d)(%-?)([01]%d)%2([0-3]%d)%s*$'
133 assert year, "failed to parse ISO 8601 date: '#{val}'"
134 os.time :year, :month, :day
135 },
136 {
137 inp: 'URL -> twitter/tweet',
138 out: 'mmm/dom',
139 transform: (href) ->
140 id = assert (href\match 'twitter.com/[^/]-/status/(%d*)'), "couldn't parse twitter/tweet URL: '#{href}'"
141 if MODE == 'CLIENT'
142 with parent = div!
143 window.twttr.widgets\createTweet id, parent
144 else
145 div blockquote {
146 class: 'twitter-tweet'
147 'data-lang': 'en'
148 a '(linked tweet)', :href
149 }
150 },
151 {
152 inp: 'URL -> youtube/video',
153 out: 'mmm/dom',
154 transform: (link) ->
155 id = link\match 'youtu%.be/([^/]+)'
156 id or= link\match 'youtube.com/watch.*[?&]v=([^&]+)'
157 id or= link\match 'youtube.com/[ev]/([^/]+)'
158 id or= link\match 'youtube.com/embed/([^/]+)'
159
160 assert id, "couldn't parse youtube URL: '#{link}'"
161
162 iframe {
163 width: 560
164 height: 315
165 frameborder: 0
166 allowfullscreen: true
167 frameBorder: 0
168 src: "//www.youtube.com/embed/#{id}"
169 }
170 },
171 {
172 inp: 'URL -> image/.+',
173 out: 'mmm/dom',
174 transform: (src, fileder) -> img :src
175 },
176 {
177 inp: 'URL -> video/.+',
178 out: 'mmm/dom',
179 transform: (src) ->
180 -- @TODO: add parsed MIME type
181 video (source :src), controls: true, loop: true
182 },
183 {
184 inp: 'text/plain',
185 out: 'mmm/dom',
186 transform: (val) -> span val
187 },
188 {
189 inp: 'alpha',
190 out: 'mmm/dom',
191 transform: single code
192 },
193 {
194 inp: 'URL -> .*',
195 out: 'mmm/dom',
196 transform: single code
197 },
198 }
199
200 if MODE == 'SERVER'
201 ok, moon = pcall require, 'moonscript.base'
202 if ok
203 _load = moon.load or moon.loadstring
204 table.insert converts, {
205 inp: 'text/moonscript -> (.+)',
206 out: '%1',
207 transform: loadwith moon.load or moon.loadstring
208 }
209
210 table.insert converts, {
211 inp: 'text/moonscript -> (.+)',
212 out: 'text/lua -> %1',
213 transform: single moon.to_lua
214 }
215 else
216 table.insert converts, {
217 inp: 'text/javascript -> (.+)',
218 out: '%1',
219 transform: (source) ->
220 f = js.new window.Function, source
221 f!
222 }
223
224 do
225 local markdown
226 if MODE == 'SERVER'
227 success, discount = pcall require, 'discount'
228 if not success
229 warn "NO MARKDOWN SUPPORT!", discount
230
231 markdown = success and (md) ->
232 res = assert discount.compile md, 'githubtags'
233 res.body
234 else
235 markdown = window and window.marked and window\marked
236
237 if markdown
238 table.insert converts, {
239 inp: 'text/markdown',
240 out: 'text/html',
241 transform: (md) -> "<div class=\"markdown\">#{markdown md}</div>"
242 }
243
244 table.insert converts, {
245 inp: 'text/markdown%+span',
246 out: 'mmm/dom',
247 transform: if MODE == 'SERVER'
248 (source) ->
249 html = markdown source
250 html = html\gsub '^<p', '<span'
251 html\gsub '/p>$', '/span>'
252 else
253 (source) ->
254 html = markdown source
255 html = html\gsub '^%s*<p>%s*', ''
256 html = html\gsub '%s*</p>%s*$', ''
257 with document\createElement 'span'
258 .innerHTML = html
259 }
0 require = relative ..., 1
1 converts = require '.converts'
2602
2613 count = (base, pattern='->') -> select 2, base\gsub pattern, ''
2624 escape_pattern = (inp) -> "^#{inp\gsub '([-/])', '%%%1'}$"
0 require = relative ..., 1
1 import div, code, img, video, blockquote, a, span, source, iframe from require 'mmm.dom'
2 import find_fileder, link_to, embed from (require 'mmm.mmmfs.util') require 'mmm.dom'
3 import render from require '.layout'
4 import tohtml from require 'mmm.component'
5
6 -- fix JS null values
7 js_fix = if MODE == 'CLIENT'
8 (arg) ->
9 return if arg == js.null
10 arg
11
12 -- limit function to one argument
13 single = (func) -> (val) -> func val
14
15 -- load a chunk using a specific 'load'er
16 loadwith = (_load) -> (val, fileder, key) ->
17 func = assert _load val, "#{fileder}##{key}"
18 func!
19
20 -- list of converts
21 -- converts each have
22 -- * inp - input type. can capture subtypes using `(.+)`
23 -- * out - output type. can substitute subtypes from inp with %1, %2 etc.
24 -- * transform - function (val: inp, fileder) -> val: out
25 converts = {
26 {
27 inp: 'fn -> (.+)',
28 out: '%1',
29 transform: (val, fileder) -> val fileder
30 },
31 {
32 inp: 'mmm/component',
33 out: 'mmm/dom',
34 transform: single tohtml
35 },
36 {
37 inp: 'mmm/dom',
38 out: 'text/html+frag',
39 transform: (node) -> if MODE == 'SERVER' then node else node.outerHTML
40 },
41 {
42 inp: 'text/html%+frag',
43 out: 'text/html',
44 transform: (html, fileder) -> render html, fileder
45 },
46 {
47 inp: 'text/html%+frag',
48 out: 'mmm/dom',
49 transform: if MODE == 'SERVER'
50 (html, fileder) ->
51 html = html\gsub '<mmm%-link%s+(.-)>(.-)</mmm%-link>', (attrs, text) ->
52 text = nil if #text == 0
53 path = ''
54 while attrs and attrs != ''
55 key, val, _attrs = attrs\match '^(%w+)="([^"]-)"%s*(.*)'
56 if not key
57 key, _attrs = attrs\match '^(%w+)%s*(.*)$'
58 val = true
59
60 attrs = _attrs
61
62 switch key
63 when 'path' then path = val
64 else warn "unkown attribute '#{key}=\"#{val}\"' in <mmm-link>"
65
66 link_to path, text, fileder
67
68 html = html\gsub '<mmm%-embed%s+(.-)>(.-)</mmm%-embed>', (attrs, desc) ->
69 path, facet = '', ''
70 opts = {}
71 if #desc != 0
72 opts.desc = desc
73
74 while attrs and attrs != ''
75 key, val, _attrs = attrs\match '^(%w+)="([^"]-)"%s*(.*)'
76 if not key
77 key, _attrs = attrs\match '^(%w+)%s*(.*)$'
78 val = true
79
80 attrs = _attrs
81
82 switch key
83 when 'path' then path = val
84 when 'facet' then facet = val
85 when 'nolink' then opts.nolink = true
86 when 'inline' then opts.inline = true
87 else warn "unkown attribute '#{key}=\"#{val}\"' in <mmm-embed>"
88
89 embed path, facet, fileder, opts
90
91 html
92 else
93 (html, fileder) ->
94 parent = with document\createElement 'div'
95 .innerHTML = html
96
97 -- copy to iterate safely, HTMLCollections update when nodes are GC'ed
98 embeds = \getElementsByTagName 'mmm-embed'
99 embeds = [embeds[i] for i=0, embeds.length - 1]
100 for element in *embeds
101 path = js_fix element\getAttribute 'path'
102 facet = js_fix element\getAttribute 'facet'
103 nolink = js_fix element\getAttribute 'nolink'
104 inline = js_fix element\getAttribute 'inline'
105 desc = js_fix element.innerText
106
107 element\replaceWith embed path or '', facet or '', fileder, { :nolink, :inline, :desc }
108
109 embeds = \getElementsByTagName 'mmm-link'
110 embeds = [embeds[i] for i=0, embeds.length - 1]
111 for element in *embeds
112 text = js_fix element.innerText
113 path = js_fix element\getAttribute 'path'
114
115 element\replaceWith link_to path or '', text, fileder
116
117 assert 1 == parent.childElementCount, "text/html with more than one child!"
118 parent.firstElementChild
119 },
120 {
121 inp: 'text/lua -> (.+)',
122 out: '%1',
123 transform: loadwith load or loadstring
124 },
125 {
126 inp: 'mmm/tpl -> (.+)',
127 out: '%1',
128 transform: (source, fileder) ->
129 source\gsub '{{(.-)}}', (expr) ->
130 path, facet = expr\match '^([%w%-_%./]*)%+(.*)'
131 assert path, "couldn't match TPL expression '#{expr}'"
132
133 (find_fileder path, fileder)\gett facet
134 },
135 {
136 inp: 'time/iso8601-date',
137 out: 'time/unix',
138 transform: (val) ->
139 year, _, month, day = val\match '^%s*(%d%d%d%d)(%-?)([01]%d)%2([0-3]%d)%s*$'
140 assert year, "failed to parse ISO 8601 date: '#{val}'"
141 os.time :year, :month, :day
142 },
143 {
144 inp: 'URL -> twitter/tweet',
145 out: 'mmm/dom',
146 transform: (href) ->
147 id = assert (href\match 'twitter.com/[^/]-/status/(%d*)'), "couldn't parse twitter/tweet URL: '#{href}'"
148 if MODE == 'CLIENT'
149 with parent = div!
150 window.twttr.widgets\createTweet id, parent
151 else
152 div blockquote {
153 class: 'twitter-tweet'
154 'data-lang': 'en'
155 a '(linked tweet)', :href
156 }
157 },
158 {
159 inp: 'URL -> youtube/video',
160 out: 'mmm/dom',
161 transform: (link) ->
162 id = link\match 'youtu%.be/([^/]+)'
163 id or= link\match 'youtube.com/watch.*[?&]v=([^&]+)'
164 id or= link\match 'youtube.com/[ev]/([^/]+)'
165 id or= link\match 'youtube.com/embed/([^/]+)'
166
167 assert id, "couldn't parse youtube URL: '#{link}'"
168
169 iframe {
170 width: 560
171 height: 315
172 frameborder: 0
173 allowfullscreen: true
174 frameBorder: 0
175 src: "//www.youtube.com/embed/#{id}"
176 }
177 },
178 {
179 inp: 'URL -> image/.+',
180 out: 'mmm/dom',
181 transform: (src, fileder) -> img :src
182 },
183 {
184 inp: 'URL -> video/.+',
185 out: 'mmm/dom',
186 transform: (src) ->
187 -- @TODO: add parsed MIME type
188 video (source :src), controls: true, loop: true
189 },
190 {
191 inp: 'text/plain',
192 out: 'mmm/dom',
193 transform: (val) -> span val
194 },
195 {
196 inp: 'alpha',
197 out: 'mmm/dom',
198 transform: single code
199 },
200 {
201 inp: 'URL -> .*',
202 out: 'mmm/dom',
203 transform: single code
204 },
205 }
206
207 if MODE == 'SERVER'
208 ok, moon = pcall require, 'moonscript.base'
209 if ok
210 _load = moon.load or moon.loadstring
211 table.insert converts, {
212 inp: 'text/moonscript -> (.+)',
213 out: '%1',
214 transform: loadwith moon.load or moon.loadstring
215 }
216
217 table.insert converts, {
218 inp: 'text/moonscript -> (.+)',
219 out: 'text/lua -> %1',
220 transform: single moon.to_lua
221 }
222 else
223 table.insert converts, {
224 inp: 'text/javascript -> (.+)',
225 out: '%1',
226 transform: (source) ->
227 f = js.new window.Function, source
228 f!
229 }
230
231 do
232 local markdown
233 if MODE == 'SERVER'
234 success, discount = pcall require, 'discount'
235 if not success
236 warn "NO MARKDOWN SUPPORT!", discount
237
238 markdown = success and (md) ->
239 res = assert discount.compile md, 'githubtags'
240 res.body
241 else
242 markdown = window and window.marked and window\marked
243
244 if markdown
245 table.insert converts, {
246 inp: 'text/markdown',
247 out: 'text/html+frag',
248 transform: (md) -> "<div class=\"markdown\">#{markdown md}</div>"
249 }
250
251 table.insert converts, {
252 inp: 'text/markdown%+span',
253 out: 'mmm/dom',
254 transform: if MODE == 'SERVER'
255 (source) ->
256 html = markdown source
257 html = html\gsub '^<p', '<span'
258 html\gsub '/p>$', '/span>'
259 else
260 (source) ->
261 html = markdown source
262 html = html\gsub '^%s*<p>%s*', ''
263 html = html\gsub '%s*</p>%s*$', ''
264 with document\createElement 'span'
265 .innerHTML = html
266 }
267
268 converts
0 require = relative ..., 1
1 import header, aside, footer, div, svg, script, g, circle, h1, span, b, a, img from require 'mmm.dom'
2 import navigate_to from (require 'mmm.mmmfs.util') require 'mmm.dom'
3
4 pick = (...) ->
5 num = select '#', ...
6 i = math.ceil math.random! * num
7 select i, ...
8
9 iconlink = (href, src, alt, style) -> a {
10 class: 'iconlink',
11 target: '_blank',
12 rel: 'me',
13 :href,
14 img :src, :alt, :style
15 }
16
17 logo = svg {
18 class: 'sun'
19 viewBox: '-0.75 -1 1.5 2'
20 xmlns: 'http://www.w3.org/2000/svg'
21 baseProfile: 'full'
22 version: '1.1'
23
24 g {
25 transform: 'translate(0 .18)'
26
27 g { class: 'circle out', circle r: '.6', fill: 'none', 'stroke-width': '.12' }
28 g { class: 'circle in', circle r: '.2', stroke: 'none' }
29 }
30 }
31
32 header = header {
33 div {
34 h1 {
35 logo
36 span {
37 span 'mmm', class: 'bold'
38 '&#8203;'
39 '.s&#8209;ol.nu'
40 }
41 }
42 span "fun stuff with code and wires"
43 -- pick 'fun', 'cool', 'weird', 'interesting', 'new'
44 -- pick 'stuff', 'things', 'projects', 'experiments', 'news'
45 -- "with"
46 -- pick 'mostly code', 'code and wires', 'silicon', 'electronics'
47 }
48 aside {
49 navigate_to '/about', 'about me'
50 navigate_to '/games', 'games'
51 navigate_to '/projects', 'other'
52 a {
53 href: 'mailto:s%20[removethis]%20[at]%20s-ol.nu'
54 'contact'
55 script "
56 var l = document.currentScript.parentElement;
57 l.href = l.href.replace('%20[at]%20', '@');
58 l.href = l.href.replace('%20[removethis]', '') + '?subject=Hey there :)';
59 "
60 }
61 }
62 }
63
64 footer = footer {
65 span {
66 'made with \xe2\x98\xbd by '
67 a 's-ol', href: 'https://twitter.com/S0lll0s'
68 ", #{os.date '%Y'}"
69 }
70 div {
71 class: 'icons',
72 iconlink 'https://github.com/s-ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/github.svg', 'github'
73 iconlink 'https://merveilles.town/@s_ol', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/mastodon.svg', 'mastodon'
74 iconlink 'https://twitter.com/S0lll0s', 'https://cdn.jsdelivr.net/npm/simple-icons@latest/icons/twitter.svg', 'twitter'
75 iconlink 'https://webring.xxiivv.com/#random', 'https://webring.xxiivv.com/icon.black.svg', 'webring',
76 { height: '1.3em', 'margin-left': '.3em', 'margin-top': '-0.12em' }
77 }
78 }
79
80 get_meta = =>
81 title = (@get 'title: text/plain') or @gett 'name: alpha'
82
83 l = (str) ->
84 str = str\gsub '[%s\\n]+$', ''
85 str\gsub '\\n', ' '
86 e = (str) -> string.format '%q', l str
87
88 meta = "
89 <meta charset=\"UTF-8\">
90 <title>#{l title}</title>
91 "
92
93 if page_meta = @get '_meta: mmm/dom'
94 meta ..= page_meta
95 else
96 meta ..= "
97 <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
98
99 <meta property=\"og:title\" content=#{e title} />
100 <meta property=\"og:type\" content=\"website\" />
101 <meta property=\"og:url\" content=\"https://mmm.s-ol.nu#{@path}/\" />
102 <meta property=\"og:site_name\" content=\"mmm\" />"
103
104 if desc = @get 'description: text/plain'
105 meta ..= "
106 <meta property=\"og:description\" content=#{e desc} />"
107
108 meta
109
110 render = (content, fileder) ->
111 buf = [[
112 <!DOCTYPE html>
113 <html>
114 <head>
115 <link rel="stylesheet" type="text/css" href="/main.css" />
116 <!--
117 <link rel="preload" as="fetch" href="/mmm/dom/init.lua" />
118 <link rel="preload" as="fetch" href="/mmm/component/init.lua" />
119 <link rel="preload" as="fetch" href="/mmm/mmmfs/init.lua" />
120 <link rel="preload" as="fetch" href="/mmm/mmmfs/fileder.lua" />
121 <link rel="preload" as="fetch" href="/mmm/mmmfs/browser.lua" />
122 -->
123
124 <link href="https://fonts.googleapis.com/css?family=Source+Sans+Pro:200,400" rel="stylesheet">
125 ]]
126 buf ..= "
127 #{get_meta fileder}
128 </head>
129 <body>
130 #{header}
131
132 #{content}
133
134 #{footer}
135 "
136 buf ..= [[
137 <script src="/highlight.pack.js"></script>
138 <script src="//cdnjs.cloudflare.com/ajax/libs/marked/0.5.1/marked.min.js"></script>
139 <script src="//cdnjs.cloudflare.com/ajax/libs/svg.js/2.6.6/svg.min.js"></script>
140 <script src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
141 <script src="/fengari-web.js"></script>
142 <script type="application/lua" src="/mmm.bundle.lua"></script>
143 <script type="application/lua">require 'mmm'</script>
144 ]]
145 buf ..= "
146 <script type=\"application/lua\">
147 on_load = on_load or {}
148 table.insert(on_load, function()
149 local path = #{string.format '%q', path}
150 local browser = require 'mmm.mmmfs.browser'
151 local root = dofile '/$bundle.lua'
152 root:mount('', true)
153
154 BROWSER = browser.Browser(root, path, true)
155 end)
156 </script>
157 </body>
158 </html>
159 "
160
161 buf
162
163 {
164 :render
165 }