implementation module htmlHandler
import StdArray, StdChar, StdList, StdStrictLists, StdString, StdTuple
import ArgEnv, StdMaybe
import htmlDataDef, htmlTrivial, htmlSettings, htmlStylelib, iDataState
import StdGeneric, GenParse, GenPrint
import httpServer, httpSubServer
import Gerda
derive gPrint (,), (,,), (,,,), UpdValue
derive gParse (,), (,,), (,,,), UpdValue
derive gHpr (,), (,,), (,,,)
derive gUpd (,,), (,,,)
derive bimap Form, [], FormId
:: Inline = Inline String
gParse{|(->)|} gArg gRes _ = Nothing
gPrint{|(->)|} gArg gRes _ _ = abort "functions can only be used with dynamic storage option!\n"
:: *HSt = { cntr :: Int // counts position in expression
, submits :: Bool // True if we are in submit form
, states :: *FormStates // all form states are collected here ...
, world :: *NWorld // to enable all other kinds of I/O
}
:: InputId :== Int // unique id for every constructor and basic value appearing in the state
:: FormUpdate :== (InputId,UpdValue) // info obtained when form is updated
mkHSt :: *FormStates *NWorld -> *HSt
mkHSt states nworld = { cntr=0, states=states, world=nworld, submits = False }
// OPTIONS
openGerda` database world
:== IF_GERDA (openGerda database world) (abort "Trying to open database while options are switched off",world)
closeGerda` gerda world
:== IF_GERDA (closeGerda gerda world) world
// top level function given to end user
// it collects all the html forms to display, adds clean styles and hidden forms, ands prints the html code to stdout
// assumes that the application is used by a server
doHtml :: !.(*HSt -> (Html,!*HSt)) !*World -> *World
doHtml userpage world
= snd (doHtmlPage External Nothing userpage [|] world)
// same as doHtml, but now a Clean Server is included
doHtmlServer :: !(*HSt -> (Html,!*HSt)) !*World -> *World
doHtmlServer userpage world
= StartServer 80 [("clean", \_ _ args -> doHtmlServer2 args userpage)] world
doHtmlServer2 :: [(String, String)] .(*HSt -> (Html,!*HSt)) *World -> ([String],String,*World)
doHtmlServer2 args userpage world
# (inout,world) = doHtmlPage Internal (Just args) userpage [|] world
# n_chars = count_chars inout 0
with
count_chars [|] n = n
count_chars [|s:l] n = count_chars l (n+size s)
# allhtmlcode = copy_strings inout n_chars (createArray n_chars '\0')
with
copy_strings [|e:l] i s
# size_e = size e
# i = i-size_e
= copy_strings l i (copy_chars e 0 i size_e s)
copy_strings [|] 0 s
= s
copy_chars :: !{#Char} !Int !Int !Int !*{#Char} -> *{#Char}
copy_chars s_s s_i d_i n d_s
| s_i (Html,!*HSt)) !*World -> *World
doHtmlSubServer (prio,min,max,location) userpage world
# (console,world) = stdio world
# location = location //+++ ".*" // added MJP: location +++
# result = RegisterSubProcToServer prio min max ".*" location
| result==1
# (_,world) = fclose (fwrites ("Error: SubServer \"" +++ location +++ "\" could *NOT* registered to an HTTP 1.1 main server\n") console) world
= world
| result==2
# (_,world) = fclose (fwrites ("SubServer \"" +++ location +++ "\" successfully registered to an HTTP 1.1 main server\n") console) world
= world
# world = WaitForMessageLoop mycallbackfun 0 world
# (_,world) = fclose console world
= world
where
mycallbackfun :: [String] Int Socket *World -> (Socket,*World)
mycallbackfun header contentlength socket world
# (method,rlocation,getDataArray,version) = GetFirstLine (hd header)
# (alldatareceived,datafromclient,socket,world) = ReceiveString 0 contentlength socket world
| socket==0 = (0,world) //socket closed or timed out
| alldatareceived== -1
# data = "NO DATA RECEIVED (CONTENTLENGTH=0)"
# data = data+++"\r\nMethod="+++method+++"\r\nLocation="+++rlocation+++
"\r\nVersion="+++version+++"\r\nHost="+++(GetHeaderData header "HOST:")
# (_,data,world) = doHtmlServer2 [] userpage world
= SendString data "text/html" header socket world
| alldatareceived<>0
# data = "THERE ARE "+++(toString alldatareceived)+++" BYTES OF DATA LEFT, DATA SO FAR: " +++ datafromclient
# data = data+++"\r\nMethod="+++method+++"\r\nLocation="+++rlocation+++
"\r\nVersion="+++version+++"\r\nHost="+++(GetHeaderData header "HOST:")
= SendString data "text/plain" header socket world
| alldatareceived==0 && rlocation <> location // server asks for files
= SendFile rlocation header socket world
# data = "ALL DATA RECEIVED: " +++ datafromclient // server asks for html code
# data = data+++"\r\nMethod="+++method+++"\r\nLocation="+++rlocation+++
"\r\nVersion="+++version+++"\r\nHost="+++(GetHeaderData header "HOST:")
# (_,htmlcode,world) = doHtmlServer2 (makeArguments datafromclient) userpage world
= SendString htmlcode "text/html" header socket world
doHtmlPage :: !ServerKind !(Maybe [(String, String)]) !.(*HSt -> (Html,!*HSt)) !*HtmlStream !*World -> (!*HtmlStream,!*World)
doHtmlPage serverkind args userpage inout world
# (gerda,world) = openGerda` MyDataBase world
# nworld = { worldC = world, inout = inout, gerda = gerda}
# (initforms,nworld) = retrieveFormStates serverkind args nworld
# (Html (Head headattr headtags) (Body attr bodytags),{states,world})
= userpage (mkHSt initforms nworld)
# (debufOutput,states) = if TraceOutput (traceStates states) (EmptyBody,states)
# (allformbodies,world) = storeFormStates states world
# {worldC,gerda,inout} = print_to_stdout
(Html (Head headattr [extra_style:headtags])
(Body (extra_body_attr ++ attr) [allformbodies:bodytags++[debugInput,debufOutput]]))
world
# world = closeGerda` gerda worldC
= (inout,world)
where
extra_body_attr = [Batt_background "back35.jpg",`Batt_Std [CleanStyle]]
extra_style = Hd_Style [] CleanStyles
debugInput = if TraceInput (traceHtmlInput serverkind args) EmptyBody
// swiss army knife editor that makes coffee too ...
mkViewForm :: !(InIDataId d) !(HBimap d v) !*HSt -> (Form d,!*HSt) | iData v
mkViewForm (init,formid) bm=:{toForm, updForm, fromForm, resetForm} hst=:{states,world,submits}
| init == Const && formid.lifespan <> Temp
= mkViewForm (init,{formid & lifespan = Temp}) bm hst // constant i-data are never stored
| init == Const // constant i-data, no look up of previous value
= calcnextView False Nothing states world
# (isupdated,view,states,world) = findFormInfo vformid states world // determine current view value in the state store
= calcnextView isupdated view states world // and calculate new i-data
where
vformid = reuseFormId formid (toForm init formid.ival Nothing)
calcnextView isupdated view states world
# (changedids,states) = getUpdateId states
# changed = {isChanged = isupdated, changedId = changedids}
# view = toForm init formid.ival view // map value to view domain, given previous view value
# view = updForm changed view // apply update function telling user if an update has taken place
# newval = fromForm changed view // convert back to data domain
# view = case resetForm of // optionally reset the view hereafter for next time
Nothing -> view
Just reset -> reset view
| formid.mode == NoForm // don't make a form at all
# (states,world) = replaceState` vformid view states world // store new value into the store of states
= ({ changed = False
, value = newval
, form = []
}
,mkHSt states world)
# (viewform,{states,world}) // make a form for it
= mkForm (init,if (init == Const) vformid (reuseFormId formid view)) ({mkHSt states world & submits = submits})
| viewform.changed && not isupdated // important: redo it all to handle the case that a user defined specialisation is updated !!
= calcnextView True (Just viewform.value) states world
# (states,world) = replaceState` vformid viewform.value states world // store new value into the store of states
= ( { changed = isupdated
, value = newval
, form = viewform.form
}
,mkHSt states world)
replaceState` vformid view states world
| init <> Const = replaceState vformid view states world
| otherwise = (states,world)
// findFormInfo :: FormId *FormStates *NWorld -> (Bool,Maybe a,*FormStates,*NWorld) | gUpd{|*|} a & gParse{|*|} a & TC a
findFormInfo formid formStates world
# (updateids,formStates) = getUpdateId formStates // get list of updated id's
| not (isMember formid.id updateids)
# (bool,justcurstate,formStates,world) = findState formid formStates world // the current form is not updated
= (False,justcurstate,formStates,world)
# (alltriplets,formStates) = getTriplets formid.id formStates // get my update triplets
= case (findState formid formStates world) of
(False,Just currentState,formStates,world) -> (False, Just currentState,formStates,world) // yes, but update already handled
(True, Just currentState,formStates,world) -> updateState alltriplets currentState formStates world // yes, handle update
(_, Nothing,formStates,world) -> (False, Nothing,formStates,world) // cannot find previously stored state
updateState alltriplets currentState formStates world
# allUpdates = [update \\ tripletupd <- alltriplets, (Just update) <- [examineTriplet tripletupd]]
# newState = applyUpdates allUpdates currentState
= (True,Just newState,formStates,world)
applyUpdates [] currentState = currentState
applyUpdates [(pos,upd):updates] currentState = applyUpdates updates (snd (gUpd{|*|} (UpdSearch upd pos) currentState))
examineTriplet :: TripletUpdate -> Maybe (Int,UpdValue)
examineTriplet tripletupd
= case parseTriplet tripletupd of
((sid,pos,UpdC s), Just "") = (Just (pos,UpdC s) )
((sid,pos,UpdC s), _) = (Just (pos,UpdC s) )
(_,_)= case parseTriplet tripletupd of
((sid,pos,UpdI i), Just ni) = (Just (pos,UpdI ni))
((sid,pos,UpdI i), _) = (Just (pos,UpdI i) )
(_,_) = case parseTriplet tripletupd of
((sid,pos,UpdR r), Just nr) = (Just (pos,UpdR nr))
((sid,pos,UpdR r), _) = (Just (pos,UpdR r) )
(_,_) = case parseTriplet tripletupd of
((sid,pos,UpdB b), Just nb) = (Just (pos,UpdB nb))
((sid,pos,UpdB b), _) = (Just (pos,UpdB b) )
(_,_) = case parseTriplet tripletupd of
((sid,pos,UpdS s), Just ns) = (Just (pos,UpdS ns))
_ = case tripletupd of
((sid,pos,UpdS s), ns) -> (Just (pos, UpdS ns))
_ -> (Nothing )
where
parseTriplet :: TripletUpdate -> (Triplet,Maybe b) | gParse {|*|} b
parseTriplet (triplet,update) = (triplet,parseString update)
// It can be convenient to explicitly delete IData, in particular for persistent IData obejct
// or to optimize iTasks
// All IData objects satisfying the predicate will be deleted, no matter where they are stored
deleteIData :: !(String -> Bool) !*HSt -> *HSt
deleteIData pred hst=:{states,world}
# (states,world) = deleteStates pred states world
= {hst & states = states, world = world}
// specialize has to be used if a programmer wants to specialize gForm.
// It remembers the current value of the index in the expression and creates an editor to show this value.
// The value might have been changed with this editor, so the value returned might differ from the value you started with!
specialize :: !((InIDataId a) *HSt -> (Form a,*HSt)) !(InIDataId a) !*HSt -> (!Form a,!*HSt) | gUpd {|*|} a
specialize editor (init,formid) hst=:{cntr = inidx,states = formStates,submits,world}
# nextidx = incrIndex inidx formid.ival // this value will be repesented differently, so increment counter
# (nv,hst) = editor (init,nformid) (setCntr 0 hst)
= (nv,{setCntr nextidx hst & submits = submits})
where
nformid = {formid & id = formid.id <+++ "_specialize_" <+++ inidx <+++ "_"}
incrIndex :: Int v -> Int | gUpd {|*|} v
incrIndex i v
# (UpdSearch _ cnt,v) = gUpd {|*|} (UpdSearch (UpdI 0) -1) v
= i + (-1 - cnt)
// gForm: automatically derives a Html form for any Clean type
mkForm :: !(InIDataId a) !*HSt -> *(Form a, !*HSt) | gForm {|*|} a
mkForm (init,formid=:{mode = Submit}) hst=:{submits = False}
# (form,hst) = gForm{|*|} (init,formid) {hst & submits = True}
# hst = {hst & submits = False}
# hidden = Input [ Inp_Name "hidden"
, Inp_Type Inp_Hidden
, Inp_Value (SV "")
] ""
# submit = Input [ Inp_Type Inp_Button
, Inp_Value (SV "Submit")
,`Inp_Events (callClean OnClick Submit formname)
] ""
# clear = Input [ Inp_Type Inp_Reset, Inp_Value (SV "Clear")] ""
# sform = [Form [ Frm_Method Post
, Frm_Name formname
] (form.form ++ [hidden,Br,submit,clear])
]
= ({form & form = sform} ,hst)
where
formname = encodeString formid.id // to enable the use of any character in a form name
mkForm inidataid hst = gForm{|*|} inidataid hst
generic gForm a :: !(InIDataId a) !*HSt -> *(Form a, !*HSt)
gForm{|Int|} (init,formid) hst
# (body,hst) = mkInput defsize (init,formid) (IV i) (UpdI i) hst
= ({ changed = False
, value = i
, form = [body]
},hst)
where
i = formid.ival
gForm{|Real|} (init,formid) hst
# (body,hst) = mkInput defsize (init,formid) (RV r) (UpdR r) hst
= ({ changed = False
, value = r
, form = [body]
},hst)
where
r = formid.ival
gForm{|Bool|} (init,formid) hst
# (body,hst) = mkInput defsize (init,formid) (BV b) (UpdB b) hst
= ({ changed = False
, value = b
, form = [body]
},hst)
where
b = formid.ival
gForm{|String|} (init,formid) hst
# (body,hst) = mkInput defsize (init,formid) (SV s) (UpdS s) hst
= ({ changed = False
, value = s
, form = [body]
},hst)
where
s = formid.ival
gForm{|UNIT|} _ hst
= ({ changed = False
, value = UNIT
, form = [EmptyBody]
},hst)
gForm{|PAIR|} gHa gHb (init,formid) hst
# (na,hst) = gHa (init,reuseFormId formid a) hst
# (nb,hst) = gHb (init,reuseFormId formid b) hst
= ({ changed = na.changed || nb.changed
, value = PAIR na.value nb.value
, form = [STable [Tbl_CellPadding (Pixels 0), Tbl_CellSpacing (Pixels 0)] [na.form,nb.form]]
},hst)
where
(PAIR a b) = formid.ival
gForm{|EITHER|} gHa gHb (init,formid) hst
= case formid.ival of
(LEFT a)
# (na,hst) = gHa (init,reuseFormId formid a) hst
= ({na & value=LEFT na.value},hst)
(RIGHT b)
# (nb,hst) = gHb (init,reuseFormId formid b) hst
= ({nb & value=RIGHT nb.value},hst)
gForm{|OBJECT|} gHo (init,formid) hst
# (no,hst) = gHo (init,reuseFormId formid o) hst
= ({no & value=OBJECT no.value},hst)
where
(OBJECT o) = formid.ival
gForm{|CONS of t|} gHc (init,formid) hst=:{cntr,submits}
| not (isEmpty t.gcd_fields)
# (nc,hst) = gHc (init,reuseFormId formid c) (setCntr (cntr+1) hst) // don't display record constructor
= ({nc & value=CONS nc.value},hst)
| t.gcd_type_def.gtd_num_conses == 1
# (nc,hst) = gHc (init,reuseFormId formid c) (setCntr (cntr+1) hst) // don't display constructors that have no alternative
= ({nc & value=CONS nc.value},hst)
| t.gcd_name.[(size t.gcd_name) - 1] == '_' // don't display constructor names which end with an underscore
# (nc,hst) = gHc (init,reuseFormId formid c) (setCntr (cntr+1) hst)
= ({nc & value=CONS nc.value},hst)
# (selector,hst) = mkConsSelector formid t hst
# (nc,hst) = gHc (init,reuseFormId formid c) hst
= ({ changed = nc.changed
, value = CONS nc.value
, form = [STable [Tbl_CellPadding (Pixels 0), Tbl_CellSpacing (Pixels 0)] [[selector,BodyTag nc.form]]]
},hst)
where
(CONS c) = formid.ival
mkConsSelector formid thiscons hst=:{cntr}
= (mkConsSel cntr myname allnames myindex formid, setCntr (cntr+1) hst)
where
myname = thiscons.gcd_name
allnames = map (\n -> n.gcd_name) thiscons.gcd_type_def.gtd_conses
myindex = case allnames ?? myname of
-1 -> abort ("cannot find index of " +++ myname )
i -> i
mkConsSel :: Int String [String] Int (FormId x) -> BodyTag
mkConsSel cntr myname list nr formid
= Select [ Sel_Name (selectorInpName +++ encodeString myname) : styles ] // changed to see changes in case of a submit
[ Option
[Opt_Value (encodeTriplet (formid.id,cntr,UpdC elem)) : if (j == nr) [Opt_Selected Selected:optionstyle] optionstyle] elem
\\ elem <- list & j <- [0..]
]
where
styles = case formid.mode of
Edit -> [ `Sel_Std [Std_Style width, EditBoxStyle]
, `Sel_Events (if submits [] (callClean OnChange Edit formid.id))
]
Submit -> [ `Sel_Std [Std_Style width, EditBoxStyle]
]
_ -> [ `Sel_Std [Std_Style width, DisplayBoxStyle]
, Sel_Disabled Disabled
]
optionstyle = case formid.mode of
Edit -> []
Submit -> []
_ -> [`Opt_Std [DisplayBoxStyle]]
width = "width:" <+++ defpixel <+++ "px"
gForm{|FIELD of d |} gHx (init,formid) hst
# (nx,hst) = gHx (init,reuseFormId formid x) hst
= ({ changed = nx.changed
, value = FIELD nx.value
, form = [STable [Tbl_CellPadding (Pixels 1), Tbl_CellSpacing (Pixels 1)] [[fieldname,BodyTag nx.form]]]
},hst)
where
(FIELD x) = formid.ival
fieldname = Input [ Inp_Type Inp_Text
, Inp_Value (SV (prettify d.gfd_name +++ ": "))
, Inp_ReadOnly ReadOnly
, Inp_Disabled Disabled
, `Inp_Std [DisplayBoxStyle]
, Inp_Size maxsize`
] ""
prettify name = mkString [toUpper lname : addspace lnames]
where
[lname:lnames] = mkList name
addspace [] = []
addspace [c:cs]
| isUpper c = [' ',toLower c:addspace cs]
| otherwise = [c:addspace cs]
maxsize` = ndefsize maxsize defsize
maxsize = takemax defsize [size (prettify gfd_name) * 8 / 10 \\ {gfd_name} <- d.gfd_cons.gcd_fields]
takemax i [] = i
takemax i [j:js]
| i > j = takemax i js
| otherwise = takemax j js
ndefsize max def
| max - def <= 0 = def
| otherwise = ndefsize max (def + defsize)
gForm{|(->)|} garg gres (init,formid) hst
= ({ changed = False, value = formid.ival, form = []},hst)
// gUpd calculates a new value given the current value and a change in the value.
// If necessary it invents new default values (e.g. when switching from Nil to Cons)
// and leaves the rest untouched.
:: UpdMode = UpdSearch UpdValue Int // search for indicated postion and update it
| UpdCreate [ConsPos] // create new values if necessary
| UpdDone // and just copy the remaining stuff
generic gUpd t :: UpdMode t -> (UpdMode,t)
gUpd{|Int|} (UpdSearch (UpdI ni) 0) _ = (UpdDone,ni) // update integer value
gUpd{|Int|} (UpdSearch val cnt) i = (UpdSearch val (dec cnt),i) // continue search, don't change
gUpd{|Int|} (UpdCreate l) _ = (UpdCreate l,0) // create default value
gUpd{|Int|} mode i = (mode,i) // don't change
gUpd{|Real|} (UpdSearch (UpdR nr) 0) _ = (UpdDone,nr) // update real value
gUpd{|Real|} (UpdSearch val cnt) r = (UpdSearch val (dec cnt),r) // continue search, don't change
gUpd{|Real|} (UpdCreate l) _ = (UpdCreate l,0.0) // create default value
gUpd{|Real|} mode r = (mode,r) // don't change
gUpd{|Bool|} (UpdSearch (UpdB nb) 0) _ = (UpdDone,nb) // update boolean value
gUpd{|Bool|} (UpdSearch val cnt) b = (UpdSearch val (dec cnt),b) // continue search, don't change
gUpd{|Bool|} (UpdCreate l) _ = (UpdCreate l,False) // create default value
gUpd{|Bool|} mode b = (mode,b) // don't change
gUpd{|String|} (UpdSearch (UpdS ns) 0) _ = (UpdDone,ns) // update string value
gUpd{|String|} (UpdSearch val cnt) s = (UpdSearch val (dec cnt),s) // continue search, don't change
gUpd{|String|} (UpdCreate l) _ = (UpdCreate l,"") // create default value
gUpd{|String|} mode s = (mode,s) // don't change
gUpd{|UNIT|} mode _ = (mode,UNIT)
gUpd{|PAIR|} gUpda gUpdb mode=:(UpdCreate l) _ // invent a pair
# (mode,a) = gUpda mode (abort "PAIR a evaluated")
# (mode,b) = gUpdb mode (abort "PAIR b evaluated")
= (mode,PAIR a b)
gUpd{|PAIR|} gUpda gUpdb mode (PAIR a b) // pass mode to a and b in all other cases
# (mode,a) = gUpda mode a
# (mode,b) = gUpdb mode b
= (mode,PAIR a b)
gUpd{|EITHER|} gUpda gUpdb (UpdCreate [ConsLeft:cl]) _
# (mode,a) = gUpda (UpdCreate cl) (abort "LEFT a evaluated")
= (mode,LEFT a)
gUpd{|EITHER|} gUpda gUpdb (UpdCreate [ConsRight:cl]) _
# (mode,b) = gUpdb (UpdCreate cl) (abort "RIGHT b evaluated")
= (mode,RIGHT b)
gUpd{|EITHER|} gUpda gUpdb (UpdCreate []) _
# (mode,b) = gUpdb (UpdCreate []) (abort "Empty EITHER evaluated")
= (mode,RIGHT b)
gUpd{|EITHER|} gUpda gUpdb mode (LEFT a)
# (mode,a) = gUpda mode a
= (mode,LEFT a)
gUpd{|EITHER|} gUpda gUpdb mode (RIGHT b)
# (mode,b) = gUpdb mode b
= (mode,RIGHT b)
gUpd{|OBJECT|} gUpdo (UpdCreate l) _ // invent new type
# (mode,o) = gUpdo (UpdCreate l) (abort "OBJECT evaluated")
= (mode,OBJECT o)
gUpd{|OBJECT of typedes|} gUpdo (UpdSearch (UpdC cname) 0) (OBJECT o) // constructor changed of this type
# (mode,o) = gUpdo (UpdCreate path) o
= (UpdDone,OBJECT o)
where
path = getConsPath (hd [cons \\ cons <- typedes.gtd_conses | cons.gcd_name == cname])
gUpd{|OBJECT|} gUpdo (UpdSearch val cnt) (OBJECT o) // search further
# (mode,o) = gUpdo (UpdSearch val (dec cnt)) o
= (mode,OBJECT o)
gUpd{|OBJECT|} gUpdo mode (OBJECT o) // other cases
# (mode,o) = gUpdo mode o
= (mode,OBJECT o)
gUpd{|CONS|} gUpdo (UpdCreate l) _ // invent new constructor ??
# (mode,c) = gUpdo (UpdCreate l) (abort "CONS evaluated")
= (mode,CONS c)
gUpd{|CONS|} gUpdo mode (CONS c) // other cases
# (mode,c) = gUpdo mode c
= (mode,CONS c)
gUpd{|FIELD|} gUpdx (UpdCreate l) _ // invent new type
# (mode,x) = gUpdx (UpdCreate l) (abort "Value of FIELD evaluated")
= (mode,FIELD x)
gUpd{|FIELD|} gUpdx mode (FIELD x) // other cases
# (mode,x) = gUpdx mode x
= (mode,FIELD x)
gUpd{|(->)|} gUpdArg gUpdRes mode f
= (mode,f)
// small utility functions
mkInput :: !Int !(InIDataId d) Value UpdValue !*HSt -> (BodyTag,*HSt)
mkInput size (init,formid=:{mode}) val updval hst=:{cntr,submits}
| mode == Edit || mode == Submit
= ( Input [ Inp_Type Inp_Text
, Inp_Value val
, Inp_Name (encodeTriplet (formid.id,cntr,updval))
, Inp_Size size
, `Inp_Std [EditBoxStyle, Std_Title (showType val)]
, `Inp_Events if (mode == Edit && not submits) (callClean OnChange formid.mode "") []
] ""
, setCntr (cntr+1) hst)
| mode == Display
= ( Input [ Inp_Type Inp_Text
, Inp_Value val
, Inp_ReadOnly ReadOnly
, `Inp_Std [DisplayBoxStyle]
, Inp_Size size
] ""
,setCntr (cntr+1) hst)
= ( EmptyBody,setCntr (cntr+1) hst)
where
showType (SV str) = "::String"
showType (NQV str) = "::String"
showType (IV i) = "::Int"
showType (RV r) = "::Real"
showType (BV b) = "::Bool"
toHtml :: a -> BodyTag | gForm {|*|} a
toHtml a
# (na,_) = mkForm (Set,mkFormId "__toHtml" a <@ Display) (mkHSt emptyFormStates undef)
= BodyTag na.form
toHtmlForm :: !(*HSt -> *(Form a,*HSt)) -> [BodyTag] | gForm{|*|}, gUpd{|*|}, gPrint{|*|}, gParse{|*|}, TC a
toHtmlForm anyform
# (na,hst) = anyform (mkHSt emptyFormStates undef)
= na.form
toBody :: (Form a) -> BodyTag
toBody form = BodyTag form.form
derive gUpd Inline
derive gParse Inline
derive gPrint Inline
derive gerda Inline
gForm{|Inline|} (init,formid) hst
# (Inline string) = formid.ival
= ({changed=False, value=formid.ival, form=[InlineCode string]},incrHSt 2 hst)
showHtml :: [BodyTag] -> Inline
showHtml bodytags = Inline (foldl (+++) "" (reverse [x \\ x <|- gHpr {|*|} [|] bodytags]))
createDefault :: a | gUpd{|*|} a
createDefault = fromJust (snd (gUpd {|*|} (UpdSearch (UpdC "Just") 0) Nothing))
derive gUpd Maybe
setCntr :: InputId *HSt -> *HSt
setCntr i hst = {hst & cntr = i}
incrHSt :: Int !*HSt -> *HSt
incrHSt i hst = {hst & cntr = hst.cntr + i} // BUG ??????
CntrHSt :: !*HSt -> (Int,*HSt)
CntrHSt hst=:{cntr} = (cntr,hst)
getChangedId :: !*HSt -> ([String],!*HSt) // id of form that has been changed by user
getChangedId hst=:{states}
# (ids,states) = getUpdateId states
= (ids,{hst & states = states })
// Enabling file IO on HSt
instance FileSystem HSt where
fopen string int hst=:{world}
# (bool,file,world) = fopen string int world
= (bool,file,{hst & world = world})
fclose file hst=:{world}
# (bool,world) = fclose file world
= (bool,{hst & world = world})
stdio hst=:{world}
# (file,world) = stdio world
= (file,{hst & world = world})
sfopen string int hst=:{world}
# (bool,file,world) = sfopen string int world
= (bool,file,{hst & world = world})
// General access to the World environment on HSt:
appWorldHSt :: !.(*World -> *World) !*HSt -> *HSt
appWorldHSt f hst=:{world}
= {hst & world=appWorldNWorld f world}
accWorldHSt :: !.(*World -> *(.a,*World)) !*HSt -> (.a,!*HSt)
accWorldHSt f hst=:{world}
# (a,world) = accWorldNWorld f world
= (a,{hst & world=world})
// test interface
runUserApplication :: .(*HSt -> *(.a,*HSt)) *FormStates *NWorld -> *(.a,*FormStates,*NWorld)
runUserApplication userpage states nworld
# (html,{states,world}) = userpage (mkHSt states nworld)
= (html,states,world)