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)

source · imports wb: extensions/web/lib/Web.xtl; sq: extensions/sqlite/lib/Sqlite.xtl

db : Char

value · line 10
db ← "work/todos.db"

made : Int

value · line 11
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

value · line 14

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 '&#x2611;' else '&#x2610;' end || '</button></form><span>' || replace(replace(replace(title, '&', '&amp;'), '<', '&lt;'), '>', '&gt;') || '</span><form method=post action=/delete><input type=hidden name=id value=' || id || '><button class=del title=delete>&#xd7;</button></form></li>', ''), '') from (select * from todos"
Used in: ᵘp̲age

ᵘf̲ilter : Char -> Char

function · line 17

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

function · line 24

"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

function · line 31

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

function · line 36
ᵘ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

function · line 37
ᵘ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

value · line 39
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

function · line 42

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

function · line 49

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

function · line 55

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

function · line 61

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

port : Int

value · line 74
port ← ʷᵇs̲erve! 8470

last : Int

value · line 76
last ← 100000 'ᵘa̲nswer p̲ower 1

port : Int

value · line 77
port ← ʷᵇs̲top! @