------------------------------------------------------------------------------ --- Library for creating web applications from ui descriptions --- based on the HTML library from PAKCS --- --- The installation of a cgi script written with this library --- can be done by the command --- --- curry makecgi -m main -o /home/user/public_html/prog.cgi prog --- --- where prog is the name of the Curry program with --- the cgi script, /home/user/public_html/prog.cgi is --- the desired location of the --- compiled cgi script, and main is a Curry expression --- like runGUI "Titel" uiwidget. --- --- @author Christof Kluss --- @version July 2013 ------------------------------------------------------------------------------ module UI2HTML ( UIWidget, UIRef, Command(..),Ref,Handler(..), Widget(Widget), Event(..),WidgetKind(..), CanvasItem(..), -- for styles StyleClass(..),Position(..),Direction(..), Style(..),BorderStyle(..),FontStyle(..),Color(..), -- addStyle,addStyles,setStyles, addHandler,addHandlers,setHandlers, setRef,getRef, -- IO Actions UI2HTML.runUI,UI2HTML.exitUI, UI2HTML.getValue,UI2HTML.setValue,UI2HTML.updateValue,UI2HTML.appendValue, UI2HTML.changeStyles,UI2HTML.setHandler,UI2HTML.setDisabled, UI2HTML.addCanvas,UI2HTML.setVisible, UI2HTML.showPopup,UI2HTML.showMessage, -- Widgets colS,col,rowS,row,matrixS,matrix, entry,entryS,label,labelS, UI.button,buttonS,simpleButton,simpleButtonS, checkButton,checkButtonS,simpleCheckButton,simpleCheckButtonS, canvas,canvasS, textEdit,textEditS, scale,scaleS,message,messageS, menuBar,menuBarS,menu,menuS,menuSeparator,menuSeparatorS, menuItem,menuItemS, listBox,listBoxS, UI.selection,UI.selectionInitial,UI.selectionInitialS, UI.radio_main,UI.radio_other, -- showBorderStyle,showColor,showPos, -- UI2HTML.UIEnv, Reconfigure(..), -- exported for GUI2HTML ui2hexps, ajaxForm,ajaxForm2, setConfig,seeText, -- setErrorBg, cgiRef2Ref,ref2cgiRef, nextHtmlForm, includeHtmlForm, onClickSpicy, changeChilds ) where import HTML import IOExts import Maybe import Json import System import UI import SpicyWeb --- The URL of the directory where the include files (Action.js, --- Throbber.gif, Throbber.png, ajaxrequest.js, default.css, prototype.js) --- are stored. includeURL :: String includeURL = "http://www.informatik.uni-kiel.de/~pakcs/UI/" jsbibsspicy :: [String] jsbibsspicy = ["prototype.js","Action.js"] jsbibs :: [String] jsbibs = ["ajaxrequest.js"] css :: [String] css = ["default.css"] data State = State (CgiRef -> String, ([(Ref CgiRef, Maybe String,[Reconfigure])], [([(String,String)],[HtmlExp])])) --- The (abstract) data type for representing environments data UIEnv = UIEnv (IORef State) type UIWidget = UI.Widget CgiRef (UIEnv -> IO()) (Doc (Ref CgiRef)) type UIRef = Ref CgiRef -- Data type for describing configurations that are applied data Reconfigure = List [String] | Disabled Bool | Visible Bool | Style [StyleClass] | Pos (Int,Int) | ErrorBg Bool conf2str :: Reconfigure -> (String,Json) conf2str val = case val of --Value str -> ("text", String str) Disabled b -> ("disabled",Bool b) Visible b -> ("visible",Bool b) Style styles -> ("style",String (styleClasses2String styles)) List strs -> ("list",Array (map String strs)) Pos (x,y) -> ("pos",Array [Int x,Int y]) ErrorBg b -> ("errorbg",Bool b) x -> ("error",String (show x)) state2json :: ([(Ref CgiRef,Maybe (String),[Reconfigure])],_) -> Json state2json (xs,_) = Array (map toJson xs) where toJson (ref,mbval,confs) = Object [("id",String (idOfRef ref)), ("changes", Object ((map conf2str confs) ++ (maybe [] (\val -> [("value",String val)]) mbval)))] execCmdAndRespond :: (UIEnv -> IO()) -> IORef State -> IO HtmlForm execCmdAndRespond cmd stateref = do cmd (UIEnv stateref) State (_,state) <- readIORef stateref let ps = snd state writeIORef stateref (State (const "",([],[]))) return $ AjaxAnswer (state2json state) ps ------------------------------------------------------------------------------- ref2cgiRef :: UIRef -> CgiRef ref2cgiRef (Ref ref) = ref cgiRef2Ref :: CgiRef -> Ref CgiRef cgiRef2Ref ref = Ref ref -- little modifications on the UI widgets for HTML output widgetUI2widgetHTML :: UIWidget -> UIWidget widgetUI2widgetHTML (UI.Widget name mbcont mbref handlers styles ws) = case name of Matrix wss -> Widget (Matrix (map (map widgetUI2widgetHTML) wss)) mbcont mbref handlers styles [] MenuBar -> ((UI.Widget name mbcont mbref handlers styles (map widgetUI2widgetHTML entrys)) `addStyles` [Class [Width 8]]) where entrys = if null ws then ws else let (UI.Widget name' mbcontent' _ _ _ ws') = head ws in case name' of Menu -> (UI.Widget (Name "option") mbcontent' Nothing [] [Class [Font Bold,Font Italic]] []):ws' ++ tail ws _ -> ws Menu -> UI.Widget name mbcont mbref handlers styles (map widgetUI2widgetHTML ws) Scale min max -> UI.Widget (Name "select") mbcont mbref handlers styles (map (\x -> UI.Widget (ListBoxItem (show x) False) (Just (show x)) Nothing [] [] []) [min .. max]) CheckButton checked -> row [UI.Widget (CheckButton checked) mbcont mbref handlers styles [], label (fromMaybe "" mbcont)] ListBox size list sel -> UI.Widget (ListBox size list sel) mbcont mbref handlers styles (selOption list 0) where selOption [] _ = [] selOption (v:vs) i = (UI.Widget (ListBoxItem (show i) (if i==sel then True else False)) (Just v) Nothing [] [] []) : selOption vs (i+1) _ -> UI.Widget name mbcont mbref handlers styles (map widgetUI2widgetHTML ws) -- translates the kind of a widget into corresponding html tag showWidgetKind :: WidgetKind _ _ _ -> String showWidgetKind kind = case kind of Matrix _ -> "span" Col -> "div" Row -> "div" Label -> "div" -- span is no block element Button -> "input" Entry -> "input" Menu -> "optgroup" MenuSeparator -> "option" MenuBar -> "select" MenuItem -> "option" (Canvas _ _) -> "canvas" (TextEdit _ _) -> "textarea" (CheckButton _) -> "input" (Scale _ _) -> "scale" (ListBox _ _ _) -> "select" ListBoxItem _ _ -> "option" (Name str) -> str --(Selection _ _) -> "select" --(SelectionItem _ _) -> "option" (RadioButton _ ) -> "input" Link -> "a" _ -> "error in UI2HTML.showWidgetKind" event2js :: Event -> String event2js event = case event of FocusOut -> "onblur" FocusIn -> "onfocus" MouseButton1 -> "onclick" MouseButton2 -> "onclick" MouseButton3 -> "onclick" KeyPress -> "onkeyup" Return -> "onkeypress" Change -> "onchange" DoubleClick -> "ondbclick" Click -> "onclick" DefaultEvent -> "default" _ -> "error in UI2HTML.event2js" event2str :: WidgetKind _ _ _ -> Event -> String event2str wkind event = case event of DefaultEvent -> case wkind of Scale _ _ -> "onchange" Entry -> "onkeypress" -- Return Name "select" -> "onchange" _ -> "onclick" _ -> event2js event event2jsfctname :: WidgetKind _ _ _ -> Event -> String event2jsfctname kind event = case event of Return -> "handleKeypress" KeyPress -> "ajaxRequest" DefaultEvent -> case kind of Entry -> "handleKeypress" _ -> "ajaxRequest" _ -> "ajaxRequest" name2attrs :: WidgetKind _ _ _ -> [(String,String)] name2attrs name = case name of TextEdit rows cols -> [("rows",show rows),("cols",show cols)] Canvas h w -> [("height",show h),("width",show w)] Button -> [("type","button")] Entry -> [("type","text")] ListBox size _ _ -> [("size",show size)] --SelectionItem _ b -> if b then [("selected","yes")] else [] ListBoxItem _ b -> if b then [("selected","yes")] else [] CheckButton checked -> [("type","checkbox")] ++ if checked then [("checked","checked")] else [] RadioButton checked -> [("type","radio")] ++ (if checked then [("checked","yes")] else []) MenuBar -> [("onchange", "ajaxRequest(event,window,this.options[this.selectedIndex].value); " ++ "this.selectedIndex=0;")] _ -> [] content2attrs :: Maybe (String) -> WidgetKind _ _ _ -> [(String,String)] content2attrs mbcontent name = case mbcontent of Nothing -> [] Just label -> case name of Menu -> [("label",htmlQuote label)] Label -> [] TextEdit _ _ -> [] MenuItem -> [] MenuSeparator -> [] Link -> [("href","#")] ListBoxItem val _ -> [("value",htmlQuote val)] _ -> [("value",htmlQuote label)] -- converts a Widget to a HtmlExp widget2hexp :: Maybe (IORef State) -> Widget CgiRef (UIEnv -> IO ()) _ -> [HtmlExp] widget2hexp mbstateref (UI.Widget kind mbcontent mbref handlers styles widgets) = handlerhexps ++ [case mbref of Nothing -> hexp Just ref -> case kind of RadioButton False -> hexp -- only the main radio button has a reference _ -> HtmlCRef hexp (ref2cgiRef ref)] where hexp1 = HtmlStruct (showWidgetKind kind) -- tag name -- set the tag attributes ((name2attrs kind) -- type attribute ++ (content2attrs mbcontent kind) -- value attribute ++ case mbref of -- name and id attribute Nothing -> [] Just ref1 -> case kind of -- span has no name attribute Label -> [("id",rid)] Link -> [("id",rid)] Row -> [("id",rid)] Col -> [("id",rid)] -- id attr is unique, -- so groups of radio buttons cannot have same id RadioButton _ -> [("name",rid)] _ -> [("id",rid),("name",rid)] where rid = idOfRef ref1 ++ if null styles -- style attribute then [] else styleClasses2Attrs styles) childhexps -- child html expressions xs = case kind of --Col -> map (\ w -> block (widget2hexp mbstateref w)) widgets Col -> [table' (map (\ w -> [widget2hexp mbstateref w]) widgets)] Row -> [table' [map (\ w -> widget2hexp mbstateref w) widgets]] Matrix wss -> [table' (map (\ ws -> map (\w -> widget2hexp mbstateref w) ws) wss) `addAttrs` (if null styles then [] else styleClasses2Attrs styles)] _ -> concatMap (widget2hexp mbstateref) widgets -- some special handling for widgets without child widgets -- the content of these widgets are in their textnodes -- e.g. a widget of kind TextEdit: childhexps = case kind of Canvas _ _ -> [htxt "canvas tag not supported"] Label -> [htxtcont] -- content TextEdit _ _ -> [htxtcont] --