diff --git a/ape/talk.hoon b/ape/talk.hoon index 25994856c..8d4e451dd 100644 --- a/ape/talk.hoon +++ b/ape/talk.hoon @@ -1677,6 +1677,9 @@ :: %app "[{(trip p.sep)}]: {(trip q.sep)}" + :: + %tax + " {}: {}" == -- :: @@ -1755,12 +1758,23 @@ =^ moz +>.$ ra-abet:(~(ra-cancel ra ost.hid ~) src.hid pax) [moz +>.$(shells (~(del by shells) ost.hid))] :: -::++ poke-bit -:: |= [~] -:: ^- (quip move +>) -:: :_ +>.$ -:: =+ paf=/(scot %p our.hid)/try/(scot %da now.hid)/talk/backlog/jam -:: [ost.hid %info /jamfile our.hid (foal paf (jam +<+.+>.$))]~ +++ poke-save + |= man=span + ^- (quip move +>) + :_ +>.$ + =+ paf=/(scot %p our.hid)/home/(scot %da now.hid)/talk/[man]/jam + =+ grams:(~(got by stories) man) + [ost.hid %info /jamfile our.hid (foal paf [%jam !>((jam -))])]~ +:: +++ poke-load + |= man=span + =+ ^= grams + %- (hard (list telegram)) + %- cue + %- (hard ,@) + .^(%cx /(scot %p our.hid)/home/(scot %da now.hid)/talk/[man]/jam) + =+ toy=(~(got by stories) man) + [~ +>.$(stories (~(put by stories) man toy(grams grams, count (lent grams))))] :: ++ prep |= [old=(unit house)] diff --git a/ape/work.hoon b/ape/work.hoon new file mode 100644 index 000000000..9ded76b68 --- /dev/null +++ b/ape/work.hoon @@ -0,0 +1,328 @@ +:: +:::: + :: +/? 314 +/- *work +/+ talk +!: +:::: + :: +|% +++ move (pair bone card) :: all actions +++ card :: general card + $% [%diff %work-report client] :: + [%peer wire dock path] :: + [%poke wire dock pear] :: + == :: +++ pear :: poke fruit + $% [%talk-command command:talk] :: + == :: +-- +!: +:::: + :: +|_ $: bowl + client + connected=_| + unordered=(map ,[@uvH @u] (pair ship flesh:work-stuff:talk)) + == +++ at + |= client-task + =| moves=(list move) + |% + ++ abet + ^- [(list move) _+>.$] + [(flop moves) +>.$(tasks (~(put by tasks) id.tax +<.$))] + :: + ++ abut + ^- [(list move) _+>.$] + [(flop moves) +>.$] + :: + ++ send-audience + |= [to=(set station:talk) action=duty:work-stuff:talk] + ^+ +> + %_ +>.$ + eny (sham eny action) + moves + :_ moves + ^- move + :* ost %poke + /sending/(scot %uv id.tax)/(scot %ud version.tax) + [our %talk] + %talk-command + ^- command:talk + :- %publish + |- ^- (list thought) + :_ ~ + :+ (shaf %task eny) + %- mo ^- (list ,[partner envelope delivery]:talk) + %+ turn (~(tap in to)) + |=(sat=station:talk [[%& sat] [*envelope %pending]]) + [now *bouquet [%tax action]] + == + == + :: + ++ send + |= action=duty:work-stuff:talk + (send-audience audience action) + :: + ++ send-archive + |= to=(set station:talk) + (send-audience to %archive id.tax) + :: + ++ send-create (send %create tax) + ++ send-change |* * + %+ send-audience + [[creator.tax (main creator.tax)] ~ ~] + [%change id.tax +<] + ++ send-update |*(* (send %update id.tax +<)) + ++ process-update + |= up=update + ^+ +> + ?- -.up + %add ?>(?=(%comment +<.up) (send-change %add-comment +>.up)) + %doer + ?- +<.up + %release (send-change %set-doer ~) + %claim (send-change %set-doer `our) + == + :: + %set + ?- +<.up + %audience (process-audience to.up) + %date-due (send-change %set-date-due +>.up) + %title (send-change %set-title +>.up) + %description (send-change %set-description +>.up) + %tags (send-change %set-tags +>.up) + %done (send-change %set-done +>.up) + == + == + ++ process-audience + |= to=(set station:talk) + ^+ +> + =. +>.$ (send-archive (~(dif in audience) to)) + =. +>.$ (send-audience (~(dif in to) audience) %create tax) + +>.$(audience to) + -- +:: +++ prep + |= $= old + $_ + =< $ + %- unit + $: client + _| + (map ,[@uvH @u] (pair ship flesh:work-stuff:talk)) + == + ^- [(list move) _+>.$] + initialize(+<+ ?~(old +<+.+>.$ u.old)) +:: +++ initialize + ^- [(list move) _.] + ?: connected + [~ .] + :_ .(connected %&) :_ ~ + [ost %peer /peering [our %talk] /f/(main our)/0] +:: +++ process-duty + |= [when=@da her=ship from=(set station:talk) action=duty:work-stuff:talk] + ^- [(list move) _+>.$] + =- =^ mof con mirror-to-web:con + [(welp mof mos) con] + ^- [mos=(list move) con=_+>.$] + ?- -.action + %create + =+ existing-task=(~(get by tasks) id.tax.action) + ?: ?& ?=(^ existing-task) + !=(tax.action tax.u.existing-task) + !archived.u.existing-task + == + ~& :* %new-task-with-old-id + her=her + from=from + new-task=tax.action + existing-task=u.existing-task + == + [~ +>.$] + ?. =(her creator.tax.action) + ~& :* %created-with-bad-creator + her=her + from=from + new-task=tax.action + existing-task=existing-task + == + [~ +>.$] + =. tasks + %^ ~(put by tasks) id.tax.action | + :_ tax.action + ?~ existing-task from + (~(uni in audience.u.existing-task) from) + =. sort ?^(existing-task sort [id.tax.action sort]) + [~ +>.$] + :: + %archive + =+ tax=(~(get by tasks) id.action) + ?~ tax + ~& :* %archive-for-nonexistent-task + her=her + from=from + action=action + == + [~ +>.$] + ?: !=(her creator.tax.u.tax) + ~& :* %archiver-not-creator + her=her + from=from + action=action + tax=tax + == + [~ +>.$] + =. tasks + %+ ~(put by tasks) id.action + :* =(~ (~(dif in audience.u.tax) from)) + (~(dif in audience.u.tax) from) + tax.u.tax + == + [~ +>.$] + :: + %change + =+ tax=(~(get by tasks) id.action) + ?~ tax + ~& :* %change-for-nonexistent-task + her=her + from=from + action=action + == + [~ +>.$] + ?: !=(our creator.tax.u.tax) + ~& :* %me-not-creator + her=her + from=from + action=action + tax=tax + == + [~ +>.$] + abet:(send-update:(at u.tax) +(version.tax.u.tax) her meat.action) + :: + %update + =+ tax=(~(get by tasks) id.action) + ?~ tax + ~& :* %update-for-nonexistent-task + her=her + from=from + action=action + == + [~ +>.$] + ?: !=(her creator.tax.u.tax) + ~& :* %her-not-creator + her=her + from=from + action=action + tax=tax + == + [~ +>.$] + ?. =(version.action +(version.tax.u.tax)) + ~& :* %update-bad-version + her + from=from + action=action + tax=tax + == + :- ~ + %_ +>.$ + unordered + %+ ~(put by unordered) + [id.action version.action] + [her.action meat.action] + == + |- + =. tasks + %+ ~(put by tasks) id.action + :+ archived.u.tax + (~(uni in audience.u.tax) from) + =. version.tax.u.tax version.action + =. date-modified.tax.u.tax when + ?- -.meat.action + %set-doer tax.u.tax(doer her.meat.action) + %set-date-due tax.u.tax(date-due wen.meat.action) + %set-tags tax.u.tax(tags tag.meat.action) + %set-title tax.u.tax(title til.meat.action) + %set-description tax.u.tax(description des.meat.action) + %set-done tax.u.tax(done ?.(don.meat.action ~ `when)) + %add-comment + %= tax.u.tax + discussion [[when her com.meat.action] discussion.tax.u.tax] + == + == + =+ ooo=(~(get by unordered) id.action +(version.action)) + ?~ ooo + [~ +>.^$] + %= $ + version.action +(version.action) + her.action p.u.ooo + meat.action q.u.ooo + == + == +:: +++ mirror-to-web + ^- [(list move) _.] + ~& [%mirroring sort=(turn sort |=(a=@uv `@uv`(rsh 2 25 a)))] + :_ . + %+ murn (~(tap by sup)) + |= [ust=bone her=ship pax=path] + ^- (unit move) + ?:(?=([%sole *] pax) ~ `[ust full-report]) +:: +++ full-report [%diff %work-report tasks sort] +++ peer-repo |=(path [[ost full-report]~ +>.$]) +++ coup + |= [way=wire saw=(unit tang)] + ^- [(list move) _+>.$] + ?> ?=(~ saw) + [~ +>.$] +:: +++ reap-peering + |= [way=wire saw=(unit tang)] + ^- [(list move) _+>.$] + ?> ?=([~ ~] +<) + [~ +>.$] +:: +++ poke-work-command + |= cod=command + ?. =(our src) + ~|([%wrong-user our=our src=src] !!) + ?- -.cod + %sort mirror-to-web(sort p.cod) + %old + =^ mow +>.$ + =+ (at (~(got by tasks) id.cod)) + abet:(process-update:- dif.cod) + =^ mov +>.$ mirror-to-web + [(welp mov mow) +>.$] + %new + =. +>.cod +>.cod(date-created now, version 0, date-modified now) + abut:send-create:(at | +.cod) + == +:: +:: XX maybe need to check that we haven't received this message before +:: by keeping a counter of last message received +++ diff-talk-report + |= [way=wire rep=report:talk] + ^- [(list move) _+>.$] + ?> ?=(%grams -.rep) + |- ^- [(list move) _+>.^$] + ?~ q.rep [~ +>.^$] + =* her p.i.q.rep + =* when p.r.q.i.q.rep + =* said r.r.q.i.q.rep + =+ ^- from=(set station:talk) + %- sa ^- (list station:talk) + %+ murn (~(tap by q.q.i.q.rep)) + |= [par=partner *] + `(unit station:talk)`?.(?=(%& -.par) ~ `p.par) + ?. ?=(%tax -.said) + $(p.rep +(p.rep), q.rep t.q.rep) + =^ mos +>.^$ (process-duty when her from +.said) + =^ mof +>.^$ $(p.rep +(p.rep), q.rep t.q.rep) + [(weld mos mof) +>.^$] +-- diff --git a/arvo/clay.hoon b/arvo/clay.hoon index 5b477dc29..555696ac0 100644 --- a/arvo/clay.hoon +++ b/arvo/clay.hoon @@ -86,8 +86,8 @@ == :: ++ rede :: universal project $: lim=@da :: complete to - qyx=cult :: subscribers ref=(unit rind) :: outgoing requests + qyx=cult :: subscribers dom=dome :: revision state dok=(unit dork) :: outstanding diffs mer=(map (pair ship desk) mery) :: outstanding merges @@ -130,9 +130,32 @@ :: |% ++ de :: per desk - |= [now=@da hen=duct hun=duct hez=(unit duct)] - |= [[who=@p for=@p] syd=@ta rede ran=rang mon=(map term beam)] - =* red +<+>- + |= [now=@da hen=duct raft] + |= [[our=@p her=@p] syd=desk] + =* ruf +>+<+> + =+ ^- [hun=(unit duct) rede] + =+ rom=(~(get by fat.ruf) her) + ?~ rom + :- ~ + %+ fall + (~(get by rus:(fall (~(get by hoy.ruf) her) *rung)) syd) + :* lim=~2000.1.1 + ref=[~ *rind] + qyx=~ + dom=*dome + dok=~ + mer=~ + == + :- `hun.u.rom + =+ jod=(fall (~(get by dos.u.rom) syd) *dojo) + :* lim=now + ref=~ + qyx=qyx.jod + dom=dom.jod + dok=dok.jod + mer=mer.jod + == + =* red -> =| yel=(list ,[p=duct q=gift]) =| byn=(list ,[p=duct q=riot]) =| reg=(list ,[p=duct q=gift]) @@ -140,11 +163,16 @@ =| tag=(list move) |% ++ abet - ^- [(list move) rede] - :_ red + ^- [(list move) raft] + :_ =+ rom=(~(get by fat.ruf) her) + ?~ rom + =+ rug=(~(put by rus:(fall (~(get by hoy.ruf) her) *rung)) syd red) + ruf(hoy (~(put by hoy.ruf) her rug)) + =+ dos=(~(put by dos.u.rom) syd [qyx dom dok mer]) + ruf(fat (~(put by fat.ruf) her [(need hun) dos])) ;: weld %+ turn (flop yel) - |=([a=duct b=gift] [hun %give b]) + |=([a=duct b=gift] [(need hun) %give b]) :: %+ turn (flop reg) |=([a=duct b=gift] [a %give b]) @@ -155,7 +183,7 @@ %+ turn (flop say) |= [a=duct b=path c=ship d=[p=@ud q=riff]] :- a - [%pass b %a %wont [who c] [%c %question p.q.d (scot %ud p.d) ~] q.d] + [%pass b %a %wont [our c] [%c %question p.q.d (scot %ud p.d) ~] q.d] :: tag == @@ -192,7 +220,7 @@ %tabl (ford-fail >%strange-gage< ~) %& (some [p.key p.val]) %| =. p.val [(sell q.p.key) p.val] - ~> %slog.[0 %*(. >%ford-fail syd %for %why< |2.+> p.val)] + ~> %slog.[0 %*(. >%ford-fail syd %her %why< |2.+> p.val)] ~ == :: @@ -247,7 +275,7 @@ +>.$(byn [[hen ~ [p.mun q.mun syd] r.mun p.dat] byn]) =- +>.$(tag [- tag]) :* hen %pass [%blab p.mun (scot q.mun) syd r.mun] - %f %exec who ~ [who syd q.mun] (lobe-to-silk:ze r.mun p.dat) + %f %exec our ~ [our syd q.mun] (lobe-to-silk:ze r.mun p.dat) == :: ++ bleb :: ship sequence @@ -276,7 +304,7 @@ [%many %| [%ud let.dom] `case`q.p.rav r.p.rav] =+ inx=nix.u.ref %= +>+.$ - say [[hen [(scot %ud inx) ~] for [inx syd ~ vaw]] say] + say [[hen [(scot %ud inx) ~] her [inx syd ~ vaw]] say] nix.u.ref +(nix.u.ref) bom.u.ref (~(put by bom.u.ref) inx [hen vaw]) fod.u.ref (~(put by fod.u.ref) hen inx) @@ -292,7 +320,7 @@ =- ?~(- ~ `[nam (lent s.bem) (sa -)]) %+ skim can |= pax=path - &(=(p.bem for) =(q.bem syd) =((flop s.bem) (scag (lent s.bem) pax))) + &(=(p.bem her) =(q.bem syd) =((flop s.bem) (scag (lent s.bem) pax))) :: ++ mont |= [pot=term pax=path] @@ -304,8 +332,8 @@ %_ +>.$ tag :_ tag - :* hen %pass [%ergoing (scot %p who) syd ~] %f - %exec who ~ [who syd %da now] %tabl + :* hen %pass [%ergoing (scot %p our) syd ~] %f + %exec our ~ [our syd %da now] %tabl ^- (list (pair silk silk)) %+ turn `(list path)`mus |= a=path @@ -331,7 +359,7 @@ =+ nux=(~(get by fod.u.ref) hen) ?~ nux +.$ %= +.$ - say [[hen [(scot %ud u.nux) ~] for [u.nux syd ~]] say] + say [[hen [(scot %ud u.nux) ~] her [u.nux syd ~]] say] fod.u.ref (~(del by fod.u.ref) hen) bom.u.ref (~(del by bom.u.ref) u.nux) == @@ -363,7 +391,7 @@ ~& [%oh-no rave=rav aeon=yon letdom=let.dom] +>.^$ ?~ u.var - (blab hen p.rav %& %null [%atom %n] ~) :: only for %x + (blab hen p.rav %& %null [%atom %n] ~) :: only her %x ?: (equivalent-data:ze u.u.ver u.u.var) $(yon +(yon)) (blab hen p.rav u.u.var) @@ -401,7 +429,7 @@ ^+ +> %_ +>.$ yel - =+ pre=`path`~[(scot %p for) syd (scot %ud let.dom)] + =+ pre=`path`~[(scot %p her) syd (scot %ud let.dom)] ?- -.lem | :_ yel [hen %note '=' %leaf :(weld (trip p.lem) " " (spud pre))] @@ -506,8 +534,8 @@ == ^- (list move) :~ :* hen %pass - [%inserting (scot %p who) syd (scot %da wen) ~] - %f %exec who ~ [who syd %da wen] %tabl + [%inserting (scot %p our) syd (scot %da wen) ~] + %f %exec our ~ [our syd %da wen] %tabl ^- (list (pair silk silk)) %+ turn ins |= [pax=path mis=miso] @@ -517,8 +545,8 @@ [%cast - [%$ p.mis]] == :* hen %pass - [%diffing (scot %p who) syd (scot %da wen) ~] - %f %exec who ~ [who syd %da wen] %tabl + [%diffing (scot %p our) syd (scot %da wen) ~] + %f %exec our ~ [our syd %da wen] %tabl ^- (list (pair silk silk)) %+ turn dif |= [pax=path mis=miso] @@ -529,8 +557,8 @@ [%pact [%$ p.-] [%$ p.mis]] == :* hen %pass - [%castifying (scot %p who) syd (scot %da wen) ~] - %f %exec who ~ [who syd %da wen] %tabl + [%castifying (scot %p our) syd (scot %da wen) ~] + %f %exec our ~ [our syd %da wen] %tabl ^- (list (pair silk silk)) %+ turn mut |= [pax=path mis=miso] @@ -548,8 +576,8 @@ :- hen ?+ -.mis !! %mut - :- [%diffing (scot %p who) syd (scot %da wen) pax] - :^ %f %exec who :+ ~ [who syd %da wen] + :- [%diffing (scot %p our) syd (scot %da wen) pax] + :^ %f %exec our :+ ~ [our syd %da wen] ^- silk :+ %diff (lobe-to-silk:ze pax (~(got by q:(aeon-to-yaki:ze let.dom)) pax)) @@ -558,8 +586,8 @@ [%cast - [%$ p.mis]] :: %ins - :- [%casting (scot %p who) syd (scot %da wen) pax] - :^ %f %exec who :+ ~ [who syd %da wen] + :- [%casting (scot %p our) syd (scot %da wen) pax] + :^ %f %exec our :+ ~ [our syd %da wen] ^- silk =+ (slag (dec (lent pax)) pax) =+ ?~(- %$ i.-) @@ -660,8 +688,8 @@ =- %_(+>.$ tag [- tag]) ^- move :* hen %pass - [%mutating (scot %p who) syd (scot %da wen) ~] - %f %exec who ~ [who syd %da wen] %tabl + [%mutating (scot %p our) syd (scot %da wen) ~] + %f %exec our ~ [our syd %da wen] %tabl ^- (list (pair silk silk)) %+ turn cat |= [pax=path cay=cage] @@ -763,8 +791,8 @@ dok ~ tag :_ tag - :* hen %pass [%ergoing (scot %p who) syd ~] %f - %exec who ~ [who syd %da now] %tabl + :* hen %pass [%ergoing (scot %p our) syd ~] %f + %exec our ~ [our syd %da now] %tabl ^- (list (pair silk silk)) %+ turn (~(tap in sum)) |= a=path @@ -821,8 +849,8 @@ %_ +>.$ tag :_ tag - :* hen %pass [%patching (scot %p who) syd ~] %f - %exec who :^ ~ [who syd %da now] %tabl + :* hen %pass [%patching (scot %p our) syd ~] %f + %exec our :^ ~ [our syd %da now] %tabl ^- (list (pair silk silk)) %+ turn (~(tap by hat)) |= [a=path b=lobe] @@ -840,6 +868,7 @@ bar=(set blob) :: new content == ^+ +> + =< wake =+ ^- nut=(map tako yaki) %- mo ^- (list (pair tako yaki)) %+ turn (~(tap in lar)) @@ -893,7 +922,7 @@ (edit wen lem) :: ++ exem :: execute merge - |= [wen=@da her=@p sud=@tas gem=germ] :: aka direct change + |= [wen=@da him=@p sud=@tas gem=germ] :: aka direct change !! :: ?. (gte p.mer let.dom) !! :: no :: =. +>.$ %= +>.$ @@ -998,8 +1027,8 @@ ~| [%x-over-network-not-implemented [p q -.r]:rut hen] !! :: =- %_(+>.$ tag [- tag]) :: :* hen - :: [%foreign-plops (scot %p who) (scot %p for) syd ~] - :: %f %exec who ~ %tabl + :: [%foreign-plops (scot %p our) (scot %p her) syd ~] + :: %f %exec our ~ %tabl :: ^- (list (pair silk silk)) :: %+ turn (~(tap in pop)) :: |= a=plop @@ -1021,8 +1050,8 @@ ^+ +> =- %_(+>.$ tag [- tag]) :* hen %pass - [%foreign-x (scot %p who) (scot %p for) syd car (scot cas) pax] - %f %exec who ~ [for syd cas] + [%foreign-x (scot %p our) (scot %p her) syd car (scot cas) pax] + %f %exec our ~ [her syd cas] [%vale peg] == :: @@ -1044,8 +1073,8 @@ =- %_(+>.$ tag [- tag]) =+ lum=(scot %da (fall lem *@da)) :* hen %pass - [%foreign-plops (scot %p who) (scot %p for) syd lum ~] - %f %exec who ~ [for syd cas] %tabl + [%foreign-plops (scot %p our) (scot %p her) syd lum ~] + %f %exec our ~ [her syd cas] %tabl ^- (list (pair silk silk)) %+ turn (~(tap in pop)) |= a=plop @@ -1298,7 +1327,7 @@ (~(del by bar) pax) %dif :: mutate, must exist =+ ber=(~(get by bar) pax) :: XX typed - =+ for==>((flop pax) ?~(. %$ i)) + =+ her==>((flop pax) ?~(. %$ i)) ?~ ber =+ har=(~(get by hat) pax) ?~ har !! @@ -1611,7 +1640,7 @@ ++ read-at-aeon :: read-at-aeon:ze |= [yon=aeon mun=mood] :: seek and read ^- (unit (unit (each cage lobe))) - ?: &(?=(%w p.mun) !?=(%ud -.q.mun)) :: NB only for speed + ?: &(?=(%w p.mun) !?=(%ud -.q.mun)) :: NB only her speed ?^(r.mun [~ ~] [~ ~ %& %aeon !>(yon)]) ?: ?=(%u p.mun) (read-u yon r.mun) @@ -1672,7 +1701,7 @@ :: ++ me :: merge ali into bob |= [ali=(pair ship desk) alh=(unit ankh)] :: from - =+ bob=`(pair ship desk)`[who syd] :: to + =+ bob=`(pair ship desk)`[our syd] :: to =+ dat=(fall (~(get by mer) ali) *mery) :: merge data =| don=? :: keep going |% @@ -2014,11 +2043,11 @@ ^- (pair silk silk) =+ cal=(~(got by can.dal.dat) pax) =+ cob=(~(got by can.dob.dat) pax) - =+ ^= for + =+ ^= her =+ (slag (dec (lent pax)) pax) ?~(- %$ i.-) :- [%$ %path !>(pax)] - [%join for [%$ cal] [%$ cob]] + [%join her [%$ cal] [%$ cob]] == == :: @@ -2188,6 +2217,7 @@ =. hit.dom (~(put by hit.dom) let.dom r.new.dat) =. ank.dat (checkout-ankh:ze (~(uni by bop.dat) p.can)) =. ank.dom ank.dat + => .(..wake wake) ?~ hez done:he =+ mus=(must-ergo (turn (~(tap by erg.dat)) head)) ?: =(~ mus) done:he @@ -2323,59 +2353,6 @@ -- -- -- -:: -++ do - |= [now=@da hen=duct [who=ship him=ship] syd=@tas ruf=raft] - =+ ^= rug ^- rung - =+ rug=(~(get by hoy.ruf) him) - ?^(rug u.rug *rung) - =+ ^= red ^- rede - =+ yit=(~(get by rus.rug) syd) - ?^(yit u.yit `rede`[~2000.1.1 ~ [~ *rind] *dome ~ ~]) - ((de now hen ~ hez.ruf) [who him] syd red ran.ruf mon.ruf) -:: -++ posh - |= [him=ship syd=desk red=rede ruf=raft] - ^- raft - =+ ^= rug ^- rung - =+ rug=(~(get by hoy.ruf) him) - ?^(rug u.rug *rung) - ruf(hoy (~(put by hoy.ruf) him rug(rus (~(put by rus.rug) syd red)))) -:: -++ un :: domestic ship - |= [who=@p now=@da hen=duct ruf=raft] - =+ ^= yar ^- room - =+ yar=(~(get by fat.ruf) who) - ?~(yar *room u.yar) - |% - ++ abet ruf(fat (~(put by fat.ruf) who yar)) - ++ pish - |= [syd=@ta red=rede run=rang] - %_ +> - ran.ruf run - dos.yar - (~(put by dos.yar) syd [qyx.red dom.red dok.red mer.red]) - == - :: - ++ wake - =+ saz=(turn (~(tap by dos.yar) ~) |=([a=@tas b=*] a)) - =| moz=(list move) - =< [moz ..wake] - |- ^+ + - ?~ saz + - =+ sog=abet:wake:(di i.saz) - $(saz t.saz, moz (weld moz -.sog), ..wake (pish i.saz +.sog ran.ruf)) - :: - ++ di - |= syd=@ta - =+ ^= saq ^- dojo - =+ saq=(~(get by dos.yar) syd) - ?~(saq *dojo u.saq) - %^ (de now hen hun.yar hez.ruf) - [who who] - syd - [[now qyx.saq ~ dom.saq dok.saq mer.saq] ran.ruf mon.ruf] - -- -- :::::::::::::::::::::::::::::::::::::::::::::::::::::::: :: section 4cA, filesystem vane :: @@ -2411,11 +2388,15 @@ [hen %give %hill (turn (~(tap by mon.ruf)) head)]~ :: %init - :_ ..^$(fat.ruf (~(put by fat.ruf) p.q.hic [hen ~])) + :_ %_ ..^$ + fat.ruf + ?< (~(has by fat.ruf) p.q.hic) + (~(put by fat.ruf) p.q.hic [-(hun hen)]:[*room .]) + == =+ [bos=(sein p.q.hic) can=(clan p.q.hic)] %- zing ^- (list (list move)) :~ ?: =(bos p.q.hic) ~ - [hen %pass / %c %merg p.q.hic %base bos %kids %init]~ + [hen %pass /init-merge %c %merg p.q.hic %base bos %kids %init]~ :: ~ == @@ -2424,28 +2405,8 @@ ?: =(%$ q.q.hic) [~ ..^$] =^ mos ruf - =+ une=(un p.q.hic now hen ruf) - =+ ^= zat - (exec:(di:une q.q.hic) now r.q.hic) - =+ zot=abet.zat - :- -.zot - =. une (pish:une q.q.hic +.zot ran.zat) - abet:une - :: ~& :+ %infoed - :: len=(lent mos) - :: %+ turn mos - :: |= move - :: ^- [duct (unit path) term term] - :: :- p - :: ^- [(unit path) term term] - :: ?+ -.q [~ *term *term] - :: %pass - :: ^- [(unit path) term term] - :: [`p.q [- +<]:q.q] - :: %give - :: ^- [(unit path) term term] - :: [~ %give -.p.q] - :: == + =+ den=((de now hen ruf) [. .]:p.q.hic q.q.hic) + abet:(exec:den now r.q.hic) [mos ..^$] :: %into @@ -2480,19 +2441,14 @@ == =+ yak=(~(got by hut.ran.ruf) (~(got by hit.dom.u.dos) let.dom.u.dos)) =+ cos=(mode-to-soba q.yak (flop s.bem) q.q.hic r.q.hic) - [hen %pass / %c %info p.bem q.bem %& cos]~ + [hen %pass /both %c %info p.bem q.bem %& cos]~ :: %merg :: direct state up ?: =(%$ q.q.hic) [~ ..^$] =^ mos ruf - =+ une=(un p.q.hic now hen ruf) - =+ ^= zat - abet:(start:(me:ze:(di:wake:une q.q.hic) [r.q.hic s.q.hic] ~) t.q.hic) - =+ zot=abet.zat - :- -.zot - =. une (pish:une q.q.hic +.zot ran.zat) - abet:une + =+ den=((de now hen ruf) [. .]:p.q.hic q.q.hic) + abet:abet:(start:(me:ze:den [r.q.hic s.q.hic] ~) t.q.hic) [mos ..^$] :: %mont @@ -2509,11 +2465,10 @@ =+ dos=(~(get by dos.u.yar) r.q.hic) ?~ dos [~ ..^$] - =+ une=(un q.q.hic now hen ruf) - =+ wex=(di:une r.q.hic) - =+ wao=(mont:wex p.q.hic s.q.hic) - =+ woo=abet:wao - [-.woo ..^$(ruf abet:(pish:une r.q.hic +.woo ran.wao))] + =^ mos ruf + =+ den=((de now hen ruf) [. .]:q.q.hic r.q.hic) + abet:(mont:den p.q.hic s.q.hic) + [mos ..^$] :: %ogre ?~ hez.ruf @@ -2538,22 +2493,11 @@ :: %warp =^ mos ruf - ?: =(p.p.q.hic q.p.q.hic) - =+ une=(un p.p.q.hic now hen ruf) - =+ wex=(di:une p.q.q.hic) - =+ ^= wao - ?~ q.q.q.hic - ease:wex - (eave:wex u.q.q.q.hic) - =+ ^= woo - abet:wao - [-.woo abet:(pish:une p.q.q.hic +.woo ran.wao)] - =+ wex=(do now hen p.q.hic p.q.q.hic ruf) - =+ ^= woo - ?~ q.q.q.hic - abet:ease:wex - abet:(eave:wex u.q.q.q.hic) - [-.woo (posh q.p.q.hic p.q.q.hic +.woo ruf)] + =+ den=((de now hen ruf) p.q.hic p.q.q.hic) + =< abet + ?~ q.q.q.hic + ease:den + (eave:den u.q.q.q.hic) [mos ..^$] :: %west @@ -2569,14 +2513,10 @@ ?> ?=([%answer @ @ ~] q.q.hic) =+ syd=(slav %tas i.t.q.q.hic) =+ inx=(slav %ud i.t.t.q.q.hic) - =+ ^= zat - =< wake - %- take-foreign-update:(do now hen p.q.hic syd ruf) - [inx ((hard (unit rand)) r.q.hic)] =^ mos ruf - =+ zot=abet.zat - [-.zot (posh q.p.q.hic syd +.zot ruf)] - [[[hen %give %mack ~] mos] ..^$(ran.ruf ran.zat)] :: merge in new obj + =+ den=((de now hen ruf) p.q.hic syd) + abet:(take-foreign-update:den inx ((hard (unit rand)) r.q.hic)) + [[[hen %give %mack ~] mos] ..^$] :: %wegh :_ ..^$ :_ ~ @@ -2614,11 +2554,8 @@ [~ ~] =+ run=((soft care) ren) ?~ run [~ ~] - =+ %. [u.run u.luk tyl] - =< aver - ?: got - (di:(un his now ~ ruf) syd) - (do now ~ [his his] syd ruf) + =+ den=((de now [/scryduct ~] ruf) [. .]:his syd) + =+ (aver:den u.run u.luk tyl) ?~ - - ?~ u.- - ?: ?=(%& -.u.u.-) ``p.u.u.- @@ -2635,9 +2572,6 @@ =+ her=(slav %p i.t.t.t.tea) =* sud i.t.t.t.t.tea =* sat i.t.t.t.t.t.tea - =+ une=(un our now hen ruf) - =+ wak=une - =+ wex=(di:wak syd) =+ dat=?-(+<.q.hin %writ [%& p.q.hin], %made [%| q.q.hin]) =+ ^- kan=(unit ankh) %+ biff (~(get by fat.ruf) her) @@ -2645,10 +2579,10 @@ %+ bind (~(get by dos) sud) |= dojo ank.dom - =+ wao=abet:(route:(me:ze:wex [her sud] kan) sat dat) - =+ woo=abet:wao - =+ sip=wake:(pish:une syd +.woo ran.wao) - [(weld -.sip -.woo) ..^$(ruf abet:[+.sip])] + =^ mos ruf + =+ den=((de now hen ruf) [. .]:our syd) + abet:abet:(route:(me:ze:den [her sud] kan) sat dat) + [mos ..^$] ?: ?=([%blab care @ @ *] tea) ?> ?=(%made +<.q.hin) ?. ?=(%& -.q.q.hin) @@ -2669,82 +2603,60 @@ ?+ -.tea !! %inserting ?> ?=([@ @ @ ~] t.tea) - =+ who=(slav %p i.t.tea) + =+ our=(slav %p i.t.tea) =+ syd=(slav %tas i.t.t.tea) =+ wen=(slav %da i.t.t.t.tea) =^ mos ruf - =+ une=(un who now hen ruf) - =+ ^= zat - (take-inserting:(di:une syd) wen q.q.hin) - =+ zot=abet.zat - [-.zot abet:(pish:une syd +.zot ran.zat)] + =+ den=((de now hen ruf) [. .]:our syd) + abet:(take-inserting:den wen q.q.hin) [mos ..^$] :: %diffing ?> ?=([@ @ @ ~] t.tea) - =+ who=(slav %p i.t.tea) + =+ our=(slav %p i.t.tea) =+ syd=(slav %tas i.t.t.tea) =+ wen=(slav %da i.t.t.t.tea) =^ mos ruf - =+ une=(un who now hen ruf) - =+ ^= zat - (take-diffing:(di:une syd) wen q.q.hin) - =+ zot=abet.zat - [-.zot abet:(pish:une syd +.zot ran.zat)] + =+ den=((de now hen ruf) [. .]:our syd) + abet:(take-diffing:den wen q.q.hin) [mos ..^$] :: %castifying ?> ?=([@ @ @ ~] t.tea) - =+ who=(slav %p i.t.tea) + =+ our=(slav %p i.t.tea) =+ syd=(slav %tas i.t.t.tea) =+ wen=(slav %da i.t.t.t.tea) =^ mos ruf - =+ une=(un who now hen ruf) - =+ ^= zat - (take-castify:(di:une syd) wen q.q.hin) - =+ zot=abet.zat - [-.zot abet:(pish:une syd +.zot ran.zat)] + =+ den=((de now hen ruf) [. .]:our syd) + abet:(take-castify:den wen q.q.hin) [mos ..^$] :: %mutating ?> ?=([@ @ @ ~] t.tea) - =+ who=(slav %p i.t.tea) + =+ our=(slav %p i.t.tea) =+ syd=(slav %tas i.t.t.tea) =+ wen=(slav %da i.t.t.t.tea) =^ mos ruf - =+ une=(un who now hen ruf) - =+ ^= zat - (take-mutating:(di:une syd) wen q.q.hin) - =+ zot=abet.zat - [-.zot abet:(pish:une syd +.zot ran.zat)] + =+ den=((de now hen ruf) [. .]:our syd) + abet:(take-mutating:den wen q.q.hin) [mos ..^$] :: %patching ?> ?=([@ @ ~] t.tea) - =+ who=(slav %p i.t.tea) + =+ our=(slav %p i.t.tea) =+ syd=(slav %tas i.t.t.tea) =^ mos ruf - ?: (~(has by fat.ruf) who) - =+ une=(un who now hen ruf) - =+ zat=(take-patch:(di:une syd) q.q.hin) - =+ zot=abet.zat - [-.zot abet:(pish:une syd +.zot ran.zat)] - =+ zax=(do now hen [who who] syd ruf) - =+ zat=(take-patch:zax q.q.hin) - =+ zot=abet.zat - [-.zot (posh who syd +.zot ruf)] + =+ den=((de now hen ruf) [. .]:our syd) + abet:(take-patch:den q.q.hin) [mos ..^$] :: %ergoing ?> ?=([@ @ ~] t.tea) - =+ who=(slav %p i.t.tea) + =+ our=(slav %p i.t.tea) =+ syd=(slav %tas i.t.t.tea) =^ mos ruf - =+ une=(un who now hen ruf) - =+ ^= zat - (take-ergo:(di:une syd) q.q.hin) - =+ zot=abet.zat - [-.zot abet:(pish:une syd +.zot ran.zat)] + =+ den=((de now hen ruf) [. .]:our syd) + abet:(take-ergo:den q.q.hin) [mos ..^$] :: %foreign-plops @@ -2753,12 +2665,10 @@ =+ her=(slav %p i.t.t.tea) =* syd i.t.t.t.tea =+ lem=(slav %da i.t.t.t.t.tea) - =+ zax=(do now hen [our her] syd ruf) - =+ zat=(take-foreign-plops:zax ?~(lem ~ `lem) q.q.hin) =^ mos ruf - =+ zot=abet.zat - [-.zot (posh her syd +.zot ruf)] - [mos ..^$(ran.ruf ran.zat)] + =+ den=((de now hen ruf) [our her] syd) + abet:(take-foreign-plops:den ?~(lem ~ `lem) q.q.hin) + [mos ..^$] :: %foreign-x ?> ?=([@ @ @ @ @ *] t.tea) @@ -2771,12 +2681,10 @@ ?> ?=([~ %$ case] -) ->+ =* pax t.t.t.t.t.t.tea - =+ zax=(do now hen [our her] syd ruf) - =+ zat=(take-foreign-x:zax car cas pax q.q.hin) =^ mos ruf - =+ zot=abet.zat - [-.zot (posh her syd +.zot ruf)] - [mos ..^$(ran.ruf ran.zat)] + =+ den=((de now hen ruf) [our her] syd) + abet:(take-foreign-x:den car cas pax q.q.hin) + [mos ..^$] == :: %mere @@ -2793,13 +2701,14 @@ :: %note [[hen %give +.q.hin]~ ..^$] %wake - =+ dal=(turn (~(tap by fat.ruf) ~) |=([a=@p b=room] a)) - =| mos=(list move) - |- ^- [p=(list move) q=_..^^$] - ?~ dal [mos ..^^$] - =+ une=(un i.dal now hen ruf) - =^ som une wake:une - $(dal t.dal, ruf abet:une, mos (weld som mos)) + ~| %why-wakey !! + :: =+ dal=(turn (~(tap by fat.ruf) ~) |=([a=@p b=room] a)) + :: =| mos=(list move) + :: |- ^- [p=(list move) q=_..^^$] + :: ?~ dal [mos ..^^$] + :: =+ une=(un i.dal now hen ruf) + :: =^ som une wake:une + :: $(dal t.dal, ruf abet:une, mos (weld som mos)) :: %writ ?> ?=([@ @ *] tea) @@ -2808,7 +2717,7 @@ =+ our=(slav %p i.tea) =+ him=(slav %p i.t.tea) :_ ..^$ - :~ :* hen %pass ~ %a + :~ :* hen %pass /writ-wont %a %wont [our him] [%c %answer t.t.tea] (bind p.+.q.hin rant-to-rand) == diff --git a/arvo/eyre.hoon b/arvo/eyre.hoon index 7a6385075..a90a635ca 100644 --- a/arvo/eyre.hoon +++ b/arvo/eyre.hoon @@ -494,14 +494,14 @@ :: ++ poke-test %+ titl 'Poke' - ;= ;button(onclick "urb.testPoke('/~/to/hi/txt.json')"): Hi anonymous - ;button(onclick "urb.testPoke('/~/as/own/~/to/hi/txt.json')"): Hi + ;= ;button(onclick "urb.testPoke('/~/to/hood/helm-hi.json')"): Hi anonymous + ;button(onclick "urb.testPoke('/~/as/own/~/to/hood/helm-hi.json')"): Hi ;pre:code#err; ;script@"/~/at/~/auth.js"; ;script:''' show = function(t){err.innerText = ":) " + Date.now() + "\n" + t} urb.testPoke = function(url){ - req(url,{xyro:{test:true}}, show) + req(url,{wire:"/",xyro:'test'}, show) } ''' == @@ -510,6 +510,9 @@ ;html ;head ;meta(charset "utf-8"); + ;meta(name "viewport", content "width=device-width, ". + "height=device-height, initial-scale=1.0, user-scalable=0, ". + "minimum-scale=1.0, maximum-scale=1.0"); ;title:"{(trip a)}" ;script(type "text/javascript", src "//cdnjs.cloudflare.com/ajax/". "libs/jquery/2.1.1/jquery.min.js"); @@ -1217,9 +1220,9 @@ ?- r.hat [%| @] (cat 3 '; Domain=' (rsh 3 1 (scot %if p.r.hat))) [%& %org %urbit *] '; Domain=.urbit.org' - [%& @ @ *] =+ dom=p.r.hat - =- (rap 3 i.dom '.' i.t.dom -) - |-(?~(t.t.dom ~ ['.' i.t.t.dom $(dom t.dom)])) + [%& @ @ *] =- (rap 3 "; Domain={-}{i.p.r.hat ~}") + (turn (flop `path`t.p.r.hat) |=(a=span (cat 3 a '.'))) + [%& *] '' :: XX security? == :: diff --git a/arvo/ford.hoon b/arvo/ford.hoon index a5d7ea479..df0e1323c 100644 --- a/arvo/ford.hoon +++ b/arvo/ford.hoon @@ -355,7 +355,7 @@ == == :: - ++ coax :: bolt across + ++ coax !. :: bolt across |* [hoc=(bolt) fun=(burg)] ?- -.q.hoc %0 =+ nuf=$:fun(+<- p.hoc) @@ -584,7 +584,7 @@ ++ fade :: compile to hood |= [cof=cafe for=mark bem=beam] ^- (bolt hood) - :: %+ cool |.(leaf/"ford: fade {<[(tope bem)]>}") + %+ cool |.(leaf/"ford: fade {<[(tope bem)]>}") %+ cope (cope (make cof [%bake for bem ~]) furl) |= [cof=cafe cay=cage] %+ (clef %hood) (fine cof bem(r [%ud 0]) cay) @@ -1128,7 +1128,10 @@ ?. ?=(%2 -.q.raf) (cope raf (flux |=(vax=vase (some [for vax])))) =- ((slog (flop `tang`-)) (flue cof)) - ?^ t.pax ~ :: error on top-level marks + =+ (lent t.pax) + ?: ?~ - | :: error if level above built + (~(has by res) (tack i.pax (scag (dec -) t.pax))) + ~ :_(q.q.raf leaf/"! {<`mark`for>} build failed, ignoring.") -- :: @@ -1380,6 +1383,7 @@ :: %tabl %- cope :_ (flux |=(rex=(list (pair gage gage)) [%tabl rex])) + !. |- ^- (bolt (list (pair gage gage))) ?~ p.kas (fine cof ~) %. [cof p.kas] diff --git a/arvo/gall.hoon b/arvo/gall.hoon index 39bc8184d..19a7b5859 100644 --- a/arvo/gall.hoon +++ b/arvo/gall.hoon @@ -761,7 +761,7 @@ ++ ap-move-quit :: give quit move |= [sto=bone vax=vase] ^- (each cove tang) - ?^ q.vax [%| (ap-suck "move: improper quit")] + ?^ q.vax [%| (ap-suck "quit: improper give")] [%& `cove`[sto %give `cuft`[%quit ~]]] :: ++ ap-move-diff :: give diff move @@ -769,20 +769,20 @@ =. vax (spec vax) ^- (each cove tang) ?. &(?=(^ q.vax) ?=(@ -.q.vax) ((sane %tas) -.q.vax)) - [%| (ap-suck "move: improper diff")] + [%| (ap-suck "diff: improper give")] [%& sto %give %diff `cage`[-.q.vax (slot 3 (spec vax))]] :: ++ ap-move-hiss :: pass %hiss |= [sto=bone vax=vase] ^- (each cove tang) ?. &(?=([p=* q=@ q=^] q.vax) ((sane %tas) q.q.vax)) - [%| (ap-suck "move: malformed hiss ask.[%hiss path mark cage]")] + [%| (ap-suck "hiss: malformed hiss ask.[%hiss wire mark cage]")] =+ gaw=(slot 7 vax) ?. &(?=([p=@ q=^] q.gaw) ((sane %tas) p.q.gaw)) - [%| (ap-suck "move: malformed cage")] + [%| (ap-suck "hiss: malformed cage")] =+ pux=((soft path) p.q.vax) ?. &(?=(^ pux) (levy u.pux (sane %ta))) - [%| (ap-suck "move: malformed path")] + [%| (ap-suck "hiss: malformed path")] :^ %& sto %pass :- [(scot %p q.q.pry) %cay u.pux] [%hiss q.q.vax [p.q.gaw (slot 3 (spec gaw))]] @@ -793,20 +793,20 @@ ?. ?& ?=([p=* [q=@ r=@] s=*] q.vax) (gte 1 (met 7 q.q.vax)) == - [%| (ap-suck "move: malformed target")] + [%| (ap-suck "mess: malformed target")] =+ pux=((soft path) p.q.vax) ?. &(?=(^ pux) (levy u.pux (sane %ta))) - [%| (ap-suck "move: malformed path")] + [%| (ap-suck "mess: malformed path")] [%& [(scot %p q.q.vax) %out r.q.vax u.pux] q.q.vax r.q.vax] :: ++ ap-move-pass :: pass general move |= [sto=bone wut=* vax=vase] ^- (each cove tang) ?. &(?=(@ wut) ((sane %tas) wut)) - [%| (ap-suck "move: malformed card")] + [%| (ap-suck "pass: malformed card")] =+ pux=((soft path) -.q.vax) ?. &(?=(^ pux) (levy u.pux (sane %ta))) - [%| (ap-suck "move: malformed path")] + [%| (ap-suck "pass: malformed path")] =+ huj=(ap-vain wut) ?~ huj [%| (ap-suck "move: unknown note {(trip wut)}")] :^ %& sto %pass @@ -855,17 +855,17 @@ (gte 1 (met 7 q.q.vax)) ((sane %tas) r.q.vax) == - [%| (ap-suck "move: malformed send")] + [%| (ap-suck "send: improper ask.[%send wire gill club]")] =+ pux=((soft path) p.q.vax) ?. &(?=(^ pux) (levy u.pux (sane %ta))) - [%| (ap-suck "move: malformed path")] + [%| (ap-suck "send: malformed path")] ?: ?=(%poke s.q.vax) =+ gav=(spec (slot 7 vax)) ?> =(%poke -.q.gav) ?. ?& ?=([p=@ q=*] t.q.vax) ((sane %tas) p.t.q.vax) == - [%| (ap-suck "move: malformed poke")] + [%| (ap-suck "send: malformed poke")] :^ %& sto %pass :- [(scot %p q.q.vax) %out r.q.vax u.pux] ^- cote @@ -873,7 +873,7 @@ [%send q.q.vax r.q.vax %poke p.t.q.vax (slot 3 (spec (slot 3 gav)))] =+ cob=((soft club) [s t]:q.vax) ?~ cob - [%| (ap-suck "move: malformed club")] + [%| (ap-suck "send: malformed club")] :^ %& sto %pass :- [(scot %p q.q.vax) %out r.q.vax u.pux] :: ~& [%ap-move-send `path`[(scot %p q.q.vax) %out r.q.vax u.pux]] diff --git a/arvo/zuse.hoon b/arvo/zuse.hoon index 184d5ced7..23575ebff 100644 --- a/arvo/zuse.hoon +++ b/arvo/zuse.hoon @@ -2355,11 +2355,11 @@ == :: ++ kiss-clay :: in request ->$ $% [%boat ~] :: pier rebooted - [%info p=@p q=@tas r=nori] :: internal edit + [%info p=@p q=desk r=nori] :: internal edit [%init p=@p] :: report install - [%into p=@tas q=? r=mode] :: external edit - [%merg p=@p q=@tas r=@p s=@tas t=germ] :: merge desks - [%mont p=@tas q=@p r=@tas s=path] :: mount to unix + [%into p=desk q=? r=mode] :: external edit + [%merg p=@p q=desk r=@p s=desk t=germ] :: merge desks + [%mont p=@tas q=@p r=desk s=path] :: mount to unix [%ogre p=$|(@tas beam)] :: delete mount point [%warp p=sock q=riff] :: file request [%wegh ~] :: report memory diff --git a/lib/base.css b/lib/base.css index 10a49bc17..bf2f4752e 100644 --- a/lib/base.css +++ b/lib/base.css @@ -166,3 +166,17 @@ input { outline: none; border-bottom: 3px solid #555; } + +@media only screen and (min-device-width: 320px) and (max-device-width: 480px) { + #c { + width: 16rem; + margin-left: -8rem; + } + #pass { + width: 16rem; + } + input { + -webkit-appearance: none; + border-radius: 0; + } +} diff --git a/lib/urb.js b/lib/urb.js index 10a5aa1f4..b00cbc8a3 100644 --- a/lib/urb.js +++ b/lib/urb.js @@ -1,7 +1,3 @@ -window.urb.seqn_u = 1 -window.urb.dely = 0 -window.urb.puls = false -window.urb.cabs = {} if(!window.urb.appl) window.urb.appl = null window.urb.req = function(method,url,params,json,cb) { @@ -83,6 +79,41 @@ window.urb.qreq = function(method,url,params,json,cb) { if(l == 0) { walk() } } +window.urb.send = function(data,params,cb) { // or send(data, cb) + if(!params || typeof params === "function") + {cb = params; params = {}} + + var url, $send + $send = this.send + + params.data = data + params.ship = params.ship || this.ship + params.appl = params.appl || this.appl + params.mark = params.mark || $send.mark + params.xyro = params.data || {} + params.wire = params.wire || "/" + + + if(!params.mark) throw new Error("You must specify a mark for urb.send.") + if(!params.appl) throw new Error("You must specify an appl for urb.send.") + + url = ["to",params.appl,params.mark] + url = "/~/"+url.join("/") + + $send.seqn++ + + this.qreq('post',url,params,true,function(err,data) { + if(err) { $send.seqn--; } + else if(data && data.data.fail && urb.wall !== false) + document.write("
"+JSON.stringify(params.xyro)+"\n"
+                            +data.data.mess+"
") // XX + if(cb) { cb.apply(this,arguments); } + }) +} +window.urb.send.seqn = 0 +window.urb.send.mark = "json" + + window.urb.gsig = function(params) { var path = params.path if(!path) path = "" @@ -92,12 +123,14 @@ window.urb.gsig = function(params) { path.replace(/[^\x00-\x7F]/g, "") } +window.urb.puls = false +window.urb.cabs = {} window.urb.poll = function(params) { if(!params) throw new Error("You must supply params to urb.poll.") var url, $this - seqn = this.seqn_u + seqn = this.poll.seqn if(params.seqn) seqn = params.seqn() url = "/~/of/"+this.ixor+"?poll="+seqn @@ -127,16 +160,16 @@ window.urb.poll = function(params) { } } - dely = params.dely || $this.dely + dely = params.dely || $this.poll.dely if(err) dely = dely+Math.ceil(dely*.02) else { - $this.dely = 0 + $this.poll.dely = 0 if(params.incs) params.incs() else - $this.seqn_u++ + $this.poll.seqn++ } setTimeout(function() { @@ -144,86 +177,18 @@ window.urb.poll = function(params) { },dely) }) } +window.urb.poll.seqn = 1 +window.urb.poll.dely = 0 -// if (window.urb.auto) { // need dependencies -// var tries = 0 -// var cnt = 0 -// var param = { -// type:"pol" -// } -// window.urb.poll(param) -// } - -// window.urb.heartbeat = function() { -// this.poll({ -// type:"heb", -// ship:this.ship, -// dely:30000, -// seqn:function() { -// return window.urb.seqn_h -// }, -// incs:function() { -// window.urb.seqn_h = window.urb.seqn_h+1 -// } -// },function() { -// console.log('heartbeat.') -// }) -// } -// window.urb.heartbeat() - -// // / // / // // -// end old %eyre code // -// // / // / // // - -window.urb.seqn_s = 0 - -// TODO urb.send(data, [params/params.appl]?, cb?) -window.urb.send = function(params,cb) { - if(!params) - throw new Error("You must supply params to urb.send.") - if(!params.appl && !this.appl){ - throw new Error("You must specify an appl for urb.send.") - } - - var url, $this - - params.ship = params.ship || this.ship - params.appl = params.appl || this.appl - params.mark = params.mark || "json" - params.xyro = params.data || {} - params.wire = params.wire || "/" - - - url = ["to",params.appl,params.mark] - url = "/~/"+url.join("/") - - this.seqn_s++ - - $this = this - this.qreq('post',url,params,true,function(err,data) { - if(err) { $this.seqn_s--; } - else if(data && data.data.fail && urb.wall !== false) - document.write("
"+JSON.stringify(params.xyro)+"\n"
-                            +data.data.mess+"
") // XX - if(cb) { cb.apply(this,arguments); } - }) -} - -window.urb.subscribe = function(params,cb) { // legacy interface - if(!params) throw new Error("You must supply params to urb.subscribe") - return window.urb.bind(params.path, params, cb, cb) -} -window.urb.bind = function(path, cb){ // or bind(path, params, cb, nicecb?) - var params, nicecb - if(arguments.length > 2) - {params = cb; cb = arguments[2], nicecb = arguments[3]} - else params = {} +window.urb.bind = function(path, params, cb, nicecb){ // or bind(path, cb) + if(!params || typeof params === "function") + {cb = params; params = {}} params.path = path if(params.path[0] !== "/") params.path = "/"+params.path params.ship = params.ship || this.ship params.appl = params.appl || this.appl - params.mark = params.mark || "json" + params.mark = params.mark || this.bind.mark params.wire = params.wire || params.path if(typeof path != "string") @@ -248,16 +213,21 @@ window.urb.bind = function(path, cb){ // or bind(path, params, cb, nicecb?) if(!err && !$this.puls) $this.poll(params) }) } +urb.bind.mark = "json" -window.urb.unsubscribe = function(params,cb) { - if(!params) throw new Error("You must supply params to urb.unsubscribe.") - +window.urb.drop = function(path, params, cb){ // or drop(path,cb) + if(typeof params === "function") + {cb = params; params = {}} + + params.path = path + if(params.path[0] !== "/") params.path = "/"+params.path params.ship = params.ship || this.ship params.appl = params.appl || this.appl params.wire = params.wire || params.path - if(!params.path) throw new Error("You must specify a path for urb.unsubscribe.") - if(!params.appl) throw new Error("You must specify an appl for urb.unsubscribe.") + if(typeof path != "string") + throw new Error("You must specify a string path for urb.drop.") + if(!params.appl) throw new Error("You must specify an appl for urb.drop.") url = "/~/is/"+this.gsig(params)+".json" method = "delete" @@ -266,6 +236,16 @@ window.urb.unsubscribe = function(params,cb) { }) } +window.urb.subscribe = function(params,cb) { // legacy interface + if(!params) throw new Error("You must supply params to urb.subscribe") + return window.urb.bind(params.path, params, cb, cb) +} + +window.urb.unsubscribe = function(params,cb) { // legacy intreface + if(!params) throw new Error("You must supply params to urb.unsubscribe.") + return window.urb.drop(params.path, params, cb) +} + window.urb.util = { isURL: function(s) { r = new RegExp('^(?!mailto:)(?:(?:http|https|ftp)://)(?:\\S+(?::\\S*)?@)?(?:(?:(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}(?:\\.(?:[0-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))|(?:(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)(?:\\.(?:[a-z\\u00a1-\\uffff0-9]+-?)*[a-z\\u00a1-\\uffff0-9]+)*(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))|localhost)(?::\\d{2,5})?(?:(/|\\?|#)[^\\s]*)?$', 'i'); diff --git a/mar/jam.hoon b/mar/jam.hoon new file mode 100644 index 000000000..a11a4103c --- /dev/null +++ b/mar/jam.hoon @@ -0,0 +1,16 @@ +:: +:::: /hoon/core/jam/mar + :: +!: +|_ mud=@ +++ grow + |% + ++ mime [/application/octet-stream (taco mud)] + -- +++ grab + |% :: convert from + ++ mime |=([p=mite q=octs] q.q) + ++ noun ,@ :: clam from %noun + -- +++ grad %mime +-- diff --git a/mar/load.hoon b/mar/load.hoon new file mode 100644 index 000000000..de150d2aa --- /dev/null +++ b/mar/load.hoon @@ -0,0 +1,10 @@ +:: +:::: /hoon/core/load/mar + :: +!: +|_ man=span +++ grab + |% :: convert from + ++ noun span :: clam from %noun + -- +-- diff --git a/mar/save.hoon b/mar/save.hoon new file mode 100644 index 000000000..614b98dc4 --- /dev/null +++ b/mar/save.hoon @@ -0,0 +1,10 @@ +:: +:::: /hoon/core/save/mar + :: +!: +|_ man=span +++ grab + |% :: convert from + ++ noun span :: clam from %noun + -- +-- diff --git a/mar/wipe.hoon b/mar/wipe.hoon new file mode 100644 index 000000000..1e9f108bf --- /dev/null +++ b/mar/wipe.hoon @@ -0,0 +1 @@ +|=(~ ~) diff --git a/mar/work/command.hoon b/mar/work/command.hoon new file mode 100644 index 000000000..4a4677cae --- /dev/null +++ b/mar/work/command.hoon @@ -0,0 +1,103 @@ +:: +:::: /hoon/command/work/mar + :: +/- *work +!: +:::: + :: +|_ mad=command +++ grab + |% ++ noun command + ++ json + => [jo ..command] + =< (corl need (cu |=(a=command a) coma)) + |% + ++ as + :: |*(a=fist (cu sa (ar a))) :: XX types + |* a=fist + %- cu :_ (ar a) + ~(gas in *(set ,_(need *a))) + ++ ot + |* a=(pole ,[@tas fist]) + |= b=json + %. ((^ot a) b) + %- slog + ?+ b ~ + [%o *] + %+ murn `(list ,[@tas fist])`a + |= [c=term d=fist] ^- (unit tank) + =+ (~(get by p.b) c) + ?~ - (some >[c (turn (~(tap by p.b)) head)]<) + =+ (d u) + ?~ - (some >[c u]<) + ~ + == + ++ of + |* a=(pole ,[@tas fist]) + |= b=json + %. ((of:jo a) b) + %- slog + ?+ b ~ + [%o *] + %+ murn `(list ,[@tas fist])`a + |= [c=term d=fist] ^- (unit tank) + =+ (~(get by p.b) c) + ?~ - ~ + =+ (d u) + ?~ - (some >[c u]<) + ~ + == + ++ id (ci (slat %uv) so) + ++ ship (su fed:ag) + ++ coma + %- of :~ + new/task old/(ot id/id dif/uppd ~) + sort/(ar id) + == + ++ task + %- ot :~ + ::index/ni + audience/audi + id/id 'date_created'^di + version/ni 'date_modified'^di + creator/ship doer/(mu ship) + tags/(as so) 'date_due'^(mu di) + done/(mu di) title/so + description/so discussion/(ar (ot date/di ship/ship body/so ~)) + == + ++ audi (as stan) + ++ stan (su ;~((glue fas) ;~(pfix sig fed:ag) urs:ab)) + ++ uppd + %- of :~ + doer/(of release/ul claim/ul ~) + add/(of comment/so ~) + :- %set + %- of :~ + audience/audi + date-due/(mu di) + title/so + description/so + tags/(as so) + done/bo + == + == + -- + -- +++ grow + |% + ++ elem ;pre: {(zing `wall`(turn (wash 0^120 >mad<) |=(a=tape ['\0a' a])))} + -- +-- +:: {new: { +:: id:'0vaof.6df9u.2agc3.d0dp1', +:: date-created:1440011611215, +:: version:1, +:: date-modified:1440011611215, +:: creator:'fyr', +:: tags:['tag'], +:: date-due:null, +:: done:false, +:: title:'Test task', +:: description:'The converter owrks right?', +:: discussion:[{date:1440011611215,ship:'sondel',body:'hi'}] +:: } } diff --git a/mar/work/report.hoon b/mar/work/report.hoon new file mode 100644 index 000000000..2f38e4cc3 --- /dev/null +++ b/mar/work/report.hoon @@ -0,0 +1,54 @@ +:: +:::: /hoon/report/work/mar + :: +/- *work +!: +:::: + :: +|_ client +++ grow +|% ++ json + =+ jope=|=(a=ship [%s (rsh 3 1 (scot %p a))]) + %- jobe :~ + sort/[%a (turn sort |=(a=@uv [%s (scot %uv a)]))] + =< tasks/(jobe (turn (~(tap by tasks)) .)) + |= [@ client-task] + =+ tax + :- (scot %uv id) + %- jobe :~ id/[%s (scot %uv id)] + tags/[%a (turn (^sort (~(tap in tags)) aor) |=(a=cord s/a))] + doer/?~(doer ~ (jope u.doer)) + title/[%s title] + creator/(jope creator) + version/(jone version) + archived/[%b archived] + =< audience/[%a (turn (~(tap in audience)) .)] + |=(a=station:talk [%s (crip "{}/{(trip q.a)}")]) + 'date_created'^(jode date-created) + 'date_modified'^(jode date-modified) + description/[%s description] + =< discussion/[%a (turn discussion .)] + |=(comment (jobe date/(jode date) ship/(jope ship) body/[%s body] ~)) + 'date_due'^?~(date-due ~ (jode u.date-due)) + done/?~(done ~ (jode u.done)) + == + == +-- -- +:: sort: ["0v111id" ...] +:: tasks: [ { +:: id:"0v111id" +:: tags:["str" ...] +:: doer:|("~ship" null) +:: title:"str" +:: creator:"~ship" +:: version:12345 +:: archived:false +:: audience:["~ship/chan" ...] +:: date_created:1262304000000 +:: date_modified:1262304000000 +:: description:"str" +:: discussion:[{date:1262304000000 ship:"~ship" body:"str"} ...] +:: date_due:?(1262304000000 null) +:: done:?(1262304000000 null) +:: } +:: ...] diff --git a/mar/work/task.hoon b/mar/work/task.hoon new file mode 100644 index 000000000..adff8b704 --- /dev/null +++ b/mar/work/task.hoon @@ -0,0 +1,104 @@ +:: +:::: /hoon/task/work/mar + :: +/- *work +!: +:::: + :: +|% +++ rend + |= a=(list $|(char dime)) ^- cord + %- crip + |- ^- tape + ?~ a ~ + ?@ i.a [i.a $(a t.a)] + (weld (scow i.a) $(a t.a)) +:: +++ indent |=(a=wain (turn a |=(b=cord (cat 3 ' ' b)))) +:: +++ undent + |* [a=wain b=$+(wain *)] ^+ [*b a] + =^ c a + |- ^- [c=wain a=wain] + ?~ a [~ a] + ?. =(' ' (end 3 2 i.a)) + [~ a] + [[- c] a]:[(rsh 3 2 i.a) $(a t.a)] + [(b `wain`c) a] +++ keen |*(_[a=,* b=rule] |=(c=nail `(like a)`(b c))) +++ parse + |* [hed=?(~ $|(@tas tape)) tal=(pole)] + ?~ hed (..$ tal) + ?^ hed ;~(pfix (just (crip hed)) (..$ tal)) + =- ?~(tal had ;~(plug had (..$ tal))) + =< had=(sear . nuck:so) + |= a=coin ^- (unit (odo:raid hed)) + ?. &(?=([%$ @ @] a) =(hed p.p.a)) ~ + (some q.p.a) +:: +++ advance + |* [a=wain b=_rule] ^+ [(wonk *b) a] + ?~(a !! ~|(i.a [(rash i.a b) t.a])) +-- +!: +:::: + :: +|_ taz=task +++ grab + |% ++ txt + |= a=wain ^+ taz + =+ ~[id=%uv "_" date-created=%da " " version=%ud date-modified=%da] + =^ b a (advance a ;~(plug (parse -) (punt (parse " " %da ~)))) + =+ [-.b `date-due=(unit ,@da)`+.b] + =^ tags a (undent a ~(gas in *(set cord))) + =^ title a ?~(a !! a) + =^ b a %+ advance a + ;~(plug (parse %p ~) (punt (parse ">" %p ~)) (punt (parse "X" %da ~))) + =+ `[creator=@p doer=(unit ,@p) done=(unit ,@da)]`b + =^ description a (undent a role) + :* id date-created version date-modified creator + doer tags date-due done title description :: XX done + |- ^- (list comment) + ?: =(~ a) ~ + =^ b a (advance a (parse ship=%p " " date=%da ~)) + =+ b + =^ body a (undent a role) + [[date ship body] $] + == + -- +++ grow + |% + ++ elem ;pre: {(zing `wall`(turn (wash 0^120 >taz<) |=(a=tape ['\0a' a])))} + ++ mime [/text/x-task (taco (role txt))] + ++ txt + =+ taz + =+ due=?~(date-due ~ ~[' ' da/u.date-due]) + :- (rend uv/id '_' da/date-created ' ' ud/version da/date-modified due) + %+ welp (indent (sort (~(tap in tags)) aor)) + :- title + =+ do=[=-(?~(doer - ['>' p/u.doer -]) ?~(done ~ ~['X' da/u.done]))] + :- (rend p/creator do) + %- zing ^- (list wain) + :- (indent (lore description)) + %+ turn discussion + |= comment ^- wain + [(rend p/ship ' ' da/date ~) (indent (lore body))] + -- +++ grad %txt +-- +:: {id}_{date-created} {version}{date-modified}{|(" {date-due}" ~)} +:: {tag1} +:: {tag2} +:: ... +:: {title} +:: {creator}{|(">{doer}" ~)}{|("X{done}" ~)} +:: {description} +:: {more description} +:: {ship1} {date} +:: {comment} +:: {more comment} +:: {more comment} +:: {ship2} {date} +:: {comment} +:: {more comment} +:: {more comment} diff --git a/pub/sole/src/main.coffee b/pub/sole/src/main.coffee index 3a153e3fb..02701dace 100644 --- a/pub/sole/src/main.coffee +++ b/pub/sole/src/main.coffee @@ -115,7 +115,7 @@ $ -> mapr = matr.props unless mapr.prompt[appl]? return print '# not-joined: '+appl - urb.unsubscribe {appl, path: "/sole", wire: "/"} + urb.drop "/sole", {appl, wire: "/"} if appl is mapr.appl then cycle() updPrompt appl, null sysStatus() @@ -135,8 +135,9 @@ $ -> # pressed = [] # ), 500 + urb.send.mark = 'sole-action' sendAction = (data)-> - if matr.props.appl then urb.send {mark: 'sole-action', data}, (e,res)-> + if matr.props.appl then urb.send data, (e,res)-> if res.status isnt 200 then $('#err')[0].innerText = res.data.mess else if data is 'ret' app = /^[a-z-]+$/.exec(buffer[""].buf.slice(1)) diff --git a/pub/talk/src/js/main.js b/pub/talk/src/js/main.js index 844a4a0ca..3abab17e6 100644 --- a/pub/talk/src/js/main.js +++ b/pub/talk/src/js/main.js @@ -1,4 +1,4 @@ -(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { - value = Math.floor(coercedNumber); - } else { - value = Math.ceil(coercedNumber); - } + value = absFloor(coercedNumber); } return value; @@ -2728,9 +2733,7 @@ moment.tz.load(require('./data/packed/latest.json')); function defineLocale (name, values) { if (values !== null) { values.abbr = name; - if (!locales[name]) { - locales[name] = new Locale(); - } + locales[name] = locales[name] || new Locale(); locales[name].set(values); // backwards compat for now: also set the locale @@ -2834,16 +2837,14 @@ moment.tz.load(require('./data/packed/latest.json')); } function zeroFill(number, targetLength, forceSign) { - var output = '' + Math.abs(number), + var absNumber = '' + Math.abs(number), + zerosToFill = targetLength - absNumber.length, sign = number >= 0; - - while (output.length < targetLength) { - output = '0' + output; - } - return (sign ? (forceSign ? '+' : '') : '-') + output; + return (sign ? (forceSign ? '+' : '') : '-') + + Math.pow(10, Math.max(0, zerosToFill)).toString().substr(1) + absNumber; } - var formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|x|X|zz?|ZZ?|.)/g; + var formattingTokens = /(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|Q|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,9}|x|X|zz?|ZZ?|.)/g; var localFormattingTokens = /(\[[^\[]*\])|(\\)?(LTS|LT|LL?L?L?|l{1,4})/g; @@ -2911,10 +2912,7 @@ moment.tz.load(require('./data/packed/latest.json')); } format = expandFormat(format, m.localeData()); - - if (!formatFunctions[format]) { - formatFunctions[format] = makeFormatFunction(format); - } + formatFunctions[format] = formatFunctions[format] || makeFormatFunction(format); return formatFunctions[format](m); } @@ -2958,8 +2956,15 @@ moment.tz.load(require('./data/packed/latest.json')); var regexes = {}; + function isFunction (sth) { + // https://github.com/moment/moment/issues/2325 + return typeof sth === 'function' && + Object.prototype.toString.call(sth) === '[object Function]'; + } + + function addRegexToken (token, regex, strictRegex) { - regexes[token] = typeof regex === 'function' ? regex : function (isStrict) { + regexes[token] = isFunction(regex) ? regex : function (isStrict) { return (isStrict && strictRegex) ? strictRegex : regex; }; } @@ -3167,12 +3172,11 @@ moment.tz.load(require('./data/packed/latest.json')); } function deprecate(msg, fn) { - var firstTime = true, - msgWithStack = msg + '\n' + (new Error()).stack; + var firstTime = true; return extend(function () { if (firstTime) { - warn(msgWithStack); + warn(msg + '\n' + (new Error()).stack); firstTime = false; } return fn.apply(this, arguments); @@ -3220,14 +3224,14 @@ moment.tz.load(require('./data/packed/latest.json')); getParsingFlags(config).iso = true; for (i = 0, l = isoDates.length; i < l; i++) { if (isoDates[i][1].exec(string)) { - // match[5] should be 'T' or undefined - config._f = isoDates[i][0] + (match[6] || ' '); + config._f = isoDates[i][0]; break; } } for (i = 0, l = isoTimes.length; i < l; i++) { if (isoTimes[i][1].exec(string)) { - config._f += isoTimes[i][0]; + // match[6] should be 'T' or space + config._f += (match[6] || ' ') + isoTimes[i][0]; break; } } @@ -3306,7 +3310,10 @@ moment.tz.load(require('./data/packed/latest.json')); addRegexToken('YYYYY', match1to6, match6); addRegexToken('YYYYYY', match1to6, match6); - addParseToken(['YYYY', 'YYYYY', 'YYYYYY'], YEAR); + addParseToken(['YYYYY', 'YYYYYY'], YEAR); + addParseToken('YYYY', function (input, array) { + array[YEAR] = input.length === 2 ? utils_hooks__hooks.parseTwoDigitYear(input) : toInt(input); + }); addParseToken('YY', function (input, array) { array[YEAR] = utils_hooks__hooks.parseTwoDigitYear(input); }); @@ -3433,18 +3440,18 @@ moment.tz.load(require('./data/packed/latest.json')); //http://en.wikipedia.org/wiki/ISO_week_date#Calculating_a_date_given_the_year.2C_week_number_and_weekday function dayOfYearFromWeeks(year, week, weekday, firstDayOfWeekOfYear, firstDayOfWeek) { - var d = createUTCDate(year, 0, 1).getUTCDay(); - var daysToAdd; - var dayOfYear; + var week1Jan = 6 + firstDayOfWeek - firstDayOfWeekOfYear, janX = createUTCDate(year, 0, 1 + week1Jan), d = janX.getUTCDay(), dayOfYear; + if (d < firstDayOfWeek) { + d += 7; + } - d = d === 0 ? 7 : d; - weekday = weekday != null ? weekday : firstDayOfWeek; - daysToAdd = firstDayOfWeek - d + (d > firstDayOfWeekOfYear ? 7 : 0) - (d < firstDayOfWeek ? 7 : 0); - dayOfYear = 7 * (week - 1) + (weekday - firstDayOfWeek) + daysToAdd + 1; + weekday = weekday != null ? 1 * weekday : firstDayOfWeek; + + dayOfYear = 1 + week1Jan + 7 * (week - 1) - d + weekday; return { - year : dayOfYear > 0 ? year : year - 1, - dayOfYear : dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear + year: dayOfYear > 0 ? year : year - 1, + dayOfYear: dayOfYear > 0 ? dayOfYear : daysInYear(year - 1) + dayOfYear }; } @@ -3730,9 +3737,19 @@ moment.tz.load(require('./data/packed/latest.json')); } function createFromConfig (config) { + var res = new Moment(checkOverflow(prepareConfig(config))); + if (res._nextDay) { + // Adding is smart enough around DST + res.add(1, 'd'); + res._nextDay = undefined; + } + + return res; + } + + function prepareConfig (config) { var input = config._i, - format = config._f, - res; + format = config._f; config._locale = config._locale || locale_locales__getLocale(config._l); @@ -3756,14 +3773,7 @@ moment.tz.load(require('./data/packed/latest.json')); configFromInput(config); } - res = new Moment(checkOverflow(config)); - if (res._nextDay) { - // Adding is smart enough around DST - res.add(1, 'd'); - res._nextDay = undefined; - } - - return res; + return config; } function configFromInput(config) { @@ -3843,7 +3853,7 @@ moment.tz.load(require('./data/packed/latest.json')); } res = moments[0]; for (i = 1; i < moments.length; ++i) { - if (moments[i][fn](res)) { + if (!moments[i].isValid() || moments[i][fn](res)) { res = moments[i]; } } @@ -3955,7 +3965,6 @@ moment.tz.load(require('./data/packed/latest.json')); } else { return local__createLocal(input).local(); } - return model._isUTC ? local__createLocal(input).zone(model._offset || 0) : local__createLocal(input).local(); } function getDateOffset (m) { @@ -4055,12 +4064,7 @@ moment.tz.load(require('./data/packed/latest.json')); } function hasAlignedHourOffset (input) { - if (!input) { - input = 0; - } - else { - input = local__createLocal(input).utcOffset(); - } + input = input ? local__createLocal(input).utcOffset() : 0; return (this.utcOffset() - input) % 60 === 0; } @@ -4073,12 +4077,24 @@ moment.tz.load(require('./data/packed/latest.json')); } function isDaylightSavingTimeShifted () { - if (this._a) { - var other = this._isUTC ? create_utc__createUTC(this._a) : local__createLocal(this._a); - return this.isValid() && compareArrays(this._a, other.toArray()) > 0; + if (typeof this._isDSTShifted !== 'undefined') { + return this._isDSTShifted; } - return false; + var c = {}; + + copyConfig(c, this); + c = prepareConfig(c); + + if (c._a) { + var other = c._isUTC ? create_utc__createUTC(c._a) : local__createLocal(c._a); + this._isDSTShifted = this.isValid() && + compareArrays(c._a, other.toArray()) > 0; + } else { + this._isDSTShifted = false; + } + + return this._isDSTShifted; } function isLocal () { @@ -4238,7 +4254,7 @@ moment.tz.load(require('./data/packed/latest.json')); var add_subtract__add = createAdder(1, 'add'); var add_subtract__subtract = createAdder(-1, 'subtract'); - function moment_calendar__calendar (time) { + function moment_calendar__calendar (time, formats) { // We want to compare the start of today, vs this. // Getting start-of-today depends on whether we're local/utc/offset or not. var now = time || local__createLocal(), @@ -4250,7 +4266,7 @@ moment.tz.load(require('./data/packed/latest.json')); diff < 1 ? 'sameDay' : diff < 2 ? 'nextDay' : diff < 7 ? 'nextWeek' : 'sameElse'; - return this.format(this.localeData().calendar(format, this, local__createLocal(now))); + return this.format(formats && formats[format] || this.localeData().calendar(format, this, local__createLocal(now))); } function clone () { @@ -4297,14 +4313,6 @@ moment.tz.load(require('./data/packed/latest.json')); } } - function absFloor (number) { - if (number < 0) { - return Math.ceil(number); - } else { - return Math.floor(number); - } - } - function diff (input, units, asFloat) { var that = cloneWithOffset(input, this), zoneDelta = (that.utcOffset() - this.utcOffset()) * 6e4, @@ -4495,6 +4503,19 @@ moment.tz.load(require('./data/packed/latest.json')); return [m.year(), m.month(), m.date(), m.hour(), m.minute(), m.second(), m.millisecond()]; } + function toObject () { + var m = this; + return { + years: m.year(), + months: m.month(), + date: m.date(), + hours: m.hours(), + minutes: m.minutes(), + seconds: m.seconds(), + milliseconds: m.milliseconds() + }; + } + function moment_valid__isValid () { return valid__isValid(this); } @@ -4666,18 +4687,20 @@ moment.tz.load(require('./data/packed/latest.json')); // HELPERS function parseWeekday(input, locale) { - if (typeof input === 'string') { - if (!isNaN(input)) { - input = parseInt(input, 10); - } - else { - input = locale.weekdaysParse(input); - if (typeof input !== 'number') { - return null; - } - } + if (typeof input !== 'string') { + return input; } - return input; + + if (!isNaN(input)) { + return parseInt(input, 10); + } + + input = locale.weekdaysParse(input); + if (typeof input === 'number') { + return input; + } + + return null; } // LOCALES @@ -4700,9 +4723,7 @@ moment.tz.load(require('./data/packed/latest.json')); function localeWeekdaysParse (weekdayName) { var i, mom, regex; - if (!this._weekdaysParse) { - this._weekdaysParse = []; - } + this._weekdaysParse = this._weekdaysParse || []; for (i = 0; i < 7; i++) { // make the regex if we don't have it already @@ -4849,12 +4870,26 @@ moment.tz.load(require('./data/packed/latest.json')); return ~~(this.millisecond() / 10); }); - function millisecond__milliseconds (token) { - addFormatToken(0, [token, 3], 0, 'millisecond'); - } + addFormatToken(0, ['SSS', 3], 0, 'millisecond'); + addFormatToken(0, ['SSSS', 4], 0, function () { + return this.millisecond() * 10; + }); + addFormatToken(0, ['SSSSS', 5], 0, function () { + return this.millisecond() * 100; + }); + addFormatToken(0, ['SSSSSS', 6], 0, function () { + return this.millisecond() * 1000; + }); + addFormatToken(0, ['SSSSSSS', 7], 0, function () { + return this.millisecond() * 10000; + }); + addFormatToken(0, ['SSSSSSSS', 8], 0, function () { + return this.millisecond() * 100000; + }); + addFormatToken(0, ['SSSSSSSSS', 9], 0, function () { + return this.millisecond() * 1000000; + }); - millisecond__milliseconds('SSS'); - millisecond__milliseconds('SSSS'); // ALIASES @@ -4865,11 +4900,19 @@ moment.tz.load(require('./data/packed/latest.json')); addRegexToken('S', match1to3, match1); addRegexToken('SS', match1to3, match2); addRegexToken('SSS', match1to3, match3); - addRegexToken('SSSS', matchUnsigned); - addParseToken(['S', 'SS', 'SSS', 'SSSS'], function (input, array) { - array[MILLISECOND] = toInt(('0.' + input) * 1000); - }); + var token; + for (token = 'SSSS'; token.length <= 9; token += 'S') { + addRegexToken(token, matchUnsigned); + } + + function parseMs(input, array) { + array[MILLISECOND] = toInt(('0.' + input) * 1000); + } + + for (token = 'S'; token.length <= 9; token += 'S') { + addParseToken(token, parseMs); + } // MOMENTS var getSetMillisecond = makeGetSet('Milliseconds', false); @@ -4916,6 +4959,7 @@ moment.tz.load(require('./data/packed/latest.json')); momentPrototype__proto.startOf = startOf; momentPrototype__proto.subtract = add_subtract__subtract; momentPrototype__proto.toArray = toArray; + momentPrototype__proto.toObject = toObject; momentPrototype__proto.toDate = toDate; momentPrototype__proto.toISOString = moment_format__toISOString; momentPrototype__proto.toJSON = moment_format__toISOString; @@ -5015,19 +5059,23 @@ moment.tz.load(require('./data/packed/latest.json')); LT : 'h:mm A', L : 'MM/DD/YYYY', LL : 'MMMM D, YYYY', - LLL : 'MMMM D, YYYY LT', - LLLL : 'dddd, MMMM D, YYYY LT' + LLL : 'MMMM D, YYYY h:mm A', + LLLL : 'dddd, MMMM D, YYYY h:mm A' }; function longDateFormat (key) { - var output = this._longDateFormat[key]; - if (!output && this._longDateFormat[key.toUpperCase()]) { - output = this._longDateFormat[key.toUpperCase()].replace(/MMMM|MM|DD|dddd/g, function (val) { - return val.slice(1); - }); - this._longDateFormat[key] = output; + var format = this._longDateFormat[key], + formatUpper = this._longDateFormat[key.toUpperCase()]; + + if (format || !formatUpper) { + return format; } - return output; + + this._longDateFormat[key] = formatUpper.replace(/MMMM|MM|DD|dddd/g, function (val) { + return val.slice(1); + }); + + return this._longDateFormat[key]; } var defaultInvalidDate = 'Invalid date'; @@ -5236,12 +5284,29 @@ moment.tz.load(require('./data/packed/latest.json')); return duration_add_subtract__addSubtract(this, input, value, -1); } + function absCeil (number) { + if (number < 0) { + return Math.floor(number); + } else { + return Math.ceil(number); + } + } + function bubble () { var milliseconds = this._milliseconds; var days = this._days; var months = this._months; var data = this._data; - var seconds, minutes, hours, years = 0; + var seconds, minutes, hours, years, monthsFromDays; + + // if we have a mix of positive and negative values, bubble down first + // check: https://github.com/moment/moment/issues/2166 + if (!((milliseconds >= 0 && days >= 0 && months >= 0) || + (milliseconds <= 0 && days <= 0 && months <= 0))) { + milliseconds += absCeil(monthsToDays(months) + days) * 864e5; + days = 0; + months = 0; + } // The following code bubbles up values, see the tests for // examples of what that means. @@ -5258,17 +5323,13 @@ moment.tz.load(require('./data/packed/latest.json')); days += absFloor(hours / 24); - // Accurately convert days to years, assume start from year 0. - years = absFloor(daysToYears(days)); - days -= absFloor(yearsToDays(years)); - - // 30 days to a month - // TODO (iskren): Use anchor date (like 1st Jan) to compute this. - months += absFloor(days / 30); - days %= 30; + // convert days to months + monthsFromDays = absFloor(daysToMonths(days)); + months += monthsFromDays; + days -= absCeil(monthsToDays(monthsFromDays)); // 12 months -> 1 year - years += absFloor(months / 12); + years = absFloor(months / 12); months %= 12; data.days = days; @@ -5278,15 +5339,15 @@ moment.tz.load(require('./data/packed/latest.json')); return this; } - function daysToYears (days) { + function daysToMonths (days) { // 400 years have 146097 days (taking into account leap year rules) - return days * 400 / 146097; + // 400 years have 12 months === 4800 + return days * 4800 / 146097; } - function yearsToDays (years) { - // years * 365 + absFloor(years / 4) - - // absFloor(years / 100) + absFloor(years / 400); - return years * 146097 / 400; + function monthsToDays (months) { + // the reverse of daysToMonths + return months * 146097 / 4800; } function as (units) { @@ -5298,11 +5359,11 @@ moment.tz.load(require('./data/packed/latest.json')); if (units === 'month' || units === 'year') { days = this._days + milliseconds / 864e5; - months = this._months + daysToYears(days) * 12; + months = this._months + daysToMonths(days); return units === 'month' ? months : months / 12; } else { // handle milliseconds separately because of floating point math errors (issue #1867) - days = this._days + Math.round(yearsToDays(this._months / 12)); + days = this._days + Math.round(monthsToDays(this._months)); switch (units) { case 'week' : return days / 7 + milliseconds / 6048e5; case 'day' : return days + milliseconds / 864e5; @@ -5352,7 +5413,7 @@ moment.tz.load(require('./data/packed/latest.json')); }; } - var duration_get__milliseconds = makeGetter('milliseconds'); + var milliseconds = makeGetter('milliseconds'); var seconds = makeGetter('seconds'); var minutes = makeGetter('minutes'); var hours = makeGetter('hours'); @@ -5430,13 +5491,36 @@ moment.tz.load(require('./data/packed/latest.json')); var iso_string__abs = Math.abs; function iso_string__toISOString() { + // for ISO strings we do not use the normal bubbling rules: + // * milliseconds bubble up until they become hours + // * days do not bubble at all + // * months bubble up until they become years + // This is because there is no context-free conversion between hours and days + // (think of clock changes) + // and also not between days and months (28-31 days per month) + var seconds = iso_string__abs(this._milliseconds) / 1000; + var days = iso_string__abs(this._days); + var months = iso_string__abs(this._months); + var minutes, hours, years; + + // 3600 seconds -> 60 minutes -> 1 hour + minutes = absFloor(seconds / 60); + hours = absFloor(minutes / 60); + seconds %= 60; + minutes %= 60; + + // 12 months -> 1 year + years = absFloor(months / 12); + months %= 12; + + // inspired by https://github.com/dordille/moment-isoduration/blob/master/moment.isoduration.js - var Y = iso_string__abs(this.years()); - var M = iso_string__abs(this.months()); - var D = iso_string__abs(this.days()); - var h = iso_string__abs(this.hours()); - var m = iso_string__abs(this.minutes()); - var s = iso_string__abs(this.seconds() + this.milliseconds() / 1000); + var Y = years; + var M = months; + var D = days; + var h = hours; + var m = minutes; + var s = seconds; var total = this.asSeconds(); if (!total) { @@ -5473,7 +5557,7 @@ moment.tz.load(require('./data/packed/latest.json')); duration_prototype__proto.valueOf = duration_as__valueOf; duration_prototype__proto._bubble = bubble; duration_prototype__proto.get = duration_get__get; - duration_prototype__proto.milliseconds = duration_get__milliseconds; + duration_prototype__proto.milliseconds = milliseconds; duration_prototype__proto.seconds = seconds; duration_prototype__proto.minutes = minutes; duration_prototype__proto.hours = hours; @@ -5511,7 +5595,7 @@ moment.tz.load(require('./data/packed/latest.json')); // Side effect imports - utils_hooks__hooks.version = '2.10.3'; + utils_hooks__hooks.version = '2.10.6'; setHookCallback(local__createLocal); @@ -5542,21 +5626,26 @@ moment.tz.load(require('./data/packed/latest.json')); return _moment; })); -},{}],"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/persistence/MessagePersistence.coffee":[function(require,module,exports){ -var MessageActions; +},{}],17:[function(require,module,exports){ +var MessageActions, send; MessageActions = require('../actions/MessageActions.coffee'); +window.urb.appl = "talk"; + +send = function(data, cb) { + return window.urb.send(data, { + mark: "talk-command" + }, cb); +}; + module.exports = { listenStation: function(station, since) { var $this; console.log('listen station'); console.log(arguments); $this = this; - return window.urb.subscribe({ - appl: "talk", - path: "/f/" + station + "/" + since - }, function(err, res) { + return window.urb.bind("/f/" + station + "/" + since, function(err, res) { var ref, ref1; if (err || !res.data) { console.log('/f/ err!'); @@ -5578,10 +5667,7 @@ module.exports = { get: function(station, start, end) { end = window.urb.util.numDot(end); start = window.urb.util.numDot(start); - return window.urb.subscribe({ - appl: "talk", - path: "/f/" + station + "/" + end + "/" + start - }, function(err, res) { + return window.urb.bind("/f/" + station + "/" + end + "/" + start, function(err, res) { var ref, ref1; if (err || !res.data) { console.log('/f/ /e/s err'); @@ -5592,10 +5678,7 @@ module.exports = { console.log(res); if ((ref = res.data) != null ? (ref1 = ref.grams) != null ? ref1.tele : void 0 : void 0) { MessageActions.loadMessages(res.data.grams, true); - return window.urb.unsubscribe({ - appl: "talk", - path: "/f/" + station + "/" + end + "/" + start - }, function(err, res) { + return window.urb.drop("/f/" + station + "/" + end + "/" + start, function(err, res) { console.log('done'); return console.log(res); }); @@ -5603,12 +5686,8 @@ module.exports = { }); }, sendMessage: function(message, cb) { - return window.urb.send({ - appl: "talk", - mark: "talk-command", - data: { - publish: [message] - } + return send({ + publish: [message] }, function(err, res) { console.log('sent'); console.log(arguments); @@ -5621,72 +5700,59 @@ module.exports = { -},{"../actions/MessageActions.coffee":"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/actions/MessageActions.coffee"}],"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/persistence/StationPersistence.coffee":[function(require,module,exports){ -var StationActions; +},{"../actions/MessageActions.coffee":1}],18:[function(require,module,exports){ +var StationActions, design, send; StationActions = require('../actions/StationActions.coffee'); +window.urb.appl = "talk"; + +send = function(data, cb) { + return window.urb.send(data, { + mark: "talk-command" + }, cb); +}; + +design = function(party, config, cb) { + return send({ + design: { + party: party, + config: config + } + }, cb); +}; + module.exports = { createStation: function(name, cb) { - return window.urb.send({ - appl: "talk", - mark: "talk-command", - data: { - design: { - party: name, - config: { - sources: [], - caption: "", - cordon: { - posture: "white", - list: [] - } - } - } + return design(name, { + sources: [], + caption: "", + cordon: { + posture: "white", + list: [] } }, cb); }, removeStation: function(name, cb) { - return window.urb.send({ - appl: "talk", - mark: "talk-command", - data: { - design: { - party: name, - config: null - } - } - }, cb); + return design(name, null, cb); }, setSources: function(station, ship, sources) { - var send; - send = { - appl: "talk", - mark: "talk-command", - data: { - design: { - party: station, - config: { - sources: sources, - caption: "", - cordon: { - posture: "white", - list: [] - } - } - } - } + var cordon; + cordon = { + posture: "white", + list: [] }; - return window.urb.send(send, function(err, res) { + return design(station, { + sources: sources, + cordon: cordon, + caption: "" + }, function(err, res) { console.log('talk-command'); return console.log(arguments); }); }, members: function() { - return window.urb.subscribe({ - appl: "talk", - path: "/a/court" - }, function(err, res) { + return window.urb.bind("/a/court", function(err, res) { var ref, ref1; if (err || !res) { console.log('/a/ err'); @@ -5701,10 +5767,7 @@ module.exports = { }); }, listen: function() { - return window.urb.subscribe({ - appl: "talk", - path: "/" - }, function(err, res) { + return window.urb.bind("/", function(err, res) { if (err || !res.data) { console.log('/ err'); console.log(err); @@ -5718,10 +5781,7 @@ module.exports = { }); }, listenStation: function(station) { - return window.urb.subscribe({ - appl: "talk", - path: "/ax/" + station - }, function(err, res) { + return window.urb.bind("/ax/" + station, function(err, res) { var ref; if (err || !res) { console.log('/ax/ err'); @@ -5746,7 +5806,7 @@ module.exports = { -},{"../actions/StationActions.coffee":"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/actions/StationActions.coffee"}],"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/stores/MessageStore.coffee":[function(require,module,exports){ +},{"../actions/StationActions.coffee":2}],19:[function(require,module,exports){ var EventEmitter, MessageDispatcher, MessageStore, _fetching, _last, _listening, _messages, _station, _typing, moment; moment = require('moment-timezone'); @@ -5892,7 +5952,7 @@ module.exports = MessageStore; -},{"../dispatcher/Dispatcher.coffee":"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/dispatcher/Dispatcher.coffee","events":"/usr/local/lib/node_modules/watchify/node_modules/browserify/node_modules/events/events.js","moment-timezone":"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/node_modules/moment-timezone/index.js"}],"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/stores/StationStore.coffee":[function(require,module,exports){ +},{"../dispatcher/Dispatcher.coffee":7,"events":22,"moment-timezone":14}],20:[function(require,module,exports){ var EventEmitter, StationDispatcher, StationStore, _audience, _config, _listening, _members, _station, _stations, _typing, _validAudience; EventEmitter = require('events').EventEmitter; @@ -6092,7 +6152,7 @@ module.exports = StationStore; -},{"../dispatcher/Dispatcher.coffee":"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/dispatcher/Dispatcher.coffee","events":"/usr/local/lib/node_modules/watchify/node_modules/browserify/node_modules/events/events.js"}],"/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/util.coffee":[function(require,module,exports){ +},{"../dispatcher/Dispatcher.coffee":7,"events":22}],21:[function(require,module,exports){ if (!window.util) { window.util = {}; } @@ -6204,7 +6264,7 @@ _.merge(window.util, { -},{}],"/usr/local/lib/node_modules/watchify/node_modules/browserify/node_modules/events/events.js":[function(require,module,exports){ +},{}],22:[function(require,module,exports){ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a @@ -6264,10 +6324,8 @@ EventEmitter.prototype.emit = function(type) { er = arguments[1]; if (er instanceof Error) { throw er; // Unhandled 'error' event - } else { - throw TypeError('Uncaught, unspecified "error" event.'); } - return false; + throw TypeError('Uncaught, unspecified "error" event.'); } } @@ -6509,4 +6567,4 @@ function isUndefined(arg) { return arg === void 0; } -},{}]},{},["/Users/galen/src/urbit-dev/urb/zod/base/pub/talk/src/js/main.coffee"]); +},{}]},{},[8]); diff --git a/pub/talk/src/js/persistence/MessagePersistence.coffee b/pub/talk/src/js/persistence/MessagePersistence.coffee index e6ac71f49..fe855071c 100644 --- a/pub/talk/src/js/persistence/MessagePersistence.coffee +++ b/pub/talk/src/js/persistence/MessagePersistence.coffee @@ -1,14 +1,13 @@ MessageActions = require '../actions/MessageActions.coffee' +window.urb.appl = "talk" +send = (data,cb)-> window.urb.send data, {mark:"talk-command"}, cb module.exports = listenStation: (station,since) -> console.log 'listen station' console.log arguments $this = this - window.urb.subscribe { - appl:"talk" - path:"/f/#{station}/#{since}" - }, (err,res) -> + window.urb.bind "/f/#{station}/#{since}", (err,res) -> if err or not res.data console.log '/f/ err!' console.log err @@ -25,10 +24,7 @@ module.exports = get: (station,start,end) -> end = window.urb.util.numDot end start = window.urb.util.numDot start - window.urb.subscribe { - appl:"talk" - path:"/f/#{station}/#{end}/#{start}" - }, (err,res) -> + window.urb.bind "/f/#{station}/#{end}/#{start}", (err,res) -> if err or not res.data console.log '/f/ /e/s err' console.log err @@ -37,22 +33,12 @@ module.exports = console.log res if res.data?.grams?.tele MessageActions.loadMessages res.data.grams,true - window.urb.unsubscribe { - appl:"talk" - path:"/f/#{station}/#{end}/#{start}" - }, (err,res) -> + window.urb.drop "/f/#{station}/#{end}/#{start}", (err,res) -> console.log 'done' console.log res sendMessage: (message,cb) -> - window.urb.send { - appl:"talk" - mark:"talk-command" - data: - publish: [ - message - ] - }, (err,res) -> + send {publish: [message]}, (err,res) -> console.log 'sent' console.log arguments cb(err,res) if cb diff --git a/pub/talk/src/js/persistence/StationPersistence.coffee b/pub/talk/src/js/persistence/StationPersistence.coffee index 23b4ba542..3ff382819 100644 --- a/pub/talk/src/js/persistence/StationPersistence.coffee +++ b/pub/talk/src/js/persistence/StationPersistence.coffee @@ -1,87 +1,55 @@ StationActions = require '../actions/StationActions.coffee' +window.urb.appl = "talk" +send = (data,cb)-> window.urb.send data, {mark:"talk-command"}, cb +design = (party,config,cb)-> send {design:{party,config}}, cb module.exports = createStation: (name,cb) -> - window.urb.send { - appl:"talk" - mark:"talk-command" - data: - design: - party:name - config: - sources:[] - caption:"" - cordon:{posture:"white", list:[]} - }, cb - - removeStation: (name,cb) -> - window.urb.send { - appl:"talk" - mark:"talk-command" - data: - design: - party:name - config:null + design name, { + sources:[] + caption:"" + cordon: posture:"white", list:[] }, cb + removeStation: (name,cb) -> design name, null, cb setSources: (station,ship,sources) -> - send = - appl:"talk" - mark:"talk-command" - data: - design: - party:station - config: - sources:sources - caption:"" - cordon:{posture:"white", list:[]} - window.urb.send send, (err,res) -> + cordon = posture:"white", list:[] + design station, {sources,cordon,caption:""}, (err,res) -> console.log 'talk-command' console.log arguments - members: -> - window.urb.subscribe { - appl:"talk" - path:"/a/court" - }, (err,res) -> - if err or not res - console.log '/a/ err' - console.log err - return - console.log '/a/' - console.log res.data - if res.data?.group?.global - StationActions.loadMembers res.data.group.global + members: -> window.urb.bind "/a/court", (err,res) -> + if err or not res + console.log '/a/ err' + console.log err + return + console.log '/a/' + console.log res.data + if res.data?.group?.global + StationActions.loadMembers res.data.group.global - listen: -> - window.urb.subscribe { - appl:"talk" - path:"/" - }, (err,res) -> - if err or not res.data - console.log '/ err' - console.log err - return - console.log '/' - console.log res.data - if res.data.house - StationActions.loadStations res.data.house + listen: -> window.urb.bind "/", (err,res) -> + if err or not res.data + console.log '/ err' + console.log err + return + console.log '/' + console.log res.data + if res.data.house + StationActions.loadStations res.data.house - listenStation: (station) -> - window.urb.subscribe { - appl:"talk" - path:"/ax/#{station}" - }, (err,res) -> - if err or not res - console.log '/ax/ err' - console.log err - return - console.log('/ax/') - console.log(res.data) - if res.data.ok is true - StationActions.listeningStation station - if res.data.group - res.data.group.global[window.util.mainStationPath(window.urb.user)] = res.data.group.local - StationActions.loadMembers res.data.group.global - if res.data.cabal?.loc - StationActions.loadConfig station,res.data.cabal.loc \ No newline at end of file + listenStation: (station) -> window.urb.bind "/ax/#{station}", (err,res) -> + if err or not res + console.log '/ax/ err' + console.log err + return + console.log('/ax/') + console.log(res.data) + if res.data.ok is true + StationActions.listeningStation station + if res.data.group + res.data.group.global[window.util.mainStationPath(window.urb.user)] = + res.data.group.local + StationActions.loadMembers res.data.group.global + if res.data.cabal?.loc + StationActions.loadConfig station,res.data.cabal.loc diff --git a/pub/work/hymn.hook b/pub/work/hymn.hook new file mode 100644 index 000000000..5165d47b0 --- /dev/null +++ b/pub/work/hymn.hook @@ -0,0 +1,34 @@ +:: +:: +:::: /hook/hymn/fab/talk/pub/ + :: +|% +++ cdnj + |= a=wall ^- marl + %+ turn a + |= lib=tape + ;script(type "text/javascript", src "//cdnjs.cloudflare.com/ajax/libs/{lib}"); +-- +:: +:::: + :: +^- manx +;html + ;head + ;meta(charset "utf-8"); + ;* %- cdnj :~ + "jquery/2.1.1/jquery.js" + "lodash.js/2.4.1/lodash.min.js" + "react/0.13.1/react.js" + == + ;script(type "text/javascript", src "/~/at/home/lib/urb.js"); + ;meta(name "viewport", content "width=device-width, height=device-height, ". + "initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0"); + ;link(type "text/css", rel "stylesheet", href "/home/pub/work/src/css/main.css"); + ;title: Work + == + ;body + ;div#c; + ;script(type "text/javascript", src "/home/pub/work/src/js/main.js"); + == +== diff --git a/pub/work/src/css/fonts.styl b/pub/work/src/css/fonts.styl new file mode 100644 index 000000000..1e64aa386 --- /dev/null +++ b/pub/work/src/css/fonts.styl @@ -0,0 +1,84 @@ +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau.woff"); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-italic.woff"); + font-weight: 400; + font-style: italic; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-medium.woff"); + font-weight: 500; + font-style: normal; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-mediumitalic.woff"); + font-weight: 500; + font-style: italic; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-bold.woff"); + font-weight: 600; + font-style: normal; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-bolditalic.woff"); + font-weight: 600; + font-style: italic; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-super.woff"); + font-weight: 600; + font-style: normal; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-superitalic.woff"); + font-weight: 600; + font-style: italic; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-extralight.woff"); + font-weight: 200; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-light.woff"); + font-weight: 300; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-regular.woff"); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-medium.woff"); + font-weight: 500; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-bold.woff"); + font-weight: 600; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-black.woff"); + font-weight: 700; + font-style: normal; +} \ No newline at end of file diff --git a/pub/work/src/css/main.css b/pub/work/src/css/main.css new file mode 100644 index 000000000..71e9f3fcb --- /dev/null +++ b/pub/work/src/css/main.css @@ -0,0 +1,485 @@ +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau.woff"); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-italic.woff"); + font-weight: 400; + font-style: italic; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-medium.woff"); + font-weight: 500; + font-style: normal; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-mediumitalic.woff"); + font-weight: 500; + font-style: italic; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-bold.woff"); + font-weight: 600; + font-style: normal; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-bolditalic.woff"); + font-weight: 600; + font-style: italic; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-super.woff"); + font-weight: 600; + font-style: normal; +} +@font-face { + font-family: "bau"; + src: url("http://storage.googleapis.com/urbit-extra/bau-superitalic.woff"); + font-weight: 600; + font-style: italic; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-extralight.woff"); + font-weight: 200; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-light.woff"); + font-weight: 300; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-regular.woff"); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-medium.woff"); + font-weight: 500; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-bold.woff"); + font-weight: 600; + font-style: normal; +} +@font-face { + font-family: "scp"; + src: url("http://storage.googleapis.com/urbit-extra/scp-black.woff"); + font-weight: 700; + font-style: normal; +} +html, +body, +input, +label, +button { + font-family: "bau"; + font-size: 18px; +} +#c { + position: absolute; + top: 0rem; + left: 50%; + width: 34rem; + margin-left: -17rem; + margin-bottom: 12rem; +} +h1 { + font-weight: 500; +} +h1.leader:after { + content: "—"; + margin-left: 0.6rem; +} +.ctrl .sort label, +.ctrl .filter label { + text-transform: capitalize; + font-size: 1rem; + letter-spacing: 0.07rem; +} +.item .status { + text-transform: uppercase; + font-size: 0.7rem; + letter-spacing: 0.07rem; +} +.ctrl { + margin-top: 2rem; +} +.ctrl h1 { + font-size: 1rem; +} +.ctrl .sorts, +.ctrl .filters { + margin-left: -0.3rem; +} +.ctrl .sorts { + margin-top: 1rem; +} +.ctrl .sort, +.ctrl .filter label { + font-weight: 500; +} +.ctrl .filter { + line-height: 2rem; + margin-right: 1rem; + vertical-align: middle; +} +.ctrl .sort { + outline: none; + border: 0; + cursor: pointer; + background-color: #000; + color: #fff; + margin-right: 1rem; +} +.ctrl .filter label { + margin-right: 0.3rem; +} +.ctrl .sort label, +.ctrl .filter label { + line-height: 2rem; +} +.ctrl .sort.s-1 label, +.ctrl .sort.s--1 label { + margin-right: 0.3rem; + cursor: pointer; +} +.input-bool { + opacity: 0.3; +} +.input-bool.true, +.input-bool.false { + opacity: 1; +} +.input-bool, +.item .done { + width: 1rem; + height: 1rem; + border: 0.2rem solid #ccc; + padding: 0; + outline: none; + background-color: #fff; +} +.input-bool.true, +.item .done-true { + background-color: #ccc; +} +.items { + margin-top: 2rem; + margin-left: -2rem; +} +.item { + display: block; + max-height: 3rem; + margin-bottom: 1.5rem; + width: 36rem; + overflow: hidden; + background-color: #fff; + transition: max-height 200ms linear; +} +.item .description, +.item .discussion { + display: none; +} +.item.expand { + max-height: 24rem; + transition: max-height 200ms linear; +} +.item.expand .description, +.item.expand .discussion { + display: inherit; +} +.item .expand { + margin-left: 2rem; + cursor: pointer; + transform-origin: 6px 12px; + transition: transform 200ms linear; + -webkit-transform-origin: 6px 12px; +} +.item.expand .expand { + transform: rotate(90deg); + -webkit-transform: rotate(90deg); + transition: transform 200ms linear; +} +.item .header { + display: block; + width: 100%; + height: 1rem; + overflow: hidden; + white-space: nowrap; +} +.item .sort, +.item .title, +.item .date, +.item .tags, +.item .comment { + line-height: 2rem; +} +.item .sort, +.item .date { + font-family: 'scp'; +} +.item .creator, +.item .audience, +.item .date, +.item .ship { + font-size: 0.7rem; +} +.item .sort { + color: #ccc; +} +.item .done { + margin-top: 0.5rem; +} +.item .creator, +.item .audience, +.item .date, +.item .ship { + text-transform: uppercase; + height: 1rem; + letter-spacing: 0.07rem; +} +.item .creator { + max-width: 7rem; + margin: 0 1rem 0 2rem; +} +.item .label { + font-style: italic; + color: #555; +} +.item:hover .action-true .label, +.item .action { + display: none; +} +.item:hover .action-true .action { + display: inline-block; +} +.item .audience { + margin-left: 2.2rem; + width: 24rem; +} +.item .audience .input { + background-color: transparent; +} +.item .sort { + font-size: 0.6rem; + width: 2rem; + text-align: center; +} +.item .title { + min-width: 16rem; + margin-left: 0.5rem; +} +.item .date { + min-width: 6rem; +} +.item .tags { + min-width: 6rem; +} +.item .description, +.item .discussion { + line-height: 2rem; + margin: 0.5rem 0 0.5rem 2rem; +} +.item .discussion { + max-height: 18rem; + overflow-x: hidden; + overflow-y: scroll; +} +.item .description textarea { + min-width: 32rem; + min-height: 6rem; +} +.item .comp { + width: 3rem; + opacity: 0; +} +.item .comp .a { + display: block; + font-size: 0.7rem; + font-weight: 500; + line-height: 0.9rem; +} +.item:hover .comp { + opacity: 1; +} +.item .submit { + font-size: 0.7rem; + font-weight: 500; + display: inline-block; + background-color: #b9b9b9; + letter-spacing: 0.07rem; + color: #fff; + border: 0; + padding: 0 0.3rem; + margin-top: 0.5rem; +} +.comment { + margin-bottom: 1rem; +} +.comment .date, +.comment .ship { + margin-right: 1rem; +} +.comment .body { + margin-top: 1rem; +} +.comment.new .input { + max-width: 31rem; + margin-top: 1rem; +} +/* global */ +.top { + vertical-align: top; +} +.ib { + display: inline-block; +} +.hidden { + display: none; +} +.a { + display: inline; + cursor: pointer; + text-decoration: underline; +} +.input { + outline: none; + padding: 0 0.6rem 0 0.3rem; + background-color: #ececec; + border: 0; + font: inherit; + resize: none; + -webkit-user-select: text; + user-select: text; +} +textarea:focus, +.input:focus { + background-color: #e6e6e6; +} +.invalid .input { + color: #f00; +} +.caret.left { + border-left: 12px solid #000; + border-top: 9px solid transparent; + border-right: 9px solid transparent; + border-bottom: 9px solid transparent; + margin-top: 0.4rem; +} +.s-1 .caret, +.caret.down { + border-left: 6px solid transparent; + border-top: 6px solid #fff; + border-right: 6px solid transparent; + border-bottom: 6px solid transparent; +} +.s--1 .caret, +.caret.up { + border-left: 6px solid transparent; + border-top: 6px solid transparent; + border-right: 6px solid transparent; + border-bottom: 6px solid #fff; +} +.hr1 { + height: 0.2rem; + width: 6rem; + background-color: #ccc; + margin-left: 2.2rem; +} +.hr2 { + height: 0.1rem; + width: 3rem; + background-color: #ccc; +} +@media only screen and (min-device-width: 320px) and (max-device-width: 480px) { + #c { + width: 100%; + margin: 0; + left: 0.6rem; + } + h1 { + font-size: 1rem; + } + input, + textarea { + -webkit-appearance: none; + border-radius: 0; + } + .input { + padding: 0 0.3rem 0 0.1rem; + } + .ctrl { + margin-top: 1rem; + } + .ctrl .sorts, + .ctrl .filters { + margin-left: 0; + } + .ctrl .filters .done label { + display: none; + } + .ctrl .sort { + padding: 0.2rem 0.3rem; + } + .ctrl .filter { + line-height: 1.3rem; + } + .ctrl .sort label, + .ctrl .filter label { + font-size: 0.7rem; + letter-spacing: 0.05rem; + line-height: 1.3rem; + } + .items { + margin-left: 0; + } + .item { + width: auto; + } + .item .sort { + display: none; + } + .item .title, + .item .date, + .item .tags { + min-width: 4rem; + max-width: 6rem; + overflow: hidden; + margin-left: 0.3rem; + height: 2rem; + } + .item .expand { + float: right; + margin-right: 0.6rem; + } + .item .description, + .item .discussion { + margin: 0.5rem 0; + } + .item .description textarea { + min-width: 18rem; + margin: 0; + } + .item .comment.new { + max-width: 18rem; + } + .item .submit { + line-height: 1.6rem; + letter-spacing: 0; + } +} diff --git a/pub/work/src/css/main.styl b/pub/work/src/css/main.styl new file mode 100644 index 000000000..966d723a2 --- /dev/null +++ b/pub/work/src/css/main.styl @@ -0,0 +1,333 @@ +// +// fonts first +// + +@import 'fonts' + +html +body +input +label +button + font-family "bau" + font-size 18px + +#c + position absolute + top 0rem + left 50% + width 34rem + margin-left -17rem + margin-bottom 12rem + +h1 + font-weight 500 + +h1.leader:after + content "—" + margin-left .6rem + +.ctrl .sort label +.ctrl .filter label + text-transform capitalize + font-size 1rem + letter-spacing .07rem + +.item .status + text-transform uppercase + font-size .7rem + letter-spacing .07rem + +.ctrl + margin-top 2rem + + h1 + font-size 1rem + + .sorts + .filters + margin-left -.3rem + + .sorts + margin-top 1rem + + .sort + .filter label + font-weight 500 + + .filter + line-height 2rem + margin-right 1rem + vertical-align middle + + .sort + outline none + border 0 + cursor pointer + background-color #000 + color #fff + margin-right 1rem + + .filter label + margin-right .3rem + + .sort label + .filter label + line-height 2rem + + .sort.s-1 + .sort.s--1 + label + margin-right .3rem + cursor pointer + +.input-bool + opacity .3 + +.input-bool.true +.input-bool.false + opacity 1 + + +.input-bool +.item .done + width 1rem + height 1rem + border .2rem solid #ccc + padding 0 + outline none + background-color #fff + +.input-bool.true +.item .done-true + background-color #ccc + +.items + margin-top 2rem + margin-left -2rem + +.item + display block + max-height 3rem + margin-bottom 1.5rem + width 36rem + overflow hidden + background-color #fff + transition max-height 200ms linear + + .description + .discussion + display none + + &.expand + max-height 24rem + transition max-height 200ms linear + + .description + .discussion + display inherit + + .expand + margin-left 2rem + cursor pointer + transform-origin 6px 12px + transition transform 200ms linear + -webkit-transform-origin 6px 12px + + &.expand .expand + transform rotate(90deg) + -webkit-transform rotate(90deg) + transition transform 200ms linear + + .header + display block + width 100% + height 1rem + overflow hidden + white-space nowrap + + .sort + .title + .date + .tags + .comment + line-height 2rem + + .sort + .date + font-family 'scp' + + .creator + .audience + .date + .ship + font-size .7rem + + .sort + color #ccc + + .done + margin-top .5rem + + .creator + .audience + .date + .ship + text-transform uppercase + height 1rem + letter-spacing .07rem + + .creator + max-width 7rem + margin 0 1rem 0 2rem + + .label + font-style italic + color #555 + + &:hover .action-true .label + .action + display none + + &:hover .action-true .action + display inline-block + + .audience + margin-left 2.2rem + width 24rem + + .audience .input + background-color transparent + + .sort + font-size .6rem + width 2rem + text-align center + + .title + min-width 16rem + margin-left .5rem + + .date + min-width 6rem + + .tags + min-width 6rem + + .description + .discussion + line-height 2rem + margin .5rem 0 .5rem 2rem + + .discussion + max-height 18rem + overflow-x hidden + overflow-y scroll + + .description textarea + min-width 32rem + min-height 6rem + + .comp + width 3rem + opacity 0 + .a + display block + font-size .7rem + font-weight 500 + line-height .9rem + + &:hover .comp + opacity 1 + + .submit + font-size .7rem + font-weight 500 + display inline-block + background-color #b9b9b9 + letter-spacing .07rem + color #fff + border 0 + padding 0 .3rem + margin-top .5rem + +.comment + margin-bottom 1rem + + .date + .ship + margin-right 1rem + + .body + margin-top 1rem + + &.new .input + max-width 31rem + margin-top 1rem + +/* global */ + +.top + vertical-align top + +.ib + display inline-block + +.hidden + display none + +.a + display inline + cursor pointer + text-decoration underline + +.input + outline none + padding 0 .6rem 0 .3rem + background-color #ececec + border 0 + font inherit + resize none + -webkit-user-select text + user-select text + +textarea:focus +.input:focus + background-color #e6e6e6 + +.invalid .input + color red + +.caret.left + border-left 12px solid #000 + border-top 9px solid transparent + border-right 9px solid transparent + border-bottom 9px solid transparent + margin-top .4rem + +.s-1 .caret +.caret.down + border-left 6px solid transparent + border-top 6px solid #fff + border-right 6px solid transparent + border-bottom 6px solid transparent + +.s--1 .caret +.caret.up + border-left 6px solid transparent + border-top 6px solid transparent + border-right 6px solid transparent + border-bottom 6px solid #fff + +.hr1 + height .2rem + width 6rem + background-color #ccc + margin-left 2.2rem + +.hr2 + height .1rem + width 3rem + background-color #ccc + +@import 'mobile' \ No newline at end of file diff --git a/pub/work/src/css/mobile.styl b/pub/work/src/css/mobile.styl new file mode 100644 index 000000000..405500d6a --- /dev/null +++ b/pub/work/src/css/mobile.styl @@ -0,0 +1,87 @@ +/* computers --------*/ +// @media only screen and (min-width: 1024px) + + +/* laptops / small screens ----------- */ +// @media only screen and (max-width: 1170px) + + +/* tablets + phones ----------- */ +// @media only screen and (min-width: 320px) and (max-width: 1024px) + +/* phones portrait and landscape ----------- */ +@media only screen and (min-device-width: 320px) and (max-device-width: 480px) + #c + width 100% + margin 0 + left .6rem + + h1 + font-size 1rem + + input + textarea + -webkit-appearance none + border-radius 0 + + .input + padding 0 .3rem 0 .1rem + + .ctrl + margin-top 1rem + + .ctrl .sorts + .ctrl .filters + margin-left 0 + + .ctrl .filters .done label + display none + + .ctrl .sort + padding .2rem .3rem + + .ctrl .filter + line-height 1.3rem + + .ctrl .sort label + .ctrl .filter label + font-size .7rem + letter-spacing .05rem + line-height 1.3rem + + .items + margin-left 0 + + .item + width auto + + .item .sort + display none + + .item .title + .item .date + .item .tags + min-width 4rem + max-width 6rem + overflow hidden + margin-left .3rem + height 2rem + + .item .expand + float right + margin-right .6rem + + .item .description + .item .discussion + margin .5rem 0 + + .item .description textarea + min-width 18rem + margin 0 + + .item .comment.new + max-width 18rem + + .item .submit + line-height 1.6rem + letter-spacing 0 \ No newline at end of file diff --git a/pub/work/src/js/actions/WorkActions.coffee b/pub/work/src/js/actions/WorkActions.coffee new file mode 100644 index 000000000..01a6b9fde --- /dev/null +++ b/pub/work/src/js/actions/WorkActions.coffee @@ -0,0 +1,61 @@ +Dispatcher = require '../dispatcher/Dispatcher.coffee' +Persistence = require '../persistence/Persistence.coffee' +{uuid32} = require '../util.coffee' + +module.exports = + newItem: ({before,after},_item={}) -> + item = + date_created: Date.now() + date_modified: Date.now() + creator: window.urb.ship + version: -1 + id: _item.id ? uuid32() + date_due: _item.date_due ? null + done: _item.done ? null + doer: _item.doer ? null + tags: _item.tags ? [] + title: _item.title ? '' + description: _item.description ? '' + discussion: _item.discussion ? [] + audience: _item.audience ? + [window.util.talk.mainStationPath window.urb.ship] + if item.date_due or item.title or item.description + item.version++ + Persistence.put new:item + Dispatcher.handleViewAction {type:'newItem', before, after, item} + + setItem: ({id,version},key,val) -> + version += 1 + key = key.split('_').join '-' + set = "#{key}": val + Persistence.put old:{id,dif:{set}} + Dispatcher.handleViewAction {type:'updateItem',id,version,key,val} + + ownItem: ({id,version},act) -> + version += 1 + Persistence.put old:{id,version,dif:doer:"#{act}":null} + + removeItem: ({id,version}) -> + if version >= 0 + Persistence.put old:{id,dif:set:audience:[]} + Dispatcher.handleViewAction {type:'archiveItem',id} + + setAudience: ({id},to) -> + Persistence.put old:{id,dif:set:audience:to} + Dispatcher.handleViewAction {type:'setAudienece',id,to} + + addComment: ({id,version},val) -> + version += 1 + Persistence.put old:{id,version,dif:add:comment:val} + + setFilter: (key,val) -> Dispatcher.handleViewAction {type:'setFilter', key,val} + setSort: (key,val) -> Dispatcher.handleViewAction {type:'setSort',key,val} + moveItem: (list,to,from) -> + Persistence.put {sort:list} + Dispatcher.handleViewAction {type:'moveItems',list,to,from} + + listenList: (type)-> + Persistence.subscribe type, (err,d)-> + if d? + {sort,tasks} = d.data + Dispatcher.handleServerAction {type:"getData",sort,tasks} diff --git a/pub/work/src/js/components/FieldComponent.coffee b/pub/work/src/js/components/FieldComponent.coffee new file mode 100644 index 000000000..e4379ec2c --- /dev/null +++ b/pub/work/src/js/components/FieldComponent.coffee @@ -0,0 +1,95 @@ +recl = React.createClass +rece = React.createElement +{div,textarea} = React.DOM + +WorkActions = require '../actions/WorkActions.coffee' + +module.exports = recl + displayName: 'Field' + getInitialState: -> invalid:no + shouldComponentUpdate: (props)-> + while @oldValue?.length + if @oldValue[0] is props.defaultValue + return false + else @oldValue.shift() + true + + render: -> + className = "#{@props.className ? @props._key} field ib" + if @state.invalid then className += " invalid" + + props = _.extend {}, @props, { + @onKeyUp + ref:'input' + defaultValue: @props.render @props.defaultValue + className: 'input ib' + } + + div {className}, + if @props.textarea then (textarea props) + else + props.contentEditable = true + props.dangerouslySetInnerHTML = + __html: $('
').text(props.defaultValue).html() + (div props) + + onKeyUp: (e) -> + $t = $(e.target).closest '.field' + _val = @getVal() + if @props.item.ghost and _val is "" + return + val = @parse _val + unless @validate val + @setState invalid:yes + return + @setState invalid:no + + unless @equal @props.defaultValue, val + @oldValue ?= [] + @oldValue.push val + if @to then clearTimeout @to + @to = setTimeout => + {item,_key,index} = @props + if item.version >= 0 + WorkActions.setItem item, _key, val + else WorkActions.newItem {}, + id: item.id + tags: item.tags + audience: item.audience + "#{_key}": val + ,1000 + + getVal: -> + if @props.textarea + $(@refs.input.getDOMNode()).val() + else $(@refs.input.getDOMNode()).text() + + parse: (text)-> switch @props._key + when 'tags' then text.trim().split(" ") + when 'audience' then text.trim().split(" ").map (a) -> "~#{a}".toLowerCase() + when 'date_due' + d = text.slice(1).replace(/\./g, "-") + return NaN if d.length < 8 + new Date(d).valueOf() + else text + + equal: (vol=(@parse ""),val) -> switch @props._key + when 'tags', 'audience' + (_.xor(vol,val).length is 0) + when 'date_due' + vol.valueOf() is val + else vol is val + + validate: (val) -> switch @props._key + when 'date_due' + !isNaN(val) + when 'audience' + for a in val + [ship,station,rest...] = a.split("/") + return no unless (rest.length is 0) and ship and station + return no if ship[0] isnt "~" + return no if ship < 3 + return no if station < 3 + yes + else yes + diff --git a/pub/work/src/js/components/FilterComponent.coffee b/pub/work/src/js/components/FilterComponent.coffee new file mode 100644 index 000000000..1259b6c23 --- /dev/null +++ b/pub/work/src/js/components/FilterComponent.coffee @@ -0,0 +1,57 @@ +recl = React.createClass +rece = React.createElement +{div,h1,label,button} = React.DOM + +module.exports = recl + onClick: (e) -> + switch @props.filters['done'] + when null + b = true + when true + b = false + when false + b = null + @props.onChange 'done',b + onKeyDown: (e) -> + if e.keyCode is 13 + e.stopPropagation() + e.preventDefault() + onKeyUp: (e) -> @change(e) + onBlur: (e) -> @change(e) + change: (e) -> + $t = $(e.target).closest('.filter') + txt = $t.find('.input').text().trim() + key = $t.attr('data-key') + if txt.length is 0 then txt = null + else switch key + when 'creator' then txt = "~#{txt}" + when 'audience' then txt = txt.split " " + when 'tags' then txt = [txt] + @props.onChange key,txt + + fields: [ {filter:'done', key:'done', title: ''}, + {filter:'owned', key:'creator', title: 'Owner:'}, + {filter:'doer', key:'doer', title: 'Doer:'} + {filter:'tag', key:'tags', title: 'Tag:'}, + {filter:'channel', key:'audience', title: 'Audience:'}, + ] + + render: -> + (div {className:'filters'}, @fields.map ({filter,key,title})=> + (div {key, 'data-key':key, className:"#{filter} filter ib"}, + (label {}, title) + switch filter + when 'done' + (button { + className:'input-bool ib '+@props.filters[key], + @onClick + },"") + else + (div { + contentEditable:true + className:'input ib' + @onKeyDown + @onKeyUp + @onBlur + },@props.filters[filter]) + )) diff --git a/pub/work/src/js/components/ItemComponent.coffee b/pub/work/src/js/components/ItemComponent.coffee new file mode 100644 index 000000000..d6cc3a519 --- /dev/null +++ b/pub/work/src/js/components/ItemComponent.coffee @@ -0,0 +1,162 @@ +recl = React.createClass +rece = React.createElement +{div,textarea,button} = React.DOM + +WorkActions = require '../actions/WorkActions.coffee' +Field = require './FieldComponent.coffee' + +module.exports = recl + displayName: 'Item' + onDragStart: (e) -> + unless @props.draggable + e.preventDefault() + return + $t = $(e.target) + @dragged = $t.closest('.item') + e.dataTransfer.effectAllowed = 'move' + e.dataTransfer.setData 'text/html',e.currentTarget + @props._dragStart e,@ + + onDragEnd: (e) -> @props._dragEnd e,@ + + onKeyDown: (e) -> + @props.title_keyDown e,@ + + kc = e.keyCode + + switch kc + # tab - expand + when 9 + if @state.expand is false + @setState {expand:true} + # esc - collapse + when 27 + @setState {expand:false} + + if (kc is 9 and @state.expand is false) or (kc is 27) + e.preventDefault() + return + + onFocus: (e) -> @props._focus e,@ + + _markDone: (e) -> + WorkActions.setItem @props.item,'done',(not (@props.item.done is true)) + + getStatus: -> switch @props.item.doer + when window.urb.ship then "owned" + when undefined then "" + when null then "available" + else "taken: ~"+@props.item.doer + + getAction: -> switch @props.item.doer + when null + action = "claim" + when window.urb.ship + action = "release" + else "" + + _changeStatus: (e) -> + return if @props.item.status is 'released' + if @props.item.status is 'accepted' and + @formatCreator(@props.item.creator) isnt window.urb.ship + return + WorkActions.ownItem @props.item,@getAction() + + _submitComment: (e) -> + $input = $(e.target).closest('.item').find('.comment .input') + val = $input.text() + return if val.length is 0 + WorkActions.addComment @props.item,val + $input.text('') + + formatDate: (d,l) -> + unless d + return "" + _d = "~#{d.getFullYear()}.#{(d.getMonth()+1)}.#{d.getDate()}" + if l + _d += "..#{d.getHours()}.#{d.getMinutes()}.#{d.getSeconds()}" + _d + + formatCreator: (o="") -> o.replace /\~/g,"" + + formatAudience: (a=[]) -> @formatCreator a.join(" ") + + getInitialState: -> {expand:false} + + renderField: (_key,props,render=_.identity)-> + {item,index} = @props + defaultValue = item[_key] + rece Field, $.extend props, {render,_key,defaultValue,item,index} + + renderTopField: (key,props,format)-> + _props = _.extend props,{className:"#{props.className ? key} top"} + @renderField key,_props,format + + componentDidMount: -> + setInterval => + $('.new.comment .date').text @formatDate new Date + , 1000 + + render: -> + itemClass = 'item' + if @state.expand then itemClass += ' expand' + + discussion = _.clone @props.item.discussion ? [] + discussion.reverse() + + status = @getStatus() + action = @getAction() + + (div { + className:itemClass + draggable:true + @onDragStart,@onDragEnd + }, + (div { + className:'header' + }, + (div {className:'creator ib'}, @formatCreator(@props.item.creator)) + (div { + className:'status ib action-'+(action.length > 0) + 'data-key':'status' + onClick:@_changeStatus + }, + (div {className:'label'}, status) + (div {className:'action a'}, action) + ) + (@renderField 'audience', {}, @formatAudience) + ) + (div {className:'sort ib top'}, @props.item.sort) + (button {className:'done ib done-'+(@props.item.done is true), onClick:@_markDone}, '') + (@renderTopField 'title', {@onFocus,@onKeyDown}) + (@renderTopField 'date_due', {className:'date'}, @formatDate) + (@renderTopField 'tags', {}, (tags=[])-> tags.join(" ")) + (div { + className:'expand ib', + onClick: (e) => @setState {expand:!@state.expand} + }, (div {className:'caret left'},"") + ) + (@renderField 'description',textarea: yes) + + (div {className:"hr"},"") + if discussion? + (div {className:"discussion"}, + (div {className:"comments"}, discussion.map (slug) => + (div {className:'comment',key:slug.date}, + (div {className:'hr2'},"") + (div {className:'ship ib'}, slug.ship) + (div {className:'date ib'}, @formatDate slug.date,true) + (div {className:'body'}, slug.body) + ) + ), + (div {className:'new comment'}, + (div {className:'hr2'},"") + (div {className:'ship ib'}, window.urb.ship) + (div {className:'date ib'}, @formatDate new Date) + (div { + contentEditable:true, + className:'input'}) + (button {className:'submit',onClick:@_submitComment},'Post') + ) + ) + ) diff --git a/pub/work/src/js/components/ListComponent.coffee b/pub/work/src/js/components/ListComponent.coffee new file mode 100644 index 000000000..afe369619 --- /dev/null +++ b/pub/work/src/js/components/ListComponent.coffee @@ -0,0 +1,174 @@ +recl = React.createClass +rece = React.createElement +{div,h1,input,textarea} = React.DOM + +WorkStore = require '../stores/WorkStore.coffee' +WorkActions = require '../actions/WorkActions.coffee' +ItemComponent = require './ItemComponent.coffee' + +ListeningComponent = require './ListeningComponent.coffee' +FilterComponent = require './FilterComponent.coffee' +SortComponent = require './SortComponent.coffee' + +module.exports = recl + displayName: 'List' + stateFromStore: -> window.canSort = WorkStore.canSort(); { + list:WorkStore.getList() + noNew:WorkStore.noNew() + canSort:WorkStore.canSort() + fulllist:WorkStore.getFullList() + listening:WorkStore.getListening() + sorts:WorkStore.getSorts() + filters:WorkStore.getFilters() + expand:false + updated:WorkStore.getUpdated() + } + + getInitialState: -> @stateFromStore() + _onChangeStore: -> @setState @stateFromStore() + + alias: -> + @$el = $ @getDOMNode() + @$items = @$el.find('.items').children() + + _focus: (e,i) -> @setState {selected:i.props.index} + + _dragStart: (e,i) -> @dragged = i.dragged + + _dragEnd: (e,i) -> + from = Number @dragged.closest('.item-wrap').attr('data-index') + to = Number @over.closest('.item-wrap').attr('data-index') + if from= 0) + WorkActions.moveItem list, to, from + @dragged.removeClass 'hidden' + @placeholder.remove() + + _dragOver: (e,i) -> + e.preventDefault() + $t = $(e.target).closest('.item') + if $t.hasClass 'placeholder' then return + if $t.length is 0 then return + @over = $t + if not @dragged.hasClass('hidden') then @dragged.addClass 'hidden' + if (e.clientY - $t[0].offsetTop) < ($t[0].offsetHeight / 2) + @drop = 'before' + @placeholder.insertBefore $t + else + @drop = 'after' + @placeholder.insertAfter $t + + title_keyDown: (e,i) -> switch e.keyCode + # enter - add new + when 13 + e.preventDefault() + return if @state.noNew + {item} = i.props + after = null; before = null + if window.getSelection().getRangeAt(0).endOffset is 0 + ins = @state.selected + before = item.id + else + after = item.id + ins = @state.selected+1 # XX consolidate + @setState {selected:ins,select:true} + unless item.ghost + {tags,audience} = item + item = {tags,audience} + WorkActions.newItem {before,after}, item + # backspace - remove if at 0 + when 8 + if (window.getSelection().getRangeAt(0).endOffset is 0) + e.preventDefault() + if (e.target.innerText.length is 0) + if @state.selected isnt 0 + @setState {selected:@state.selected-1,select:"end"} + WorkActions.removeItem i.props.item + else if ({index} = i.props; index > 0) and + (prev = @state.list[i.props.index - 1]; prev.version < 0) + WorkActions.removeItem prev + # up + when 38 + e.preventDefault() + last = @state.selected-1 + if last<0 then last = @state.list.length-1 + @$items.eq(last).find('.title .input').focus() + @setState {select:"end"} + # down + when 40 + e.preventDefault() + next = @state.selected+1 + if next is @state.list.length then next = 0 + @$items.eq(next).find('.title .input').focus() + @setState {select:"end"} + + _changeListening: -> + + _changeFilter: (key,val) -> WorkActions.setFilter key,val + + _changeSort: (key,val) -> WorkActions.setSort key,val + + componentDidMount: -> + @placeholder = $ "
x
" + WorkStore.addChangeListener @_onChangeStore + WorkActions.listenList @props.list + @alias() + + componentDidUpdate: (_props,_state)-> + @alias() + if @state.select + $title = @$items.eq(@state.selected).find('.title .input') + if @state.selected? + $title.focus() + if @state.select is "end" + r = window.getSelection().getRangeAt(0) + r.setStart $title[0],0 + r.setEnd $title[0],0 + s = window.getSelection() + s.removeAllRanges() + s.addRange r + @setState {select:false} + + render: -> + (div {}, + (div {className:'ctrl'}, + rece(ListeningComponent, { + listening:@state.listening + onChange:@_changeListening + }) + (div {className:'transforms'}, + rece(FilterComponent, { + filters:@state.filters + onChange:@_changeFilter + }) + rece(SortComponent, { + sorts:@state.sorts + onChange:@_changeSort + }) + ) + ) + (div { + className:'items' + onDragOver:@_dragOver + }, _.map @state.list,(item,index) => + className = "item-wrap" + key = item.id + draggable = @state.canSort + if item.ghost + className += " ghost" + draggable = false + (div {className,key,'data-index':index}, + rece(ItemComponent,{ + item + index + draggable + @_focus + @title_keyDown + @_dragStart + @_dragEnd}) + ) + ) + ) diff --git a/pub/work/src/js/components/ListeningComponent.coffee b/pub/work/src/js/components/ListeningComponent.coffee new file mode 100644 index 000000000..edada9a16 --- /dev/null +++ b/pub/work/src/js/components/ListeningComponent.coffee @@ -0,0 +1,7 @@ +recl = React.createClass +rece = React.createElement +{div,h1,input,textarea} = React.DOM + +module.exports = recl + render: -> + (div {className:'listening'}, "") diff --git a/pub/work/src/js/components/SortComponent.coffee b/pub/work/src/js/components/SortComponent.coffee new file mode 100644 index 000000000..bb30c29a0 --- /dev/null +++ b/pub/work/src/js/components/SortComponent.coffee @@ -0,0 +1,26 @@ +recl = React.createClass +rece = React.createElement +{div,h1,button,label} = React.DOM + +module.exports = recl + onClick: (e) -> + $t = $(e.target).closest '.sort' + key = $t.attr('data-key') + sor = Number $t.attr 'data-state' + if sor is 0 then sor = 1 + else if sor is 1 then sor = -1 + else if sor is -1 then sor = 0 + @props.onChange key,sor + render: -> + (div {className:'sorts'}, _.map @props.sorts,(state,key) => + (button { + key + @onClick + 'data-key':key + 'data-state':state + className:"sort s-#{state}" + }, + (label {}, key) + (div {className:'caret ib'}, '') + ) + ) diff --git a/pub/work/src/js/components/WorkComponent.coffee b/pub/work/src/js/components/WorkComponent.coffee new file mode 100644 index 000000000..a7663046a --- /dev/null +++ b/pub/work/src/js/components/WorkComponent.coffee @@ -0,0 +1,12 @@ +recl = React.createClass +rece = React.createElement +{div,h1} = React.DOM + +ListComponent = require './ListComponent.coffee' + +module.exports = recl + render: -> + (div {}, + (h1 {className:'leader'}, "Work") + (rece(ListComponent,{list:'upcoming'})) + ) diff --git a/pub/work/src/js/dispatcher/dispatcher.coffee b/pub/work/src/js/dispatcher/dispatcher.coffee new file mode 100644 index 000000000..4e65c7a0c --- /dev/null +++ b/pub/work/src/js/dispatcher/dispatcher.coffee @@ -0,0 +1,13 @@ +Dispatcher = require('flux').Dispatcher + +module.exports = _.merge new Dispatcher(), { + handleServerAction: (action) -> + @dispatch + source: 'server' + action: action + + handleViewAction: (action) -> + @dispatch + source: 'view' + action: action +} \ No newline at end of file diff --git a/pub/work/src/js/main.coffee b/pub/work/src/js/main.coffee new file mode 100644 index 000000000..e0f47bc08 --- /dev/null +++ b/pub/work/src/js/main.coffee @@ -0,0 +1,5 @@ +WorkComponent = require './components/WorkComponent.coffee' +window.util = _.extend window.util || {}, require './util.coffee' + +$ -> + React.render React.createElement(WorkComponent),$('#c')[0] diff --git a/pub/work/src/js/main.js b/pub/work/src/js/main.js new file mode 100644 index 000000000..b5020ac28 --- /dev/null +++ b/pub/work/src/js/main.js @@ -0,0 +1,2125 @@ +(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o= 0) { + Persistence.put({ + old: { + id: id, + dif: { + set: { + audience: [] + } + } + } + }); + } + return Dispatcher.handleViewAction({ + type: 'archiveItem', + id: id + }); + }, + setAudience: function(arg, to) { + var id; + id = arg.id; + Persistence.put({ + old: { + id: id, + dif: { + set: { + audience: to + } + } + } + }); + return Dispatcher.handleViewAction({ + type: 'setAudienece', + id: id, + to: to + }); + }, + addComment: function(arg, val) { + var id, version; + id = arg.id, version = arg.version; + version += 1; + return Persistence.put({ + old: { + id: id, + version: version, + dif: { + add: { + comment: val + } + } + } + }); + }, + setFilter: function(key, val) { + return Dispatcher.handleViewAction({ + type: 'setFilter', + key: key, + val: val + }); + }, + setSort: function(key, val) { + return Dispatcher.handleViewAction({ + type: 'setSort', + key: key, + val: val + }); + }, + moveItem: function(list, to, from) { + Persistence.put({ + sort: list + }); + return Dispatcher.handleViewAction({ + type: 'moveItems', + list: list, + to: to, + from: from + }); + }, + listenList: function(type) { + return Persistence.subscribe(type, function(err, d) { + var ref, sort, tasks; + if (d != null) { + ref = d.data, sort = ref.sort, tasks = ref.tasks; + return Dispatcher.handleServerAction({ + type: "getData", + sort: sort, + tasks: tasks + }); + } + }); + } +}; + + + +},{"../dispatcher/Dispatcher.coffee":9,"../persistence/Persistence.coffee":15,"../util.coffee":17}],2:[function(require,module,exports){ +var WorkActions, div, rece, recl, ref, textarea, + slice = [].slice; + +recl = React.createClass; + +rece = React.createElement; + +ref = React.DOM, div = ref.div, textarea = ref.textarea; + +WorkActions = require('../actions/WorkActions.coffee'); + +module.exports = recl({ + displayName: 'Field', + getInitialState: function() { + return { + invalid: false + }; + }, + shouldComponentUpdate: function(props) { + var ref1; + while ((ref1 = this.oldValue) != null ? ref1.length : void 0) { + if (this.oldValue[0] === props.defaultValue) { + return false; + } else { + this.oldValue.shift(); + } + } + return true; + }, + render: function() { + var className, props, ref1; + className = ((ref1 = this.props.className) != null ? ref1 : this.props._key) + " field ib"; + if (this.state.invalid) { + className += " invalid"; + } + props = _.extend({}, this.props, { + onKeyUp: this.onKeyUp, + ref: 'input', + defaultValue: this.props.render(this.props.defaultValue), + className: 'input ib' + }); + return div({ + className: className + }, this.props.textarea ? textarea(props) : (props.contentEditable = true, props.dangerouslySetInnerHTML = { + __html: $('
').text(props.defaultValue).html() + }, div(props))); + }, + onKeyUp: function(e) { + var $t, _val, val; + $t = $(e.target).closest('.field'); + _val = this.getVal(); + if (this.props.item.ghost && _val === "") { + return; + } + val = this.parse(_val); + if (!this.validate(val)) { + this.setState({ + invalid: true + }); + return; + } + this.setState({ + invalid: false + }); + if (!this.equal(this.props.defaultValue, val)) { + if (this.oldValue == null) { + this.oldValue = []; + } + this.oldValue.push(val); + if (this.to) { + clearTimeout(this.to); + } + return this.to = setTimeout((function(_this) { + return function() { + var _key, index, item, obj, ref1; + ref1 = _this.props, item = ref1.item, _key = ref1._key, index = ref1.index; + if (item.version >= 0) { + return WorkActions.setItem(item, _key, val); + } else { + return WorkActions.newItem({}, ( + obj = { + id: item.id, + tags: item.tags, + audience: item.audience + }, + obj["" + _key] = val, + obj + )); + } + }; + })(this), 1000); + } + }, + getVal: function() { + if (this.props.textarea) { + return $(this.refs.input.getDOMNode()).val(); + } else { + return $(this.refs.input.getDOMNode()).text(); + } + }, + parse: function(text) { + var d; + switch (this.props._key) { + case 'tags': + return text.trim().split(" "); + case 'audience': + return text.trim().split(" ").map(function(a) { + return ("~" + a).toLowerCase(); + }); + case 'date_due': + d = text.slice(1).replace(/\./g, "-"); + if (d.length < 8) { + return NaN; + } + return new Date(d).valueOf(); + default: + return text; + } + }, + equal: function(vol, val) { + if (vol == null) { + vol = this.parse(""); + } + switch (this.props._key) { + case 'tags': + case 'audience': + return _.xor(vol, val).length === 0; + case 'date_due': + return vol.valueOf() === val; + default: + return vol === val; + } + }, + validate: function(val) { + var a, i, len, ref1, rest, ship, station; + switch (this.props._key) { + case 'date_due': + return !isNaN(val); + case 'audience': + for (i = 0, len = val.length; i < len; i++) { + a = val[i]; + ref1 = a.split("/"), ship = ref1[0], station = ref1[1], rest = 3 <= ref1.length ? slice.call(ref1, 2) : []; + if (!((rest.length === 0) && ship && station)) { + return false; + } + if (ship[0] !== "~") { + return false; + } + if (ship < 3) { + return false; + } + if (station < 3) { + return false; + } + } + return true; + default: + return true; + } + } +}); + + + +},{"../actions/WorkActions.coffee":1}],3:[function(require,module,exports){ +var button, div, h1, label, rece, recl, ref; + +recl = React.createClass; + +rece = React.createElement; + +ref = React.DOM, div = ref.div, h1 = ref.h1, label = ref.label, button = ref.button; + +module.exports = recl({ + onClick: function(e) { + var b; + switch (this.props.filters['done']) { + case null: + b = true; + break; + case true: + b = false; + break; + case false: + b = null; + } + return this.props.onChange('done', b); + }, + onKeyDown: function(e) { + if (e.keyCode === 13) { + e.stopPropagation(); + return e.preventDefault(); + } + }, + onKeyUp: function(e) { + return this.change(e); + }, + onBlur: function(e) { + return this.change(e); + }, + change: function(e) { + var $t, key, txt; + $t = $(e.target).closest('.filter'); + txt = $t.find('.input').text().trim(); + key = $t.attr('data-key'); + if (txt.length === 0) { + txt = null; + } else { + switch (key) { + case 'creator': + txt = "~" + txt; + break; + case 'audience': + txt = txt.split(" "); + break; + case 'tags': + txt = [txt]; + } + } + return this.props.onChange(key, txt); + }, + fields: [ + { + filter: 'done', + key: 'done', + title: '' + }, { + filter: 'owned', + key: 'creator', + title: 'Owner:' + }, { + filter: 'doer', + key: 'doer', + title: 'Doer:' + }, { + filter: 'tag', + key: 'tags', + title: 'Tag:' + }, { + filter: 'channel', + key: 'audience', + title: 'Audience:' + } + ], + render: function() { + return div({ + className: 'filters' + }, this.fields.map((function(_this) { + return function(arg) { + var filter, key, title; + filter = arg.filter, key = arg.key, title = arg.title; + return div({ + key: key, + 'data-key': key, + className: filter + " filter ib" + }, label({}, title), (function() { + switch (filter) { + case 'done': + return button({ + className: 'input-bool ib ' + this.props.filters[key], + onClick: this.onClick + }, ""); + default: + return div({ + contentEditable: true, + className: 'input ib', + onKeyDown: this.onKeyDown, + onKeyUp: this.onKeyUp, + onBlur: this.onBlur + }, this.props.filters[filter]); + } + }).call(_this)); + }; + })(this))); + } +}); + + + +},{}],4:[function(require,module,exports){ +var Field, WorkActions, button, div, rece, recl, ref, textarea; + +recl = React.createClass; + +rece = React.createElement; + +ref = React.DOM, div = ref.div, textarea = ref.textarea, button = ref.button; + +WorkActions = require('../actions/WorkActions.coffee'); + +Field = require('./FieldComponent.coffee'); + +module.exports = recl({ + displayName: 'Item', + onDragStart: function(e) { + var $t; + if (!this.props.draggable) { + e.preventDefault(); + return; + } + $t = $(e.target); + this.dragged = $t.closest('.item'); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/html', e.currentTarget); + return this.props._dragStart(e, this); + }, + onDragEnd: function(e) { + return this.props._dragEnd(e, this); + }, + onKeyDown: function(e) { + var kc; + this.props.title_keyDown(e, this); + kc = e.keyCode; + switch (kc) { + case 9: + if (this.state.expand === false) { + this.setState({ + expand: true + }); + } + break; + case 27: + this.setState({ + expand: false + }); + } + if ((kc === 9 && this.state.expand === false) || (kc === 27)) { + e.preventDefault(); + } + }, + onFocus: function(e) { + return this.props._focus(e, this); + }, + _markDone: function(e) { + return WorkActions.setItem(this.props.item, 'done', !(this.props.item.done === true)); + }, + getStatus: function() { + switch (this.props.item.doer) { + case window.urb.ship: + return "owned"; + case void 0: + return ""; + case null: + return "available"; + default: + return "taken: ~" + this.props.item.doer; + } + }, + getAction: function() { + var action; + switch (this.props.item.doer) { + case null: + return action = "claim"; + case window.urb.ship: + return action = "release"; + default: + return ""; + } + }, + _changeStatus: function(e) { + if (this.props.item.status === 'released') { + return; + } + if (this.props.item.status === 'accepted' && this.formatCreator(this.props.item.creator) !== window.urb.ship) { + return; + } + return WorkActions.ownItem(this.props.item, this.getAction()); + }, + _submitComment: function(e) { + var $input, val; + $input = $(e.target).closest('.item').find('.comment .input'); + val = $input.text(); + if (val.length === 0) { + return; + } + WorkActions.addComment(this.props.item, val); + return $input.text(''); + }, + formatDate: function(d, l) { + var _d; + if (!d) { + return ""; + } + _d = "~" + (d.getFullYear()) + "." + (d.getMonth() + 1) + "." + (d.getDate()); + if (l) { + _d += ".." + (d.getHours()) + "." + (d.getMinutes()) + "." + (d.getSeconds()); + } + return _d; + }, + formatCreator: function(o) { + if (o == null) { + o = ""; + } + return o.replace(/\~/g, ""); + }, + formatAudience: function(a) { + if (a == null) { + a = []; + } + return this.formatCreator(a.join(" ")); + }, + getInitialState: function() { + return { + expand: false + }; + }, + renderField: function(_key, props, render) { + var defaultValue, index, item, ref1; + if (render == null) { + render = _.identity; + } + ref1 = this.props, item = ref1.item, index = ref1.index; + defaultValue = item[_key]; + return rece(Field, $.extend(props, { + render: render, + _key: _key, + defaultValue: defaultValue, + item: item, + index: index + })); + }, + renderTopField: function(key, props, format) { + var _props, ref1; + _props = _.extend(props, { + className: ((ref1 = props.className) != null ? ref1 : key) + " top" + }); + return this.renderField(key, _props, format); + }, + componentDidMount: function() { + return setInterval((function(_this) { + return function() { + return $('.new.comment .date').text(_this.formatDate(new Date)); + }; + })(this), 1000); + }, + render: function() { + var action, discussion, itemClass, ref1, status; + itemClass = 'item'; + if (this.state.expand) { + itemClass += ' expand'; + } + discussion = _.clone((ref1 = this.props.item.discussion) != null ? ref1 : []); + discussion.reverse(); + status = this.getStatus(); + action = this.getAction(); + return div({ + className: itemClass, + draggable: true, + onDragStart: this.onDragStart, + onDragEnd: this.onDragEnd + }, div({ + className: 'header' + }, div({ + className: 'creator ib' + }, this.formatCreator(this.props.item.creator)), div({ + className: 'status ib action-' + (action.length > 0), + 'data-key': 'status', + onClick: this._changeStatus + }, div({ + className: 'label' + }, status), div({ + className: 'action a' + }, action)), this.renderField('audience', {}, this.formatAudience)), div({ + className: 'sort ib top' + }, this.props.item.sort), button({ + className: 'done ib done-' + (this.props.item.done === true), + onClick: this._markDone + }, ''), this.renderTopField('title', { + onFocus: this.onFocus, + onKeyDown: this.onKeyDown + }), this.renderTopField('date_due', { + className: 'date' + }, this.formatDate), this.renderTopField('tags', {}, function(tags) { + if (tags == null) { + tags = []; + } + return tags.join(" "); + }), div({ + className: 'expand ib', + onClick: (function(_this) { + return function(e) { + return _this.setState({ + expand: !_this.state.expand + }); + }; + })(this) + }, div({ + className: 'caret left' + }, "")), this.renderField('description', { + textarea: true + }), div({ + className: "hr" + }, ""), discussion != null ? div({ + className: "discussion" + }, div({ + className: "comments" + }, discussion.map((function(_this) { + return function(slug) { + return div({ + className: 'comment', + key: slug.date + }, div({ + className: 'hr2' + }, ""), div({ + className: 'ship ib' + }, slug.ship), div({ + className: 'date ib' + }, _this.formatDate(slug.date, true)), div({ + className: 'body' + }, slug.body)); + }; + })(this))), div({ + className: 'new comment' + }, div({ + className: 'hr2' + }, ""), div({ + className: 'ship ib' + }, window.urb.ship), div({ + className: 'date ib' + }, this.formatDate(new Date)), div({ + contentEditable: true, + className: 'input' + }), button({ + className: 'submit', + onClick: this._submitComment + }, 'Post'))) : void 0); + } +}); + + + +},{"../actions/WorkActions.coffee":1,"./FieldComponent.coffee":2}],5:[function(require,module,exports){ +var FilterComponent, ItemComponent, ListeningComponent, SortComponent, WorkActions, WorkStore, div, h1, input, rece, recl, ref, textarea; + +recl = React.createClass; + +rece = React.createElement; + +ref = React.DOM, div = ref.div, h1 = ref.h1, input = ref.input, textarea = ref.textarea; + +WorkStore = require('../stores/WorkStore.coffee'); + +WorkActions = require('../actions/WorkActions.coffee'); + +ItemComponent = require('./ItemComponent.coffee'); + +ListeningComponent = require('./ListeningComponent.coffee'); + +FilterComponent = require('./FilterComponent.coffee'); + +SortComponent = require('./SortComponent.coffee'); + +module.exports = recl({ + displayName: 'List', + stateFromStore: function() { + window.canSort = WorkStore.canSort(); + return { + list: WorkStore.getList(), + noNew: WorkStore.noNew(), + canSort: WorkStore.canSort(), + fulllist: WorkStore.getFullList(), + listening: WorkStore.getListening(), + sorts: WorkStore.getSorts(), + filters: WorkStore.getFilters(), + expand: false, + updated: WorkStore.getUpdated() + }; + }, + getInitialState: function() { + return this.stateFromStore(); + }, + _onChangeStore: function() { + return this.setState(this.stateFromStore()); + }, + alias: function() { + this.$el = $(this.getDOMNode()); + return this.$items = this.$el.find('.items').children(); + }, + _focus: function(e, i) { + return this.setState({ + selected: i.props.index + }); + }, + _dragStart: function(e, i) { + return this.dragged = i.dragged; + }, + _dragEnd: function(e, i) { + var from, id, list, sort, to, version; + from = Number(this.dragged.closest('.item-wrap').attr('data-index')); + to = Number(this.over.closest('.item-wrap').attr('data-index')); + if (from < to) { + to--; + } + if (this.drop === 'after') { + to++; + } + sort = _.clone(this.state.list); + sort.splice(to, 0, sort.splice(from, 1)[0]); + list = (function() { + var j, len, ref1, results; + results = []; + for (j = 0, len = sort.length; j < len; j++) { + ref1 = sort[j], id = ref1.id, version = ref1.version; + if ((version != null ? version : -1) >= 0) { + results.push(id); + } + } + return results; + })(); + WorkActions.moveItem(list, to, from); + this.dragged.removeClass('hidden'); + return this.placeholder.remove(); + }, + _dragOver: function(e, i) { + var $t; + e.preventDefault(); + $t = $(e.target).closest('.item'); + if ($t.hasClass('placeholder')) { + return; + } + if ($t.length === 0) { + return; + } + this.over = $t; + if (!this.dragged.hasClass('hidden')) { + this.dragged.addClass('hidden'); + } + if ((e.clientY - $t[0].offsetTop) < ($t[0].offsetHeight / 2)) { + this.drop = 'before'; + return this.placeholder.insertBefore($t); + } else { + this.drop = 'after'; + return this.placeholder.insertAfter($t); + } + }, + title_keyDown: function(e, i) { + var after, audience, before, index, ins, item, last, next, prev, ref1, tags; + switch (e.keyCode) { + case 13: + e.preventDefault(); + if (this.state.noNew) { + return; + } + item = i.props.item; + after = null; + before = null; + if (window.getSelection().getRangeAt(0).endOffset === 0) { + ins = this.state.selected; + before = item.id; + } else { + after = item.id; + ins = this.state.selected + 1; + this.setState({ + selected: ins, + select: true + }); + } + if (!item.ghost) { + tags = item.tags, audience = item.audience; + item = { + tags: tags, + audience: audience + }; + } + return WorkActions.newItem({ + before: before, + after: after + }, item); + case 8: + if (window.getSelection().getRangeAt(0).endOffset === 0) { + e.preventDefault(); + if (e.target.innerText.length === 0) { + if (this.state.selected !== 0) { + this.setState({ + selected: this.state.selected - 1, + select: "end" + }); + } + return WorkActions.removeItem(i.props.item); + } else if (((ref1 = i.props, index = ref1.index, ref1), index > 0) && (prev = this.state.list[i.props.index - 1], prev.version < 0)) { + return WorkActions.removeItem(prev); + } + } + break; + case 38: + e.preventDefault(); + last = this.state.selected - 1; + if (last < 0) { + last = this.state.list.length - 1; + } + this.$items.eq(last).find('.title .input').focus(); + return this.setState({ + select: "end" + }); + case 40: + e.preventDefault(); + next = this.state.selected + 1; + if (next === this.state.list.length) { + next = 0; + } + this.$items.eq(next).find('.title .input').focus(); + return this.setState({ + select: "end" + }); + } + }, + _changeListening: function() {}, + _changeFilter: function(key, val) { + return WorkActions.setFilter(key, val); + }, + _changeSort: function(key, val) { + return WorkActions.setSort(key, val); + }, + componentDidMount: function() { + this.placeholder = $("
x
"); + WorkStore.addChangeListener(this._onChangeStore); + WorkActions.listenList(this.props.list); + return this.alias(); + }, + componentDidUpdate: function(_props, _state) { + var $title, r, s; + this.alias(); + if (this.state.select) { + $title = this.$items.eq(this.state.selected).find('.title .input'); + if (this.state.selected != null) { + $title.focus(); + } + if (this.state.select === "end") { + r = window.getSelection().getRangeAt(0); + r.setStart($title[0], 0); + r.setEnd($title[0], 0); + s = window.getSelection(); + s.removeAllRanges(); + s.addRange(r); + } + return this.setState({ + select: false + }); + } + }, + render: function() { + return div({}, div({ + className: 'ctrl' + }, rece(ListeningComponent, { + listening: this.state.listening, + onChange: this._changeListening + }), div({ + className: 'transforms' + }, rece(FilterComponent, { + filters: this.state.filters, + onChange: this._changeFilter + }), rece(SortComponent, { + sorts: this.state.sorts, + onChange: this._changeSort + }))), div({ + className: 'items', + onDragOver: this._dragOver + }, _.map(this.state.list, (function(_this) { + return function(item, index) { + var className, draggable, key; + className = "item-wrap"; + key = item.id; + draggable = _this.state.canSort; + if (item.ghost) { + className += " ghost"; + draggable = false; + } + return div({ + className: className, + key: key, + 'data-index': index + }, rece(ItemComponent, { + item: item, + index: index, + draggable: draggable, + _focus: _this._focus, + title_keyDown: _this.title_keyDown, + _dragStart: _this._dragStart, + _dragEnd: _this._dragEnd + })); + }; + })(this)))); + } +}); + + + +},{"../actions/WorkActions.coffee":1,"../stores/WorkStore.coffee":16,"./FilterComponent.coffee":3,"./ItemComponent.coffee":4,"./ListeningComponent.coffee":6,"./SortComponent.coffee":7}],6:[function(require,module,exports){ +var div, h1, input, rece, recl, ref, textarea; + +recl = React.createClass; + +rece = React.createElement; + +ref = React.DOM, div = ref.div, h1 = ref.h1, input = ref.input, textarea = ref.textarea; + +module.exports = recl({ + render: function() { + return div({ + className: 'listening' + }, ""); + } +}); + + + +},{}],7:[function(require,module,exports){ +var button, div, h1, label, rece, recl, ref; + +recl = React.createClass; + +rece = React.createElement; + +ref = React.DOM, div = ref.div, h1 = ref.h1, button = ref.button, label = ref.label; + +module.exports = recl({ + onClick: function(e) { + var $t, key, sor; + $t = $(e.target).closest('.sort'); + key = $t.attr('data-key'); + sor = Number($t.attr('data-state')); + if (sor === 0) { + sor = 1; + } else if (sor === 1) { + sor = -1; + } else if (sor === -1) { + sor = 0; + } + return this.props.onChange(key, sor); + }, + render: function() { + return div({ + className: 'sorts' + }, _.map(this.props.sorts, (function(_this) { + return function(state, key) { + return button({ + key: key, + onClick: _this.onClick, + 'data-key': key, + 'data-state': state, + className: "sort s-" + state + }, label({}, key), div({ + className: 'caret ib' + }, '')); + }; + })(this))); + } +}); + + + +},{}],8:[function(require,module,exports){ +var ListComponent, div, h1, rece, recl, ref; + +recl = React.createClass; + +rece = React.createElement; + +ref = React.DOM, div = ref.div, h1 = ref.h1; + +ListComponent = require('./ListComponent.coffee'); + +module.exports = recl({ + render: function() { + return div({}, h1({ + className: 'leader' + }, "Work"), rece(ListComponent, { + list: 'upcoming' + })); + } +}); + + + +},{"./ListComponent.coffee":5}],9:[function(require,module,exports){ +var Dispatcher; + +Dispatcher = require('flux').Dispatcher; + +module.exports = _.merge(new Dispatcher(), { + handleServerAction: function(action) { + return this.dispatch({ + source: 'server', + action: action + }); + }, + handleViewAction: function(action) { + return this.dispatch({ + source: 'view', + action: action + }); + } +}); + + + +},{"flux":11}],10:[function(require,module,exports){ +var WorkComponent; + +WorkComponent = require('./components/WorkComponent.coffee'); + +window.util = _.extend(window.util || {}, require('./util.coffee')); + +$(function() { + return React.render(React.createElement(WorkComponent), $('#c')[0]); +}); + + + +},{"./components/WorkComponent.coffee":8,"./util.coffee":17}],11:[function(require,module,exports){ +/** + * Copyright (c) 2014-2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +module.exports.Dispatcher = require('./lib/Dispatcher') + +},{"./lib/Dispatcher":12}],12:[function(require,module,exports){ +/* + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule Dispatcher + * @typechecks + */ + +"use strict"; + +var invariant = require('./invariant'); + +var _lastID = 1; +var _prefix = 'ID_'; + +/** + * Dispatcher is used to broadcast payloads to registered callbacks. This is + * different from generic pub-sub systems in two ways: + * + * 1) Callbacks are not subscribed to particular events. Every payload is + * dispatched to every registered callback. + * 2) Callbacks can be deferred in whole or part until other callbacks have + * been executed. + * + * For example, consider this hypothetical flight destination form, which + * selects a default city when a country is selected: + * + * var flightDispatcher = new Dispatcher(); + * + * // Keeps track of which country is selected + * var CountryStore = {country: null}; + * + * // Keeps track of which city is selected + * var CityStore = {city: null}; + * + * // Keeps track of the base flight price of the selected city + * var FlightPriceStore = {price: null} + * + * When a user changes the selected city, we dispatch the payload: + * + * flightDispatcher.dispatch({ + * actionType: 'city-update', + * selectedCity: 'paris' + * }); + * + * This payload is digested by `CityStore`: + * + * flightDispatcher.register(function(payload) { + * if (payload.actionType === 'city-update') { + * CityStore.city = payload.selectedCity; + * } + * }); + * + * When the user selects a country, we dispatch the payload: + * + * flightDispatcher.dispatch({ + * actionType: 'country-update', + * selectedCountry: 'australia' + * }); + * + * This payload is digested by both stores: + * + * CountryStore.dispatchToken = flightDispatcher.register(function(payload) { + * if (payload.actionType === 'country-update') { + * CountryStore.country = payload.selectedCountry; + * } + * }); + * + * When the callback to update `CountryStore` is registered, we save a reference + * to the returned token. Using this token with `waitFor()`, we can guarantee + * that `CountryStore` is updated before the callback that updates `CityStore` + * needs to query its data. + * + * CityStore.dispatchToken = flightDispatcher.register(function(payload) { + * if (payload.actionType === 'country-update') { + * // `CountryStore.country` may not be updated. + * flightDispatcher.waitFor([CountryStore.dispatchToken]); + * // `CountryStore.country` is now guaranteed to be updated. + * + * // Select the default city for the new country + * CityStore.city = getDefaultCityForCountry(CountryStore.country); + * } + * }); + * + * The usage of `waitFor()` can be chained, for example: + * + * FlightPriceStore.dispatchToken = + * flightDispatcher.register(function(payload) { + * switch (payload.actionType) { + * case 'country-update': + * flightDispatcher.waitFor([CityStore.dispatchToken]); + * FlightPriceStore.price = + * getFlightPriceStore(CountryStore.country, CityStore.city); + * break; + * + * case 'city-update': + * FlightPriceStore.price = + * FlightPriceStore(CountryStore.country, CityStore.city); + * break; + * } + * }); + * + * The `country-update` payload will be guaranteed to invoke the stores' + * registered callbacks in order: `CountryStore`, `CityStore`, then + * `FlightPriceStore`. + */ + + function Dispatcher() { + this.$Dispatcher_callbacks = {}; + this.$Dispatcher_isPending = {}; + this.$Dispatcher_isHandled = {}; + this.$Dispatcher_isDispatching = false; + this.$Dispatcher_pendingPayload = null; + } + + /** + * Registers a callback to be invoked with every dispatched payload. Returns + * a token that can be used with `waitFor()`. + * + * @param {function} callback + * @return {string} + */ + Dispatcher.prototype.register=function(callback) { + var id = _prefix + _lastID++; + this.$Dispatcher_callbacks[id] = callback; + return id; + }; + + /** + * Removes a callback based on its token. + * + * @param {string} id + */ + Dispatcher.prototype.unregister=function(id) { + invariant( + this.$Dispatcher_callbacks[id], + 'Dispatcher.unregister(...): `%s` does not map to a registered callback.', + id + ); + delete this.$Dispatcher_callbacks[id]; + }; + + /** + * Waits for the callbacks specified to be invoked before continuing execution + * of the current callback. This method should only be used by a callback in + * response to a dispatched payload. + * + * @param {array} ids + */ + Dispatcher.prototype.waitFor=function(ids) { + invariant( + this.$Dispatcher_isDispatching, + 'Dispatcher.waitFor(...): Must be invoked while dispatching.' + ); + for (var ii = 0; ii < ids.length; ii++) { + var id = ids[ii]; + if (this.$Dispatcher_isPending[id]) { + invariant( + this.$Dispatcher_isHandled[id], + 'Dispatcher.waitFor(...): Circular dependency detected while ' + + 'waiting for `%s`.', + id + ); + continue; + } + invariant( + this.$Dispatcher_callbacks[id], + 'Dispatcher.waitFor(...): `%s` does not map to a registered callback.', + id + ); + this.$Dispatcher_invokeCallback(id); + } + }; + + /** + * Dispatches a payload to all registered callbacks. + * + * @param {object} payload + */ + Dispatcher.prototype.dispatch=function(payload) { + invariant( + !this.$Dispatcher_isDispatching, + 'Dispatch.dispatch(...): Cannot dispatch in the middle of a dispatch.' + ); + this.$Dispatcher_startDispatching(payload); + try { + for (var id in this.$Dispatcher_callbacks) { + if (this.$Dispatcher_isPending[id]) { + continue; + } + this.$Dispatcher_invokeCallback(id); + } + } finally { + this.$Dispatcher_stopDispatching(); + } + }; + + /** + * Is this Dispatcher currently dispatching. + * + * @return {boolean} + */ + Dispatcher.prototype.isDispatching=function() { + return this.$Dispatcher_isDispatching; + }; + + /** + * Call the callback stored with the given id. Also do some internal + * bookkeeping. + * + * @param {string} id + * @internal + */ + Dispatcher.prototype.$Dispatcher_invokeCallback=function(id) { + this.$Dispatcher_isPending[id] = true; + this.$Dispatcher_callbacks[id](this.$Dispatcher_pendingPayload); + this.$Dispatcher_isHandled[id] = true; + }; + + /** + * Set up bookkeeping needed when dispatching. + * + * @param {object} payload + * @internal + */ + Dispatcher.prototype.$Dispatcher_startDispatching=function(payload) { + for (var id in this.$Dispatcher_callbacks) { + this.$Dispatcher_isPending[id] = false; + this.$Dispatcher_isHandled[id] = false; + } + this.$Dispatcher_pendingPayload = payload; + this.$Dispatcher_isDispatching = true; + }; + + /** + * Clear bookkeeping used for dispatching. + * + * @internal + */ + Dispatcher.prototype.$Dispatcher_stopDispatching=function() { + this.$Dispatcher_pendingPayload = null; + this.$Dispatcher_isDispatching = false; + }; + + +module.exports = Dispatcher; + +},{"./invariant":13}],13:[function(require,module,exports){ +/** + * Copyright (c) 2014, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule invariant + */ + +"use strict"; + +/** + * Use invariant() to assert state which your program assumes to be true. + * + * Provide sprintf-style format (only %s is supported) and arguments + * to provide information about what broke and what you were + * expecting. + * + * The invariant message will be stripped in production, but the invariant + * will remain to ensure logic does not differ in production. + */ + +var invariant = function(condition, format, a, b, c, d, e, f) { + if (false) { + if (format === undefined) { + throw new Error('invariant requires an error message argument'); + } + } + + if (!condition) { + var error; + if (format === undefined) { + error = new Error( + 'Minified exception occurred; use the non-minified dev environment ' + + 'for the full error message and additional helpful warnings.' + ); + } else { + var args = [a, b, c, d, e, f]; + var argIndex = 0; + error = new Error( + 'Invariant Violation: ' + + format.replace(/%s/g, function() { return args[argIndex++]; }) + ); + } + + error.framesToPop = 1; // we don't care about invariant's own frame + throw error; + } +}; + +module.exports = invariant; + +},{}],14:[function(require,module,exports){ +'use strict'; + +function ToObject(val) { + if (val == null) { + throw new TypeError('Object.assign cannot be called with null or undefined'); + } + + return Object(val); +} + +module.exports = Object.assign || function (target, source) { + var pendingException; + var from; + var keys; + var to = ToObject(target); + + for (var s = 1; s < arguments.length; s++) { + from = arguments[s]; + keys = Object.keys(Object(from)); + + for (var i = 0; i < keys.length; i++) { + try { + to[keys[i]] = from[keys[i]]; + } catch (err) { + if (pendingException === undefined) { + pendingException = err; + } + } + } + } + + if (pendingException) { + throw pendingException; + } + + return to; +}; + +},{}],15:[function(require,module,exports){ +var cache, listeners; + +urb.appl = 'work'; + +listeners = {}; + +cache = null; + +urb.bind("/repo", function(err, dat) { + var cb, k, results; + if (err) { + return document.write(err); + } else { + cache = dat; + results = []; + for (k in listeners) { + cb = listeners[k]; + results.push(cb(null, dat)); + } + return results; + } +}); + +module.exports = { + put: function(update, cb) { + return urb.send(update, { + mark: 'work-command' + }, cb); + }, + subscribe: function(key, cb) { + listeners[key] = cb; + if (cache != null) { + return cb(null, cache); + } + } +}; + + + +},{}],16:[function(require,module,exports){ +var Dispatcher, EventEmitter, WorkStore, _filters, _ghost, _list, _listening, _sorts, _tasks, _updated, assign, uuid32; + +EventEmitter = require('events').EventEmitter; + +assign = require('object-assign'); + +Dispatcher = require('../dispatcher/Dispatcher.coffee'); + +uuid32 = require('../util.coffee').uuid32; + +_tasks = {}; + +_list = []; + +_listening = []; + +_updated = Date.now(); + +_filters = { + done: null, + creator: null, + doer: null, + audience: null, + tags: null +}; + +_sorts = { + sort: 0, + title: 0, + creator: 0, + date_due: 0 +}; + +_ghost = { + id: uuid32() +}; + +WorkStore = assign({}, EventEmitter.prototype, { + emitChange: function() { + return this.emit('change'); + }, + addChangeListener: function(cb) { + return this.on('change', cb); + }, + removeChangeListener: function(cb) { + return this.removeListener("change", cb); + }, + getData: function(arg) { + var sort, tasks; + sort = arg.sort, tasks = arg.tasks; + sort.map((function(_this) { + return function(id, index) { + if (!_tasks[id]) { + _list.splice(index, 0, id); + } + if (!tasks[id]) { + return console.log("lost", id); + } else if (!_tasks[id] || tasks[id].version > _tasks[id].version) { + return _tasks[id] = _this.itemFromData(tasks[id], index); + } + }; + })(this)); + return _updated = Date.now(); + }, + getUpdated: function() { + return _updated; + }, + getFullList: function() { + return _list; + }, + getList: function(key) { + var _k, _v, add, c, ghost, i, id, k, len, list, task, v; + list = []; + for (i = 0, len = _list.length; i < len; i++) { + id = _list[i]; + task = _tasks[id]; + if ((task == null) || task.archived) { + continue; + } + add = true; + for (_k in _filters) { + _v = _filters[_k]; + if (_v === null) { + continue; + } + c = task[_k]; + add = (function() { + switch (_k) { + case 'tags': + case 'audience': + return _.intersection(c, _v).length !== 0; + case 'creator': + return c === _v.replace(/\~/g, ""); + case 'done': + return !!c === _v; + default: + return c === _v; + } + })(); + if (!add) { + break; + } + } + if (add) { + list.push(task); + } + } + if (_.uniq(_.values(_sorts)).length > 1) { + for (k in _sorts) { + v = _sorts[k]; + if (v !== 0) { + break; + } + } + list = _.sortBy(list, k, k); + if (v === -1) { + list.reverse(); + } + } + if (!this.noNew()) { + ghost = $.extend({ + ghost: true, + version: -1 + }, _ghost); + if (_filters.tags) { + ghost.tags = _filters.tags; + } + if (_filters.audience) { + ghost.audience = _filters.audience; + } + list.push(ghost); + } + return list; + }, + newItem: function(arg) { + var after, before, index, item; + before = arg.before, after = arg.after, item = arg.item; + if (before) { + index = _list.indexOf(before); + if (index === -1) { + index = null; + } + } + if (after) { + index = 1 + _list.indexOf(after); + if (index === 0) { + index = null; + } + } + if (index == null) { + index = _list.length; + } + if (item.id === _ghost.id) { + _ghost.id = uuid32(); + } + if (_tasks[item.id] == null) { + _list.splice(index, 0, item.id); + } else if (_tasks[item.id].version >= 0) { + throw new Error("Collision: already have " + item.id); + } + return _tasks[item.id] = this.itemFromData(item, index); + }, + getListening: function() { + return _listening; + }, + getFilters: function() { + return _filters; + }, + setFilter: function(arg) { + var key, val; + key = arg.key, val = arg.val; + return _filters[key] = val; + }, + getSorts: function() { + return _sorts; + }, + setSort: function(arg) { + var k, key, v, val; + key = arg.key, val = arg.val; + for (k in _sorts) { + v = _sorts[k]; + _sorts[k] = 0; + } + return _sorts[key] = val; + }, + canSort: function() { + var k, v; + for (k in _sorts) { + v = _sorts[k]; + if (k === "sort" && v === 1) { + return true; + } else if (v !== 0) { + return false; + } + } + return true; + }, + noNew: function() { + return (_filters.done === true) || (_filters.creator != null) && _filters.owner !== urb.ship; + }, + itemFromData: function(item, index) { + var _item; + if (index == null) { + index = 0; + } + _item = _.extend({ + sort: index + }, item); + _item.date_modified = new Date(item.date_modified); + _item.date_created = new Date(item.date_created); + if (item.date_due != null) { + _item.date_due = new Date(item.date_due); + } + if (item.done != null) { + _item.done = new Date(item.done); + } + _item.discussion = item.discussion.map(function(arg) { + var body, date, ship; + ship = arg.ship, body = arg.body, date = arg.date; + return { + ship: ship, + body: body, + date: new Date(date) + }; + }); + return _item; + }, + moveItems: function(arg) { + var from, list, to; + list = arg.list, to = arg.to, from = arg.from; + _tasks[_list[from]].sort = _tasks[_list[to]].sort; + return _list = list; + }, + setAudience: function(arg) { + var id, to; + id = arg.id, to = arg.to; + return _tasks[id].audience = to; + }, + archiveItem: function(arg) { + var id; + id = arg.id; + return _tasks[id].archived = true; + }, + updateItem: function(arg) { + var id, key, val, version; + id = arg.id, version = arg.version, key = arg.key, val = arg.val; + _tasks[id].version = version; + if (key === 'done') { + return _tasks[id].done = val; + } + } +}); + +WorkStore.setMaxListeners(100); + +WorkStore.dispatchToken = Dispatcher.register(function(p) { + var a; + a = p.action; + if (WorkStore[a.type]) { + WorkStore[a.type](a); + return WorkStore.emitChange(); + } +}); + +module.exports = WorkStore; + + + +},{"../dispatcher/Dispatcher.coffee":9,"../util.coffee":17,"events":18,"object-assign":14}],17:[function(require,module,exports){ +module.exports = { + uuid32: function() { + var i, str, vals; + vals = (function() { + var j, results; + results = []; + for (i = j = 0; j <= 5; i = ++j) { + str = Math.ceil(Math.random() * 10000000).toString(32); + results.push(("00000" + str).substr(-5, 5)); + } + return results; + })(); + vals.unshift(Math.ceil(Math.random() * 8)); + return "0v" + vals.join('.'); + }, + getScroll: function() { + return this.writingPosition = $('#c').outerHeight(true) + $('#c').offset().top - $(window).height(); + }, + setScroll: function() { + window.util.getScroll(); + return $(window).scrollTop($("#c").height()); + }, + isScrolling: function() { + if (!window.util.writingPosition) { + window.util.getScroll(); + } + return $(window).scrollTop() + $('#writing').outerHeight() < window.util.writingPosition; + }, + checkScroll: function() { + if (window.util.isScrolling()) { + return $('body').addClass('scrolling'); + } else { + return $('body').removeClass('scrolling'); + } + }, + talk: { + mainStations: ["court", "floor", "porch"], + mainStationPath: function(user) { + return "~" + user + "/" + (window.util.talk.mainStation(user)); + }, + mainStation: function(user) { + if (!user) { + user = window.urb.user; + } + switch (user.length) { + case 3: + return "court"; + case 6: + return "floor"; + case 13: + return "porch"; + } + } + } +}; + + + +},{}],18:[function(require,module,exports){ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +function EventEmitter() { + this._events = this._events || {}; + this._maxListeners = this._maxListeners || undefined; +} +module.exports = EventEmitter; + +// Backwards-compat with node 0.10.x +EventEmitter.EventEmitter = EventEmitter; + +EventEmitter.prototype._events = undefined; +EventEmitter.prototype._maxListeners = undefined; + +// By default EventEmitters will print a warning if more than 10 listeners are +// added to it. This is a useful default which helps finding memory leaks. +EventEmitter.defaultMaxListeners = 10; + +// Obviously not all Emitters should be limited to 10. This function allows +// that to be increased. Set to zero for unlimited. +EventEmitter.prototype.setMaxListeners = function(n) { + if (!isNumber(n) || n < 0 || isNaN(n)) + throw TypeError('n must be a positive number'); + this._maxListeners = n; + return this; +}; + +EventEmitter.prototype.emit = function(type) { + var er, handler, len, args, i, listeners; + + if (!this._events) + this._events = {}; + + // If there is no 'error' event listener then throw. + if (type === 'error') { + if (!this._events.error || + (isObject(this._events.error) && !this._events.error.length)) { + er = arguments[1]; + if (er instanceof Error) { + throw er; // Unhandled 'error' event + } + throw TypeError('Uncaught, unspecified "error" event.'); + } + } + + handler = this._events[type]; + + if (isUndefined(handler)) + return false; + + if (isFunction(handler)) { + switch (arguments.length) { + // fast cases + case 1: + handler.call(this); + break; + case 2: + handler.call(this, arguments[1]); + break; + case 3: + handler.call(this, arguments[1], arguments[2]); + break; + // slower + default: + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + handler.apply(this, args); + } + } else if (isObject(handler)) { + len = arguments.length; + args = new Array(len - 1); + for (i = 1; i < len; i++) + args[i - 1] = arguments[i]; + + listeners = handler.slice(); + len = listeners.length; + for (i = 0; i < len; i++) + listeners[i].apply(this, args); + } + + return true; +}; + +EventEmitter.prototype.addListener = function(type, listener) { + var m; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events) + this._events = {}; + + // To avoid recursion in the case that type === "newListener"! Before + // adding it to the listeners, first emit "newListener". + if (this._events.newListener) + this.emit('newListener', type, + isFunction(listener.listener) ? + listener.listener : listener); + + if (!this._events[type]) + // Optimize the case of one listener. Don't need the extra array object. + this._events[type] = listener; + else if (isObject(this._events[type])) + // If we've already got an array, just append. + this._events[type].push(listener); + else + // Adding the second element, need to change to array. + this._events[type] = [this._events[type], listener]; + + // Check for listener leak + if (isObject(this._events[type]) && !this._events[type].warned) { + var m; + if (!isUndefined(this._maxListeners)) { + m = this._maxListeners; + } else { + m = EventEmitter.defaultMaxListeners; + } + + if (m && m > 0 && this._events[type].length > m) { + this._events[type].warned = true; + console.error('(node) warning: possible EventEmitter memory ' + + 'leak detected. %d listeners added. ' + + 'Use emitter.setMaxListeners() to increase limit.', + this._events[type].length); + if (typeof console.trace === 'function') { + // not supported in IE 10 + console.trace(); + } + } + } + + return this; +}; + +EventEmitter.prototype.on = EventEmitter.prototype.addListener; + +EventEmitter.prototype.once = function(type, listener) { + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + var fired = false; + + function g() { + this.removeListener(type, g); + + if (!fired) { + fired = true; + listener.apply(this, arguments); + } + } + + g.listener = listener; + this.on(type, g); + + return this; +}; + +// emits a 'removeListener' event iff the listener was removed +EventEmitter.prototype.removeListener = function(type, listener) { + var list, position, length, i; + + if (!isFunction(listener)) + throw TypeError('listener must be a function'); + + if (!this._events || !this._events[type]) + return this; + + list = this._events[type]; + length = list.length; + position = -1; + + if (list === listener || + (isFunction(list.listener) && list.listener === listener)) { + delete this._events[type]; + if (this._events.removeListener) + this.emit('removeListener', type, listener); + + } else if (isObject(list)) { + for (i = length; i-- > 0;) { + if (list[i] === listener || + (list[i].listener && list[i].listener === listener)) { + position = i; + break; + } + } + + if (position < 0) + return this; + + if (list.length === 1) { + list.length = 0; + delete this._events[type]; + } else { + list.splice(position, 1); + } + + if (this._events.removeListener) + this.emit('removeListener', type, listener); + } + + return this; +}; + +EventEmitter.prototype.removeAllListeners = function(type) { + var key, listeners; + + if (!this._events) + return this; + + // not listening for removeListener, no need to emit + if (!this._events.removeListener) { + if (arguments.length === 0) + this._events = {}; + else if (this._events[type]) + delete this._events[type]; + return this; + } + + // emit removeListener for all listeners on all events + if (arguments.length === 0) { + for (key in this._events) { + if (key === 'removeListener') continue; + this.removeAllListeners(key); + } + this.removeAllListeners('removeListener'); + this._events = {}; + return this; + } + + listeners = this._events[type]; + + if (isFunction(listeners)) { + this.removeListener(type, listeners); + } else { + // LIFO order + while (listeners.length) + this.removeListener(type, listeners[listeners.length - 1]); + } + delete this._events[type]; + + return this; +}; + +EventEmitter.prototype.listeners = function(type) { + var ret; + if (!this._events || !this._events[type]) + ret = []; + else if (isFunction(this._events[type])) + ret = [this._events[type]]; + else + ret = this._events[type].slice(); + return ret; +}; + +EventEmitter.listenerCount = function(emitter, type) { + var ret; + if (!emitter._events || !emitter._events[type]) + ret = 0; + else if (isFunction(emitter._events[type])) + ret = 1; + else + ret = emitter._events[type].length; + return ret; +}; + +function isFunction(arg) { + return typeof arg === 'function'; +} + +function isNumber(arg) { + return typeof arg === 'number'; +} + +function isObject(arg) { + return typeof arg === 'object' && arg !== null; +} + +function isUndefined(arg) { + return arg === void 0; +} + +},{}]},{},[10]); diff --git a/pub/work/src/js/package.json b/pub/work/src/js/package.json new file mode 100644 index 000000000..31d10a1d7 --- /dev/null +++ b/pub/work/src/js/package.json @@ -0,0 +1,17 @@ +{ + "name": "urbit-work", + "version": "0.0.0", + "repository": { + "type":"git", + "url":"https://github.com/urbit/urbit" + }, + "description": "urbit work frontend", + "main": "main.js", + "dependencies": { + "coffeeify": "~0.7.0", + "flux": "~2.0.1", + "lodash": "~2.4.1", + "moment-timezone": "~0.2.4", + "object-assign": "^1.0.0" + } +} diff --git a/pub/work/src/js/persistence/Persistence.coffee b/pub/work/src/js/persistence/Persistence.coffee new file mode 100644 index 000000000..de1db17de --- /dev/null +++ b/pub/work/src/js/persistence/Persistence.coffee @@ -0,0 +1,14 @@ +urb.appl = 'work' +listeners = {} +cache = null +urb.bind "/repo", (err,dat)-> + if err + document.write err + else + cache = dat + (cb null,dat) for k,cb of listeners +module.exports = + put: (update,cb) -> urb.send(update,{mark:'work-command'},cb) + subscribe: (key,cb) -> + listeners[key] = cb + (cb null,cache) if cache? diff --git a/pub/work/src/js/stores/WorkStore.coffee b/pub/work/src/js/stores/WorkStore.coffee new file mode 100644 index 000000000..682388670 --- /dev/null +++ b/pub/work/src/js/stores/WorkStore.coffee @@ -0,0 +1,145 @@ +EventEmitter = require('events').EventEmitter +assign = require 'object-assign' +Dispatcher = require '../dispatcher/Dispatcher.coffee' +{uuid32} = require '../util.coffee' + +_tasks = {} +_list = [] +_listening = [] +_updated = Date.now() +_filters = + done:null + creator:null + doer:null + audience:null + tags:null +_sorts = + sort:0 + title:0 + creator:0 + date_due:0 +_ghost = id:uuid32() + +WorkStore = assign {},EventEmitter.prototype,{ + emitChange: -> @emit 'change' + addChangeListener: (cb) -> @on 'change', cb + removeChangeListener: (cb) -> @removeListener "change", cb + + getData: ({sort,tasks})-> + sort.map (id,index)=> + unless _tasks[id] + _list.splice index, 0, id + if !tasks[id] + console.log "lost", id + else if !_tasks[id] or tasks[id].version > _tasks[id].version + _tasks[id] = @itemFromData tasks[id], index + _updated = Date.now() + + getUpdated: -> _updated + + getFullList: -> _list + getList: (key) -> + list = [] + for id in _list + task = _tasks[id] + if !task? or task.archived + continue + add = true + for _k,_v of _filters + if _v is null then continue + c = task[_k] + add = switch _k + when 'tags', 'audience' + _.intersection(c,_v).length isnt 0 + when 'creator' + c is _v.replace(/\~/g, "") + when 'done' + !!c is _v + else c is _v + break unless add + if add + list.push task + if _.uniq(_.values(_sorts)).length > 1 + for k,v of _sorts + if v isnt 0 + break + list = _.sortBy list,k,k + if v is -1 then list.reverse() + unless @noNew() + ghost = $.extend {ghost:true,version:-1}, _ghost + if _filters.tags then ghost.tags = _filters.tags + if _filters.audience then ghost.audience = _filters.audience + list.push ghost + list + + newItem: ({before,after,item}) -> + if before + index = _list.indexOf before + if index is -1 then index = null + if after + index = 1 + _list.indexOf after + if index is 0 then index = null + + index ?= _list.length + + if item.id is _ghost.id + _ghost.id = uuid32() + unless _tasks[item.id]? + _list.splice index,0,item.id + else if _tasks[item.id].version >= 0 + throw new Error "Collision: already have #{item.id}" + _tasks[item.id] = @itemFromData item, index + + getListening: -> _listening + getFilters: -> _filters + setFilter: ({key,val}) -> + _filters[key] = val + getSorts: -> _sorts + setSort: ({key,val}) -> + for k,v of _sorts + _sorts[k] = 0 + _sorts[key] = val + + canSort: -> + for k,v of _sorts + if k is "sort" and v is 1 + return true + else if v isnt 0 + return false + true + + noNew: -> + (_filters.done is true) or + _filters.creator? and _filters.owner isnt urb.ship + + itemFromData: (item,index=0)-> + _item = _.extend {sort:index}, item + _item.date_modified = new Date item.date_modified + _item.date_created = new Date item.date_created + _item.date_due = new Date item.date_due if item.date_due? + _item.done = new Date item.done if item.done? + _item.discussion = item.discussion.map ({ship,body,date}) -> + {ship,body,date: new Date date} + _item + + moveItems: ({list,to,from}) -> + _tasks[_list[from]].sort = _tasks[_list[to]].sort + _list = list + setAudience: ({id,to})-> _tasks[id].audience = to + archiveItem: ({id})-> _tasks[id].archived = true + updateItem: ({id,version,key,val})-> + _tasks[id].version = version + _tasks[id].done = val if key is 'done' + +} + +WorkStore.setMaxListeners 100 + +WorkStore.dispatchToken = Dispatcher.register (p) -> + a = p.action + + if WorkStore[a.type] + WorkStore[a.type] a + WorkStore.emitChange() + +module.exports = WorkStore diff --git a/pub/work/src/js/util.coffee b/pub/work/src/js/util.coffee new file mode 100644 index 000000000..9fb50242d --- /dev/null +++ b/pub/work/src/js/util.coffee @@ -0,0 +1,38 @@ +module.exports = + uuid32: -> + vals = for i in [0..5] + str = Math.ceil(Math.random()*10000000).toString(32) + ("00000"+str).substr(-5,5) + vals.unshift Math.ceil(Math.random()*8) + "0v" + vals.join '.' + + getScroll: -> + @writingPosition = $('#c').outerHeight(true)+$('#c').offset().top-$(window).height() + + setScroll: -> + window.util.getScroll() + $(window).scrollTop($("#c").height()) + + isScrolling: -> + if not window.util.writingPosition + window.util.getScroll() + return ($(window).scrollTop()+$('#writing').outerHeight() < window.util.writingPosition) + + checkScroll: -> + if window.util.isScrolling() + $('body').addClass 'scrolling' + else + $('body').removeClass 'scrolling' + talk: + mainStations: ["court","floor","porch"] + mainStationPath: (user) -> "~#{user}/#{window.util.talk.mainStation(user)}" + mainStation: (user) -> + if not user then user = window.urb.user + switch user.length + when 3 + return "court" + when 6 + return "floor" + when 13 + return "porch" + diff --git a/pub/work/test.work-task b/pub/work/test.work-task new file mode 100644 index 000000000..fcf9438a3 --- /dev/null +++ b/pub/work/test.work-task @@ -0,0 +1,8 @@ +0v0_~1999.1.1 2~1999.1.2 ~1999.5.20 + Tagged! +Yoooo +~fyr>~zod + Testin +~doznec ~2015.1.3 + how long has + this been around? diff --git a/sur/talk.hoon b/sur/talk.hoon index 062fb1825..8940f1630 100644 --- a/sur/talk.hoon +++ b/sur/talk.hoon @@ -64,6 +64,7 @@ [%lin p=? q=@t] :: no=@, text line [%mor p=(list speech)] :: multiplex [%app p=@tas q=@t] :: app message + [%tax p=duty:work-stuff] :: == :: ++ serial ,@uvH :: unique identity ++ partner (each station passport) :: interlocutor @@ -77,7 +78,41 @@ [%text (list ,@t)] :: text lines [%tank (list tank)] :: tank list == :: - :: markdown - :: image - :: mime object +++ work-stuff :: + |% :: + ++ duty :: + $% [%create tax=task] :: create new task + [%archive id=@uvH] :: archive task + [%change id=@uvH meat=flesh] :: request change + [%update id=@uvH version=@u her=ship meat=flesh]:: broadcast change + == :: + ++ flesh :: + $% [%set-doer her=(unit ,@p)] :: set doer + [%set-date-due wen=(unit ,@da)] :: set due date + [%set-tags tag=(set ,@t)] :: set tags + [%set-title til=@t] :: set title + [%set-description des=@t] :: set description + [%set-done don=?] :: set done + [%add-comment com=@t] :: add comment + == :: + ++ task :: + $: id=@uvH :: + date-created=@da :: + version=@u :: + date-modified=@da :: + creator=@p :: + doer=(unit ,@p) :: + tags=(set ,@t) :: + date-due=(unit ,@da) :: + done=(unit ,@da) :: + title=@t :: + description=@t :: + discussion=(list comment) :: + == :: + ++ comment :: + $: date=@da :: + ship=@p :: + body=@t :: + == :: + -- -- diff --git a/sur/work.hoon b/sur/work.hoon new file mode 100644 index 000000000..2b5b1ddb3 --- /dev/null +++ b/sur/work.hoon @@ -0,0 +1,53 @@ +/- talk +|% +++ client + $: tasks=(map ,@uvH client-task) + sort=(list ,@uvH) + == +++ client-task + $: archived=_| + audience=(set station:talk) + tax=task + == +++ task + $: id=@uvH + date-created=@da + version=@u + date-modified=@da + creator=@p + doer=(unit ,@p) + tags=(set ,@t) + date-due=(unit ,@da) + done=(unit ,@da) + title=@t + description=@t + discussion=(list comment) + == +++ comment + $: date=@da + ship=@p + body=@t + == +++ command + $% [%new audience=(set station:talk) task] + [%old id=@uvH dif=update] + [%sort p=(list ,@uvH)] + == +++ update + $% $: %set + $% [%audience to=(set station:talk)] + [%date-due p=(unit ,@da)] + [%title p=@t] + [%description p=@t] + [%tags p=(set ,@t)] + [%done p=?] + == == + $: %add + $% [%comment @t] + == == + $: %doer + $% [%release ~] + [%claim ~] + == == + == +--