------------------------------------------------------------------------------ --- A library to support the type-oriented construction of Web User Interfaces --- (WUIs). --- --- The ideas behind the application and implementation of WUIs are --- described in a paper that is available via --- [this web page](http://www.informatik.uni-kiel.de/~pakcs/WUI). --- --- In addition to the original library, this version provides also support --- for JavaScript. --- --- @author Michael Hanus --- @version February, 2009 --- @category web ------------------------------------------------------------------------------ {-# OPTIONS_CYMAKE -Wno-incomplete-patterns #-} module WUIjs(--WuiState,cgiRef2state,state2cgiRef,value2state,state2value, --states2state,state2states,altstate2state,state2altstate, Rendering,WuiSpec, withRendering,withError,withCondition,withConditionJS, adaptWSpec,transformWSpec, wHidden,wConstant,wInt, wString,wStringSize,wRequiredString,wRequiredStringSize,wTextArea, wSelect,wSelectInt,wSelectBool,wRadioSelect,wRadioBool,wCheckBool, wMultiCheckSelect, wPair,wTriple,w4Tuple,w5Tuple,w6Tuple,w7Tuple,w8Tuple, w9Tuple,w10Tuple,w11Tuple,w12Tuple, wCons2,wCons3,wCons4,wCons5,wCons6,wCons7, wCons8,wCons9,wCons10,wCons11,wCons12,wJoinTuple, wMaybe,wCheckMaybe,wRadioMaybe, wList,wListWithHeadings,wHList,wMatrix,wEither, WTree(..),wTree, WuiHandler,wuiHandler2button, renderTuple,renderTaggedTuple,renderList, mainWUI,wui2html,wuiInForm,wuiWithErrorForm,wuiHandler2button, wCons2JS,wCons3JS,wCons4JS,wCons5JS,wCons6JS,wCons7JS, -- internal... wCons8JS,wCons9JS,wCons10JS,wCons11JS,wCons12JS, -- internal... withConditionJSName -- internal... ) where import Char(isDigit,isSpace) import FunctionInversion (invf1) import HTML.Base import List(elemIndex,intersperse) import Maybe import Read(readNat) import ReadShowTerm import JavaScript.Types import JavaScript.Show infixl 0 `withRendering` infixl 0 `withError` infixl 0 `withCondition` infixl 0 `withConditionJS` ------------------------------------------------------------------------------ --- An internal WUI state is used to maintain the cgi references of the input --- fields as a structure that corresponds to the structure of the edit data. --- The last argument is always a JavaScript expression to access the input data --- stored in the form elements. data WuiState = -- reference to elementary input field: Ref CgiRef (Maybe JSExp) -- string representation of a hidden value: | Hidden String (Maybe JSExp) -- composition of trees (substructures): | CompNode [WuiState] (Maybe JSExp) -- alternative of trees (union of substructures): | AltNode (Int,WuiState) (Maybe JSExp) cgiRef2state :: CgiRef -> Maybe JSExp -> WuiState cgiRef2state cr js = Ref cr js state2cgiRef :: WuiState -> CgiRef state2cgiRef (Ref cr _) = cr value2state :: _ -> WuiState value2state v = Hidden (showQTerm v) Nothing state2value :: WuiState -> _ state2value (Hidden s _) = readQTerm s -- Combine several WUI states into a single state. -- The second argument is a string representation of the constructor -- used in the JavaScript processing the of data or Nothing if processing -- by JavaScript is not possible. states2state :: [WuiState] -> Maybe ([JSExp]->JSExp) -> WuiState states2state sts Nothing = CompNode sts Nothing states2state sts (Just jscomb) = CompNode sts (if Nothing `elem` jsOfElems then Nothing else Just (jscomb (map fromJust jsOfElems))) where jsOfElems = map jsAccessToState sts state2states :: WuiState -> [WuiState] state2states (CompNode sts _) = sts altstate2state :: (Int,WuiState) -> WuiState altstate2state alt@(_,st) = AltNode alt (jsAccessToState st) state2altstate :: WuiState -> (Int,WuiState) state2altstate (AltNode alt _) = alt -- Get the JavaScript function to access the current input values. jsAccessToState :: WuiState -> Maybe JSExp jsAccessToState (Ref _ js) = js jsAccessToState (Hidden _ js) = js jsAccessToState (CompNode _ js) = js jsAccessToState (AltNode _ js) = js -- Set the JavaScript function to access the current input values to Nothing: setNoJSAccessInWuiState :: WuiState -> WuiState setNoJSAccessInWuiState (Ref cref _) = Ref cref Nothing setNoJSAccessInWuiState (Hidden s _) = Hidden s Nothing setNoJSAccessInWuiState (CompNode sts _) = CompNode sts Nothing setNoJSAccessInWuiState (AltNode alts _) = AltNode alts Nothing -- Compute a unique name for a state (used to identify error message tables). state2refname :: WuiState -> String state2refname (Ref cref _) = idOfCgiRef cref state2refname (Hidden _ _) = "" state2refname (CompNode ws _) = "comp" ++ concatMap (("_"++) . state2refname) ws state2refname (AltNode (i,s) _) = "alt_" ++ show i ++ "_" ++ state2refname s ------------------------------------------------------------------------------ --- A rendering is a function that combines the visualization of components --- of a data structure into some HTML expression. type Rendering = [HtmlExp] -> HtmlExp --- WuiParams specify the parameters of an individual Wui component type: --- * the standard rendering --- * an error message shown in case of illegal inputs --- * a condition to specify legal input values --- * optionally a JavaScript function name implementing the condition type WuiParams a = (Rendering, String, a->Bool, Maybe String) renderOf :: WuiParams a -> Rendering renderOf (render,_,_,_) = render errorOf :: WuiParams a -> String errorOf (_,err,_,_) = err conditionOf :: WuiParams a -> (a -> Bool) conditionOf (_,_,c,_) = c jsConditionOf :: WuiParams a -> Maybe String jsConditionOf (_,_,_,jsc) = jsc ------------------------------------------------------------------------------ --- The type HtmlState describes a value consisting of an HTML expression --- (usually containing some input elements), possibly a JavaScript expression --- that checks the validity of the value, and a WUI state containing --- references to input elements in the HTML expression. type HtmlState = (HtmlExp, Maybe JSExp, WuiState) ------------------------------------------------------------------------------ --- A handler for a WUI is an event handler for HTML forms possibly with some --- specific JavaScript code attached. data WuiHandler = WHandler HtmlHandler (Maybe JSExp) --- Transform a WUI handler into a submit button with a given label string. wuiHandler2button :: String -> WuiHandler -> HtmlExp wuiHandler2button title (WHandler handler mbjs) = let bt = button title handler in maybe bt (\jse->bt `addAttr` ("onclick","AllowSubmission = " ++ showJSExp jse)) mbjs ------------------------------------------------------------------------------ --- The type of WUI specifications. --- The first component are parameters specifying the behavior of this WUI type --- (rendering, error message, and constraints on inputs). --- The second component is a "show" function returning an HTML expression for --- the edit fields and a WUI state containing the CgiRefs to extract --- the values from the edit fields. --- The third component is "read" function to extract the values from --- the edit fields for a given cgi environment (returned as (Just v)). --- If the value is not legal, Nothing is returned. The second component --- of the result contains an HTML edit expression --- together with a WUI state to edit the value again. data WuiSpec a = WuiSpec (WuiParams a) (WuiParams a -> a -> HtmlState) (WuiParams a -> CgiEnv -> WuiState -> (Maybe a,HtmlState)) --- Puts a new rendering function into a WUI specification. withRendering :: WuiSpec a -> Rendering -> WuiSpec a withRendering (WuiSpec (_,errmsg,legal,jsck) showhtml readvalue) render = WuiSpec (render,errmsg,legal,jsck) showhtml readvalue --- Puts a new error message into a WUI specification. withError :: WuiSpec a -> String -> WuiSpec a withError (WuiSpec (render,_,legal,jsck) showhtml readvalue) errmsg = WuiSpec (render,errmsg,legal,jsck) showhtml readvalue --- Puts a new condition into a WUI specification. withCondition :: WuiSpec a -> (a -> Bool) -> WuiSpec a withCondition (WuiSpec (render,errmsg,_,_) showhtml readvalue) legal = WuiSpec (render,errmsg,legal,Nothing) showhtml readvalue --- Puts a new JavaScript implementation of the condition --- into a WUI specification. withConditionJS :: WuiSpec a -> (a->Bool) -> WuiSpec a withConditionJS (WuiSpec (render,errmsg,_,_) showhtml readvalue) legal = WuiSpec (render,errmsg,legal,Nothing) showhtml readvalue --- Puts a new JavaScript implementation of the condition --- into a WUI specification. withConditionJSName :: WuiSpec a -> (a->Bool,String) -> WuiSpec a withConditionJSName (WuiSpec (render,errmsg,_,_) showhtml readvalue) (legal,jsck) = WuiSpec (render,errmsg,legal,if null jsck then Nothing else Just jsck) showhtml readvalue --- Transforms a WUI specification from one type to another. transformWSpec :: (a->b,b->a) -> WuiSpec a -> WuiSpec b transformWSpec (a2b,b2a) (WuiSpec wparamsa showhtmla readvaluea) = WuiSpec (transParamA2B wparamsa) (\wparamsb b -> setNoJSAccessInHtmlState (showhtmla (transParamB2A wparamsb) (b2a b))) (\wparamsb env wst -> let (mba,errstate) = readvaluea (transParamB2A wparamsb) env wst in (maybe Nothing (Just . a2b) mba, setNoJSAccessInHtmlState errstate)) where transParamA2B (render,errmsg,legal,_) = -- since we can't transform JS check code for type a into b, we ignore it: (render, errmsg, legal . b2a, Nothing) transParamB2A (render,errmsg,legal,_) = (render, errmsg, legal . a2b, jsConditionOf wparamsa) -- transform an HtmlState so that the JavaScript access function in -- the corresponding WuiState is set to Nothing (this is necessary since -- the JavaScript access function accesses values of type a and cannot -- be transformed in general to an access function for b values) setNoJSAccessInHtmlState (hexp,jsexp,ws) = (hexp, jsexp, setNoJSAccessInWuiState ws) --- Adapt a WUI specification to a new type. For this purpose, --- the first argument must be a transformation mapping values --- from the old type to the new type. This function must be bijective --- and operationally invertible (i.e., the inverse must be computable --- by narrowing). Otherwise, use transformWSpec! adaptWSpec :: (a->b) -> WuiSpec a -> WuiSpec b adaptWSpec a2b = transformWSpec (a2b, invf1 a2b) ------------------------------------------------------------------------------ -- A collection of basic WUIs and WUI combinators: --- A hidden widget for a value that is not shown in the WUI. --- Usually, this is used in components of larger --- structures, e.g., internal identifiers, data base keys. wHidden :: WuiSpec a wHidden = WuiSpec (head,"?",const True,Nothing) -- dummy values, not used (\_ v -> (hempty, Nothing, value2state v)) (\_ _ s -> (Just (state2value s), (hempty,Nothing,s))) --- A widget for values that are shown but cannot be modified. --- The first argument is a mapping of the value into a HTML expression --- to show this value. wConstant :: (a->HtmlExp) -> WuiSpec a wConstant showhtml = WuiSpec (head,"?",const True,Nothing) (\wparams v -> ((renderOf wparams) [showhtml v], Nothing, value2state v)) (\wparams _ s -> let v = state2value s in (Just v, ((renderOf wparams) [showhtml v], Nothing, s))) --- A widget for editing integer values. wInt :: WuiSpec Int wInt = WuiSpec (head,"Illegal integer:",const True,Nothing) (\(render,errmsg,_,jsck) v -> intWidget errmsg jsck render (showInt v)) (\(render,errmsg,legal,jsck) env s -> let input = env (state2cgiRef s) renderr hexps = addErrMsg False False False errmsg "" (render hexps) in maybe (Nothing, intWidget errmsg jsck renderr input) (\v -> if legal v then (Just v, intWidget errmsg jsck render input) else (Nothing, intWidget errmsg jsck renderr input)) (readMaybeInt (stripSpaces input))) where showInt i = if i<0 then '-' : show (-i) else show i intWidget errmsg mbjs render s = (addErrMsg True True False errmsg refname (render [textfield ref s `addAttr` ("size","6") `addAttr` ("onblur",showJSExp jsCheckCall)]), Just jsCheckCall, cgiRef2state ref (Just jsaccess)) where ref free refname = idOfCgiRef ref jsaccess = JSFCall "intValueOf" [JSString refname] parseIntCheckCall = JSFCall "parseIntCheck" [JSString refname] intCheckCall = maybe parseIntCheckCall (\jsf->JSOp "&&" parseIntCheckCall (JSFCall jsf [jsaccess])) mbjs jsCheckCall = JSFCall "setErrorClassName" [JSString refname,intCheckCall] -- Remove leading and ending spaces in a string. stripSpaces :: String -> String stripSpaces = reverse . dropWhile isSpace . reverse . dropWhile isSpace -- Read a (possibly negative) integer in a string. -- Return Nothing is this is not an integer string. readMaybeInt :: String -> Maybe Int readMaybeInt "" = Nothing readMaybeInt (v:s) | v=='-' = maybe Nothing (\i->Just (-i)) (acc 0 s) | isDigit v = acc 0 (v:s) | otherwise = Nothing where acc n "" = Just n acc n (c:cs) | isDigit c = acc (10*n + ord c - ord '0') cs | otherwise = Nothing checkLegalInput :: WuiParams a -> (String -> Maybe String -> Rendering -> a -> HtmlState) -> Bool -> a -> (Maybe a,HtmlState) checkLegalInput (render,errmsg,legal,jsck) value2widget errorastable value = if legal value then (Just value, value2widget errmsg jsck render value) else (Nothing, value2widget errmsg jsck (\hes -> addErrMsg False False errorastable errmsg "" (render hes)) value) --- A predefined filter for processing string inputs. --- Here, we replace \r\n by \n: filterStringInput :: String -> String filterStringInput = removeCRs --- Replace all \r\n by \n: removeCRs :: String -> String removeCRs [] = [] removeCRs [c] = [c] removeCRs (c1:c2:cs) = if c1=='\r' && c2=='\n' then '\n' : removeCRs cs else c1 : removeCRs (c2:cs) --- A widget for editing string values. wString :: WuiSpec String wString = wStringAttrs [] --- A widget for editing string values with a size attribute. wStringSize :: Int -> WuiSpec String wStringSize size = wStringAttrs [("size",show size)] --- A widget for editing string values with some attributes for the --- text field. wStringAttrs :: [(String,String)] -> WuiSpec String wStringAttrs attrs = WuiSpec (head, "?", const True, Nothing) (\(render,errmsg,_,jsck) v -> stringWidget errmsg jsck render v) (\wparams env s -> checkLegalInput wparams stringWidget False (filterStringInput (env (state2cgiRef s)))) where stringWidget errmsg mbjs render v = (addErrMsg (isJust jsCheckCall) True False errmsg refname (render [foldr (flip addAttr) (textfield ref v) (attrs++onblurAttr)]), jsCheckCall, cgiRef2state ref (Just jsaccess)) where ref free refname = idOfCgiRef ref jsaccess = JSFCall "stringValueOf" [JSString refname] jsCheckCall = maybeJSFun2checkCall refname jsaccess mbjs onblurAttr = maybe [] (\jsc->[("onblur",showJSExp jsc)]) jsCheckCall --- A widget for editing string values that are required to be non-empty. wRequiredString :: WuiSpec String wRequiredString = withConditionJSName (wString `withError` "Missing input:") (not . null, "notEmpty") --- A widget with a size attribute for editing string values --- that are required to be non-empty. wRequiredStringSize :: Int -> WuiSpec String wRequiredStringSize size = withConditionJSName (wStringSize size `withError` "Missing input:") (not . null, "notEmpty") --- A widget for editing string values in a text area. --- The argument specifies the height and width of the text area. wTextArea :: (Int,Int) -> WuiSpec String wTextArea dims = WuiSpec (head, "?", const True, Nothing) (\ (render,errmsg,_,jsck) v -> textareaWidget errmsg jsck render v) (\wparams env s -> checkLegalInput wparams textareaWidget False (filterStringInput (env (state2cgiRef s)))) where textareaWidget errmsg mbjs render v = (addErrMsg (isJust jsCheckCall) True False errmsg refname (render [textarea ref dims v `addAttrs` onblurAttr]), jsCheckCall, cgiRef2state ref (Just jsaccess)) where ref free refname = idOfCgiRef ref jsaccess = JSFCall "stringValueOf" [JSString refname] jsCheckCall = maybeJSFun2checkCall refname jsaccess mbjs onblurAttr = maybe [] (\jsc->[("onblur",showJSExp jsc)]) jsCheckCall --- A widget to select a value from a given list of values. --- The current value should be contained in the value list and is preselected. --- The first argument is a mapping from values into strings to be shown --- in the selection widget. wSelect :: Eq a => (a->String) -> [a] -> WuiSpec a wSelect showelem selset = WuiSpec (head,"?",const True, Nothing) (\ (render,errmsg,_,jsck) v -> selWidget errmsg jsck render v) (\wparams env s -> checkLegalInput wparams selWidget False (selset !! readNat (env (state2cgiRef s)))) where selWidget _ _ render v = (render [maybe (selection ref namevalues) (\i -> selectionInitial ref namevalues i) idx], Nothing, cgiRef2state ref Nothing) where ref free idx = elemIndex v selset namevalues = zip (map showelem selset) (map show [0..]) --- A widget to select a value from a given list of values that are --- representable in JavaScript. In contrast to the more general combinator --- wSelect, this combinator supports the client-side checking of --- conditions by JavaScipt. --- The current value should be contained in the value list and is preselected. --- The first argument is a mapping from values into strings to be shown --- in the selection widget. --- The second argument maps value into the corresponding JavaScript --- representation. wSelectJS :: Eq a => (a->String) -> (a->JSExp) -> [a] -> WuiSpec a wSelectJS showelem showjselem selset = WuiSpec (head,"?",const True, Nothing) (\ (render,errmsg,_,jsck) v -> selWidget errmsg jsck render v) (\wparams env s -> checkLegalInput wparams selWidget False (selset !! readNat (env (state2cgiRef s)))) where selWidget errmsg mbjs render v = (addErrMsg (isJust jsCheckCall) True False errmsg refname (render [maybe (selection ref namevalues) (\i -> selectionInitial ref namevalues i) idx]), jsCheckCall, cgiRef2state ref (Just jsaccess)) where ref free refname = idOfCgiRef ref jsaccess = JSFCall "selectValueOf" [JSString refname, JSFCall "new Array" (map showjselem selset)] jsCheckCall = maybeJSFun2checkCall refname jsaccess mbjs idx = elemIndex v selset namevalues = zip (map showelem selset) (map show [0..]) --- A widget to select a value from a given list of integers (provided as --- the argument). --- The current value should be contained in the value list and is preselected. wSelectInt :: [Int] -> WuiSpec Int wSelectInt = wSelectJS show JSInt --- A widget to select a Boolean value via a selection box. --- The arguments are the strings that are shown for the values --- True and False in the selection box, respectively. --- @param true - string for selection of True --- @param false - string for selection of False --- @return a WUI specification for a Boolean selection widget wSelectBool :: String -> String -> WuiSpec Bool wSelectBool true false = wSelectJS (\b->if b then true else false) JSBool [True,False] --- A widget to select a Boolean value via a check box. --- The first argument are HTML expressions that are shown after the --- check box. The result is True if the box is checked. wCheckBool :: [HtmlExp] -> WuiSpec Bool wCheckBool hexps = WuiSpec (head, "?", const True, Nothing) (\ (render,errmsg,_,jsck) v -> checkWidget errmsg jsck render v) (\wparams env s -> checkLegalInput wparams checkWidget False (env (state2cgiRef s)=="True")) where checkWidget errmsg mbjs render v = (addErrMsg (isJust jsCheckCall) True False errmsg refname (render [inline ((if v then checkedbox else checkbox) ref "True" : hexps)]), jsCheckCall, cgiRef2state ref (Just jsaccess)) where ref free refname = idOfCgiRef ref jsaccess = JSFCall "checkBoxValueOf" [JSString refname] jsCheckCall = maybeJSFun2checkCall refname jsaccess mbjs --- A widget to select a list of values from a given list of values --- via check boxes. --- The current values should be contained in the value list --- and are preselected. --- The first argument is a mapping from values into HTML expressions --- that are shown for each item after the check box. wMultiCheckSelect :: Eq a => (a->[HtmlExp]) -> [a] -> WuiSpec [a] wMultiCheckSelect showelem selset = WuiSpec (renderTuple, tupleError, const True, Nothing) (\ (render,errmsg,_,jsck) vs -> checkWidget errmsg jsck render vs) (\wparams env st -> checkLegalInput wparams checkWidget True (concatMap (\ (ref,s) -> if env ref=="True" then [s] else []) (zip (map state2cgiRef (state2states st)) selset))) where checkWidget _ _ render vs = -- TODO: add JavaScript code let refs = take (length selset) newVars numsetitems = zip refs selset showItem (ref,s) = inline ((if s `elem` vs then checkedbox else checkbox) ref "True" : showelem s) in (render (map showItem numsetitems), Nothing, states2state (map (\cref->cgiRef2state cref Nothing) refs) Nothing) newVars :: [_] newVars = unknown : newVars --- A widget to select a value from a given list of values via a radio button. --- The current value should be contained in the value list and is preselected. --- The first argument is a mapping from values into HTML expressions --- that are shown for each item after the radio button. wRadioSelect :: Eq a => (a->[HtmlExp]) -> [a] -> WuiSpec a wRadioSelect showelem selset = WuiSpec (renderTuple, tupleError, const True, Nothing) (\ (render,errmsg,_,jsck) v -> radioWidget errmsg jsck render v) (\wparams env s -> checkLegalInput wparams radioWidget True (selset !! readNat (env (state2cgiRef s)))) where radioWidget _ _ render v = -- TODO: add JavaScript code (render (map showItem numhitems), Nothing, cgiRef2state ref Nothing) where ref free idx = maybe 0 id (elemIndex v selset) numhitems = zip [0..] (map showelem selset) showItem (i,s) = table [[[(if i==idx then radio_main else radio_other) ref (show i)],s]] --- A widget to select a Boolean value via a radio button. --- The arguments are the lists of HTML expressions that are shown after --- the True and False radio buttons, respectively. --- @param true - HTML expressions for True radio button --- @param false - HTML expressions for False radio button --- @return a WUI specification for a Boolean selection widget wRadioBool :: [HtmlExp] -> [HtmlExp] -> WuiSpec Bool wRadioBool truehexps falsehexps = wRadioSelect (\b->if b then truehexps else falsehexps) [True,False] ----------------------------------------------------------------------- -- Auxliary functions used in WUI combinators: --- Translate a (maybe) JavaScript check function into a JavaScript call: maybeJSFun2checkCall _ _ Nothing = Nothing maybeJSFun2checkCall refname jsaccessvalue (Just jsf) = Just (JSFCall "setErrorClassName" [JSString refname, JSFCall jsf [jsaccessvalue]]) --- Conditionally (if first argument is true) adds an error message --- that is initially hidden (if second argument is false). --- If the third argument is true, the error message is framed into a table --- around the HTML expression and the message is shown as the first row of --- the a table, otherwise the error message is shown in front of the --- around an HTML expression. addErrMsg :: Bool -> Bool -> Bool -> String -> String -> HtmlExp -> HtmlExp addErrMsg hasjsck hide astable errmsg refname hexp = if not hide || hasjsck then if astable then table [[[style errmsgclass [htxt errmsg] `addAttrs` msgidattr]],[[hexp]]] `addAttrs` ([("class",tableclass)] ++ tblidattr) else inline [style errmsgclass [htxt errmsg] `addAttrs` msgidattr, hexp] else hexp where msgidattr = if null refname then [] else [("id","MSG_"++refname)] tblidattr = if null refname then [] else [("id",refname)] tableclass = if hide then "wuipassiveerrmsg" else "wuiactiveerrmsg" errmsgclass = if hide then "wuihide" else "wuinohide" -- Tuple constructor of arity n in JavaScript: jsTupleCons n = jsConsTerm ('(' : replicate (n - 1) ',' ++ ")") -- Join a list of input fields (each consisting of HTML expressions, -- JavaScript checks, WUI states) into a single structure accordding -- to the WUI parameters. If the second argument is True, the error message -- is hidden until explicitly displayed by the JavaScript checks. -- The third argument (jscomb) is a JavaScript combinator that combines -- the expressions to access the values of the subfields into an expression -- to access the complete combined value (or Nothing if such a combination -- is not possible) joinSubFields :: WuiParams _ -> Bool -> [(HtmlExp,Maybe JSExp,WuiState)] -> Maybe ([JSExp]->JSExp) -> (HtmlExp,Maybe JSExp,WuiState) joinSubFields (render,errmsg,_,mbjsck) hide subfields jscomb = let (subhexps,subjscks,substates) = unzip3 subfields consstate = states2state substates jscomb refname = state2refname consstate jscall = jsCheckCallFromState consstate mbjsck jscks = catMaybes subjscks in (addErrMsg (isJust jscall) hide True errmsg refname (render subhexps), if null jscks then jscall else Just (maybe (parAndJS jscks) (\jsc->JSOp "&&" (JSOp "&&" (JSFCall "unsetErrorClassName" [JSString refname]) (parAndJS jscks)) jsc) jscall), consstate) where -- generate strict conjunction of non-empty list of JavaScript expressions: parAndJS [j] = j parAndJS (j1:j2:js) = JSFCall "And" [j1,parAndJS (j2:js)] -- Check and join values of subelements. -- If there is an error is some subfield (indicated by the fourth argument), -- return Nothing, otherwise check the combined value. checkAndJoinSubFields wparams subfields jscons errorinsubfields joinvalue = let combine hide = joinSubFields wparams hide subfields jscons in if errorinsubfields then (Nothing, combine True) else if (conditionOf wparams) joinvalue then (Just joinvalue, combine True) else (Nothing, combine False) -- Generate a single structure that is a given alternative of an input field -- (each consisting of an HTML expression, JavaScript check, WUI state) -- accordding to the WUI parameters. -- If the second argument is True, the error message -- is hidden until explicitly displayed by the JavaScript checks. altSubField :: WuiParams _ -> Bool -> HtmlExp -> Maybe JSExp -> (Int,WuiState) -> (HtmlExp,Maybe JSExp,WuiState) altSubField (render,errmsg,_,mbjsck) hide subhexp subjsck altstate = let consstate = altstate2state altstate refname = state2refname consstate jscons = jsCheckCallFromState consstate mbjsck in (addErrMsg (isJust jscons) hide False errmsg refname (render [subhexp]), maybe jscons (\subjs -> Just (maybe subjs (\jsc->JSOp "&&" (JSOp "&&" (JSFCall "unsetErrorClassName" [JSString refname]) subjs) jsc) jscons)) subjsck, consstate) jsCheckCallFromState _ Nothing = Nothing jsCheckCallFromState wstate (Just jsck) = maybe Nothing (\jsstateaccess -> Just (JSFCall "setErrorClassName" [JSString (state2refname wstate), JSFCall jsck [jsstateaccess]])) (jsAccessToState wstate) ------------------------------------------------------------------------- --- WUI combinator to combine two tuples into a joint tuple. --- It is similar to wPair but renders both components as a single --- tuple provided that the components are already rendered as tuples, --- i.e., by the rendering function renderTuple. --- This combinator is useful to define combinators for large tuples. wJoinTuple :: (Eq a, Eq b) => WuiSpec a -> WuiSpec b -> WuiSpec (a,b) wJoinTuple (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) = WuiSpec (renderTuple, tupleError, const True, Nothing) showc readc where jscons = Just (jsTupleCons 2) render2joinrender render [h1,h2] = let h1s = unRenderTuple h1 h2s = unRenderTuple h2 in render (h1s++h2s) addJoinRender (render,errmsg,legal,jsck) = (render2joinrender render,errmsg,legal,jsck) showc wparams vc | (va,vb) =:<= vc = joinSubFields (addJoinRender wparams) True [showa wparama va, showb wparamb vb] jscons where va,vb free readc wparams env s = let [ra,rb] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb in checkAndJoinSubFields (addJoinRender wparams) [fielda,fieldb] jscons (rav==Nothing || rbv==Nothing) (fromJust rav, fromJust rbv) --- WUI combinator for pairs. wPair :: (Eq a, Eq b) => WuiSpec a -> WuiSpec b -> WuiSpec (a,b) wPair = wCons2JS (Just (jsTupleCons 2)) (\a b -> (a,b)) --- WUI combinator for constructors of arity 2. --- The first argument is the binary constructor. --- The second and third arguments are the WUI specifications --- for the argument types. wCons2 :: (Eq a, Eq b) => (a->b->c) -> WuiSpec a -> WuiSpec b -> WuiSpec c wCons2 = wCons2JS Nothing wCons2JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) = WuiSpec (renderTuple, tupleError, const True, Nothing) showc readc where showc wparams vc | cons va vb =:<= vc = joinSubFields wparams True [showa wparama va, showb wparamb vb] jscons where va,vb free readc wparams env s = let [ra,rb] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb in checkAndJoinSubFields wparams [fielda,fieldb] jscons (rav==Nothing || rbv==Nothing) (cons (fromJust rav) (fromJust rbv)) --- WUI combinator for triples. wTriple :: (Eq a, Eq b, Eq c) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec (a,b,c) wTriple = wCons3JS (Just (jsTupleCons 3)) (\a b c -> (a,b,c)) --- WUI combinator for constructors of arity 3. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons3 :: (Eq a, Eq b, Eq c) => (a->b->c->d) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d wCons3 = wCons3JS Nothing wCons3JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) = WuiSpec (renderTuple, tupleError, const True, Nothing) showd readd where showd wparams vd | cons va vb vc =:<= vd = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc] jscons where va,vb,vc free readd wparams env s = let [ra,rb,rc] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc in checkAndJoinSubFields wparams [fielda,fieldb,fieldc] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv)) --- WUI combinator for tuples of arity 4. w4Tuple :: (Eq a, Eq b, Eq c, Eq d) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec (a,b,c,d) w4Tuple = wCons4JS (Just (jsTupleCons 4)) (\a b c d -> (a,b,c,d)) --- WUI combinator for constructors of arity 4. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons4 :: (Eq a, Eq b, Eq c, Eq d) => (a->b->c->d->e) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e wCons4 = wCons4JS Nothing wCons4JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) = WuiSpec (renderTuple,tupleError, const True, Nothing) showe reade where showe wparams ve | cons va vb vc vd =:<= ve = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd] jscons where va,vb,vc,vd free reade wparams env s = let [ra,rb,rc,rd] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv)) --- WUI combinator for tuples of arity 5. w5Tuple :: (Eq a, Eq b, Eq c, Eq d, Eq e) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec (a,b,c,d,e) w5Tuple = wCons5JS (Just (jsTupleCons 5)) (\a b c d e -> (a,b,c,d,e)) --- WUI combinator for constructors of arity 5. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons5 :: (Eq a, Eq b, Eq c, Eq d, Eq e) => (a->b->c->d->e->f) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f wCons5 = wCons5JS Nothing wCons5JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) (WuiSpec wparame showe reade) = WuiSpec (renderTuple,tupleError, const True, Nothing) showf readf where showf wparams vl | cons va vb vc vd ve =:<= vl = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd, showe wparame ve] jscons where va,vb,vc,vd,ve free readf wparams env s = let [ra,rb,rc,rd,re] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd (rev,fielde) = reade wparame env re in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd, fielde] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing || rev==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv) (fromJust rev)) --- WUI combinator for tuples of arity 6. w6Tuple :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec (a,b,c,d,e,f) w6Tuple = wCons6JS (Just (jsTupleCons 6)) (\a b c d e f -> (a,b,c,d,e,f)) --- WUI combinator for constructors of arity 6. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons6 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f) => (a->b->c->d->e->f->g) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g wCons6 = wCons6JS Nothing wCons6JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) (WuiSpec wparame showe reade) (WuiSpec wparamf showf readf) = WuiSpec (renderTuple,tupleError, const True, Nothing) showg readg where showg wparams vl | cons va vb vc vd ve vf =:<= vl = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd, showe wparame ve, showf wparamf vf] jscons where va,vb,vc,vd,ve,vf free readg wparams env s = let [ra,rb,rc,rd,re,rf] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd (rev,fielde) = reade wparame env re (rfv,fieldf) = readf wparamf env rf in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd, fielde,fieldf] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing || rev==Nothing || rfv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv) (fromJust rev) (fromJust rfv)) --- WUI combinator for tuples of arity 7. w7Tuple :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec (a,b,c,d,e,f,g) w7Tuple = wCons7JS (Just (jsTupleCons 7)) (\a b c d e f g -> (a,b,c,d,e,f,g)) --- WUI combinator for constructors of arity 7. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons7 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g) => (a->b->c->d->e->f->g->h) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h wCons7 = wCons7JS Nothing wCons7JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) (WuiSpec wparame showe reade) (WuiSpec wparamf showf readf) (WuiSpec wparamg showg readg) = WuiSpec (renderTuple,tupleError, const True, Nothing) showh readh where showh wparams vl | cons va vb vc vd ve vf vg =:<= vl = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd, showe wparame ve, showf wparamf vf, showg wparamg vg] jscons where va,vb,vc,vd,ve,vf,vg free readh wparams env s = let [ra,rb,rc,rd,re,rf,rg] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd (rev,fielde) = reade wparame env re (rfv,fieldf) = readf wparamf env rf (rgv,fieldg) = readg wparamg env rg in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd, fielde,fieldf,fieldg] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing || rev==Nothing || rfv==Nothing || rgv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv) (fromJust rev) (fromJust rfv) (fromJust rgv)) --- WUI combinator for tuples of arity 8. w8Tuple :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec (a,b,c,d,e,f,g,h) w8Tuple = wCons8JS (Just (jsTupleCons 8)) (\a b c d e f g h -> (a,b,c,d,e,f,g,h)) --- WUI combinator for constructors of arity 8. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons8 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h) => (a->b->c->d->e->f->g->h->i) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i wCons8 = wCons8JS Nothing wCons8JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) (WuiSpec wparame showe reade) (WuiSpec wparamf showf readf) (WuiSpec wparamg showg readg) (WuiSpec wparamh showh readh) = WuiSpec (renderTuple,tupleError, const True, Nothing) showi readi where showi wparams vl | cons va vb vc vd ve vf vg vh =:<= vl = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd, showe wparame ve, showf wparamf vf, showg wparamg vg, showh wparamh vh] jscons where va,vb,vc,vd,ve,vf,vg,vh free readi wparams env s = let [ra,rb,rc,rd,re,rf,rg,rh] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd (rev,fielde) = reade wparame env re (rfv,fieldf) = readf wparamf env rf (rgv,fieldg) = readg wparamg env rg (rhv,fieldh) = readh wparamh env rh in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd, fielde,fieldf,fieldg,fieldh] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing || rev==Nothing || rfv==Nothing || rgv==Nothing || rhv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv) (fromJust rev) (fromJust rfv) (fromJust rgv) (fromJust rhv)) --- WUI combinator for tuples of arity 9. w9Tuple :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i -> WuiSpec (a,b,c,d,e,f,g,h,i) w9Tuple = wCons9JS (Just (jsTupleCons 9)) (\a b c d e f g h i -> (a,b,c,d,e,f,g,h,i)) --- WUI combinator for constructors of arity 9. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons9 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i) => (a->b->c->d->e->f->g->h->i->j) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i -> WuiSpec j wCons9 = wCons9JS Nothing wCons9JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) (WuiSpec wparame showe reade) (WuiSpec wparamf showf readf) (WuiSpec wparamg showg readg) (WuiSpec wparamh showh readh) (WuiSpec wparami showi readi) = WuiSpec (renderTuple,tupleError, const True, Nothing) showj readj where showj wparams vl | cons va vb vc vd ve vf vg vh vi =:<= vl = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd, showe wparame ve, showf wparamf vf, showg wparamg vg, showh wparamh vh, showi wparami vi] jscons where va,vb,vc,vd,ve,vf,vg,vh,vi free readj wparams env s = let [ra,rb,rc,rd,re,rf,rg,rh,ri] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd (rev,fielde) = reade wparame env re (rfv,fieldf) = readf wparamf env rf (rgv,fieldg) = readg wparamg env rg (rhv,fieldh) = readh wparamh env rh (riv,fieldi) = readi wparami env ri in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd, fielde,fieldf,fieldg,fieldh, fieldi] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing || rev==Nothing || rfv==Nothing || rgv==Nothing || rhv==Nothing || riv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv) (fromJust rev) (fromJust rfv) (fromJust rgv) (fromJust rhv) (fromJust riv)) --- WUI combinator for tuples of arity 10. w10Tuple :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i -> WuiSpec j -> WuiSpec (a,b,c,d,e,f,g,h,i,j) w10Tuple = wCons10JS (Just (jsTupleCons 10)) (\a b c d e f g h i j -> (a,b,c,d,e,f,g,h,i,j)) --- WUI combinator for constructors of arity 10. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons10 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j) => (a->b->c->d->e->f->g->h->i->j->k) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i -> WuiSpec j -> WuiSpec k wCons10 = wCons10JS Nothing wCons10JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) (WuiSpec wparame showe reade) (WuiSpec wparamf showf readf) (WuiSpec wparamg showg readg) (WuiSpec wparamh showh readh) (WuiSpec wparami showi readi) (WuiSpec wparamj showj readj) = WuiSpec (renderTuple,tupleError, const True, Nothing) showk readk where showk wparams vl | cons va vb vc vd ve vf vg vh vi vj =:<= vl = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd, showe wparame ve, showf wparamf vf, showg wparamg vg, showh wparamh vh, showi wparami vi, showj wparamj vj] jscons where va,vb,vc,vd,ve,vf,vg,vh,vi,vj free readk wparams env s = let [ra,rb,rc,rd,re,rf,rg,rh,ri,rj] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd (rev,fielde) = reade wparame env re (rfv,fieldf) = readf wparamf env rf (rgv,fieldg) = readg wparamg env rg (rhv,fieldh) = readh wparamh env rh (riv,fieldi) = readi wparami env ri (rjv,fieldj) = readj wparamj env rj in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd, fielde,fieldf,fieldg,fieldh, fieldi,fieldj] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing || rev==Nothing || rfv==Nothing || rgv==Nothing || rhv==Nothing || riv==Nothing || rjv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv) (fromJust rev) (fromJust rfv) (fromJust rgv) (fromJust rhv) (fromJust riv) (fromJust rjv)) --- WUI combinator for tuples of arity 11. w11Tuple :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i -> WuiSpec j -> WuiSpec k -> WuiSpec (a,b,c,d,e,f,g,h,i,j,k) w11Tuple = wCons11JS (Just (jsTupleCons 11)) (\a b c d e f g h i j k -> (a,b,c,d,e,f,g,h,i,j,k)) --- WUI combinator for constructors of arity 11. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons11 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k) => (a->b->c->d->e->f->g->h->i->j->k->l) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i -> WuiSpec j -> WuiSpec k -> WuiSpec l wCons11 = wCons11JS Nothing wCons11JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) (WuiSpec wparame showe reade) (WuiSpec wparamf showf readf) (WuiSpec wparamg showg readg) (WuiSpec wparamh showh readh) (WuiSpec wparami showi readi) (WuiSpec wparamj showj readj) (WuiSpec wparamk showk readk) = WuiSpec (renderTuple,tupleError, const True, Nothing) showl readl where showl wparams vl | cons va vb vc vd ve vf vg vh vi vj vk =:<= vl = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd, showe wparame ve, showf wparamf vf, showg wparamg vg, showh wparamh vh, showi wparami vi, showj wparamj vj, showk wparamk vk] jscons where va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk free readl wparams env s = let [ra,rb,rc,rd,re,rf,rg,rh,ri,rj,rk] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd (rev,fielde) = reade wparame env re (rfv,fieldf) = readf wparamf env rf (rgv,fieldg) = readg wparamg env rg (rhv,fieldh) = readh wparamh env rh (riv,fieldi) = readi wparami env ri (rjv,fieldj) = readj wparamj env rj (rkv,fieldk) = readk wparamk env rk in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd, fielde,fieldf,fieldg,fieldh, fieldi,fieldj,fieldk] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing || rev==Nothing || rfv==Nothing || rgv==Nothing || rhv==Nothing || riv==Nothing || rjv==Nothing || rkv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv) (fromJust rev) (fromJust rfv) (fromJust rgv) (fromJust rhv) (fromJust riv) (fromJust rjv) (fromJust rkv)) --- WUI combinator for tuples of arity 12. w12Tuple :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l) => WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i -> WuiSpec j -> WuiSpec k -> WuiSpec l -> WuiSpec (a,b,c,d,e,f,g,h,i,j,k,l) w12Tuple = wCons12JS (Just (jsTupleCons 12)) (\a b c d e f g h i j k l -> (a,b,c,d,e,f,g,h,i,j,k,l)) --- WUI combinator for constructors of arity 12. --- The first argument is the ternary constructor. --- The further arguments are the WUI specifications for the argument types. wCons12 :: (Eq a, Eq b, Eq c, Eq d, Eq e, Eq f, Eq g, Eq h, Eq i, Eq j, Eq k, Eq l) => (a->b->c->d->e->f->g->h->i->j->k->l->m) -> WuiSpec a -> WuiSpec b -> WuiSpec c -> WuiSpec d -> WuiSpec e -> WuiSpec f -> WuiSpec g -> WuiSpec h -> WuiSpec i -> WuiSpec j -> WuiSpec k -> WuiSpec l -> WuiSpec m wCons12 = wCons12JS Nothing wCons12JS jscons cons (WuiSpec wparama showa reada) (WuiSpec wparamb showb readb) (WuiSpec wparamc showc readc) (WuiSpec wparamd showd readd) (WuiSpec wparame showe reade) (WuiSpec wparamf showf readf) (WuiSpec wparamg showg readg) (WuiSpec wparamh showh readh) (WuiSpec wparami showi readi) (WuiSpec wparamj showj readj) (WuiSpec wparamk showk readk) (WuiSpec wparaml showl readl) = WuiSpec (renderTuple,tupleError, const True, Nothing) showm readm where showm wparams vm | cons va vb vc vd ve vf vg vh vi vj vk vl =:<= vm = joinSubFields wparams True [showa wparama va, showb wparamb vb, showc wparamc vc, showd wparamd vd, showe wparame ve, showf wparamf vf, showg wparamg vg, showh wparamh vh, showi wparami vi, showj wparamj vj, showk wparamk vk, showl wparaml vl] jscons where va,vb,vc,vd,ve,vf,vg,vh,vi,vj,vk,vl free readm wparams env s = let [ra,rb,rc,rd,re,rf,rg,rh,ri,rj,rk,rl] = state2states s (rav,fielda) = reada wparama env ra (rbv,fieldb) = readb wparamb env rb (rcv,fieldc) = readc wparamc env rc (rdv,fieldd) = readd wparamd env rd (rev,fielde) = reade wparame env re (rfv,fieldf) = readf wparamf env rf (rgv,fieldg) = readg wparamg env rg (rhv,fieldh) = readh wparamh env rh (riv,fieldi) = readi wparami env ri (rjv,fieldj) = readj wparamj env rj (rkv,fieldk) = readk wparamk env rk (rlv,fieldl) = readl wparaml env rl in checkAndJoinSubFields wparams [fielda,fieldb,fieldc,fieldd, fielde,fieldf,fieldg,fieldh, fieldi,fieldj,fieldk,fieldl] jscons (rav==Nothing || rbv==Nothing || rcv==Nothing || rdv==Nothing || rev==Nothing || rfv==Nothing || rgv==Nothing || rhv==Nothing || riv==Nothing || rjv==Nothing || rkv==Nothing || rlv==Nothing) (cons (fromJust rav) (fromJust rbv) (fromJust rcv) (fromJust rdv) (fromJust rev) (fromJust rfv) (fromJust rgv) (fromJust rhv) (fromJust riv) (fromJust rjv) (fromJust rkv) (fromJust rlv)) --- WUI combinator for list structures where the list elements are vertically --- aligned in a table. wList :: Eq a => WuiSpec a -> WuiSpec [a] wList (WuiSpec rendera showa reada) = WuiSpec (renderList,"Illegal list:",const True, Nothing) (\wparams vas -> joinSubFields wparams True (map (showa rendera) vas) (Just jsListComb)) (\wparams env s -> let rvs = map (reada rendera env) (state2states s) combine hide = joinSubFields wparams hide (map snd rvs) (Just jsListComb) in if Nothing `elem` (map fst rvs) then (Nothing, combine True) else let value = map (fromJust . fst) rvs in if (conditionOf wparams) value then (Just value, combine True) else (Nothing, combine False) ) where jsListComb args = JSFCall "array2list" [JSFCall "new Array" args] --- Add headings to a standard WUI for list structures: wListWithHeadings :: Eq a => [String] -> WuiSpec a -> WuiSpec [a] wListWithHeadings headings wspec = wList wspec `withRendering` renderHeadings where renderHeadings hs = addHeadings (renderList hs) (map (\s->[htxt s]) headings) --- WUI combinator for list structures where the list elements are horizontally --- aligned in a table. wHList :: Eq a => WuiSpec a -> WuiSpec [a] wHList wspec = wList wspec `withRendering` renderTuple --- WUI for matrices, i.e., list of list of elements --- visualized as a matrix. wMatrix :: Eq a => WuiSpec a -> WuiSpec [[a]] wMatrix wspec = wList (wHList wspec) --- WUI for Maybe values. It is constructed from a WUI for --- Booleans and a WUI for the potential values. Nothing corresponds --- to a selection of False in the Boolean WUI. --- The value WUI is shown after the Boolean WUI. --- @param wspecb - a WUI specification for Boolean values --- @param wspeca - a WUI specification for the type of potential values --- @param def - a default value that is used if the current value is Nothing wMaybe :: Eq a => WuiSpec Bool -> WuiSpec a -> a -> WuiSpec (Maybe a) wMaybe (WuiSpec paramb showb readb) (WuiSpec parama showa reada) def = WuiSpec (renderTuple, tupleError, const True, Nothing) (\wparams mbs -> joinSubFields wparams True [showb paramb (mbs/=Nothing), showa parama (maybe def id mbs)] Nothing) (\wparams env s -> let [rb,ra] = state2states s (rbv,fieldb) = readb paramb env rb (rav,fielda) = reada parama env ra combine hide = joinSubFields wparams hide [fieldb,fielda] Nothing in if rbv==Nothing || rav==Nothing then (Nothing, combine True) else let value = if fromJust rbv then Just (fromJust rav) else Nothing in if (conditionOf wparams) value then (Just value, combine True) else (Nothing, combine False)) --- A WUI for Maybe values where a check box is used to select Just. --- The value WUI is shown after the check box. --- @param wspec - a WUI specification for the type of potential values --- @param hexps - a list of HTML expressions shown after the check box --- @param def - a default value if the current value is Nothing wCheckMaybe :: Eq a => WuiSpec a -> [HtmlExp] -> a -> WuiSpec (Maybe a) wCheckMaybe wspec exps = wMaybe (wCheckBool exps) wspec --- A WUI for Maybe values where radio buttons are used to switch --- between Nothing and Just. --- The value WUI is shown after the radio button WUI. --- @param wspec - a WUI specification for the type of potential values --- @param hexps - a list of HTML expressions shown after the Nothing button --- @param hexps - a list of HTML expressions shown after the Just button --- @param def - a default value if the current value is Nothing wRadioMaybe :: Eq a => WuiSpec a -> [HtmlExp] -> [HtmlExp] -> a -> WuiSpec (Maybe a) wRadioMaybe wspec hnothing hjust = wMaybe wBool wspec where wBool = wRadioSelect (\b->if b then hjust else hnothing) [False,True] --- WUI for union types. --- Here we provide only the implementation for Either types --- since other types with more alternatives can be easily reduced to this case. wEither :: (Eq a, Eq b) => WuiSpec a -> WuiSpec b -> WuiSpec (Either a b) wEither (WuiSpec rendera showa reada) (WuiSpec renderb showb readb) = WuiSpec (head, "?", const True, Nothing) showEither readEither where showEither wparams (Left va) = let (hea,jsa,rta) = showa rendera va in altSubField wparams True hea jsa (1,rta) showEither wparams (Right vb) = let (heb,jsb,rtb) = showb renderb vb in altSubField wparams True heb jsb (2,rtb) readEither wparams env s = let (altindex,rab) = state2altstate s in case altindex of 1 -> let (rv,(he,jsck,rst)) = reada rendera env rab in checkValue (rv==Nothing) (Left (fromJust rv)) he jsck (1,rst) 2 -> let (rv,(he,jsck,rst)) = readb renderb env rab in checkValue (rv==Nothing) (Right (fromJust rv)) he jsck (2,rst) where checkValue isnothing value hexp jsck altstate = let combine hide = altSubField wparams hide hexp jsck altstate in if isnothing then (Nothing, combine True) else if (conditionOf wparams) value then (Just value, combine True) else (Nothing, combine False) --- A simple tree structure to demonstrate the construction of WUIs for tree --- types. data WTree a = WLeaf a | WNode [WTree a] deriving Eq --- WUI for tree types. --- The rendering specifies the rendering of inner nodes. --- Leaves are shown with their default rendering. wTree :: Eq a => WuiSpec a -> WuiSpec (WTree a) wTree (WuiSpec rendera showa reada) = WuiSpec (renderList, "Illegal tree:", const True, Nothing) showTree readTree where showTree _ (WLeaf va) = let (hea,jsa,rta) = showa rendera va in altSubField rendera True hea jsa (1,rta) showTree wparams (WNode ns) = let (hes,jscks,sts) = joinSubFields wparams True (map (showTree wparams) ns) Nothing in (hes,jscks,altstate2state (2,sts)) readTree wpar env s = let (altindex,rab) = state2altstate s in case altindex of 1 -> let (rv,(he,jsck,rst)) = reada rendera env rab combine hide = altSubField rendera hide he jsck (1,rst) in checkValue combine (rv==Nothing) (WLeaf (fromJust rv)) 2 -> let rvs = map (readTree wpar env) (state2states rab) combine hide = let (hes,jscks,sts) = joinSubFields wpar hide (map snd rvs) Nothing in (hes,jscks,altstate2state (2,sts)) in checkValue combine (Nothing `elem` (map fst rvs)) (WNode (map (fromJust . fst) rvs)) where checkValue combine isnothing value = if isnothing then (Nothing, combine True) else if conditionOf wpar value then (Just value, combine True) else (Nothing, combine False) ------------------------------------------------------------------------------- -- Definition of standard rendering functions --- Standard rendering of tuples as a table with a single row. --- Thus, the elements are horizontally aligned. renderTuple :: Rendering renderTuple hexps = table [map (\h->[h]) hexps] --- Inverse operation of renderTuple. If the argument has not the --- shape of the renderTuple output, it is returned unchanged. --- In future versions, this operation is better implemented using --- functional logic features, but currently the encapsulated search --- is a bit weak for this purpose. unRenderTuple :: HtmlExp -> [HtmlExp] unRenderTuple hexp = if isTupleTable hexp then getTupleTableElems hexp else [hexp] where isTupleTable he = case he of HtmlStruct "table" [] [HtmlStruct "tr" [] tds] -> all isSingleElem tds _ -> False isSingleElem he = case he of HtmlStruct "td" _ [_] -> True _ -> False getTupleTableElems (HtmlStruct "table" [] [HtmlStruct "tr" [] tds]) = map (\ (HtmlStruct "td" _ [e]) -> e) tds -- Standard error message for tuples: tupleError :: String tupleError = "Illegal combination:" --- Standard rendering of tuples with a tag for each element. --- Thus, each is preceded by a tag, that is set in bold, and all --- elements are vertically aligned. renderTaggedTuple :: [String] -> Rendering renderTaggedTuple tags hexps = table (map (\(t,h)->[[bold [htxt t]],[h]]) (zip tags hexps)) --- Standard rendering of lists as a table with a row for each item: --- Thus, the elements are vertically aligned. renderList :: Rendering renderList hexps = mergeTableOfTable (table (map (\h->[[h]]) hexps)) `addAttr` ("border","1") mergeTableOfTable :: HtmlExp -> HtmlExp mergeTableOfTable (HtmlStruct "table" attrs rows) = HtmlStruct "table" attrs (if all isRowWithSingleTableData rows then map mergeRowWithSingleTableData rows else rows ) isRowWithSingleTableData :: HtmlExp -> Bool isRowWithSingleTableData row = case row of (HtmlStruct "tr" [] [HtmlStruct "td" [] [HtmlStruct "table" _ [HtmlStruct "tr" _ _]]]) -> True _ -> False mergeRowWithSingleTableData :: HtmlExp -> HtmlExp mergeRowWithSingleTableData (HtmlStruct "tr" [] [HtmlStruct "td" [] [HtmlStruct "table" _ [row]]]) = row ------------------------------------------------------------------------------- -- Main operations to generate HTML structures and handlers from -- WUI specifications: --- Generates an HTML form from a WUI data specification, --- an initial value and an update form. mainWUI :: WuiSpec a -> a -> (a -> IO HtmlForm) -> IO HtmlForm mainWUI wuispec val store = do let (hexp,whandler) = wui2html wuispec val store return $ form "WUI" [hexp, breakline, wuiHandler2button "Submit" whandler] --- Generates HTML editors and a handler from a WUI data specification, --- an initial value and an update form. wui2html :: WuiSpec a -> a -> (a -> IO HtmlForm) -> (HtmlExp,WuiHandler) wui2html wspec val store = wuiWithErrorForm wspec val store standardErrorForm --- A standard error form for WUIs. standardErrorForm :: HtmlExp -> WuiHandler -> IO HtmlForm standardErrorForm hexp whandler = return $ standardForm "Input error" [hexp, wuiHandler2button "Submit" whandler] --- Puts a WUI into a HTML form containing "holes" for the WUI and the --- handler. wuiInForm :: WuiSpec a -> a -> (a -> IO HtmlForm) -> (HtmlExp -> WuiHandler -> IO HtmlForm) -> IO HtmlForm wuiInForm wspec val store userform = answerForm (wuiWithErrorForm wspec val store userform) where answerForm (hexp,whandler) = userform hexp whandler --- Generates HTML editors and a handler from a WUI data specification, --- an initial value and an update form. In addition to wui2html, --- we can provide a skeleton form used to show illegal inputs. wuiWithErrorForm :: WuiSpec a -> a -> (a -> IO HtmlForm) -> (HtmlExp -> WuiHandler -> IO HtmlForm) -> (HtmlExp,WuiHandler) wuiWithErrorForm wspec val store errorform = showAndReadWUI wspec store errorform (generateWUI wspec val) generateWUI :: WuiSpec a -> a -> (HtmlExp,Maybe JSExp, CgiEnv -> (Maybe a,HtmlState)) generateWUI (WuiSpec wparams showhtml readval) val = hst2result (showhtml wparams val) where hst2result (htmledits,jsfs,wstate) = (htmledits, jsfs, \env -> readval wparams env wstate) showAndReadWUI :: WuiSpec a -> (a -> IO HtmlForm) -> (HtmlExp -> WuiHandler -> IO HtmlForm) -> (HtmlExp,Maybe JSExp,CgiEnv->(Maybe a,HtmlState)) -> (HtmlExp,WuiHandler) showAndReadWUI wspec store errorform (htmledits,jsfs,readenv) = (inline [wuiStyleSheet, htmledits], WHandler (htmlhandler wspec) jsfs) where htmlhandler wui@(WuiSpec wparams _ readval) env = let (mbnewval, (htmlerrform,htmlerrjsfs,errwstate)) = readenv env in maybe (let (errhexp,errwhdl) = showAndReadWUI wui store errorform (htmlerrform,htmlerrjsfs, \errenv -> readval wparams errenv errwstate) in errorform errhexp errwhdl) (\newval -> seq (normalForm newval) -- to strip off unused lvars (store newval)) mbnewval -------------------------------------------------------------------------- -- The style sheet used in WUIs: wuiStyleSheet :: HtmlExp wuiStyleSheet = styleSheet $ "\n.wuihide { display: none; }\n" ++ ".wuinohide { display: inline; color: red; font-weight: bold ; background-color: yellow; }\n" ++ ".wuipassiveerrmsg { padding: 0px; border-spacing: 0px; }\n"++ ".wuiactiveerrmsg { padding: 0px; border-spacing: 0px; background-color: yellow; border-width: 2px; border-style: solid; border-color: red; }\n" --------------------------------------------------------------------------