programextensions/web/demos/todomvc.xtl
TodoMVC on the web: the todos live in SQLite, the X_eTaL program is the server -- it routes each request, keeps the filter, counts what is left, and writes the page; forms post and are redirected back. Run: just demo web todomvc, then open http://127.0.0.1:8470/ (the list is kept in work/todos.db; Quit ends the program)
made : Int
made ← db sq:e̲xec "create table if not exists todos (id integer primary key, title text not null, done integer not null default 0)"
items : Char
each todo as a list item, written by SQL (the title escaped there)
items ← "select coalesce(group_concat('<li class=' || case when done then 'done' else 'open' end || '><form method=post action=/toggle><input type=hidden name=id value=' || id || '><button class=tick title=toggle>' || case when done then '☑' else '☐' end || '</button></form><span>' || replace(replace(replace(title, '&', '&'), '<', '<'), '>', '>') || '</span><form method=post action=/delete><input type=hidden name=id value=' || id || '><button class=del title=delete>×</button></form></li>', ''), '') from (select * from todos"
Used in: ᵘp̲age
ᵘf̲ilter : Char -> Char
the filter a request asks for (?show=active or completed), as SQL
ᵘf̲ilter ← { s → s m̲atch "active" ? " where done = 0" s m̲atch "completed" ? " where done = 1" "" }
Used in: ᵘp̲age
ᵘl̲eft : Num a => a -> Char
"1 item left", "3 items left": counted in X_eTaL from the done column
ᵘl̲eft ← { d → n ← '+ r̲/ 0 = r̲avel d n = 1 ? "1 item left" (f̲ormat n) c̲at " items left" }
Used in: ᵘp̲age
ᵘo̲n : Char -> Char -> Char
a filter link, marked when chosen
ᵘo̲n ← { s name → s m̲atch name ? " class=on" ((0 = t̲ally s) ∧ name m̲atch "all") ? " class=on" "" }
Used in: ᵘl̲ink
ᵘl̲ink : Char -> Char -> Char
ᵘl̲ink ← { s name → "<a href='/?show=" c̲at name c̲at "'" c̲at (s ᵘo̲n name) c̲at ">" c̲at name c̲at "</a>" }
Used in: ᵘl̲inks
ᵘl̲inks : Char -> Char
ᵘl̲inks ← { s → (s ᵘl̲ink "all") c̲at " " c̲at (s ᵘl̲ink "active") c̲at " " c̲at s ᵘl̲ink "completed" }
Used in: ᵘp̲age
style : Char
style ← "<style>body{background:#f5f5f5;font:15px system-ui,sans-serif;color:#333}main{max-width:520px;margin:2em auto}h1{color:#c0392b;font-weight:200;font-size:56px;text-align:center;margin:.2em}.box{background:#fff;box-shadow:0 2px 6px #0003}form.add input{width:100%;box-sizing:border-box;font-size:20px;padding:.6em;border:0;border-bottom:1px solid #eee}ul{list-style:none;margin:0;padding:0}li{display:flex;align-items:center;border-bottom:1px solid #eee;padding:.3em .5em}li span{flex:1;font-size:18px;padding:.3em}li.done span{text-decoration:line-through;color:#aaa}li form{margin:0}button{border:0;background:none;font-size:20px;cursor:pointer}.del{color:#c0392b}footer{display:flex;justify-content:space-between;padding:.6em;font-size:13px;color:#777}footer a{margin:0 .3em;color:#777}a.on{color:#c0392b;font-weight:bold}p.note{text-align:center;color:#999;font-size:12px}</style>"
Used in: ᵘp̲age
ᵘp̲age : Char -> Char
the whole page
ᵘp̲age ← { s → done ← db sq:n̲ums "select done from todos" list ← r̲avel db sq:t̲exts items c̲at (ᵘf̲ilter s) c̲at " order by id)" "<!doctype html><html><head><meta charset='utf-8'><title>todos -- X_eTaL on the web</title>" c̲at style c̲at "</head><body><main><h1>todos</h1><div class=box><form class=add method=post action=/add><input name=title placeholder='What needs to be done?' autofocus autocomplete=off></form><ul>" c̲at list c̲at "</ul><footer><span>" c̲at (ᵘl̲eft done) c̲at "</span><span>" c̲at (ᵘl̲inks s) c̲at "</span><form method=post action=/clear><button style='font-size:13px;color:#777'>Clear completed</button></form></footer></div><p class=note>Served by an X_eTaL program; kept in SQLite. <a href='/quit'>Quit</a></p></main></body></html>" }
Used in: ᵘa̲nswer
ᵘb̲ack : Any a => a -> Int
a change to the list, then back to the page (303 See Other)
ᵘb̲ack ← { n → h ← ʷᵇh̲eader! "Location: /" 303 ʷᵇr̲eply! "" }
Used in: ᵘa̲nswer
ᵘa̲dd : Char -> Int
a new todo, unless its title is empty
ᵘa̲dd ← { t → 0 = t̲ally t ? 0 db sq:e̲xec "insert into todos (title) values (" c̲at (sq:q̲uote t) c̲at ")" }
Used in: ᵘa̲nswer
ᵘa̲nswer : Num a => a -> a
one request; 0 once quit
ᵘa̲nswer ← { k → 0 = k ? 0 r ← ʷᵇn̲ext! 60 r m̲atch "none" ? k r m̲atch "GET /" ? (200 ʷᵇr̲eply! ᵘp̲age ʷᵇp̲aram "show") r̲ight k r m̲atch "POST /add" ? (ᵘb̲ack ᵘa̲dd ʷᵇp̲aram "title") r̲ight k r m̲atch "POST /toggle" ? (ᵘb̲ack db sq:e̲xec "update todos set done = 1 - done where id = " c̲at sq:q̲uote ʷᵇp̲aram "id") r̲ight k r m̲atch "POST /delete" ? (ᵘb̲ack db sq:e̲xec "delete from todos where id = " c̲at sq:q̲uote ʷᵇp̲aram "id") r̲ight k r m̲atch "POST /clear" ? (ᵘb̲ack db sq:e̲xec "delete from todos where done = 1") r̲ight k r m̲atch "GET /quit" ? (200 ʷᵇr̲eply! "bye") r̲ight 0 (404 ʷᵇr̲eply! "no such page: " c̲at ʷᵇp̲ath @) r̲ight k }
Used in: last