------------------------------------------------------------------------------ --- Library for constructing static and dynamic HTML pages. --- [This paper](http://www.informatik.uni-kiel.de/~mh/papers/PADL01.html) --- contains a description of the basic ideas behind this library. --- --- The installation of a cgi script written with this library --- can be done by the command --- --- curry makecgi -m initialForm -o /home/joe/public_html/prog.cgi prog --- --- where `prog` is the name of the Curry program with --- the cgi script, `/home/joe/public_html/prog.cgi` is --- the desired location of the --- compiled cgi script, and `initialForm` is the Curry expression --- (of type IO HtmlForm) computing the HTML form (where `curry` --- is the shell command calling the Curry system PAKCS or KiCS2). --- --- @author Michael Hanus (with extensions by Bernd Brassel and Marco Comini) --- @version October 2016 --- @category web ------------------------------------------------------------------------------ {-# OPTIONS_CYMAKE -Wno-incomplete-patterns #-} module HTML.Base ( HtmlExp(..), textOf, HtmlPage(..), PageParam(..), HtmlForm(..), FormParam(..), CookieParam(..), CgiRef,idOfCgiRef,CgiEnv,HtmlHandler, defaultEncoding, form,standardForm,answerText,answerEncText, cookieForm,getCookies, page,standardPage, pageEnc,pageCSS,pageMetaInfo,pageLinkInfo,pageBodyAttr,addPageParam, formEnc,formCSS,formMetaInfo,formBodyAttr,addFormParam,addFormParams, htxt,htxts,hempty,nbsp,h1,h2,h3,h4,h5, par,section,header,footer,emphasize,strong,bold,italic,nav,code, center,blink,teletype,pre,verbatim,address,href,anchor, ulist,olist,litem,dlist,table,headedTable,addHeadings, hrule,breakline,image, styleSheet,style,textstyle,blockstyle,inline,block, redirect,expires, button,resetbutton,imageButton,coordinates, textfield,password,textarea,checkbox,checkedbox, radio_main,radio_main_off,radio_other, selection,selectionInitial,multipleSelection, hiddenfield,htmlQuote,htmlIsoUmlauts,addAttr,addAttrs,addClass, showHtmlExps,showHtmlExp,showHtmlPage, htmlPrelude, htmlTagAttrs, getUrlParameter,urlencoded2string,string2urlencoded, addSound,addCookies, formatCookie ) where import Char ( isAlphaNum, isSpace ) import ReadNumeric ( readNat, readHex ) import System ( getEnviron ) import Time ( CalendarTime(..), ClockTime, toTimeString, toUTCTime ) infixl 0 `addAttr` infixl 0 `addAttrs` infixl 0 `addClass` infixl 0 `addPageParam` infixl 0 `addFormParam` ------------------------------------------------------------------------------ --- The default encoding used in generated web pages. defaultEncoding :: String defaultEncoding = "utf-8" --"iso-8859-1" ------------------------------------------------------------------------------ --- The (abstract) data type for representing references to input elements --- in HTML forms. data CgiRef = CgiRef String --- Internal identifier of a CgiRef (intended only for internal use in other --- libraries!). idOfCgiRef :: CgiRef -> String idOfCgiRef (CgiRef i) = i --- The type for representing cgi environments --- (i.e., mappings from cgi references to the corresponding values of --- the input elements). type CgiEnv = CgiRef -> String --- The type of event handlers in HTML forms. type HtmlHandler = CgiEnv -> IO HtmlForm --- The data type for representing HTML expressions. --- @cons HtmlText s - a text string without any further structure --- @cons HtmlStruct t as hs - a structure with a tag, attributes, and --- HTML expressions inside the structure --- @cons HtmlCRef h ref - an input element (described by the first argument) --- with a cgi reference --- @cons HtmlEvent h hdlr - an input element (first arg) with an associated --- event handler (tpyically, a submit button) data HtmlExp = HtmlText String | HtmlStruct String [(String,String)] [HtmlExp] | HtmlCRef HtmlExp CgiRef | HtmlEvent HtmlExp HtmlHandler --- Extracts the textual contents of a list of HTML expressions. --- --- For instance, --- textOf [HtmlText "xy", HtmlStruct "a" [] [HtmlText "bc"]] == "xy bc" textOf :: [HtmlExp] -> String textOf = unwords . filter (not . null) . map textOfHtmlExp where textOfHtmlExp (HtmlText s) = s textOfHtmlExp (HtmlStruct _ _ hs) = textOf hs textOfHtmlExp (HtmlCRef hexp _) = textOf [hexp] textOfHtmlExp (HtmlEvent hexp _) = textOf [hexp] ------------------------------------------------------------------------------ --- The data type for representing HTML forms (active web pages) --- and return values of HTML forms. --- @cons HtmlForm t ps hs - an HTML form with title t, optional parameters --- (e.g., cookies) ps, and contents hs --- @cons HtmlAnswer t c - an answer in an arbitrary format where t --- is the content type (e.g., "text/plain") and c is the contents data HtmlForm = HtmlForm String [FormParam] [HtmlExp] | HtmlAnswer String String -- content type (e.g., "text/plain") / content --- The possible parameters of an HTML form. --- The parameters of a cookie (FormCookie) are its name and value and --- optional parameters (expiration date, domain, path (e.g., the path "/" --- makes the cookie valid for all documents on the server), security) which --- are collected in a list. --- @cons FormCookie name value params - a cookie to be sent to the --- client's browser --- @cons FormCSS s - a URL for a CSS file for this form --- @cons FormJScript s - a URL for a Javascript file for this form --- @cons FormOnSubmit s - a JavaScript statement to be executed when the form --- is submitted (i.e., <form ... onsubmit="s">) --- @cons FormTarget s - a name of a target frame where the output of the --- script should be represented (should only be used --- for scripts running in a frame) --- @cons FormEnc - the encoding scheme of this form --- @cons FormMeta as - meta information (in form of attributes) for this form --- @cons HeadInclude he - HTML expression to be included in form header --- @cons MultipleHandlers - indicates that the event handlers of the form --- can be multiply used (i.e., are not deleted if the form is submitted --- so that they are still available when going back in the browser; --- but then there is a higher risk that the web server process --- might overflow with unused events); the default is a single use --- of event handlers, i.e., one cannot use the back button in the --- browser and submit the same form again (which is usually --- a reasonable behavior to avoid double submissions of data). --- @cons BodyAttr ps - optional attribute for the body element (more than --- one occurrence is allowed) data FormParam = FormCookie String String [CookieParam] | FormCSS String | FormJScript String | FormOnSubmit String | FormTarget String | FormEnc String | FormMeta [(String,String)] | HeadInclude HtmlExp | MultipleHandlers | BodyAttr (String,String) --- An encoding scheme for a HTML form. formEnc :: String -> FormParam formEnc enc = FormEnc enc --- A URL for a CSS file for a HTML form. formCSS :: String -> FormParam formCSS css = FormCSS css --- Meta information for a HTML form. The argument is a list of --- attributes included in the `meta`-tag in the header for this form. formMetaInfo :: [(String,String)] -> FormParam formMetaInfo attrs = FormMeta attrs --- Optional attribute for the body element of the HTML form. --- More than one occurrence is allowed, i.e., all such attributes are --- collected. formBodyAttr :: (String,String) -> FormParam formBodyAttr attr = BodyAttr attr --- A cookie to be sent to the client's browser when a HTML form is --- requested. formCookie :: (String,String) -> FormParam formCookie (n,v) = FormCookie n v [] --- The possible parameters of a cookie. data CookieParam = CookieExpire ClockTime | CookieDomain String | CookiePath String | CookieSecure --- A basic HTML form for active web pages with the default encoding --- and a default background. --- @param title - the title of the form --- @param hexps - the form's body (list of HTML expressions) --- @return an HTML form form :: String -> [HtmlExp] -> HtmlForm form title hexps = HtmlForm title [] hexps --- A standard HTML form for active web pages where the title is included --- in the body as the first header. --- @param title - the title of the form --- @param hexps - the form's body (list of HTML expressions) --- @return an HTML form with the title as the first header standardForm :: String -> [HtmlExp] -> HtmlForm standardForm title hexps = form title (h1 [htxt title] : hexps) --- An HTML form with simple cookies. --- The cookies are sent to the client's browser together with this form. --- @param title - the title of the form --- @param cookies - the cookies as a list of name/value pairs --- @param hexps - the form's body (list of HTML expressions) --- @return an HTML form cookieForm :: String -> [(String,String)] -> [HtmlExp] -> HtmlForm cookieForm t cs he = HtmlForm t (map (\(n,v)->FormCookie n v []) cs) he --- Add simple cookie to HTML form. --- The cookies are sent to the client's browser together with this form. --- @param cs - the cookies as a list of name/value pairs --- @param form - the form to add cookies to --- @return a new HTML form addCookies :: [(String,String)] -> HtmlForm -> HtmlForm addCookies cs (HtmlForm t fas hs) = HtmlForm t (map (\ (n,v) -> FormCookie n v []) cs ++ fas) hs addCookies _ (HtmlAnswer _ _) = error "addCookies: cannot add cookie to Html answer" -- Shows the cookie in standard syntax: formatCookie :: (String,String,[CookieParam]) -> String formatCookie (name,value,params) = "Set-Cookie: " ++ name ++ "=" ++ string2urlencoded value ++ concatMap (\p->"; "++formatCookieParam p) params -- Formats a cookie parameter: formatCookieParam :: CookieParam -> String formatCookieParam (CookieExpire e) = "expires=" ++ toCookieDateString e formatCookieParam (CookieDomain d) = "domain=" ++ d formatCookieParam (CookiePath p) = "path=" ++ p formatCookieParam CookieSecure = "secure" -- Formats a clock time into a date string for cookies: toCookieDateString :: ClockTime -> String toCookieDateString time = let (CalendarTime y mo d h mi s tz) = toUTCTime time in (show d ++ "-" ++ shortMonths!!(mo-1) ++ "-" ++ show y ++ " " ++ toTimeString (CalendarTime y mo d h mi s tz) ++ " UTC") where shortMonths = ["Jan","Feb","Mar","Apr","May","Jun", "Jul","Aug","Sep","Oct","Nov","Dec"] --- A textual result instead of an HTML form as a result for active web pages. --- @param txt - the contents of the result page --- @return an HTML answer form answerText :: String -> HtmlForm answerText = HtmlAnswer "text/plain" --- A textual result instead of an HTML form as a result for active web pages --- where the encoding is given as the first parameter. --- @param enc - the encoding of the text(e.g., "utf-8" or "iso-8859-1") --- @param txt - the contents of the result page --- @return an HTML answer form answerEncText :: String -> String -> HtmlForm answerEncText enc = HtmlAnswer ("text/plain; charset="++enc) --- Adds a parameter to an HTML form. --- @param form - a form --- @param param - a form's parameter --- @return an HTML form addFormParam :: HtmlForm -> FormParam -> HtmlForm addFormParam (HtmlForm title params hexps) param = HtmlForm title (param:params) hexps addFormParam hexp@(HtmlAnswer _ _) _ = hexp addFormParams :: HtmlForm -> [FormParam] -> HtmlForm addFormParams hform [] = hform addFormParams hform (fp:fps) = addFormParams (hform `addFormParam` fp) fps --- Adds redirection to given HTML form. --- @param secs - Number of seconds to wait before executing autromatic redirection --- @param url - The URL whereto redirect to --- @param form - The form to add the header information to redirect :: Int -> String -> HtmlForm -> HtmlForm redirect secs url hform = hform `addFormParam` HeadInclude (HtmlStruct "meta" [("http-equiv","refresh"), ("content",show secs++"; URL="++url)] []) --- Adds expire time to given HTML form. --- @param secs - Number of seconds before document expires --- @param form - The form to add the header information to expires :: Int -> HtmlForm -> HtmlForm expires secs hform = hform `addFormParam` HeadInclude (HtmlStruct "meta" [("http-equiv","expires"), ("content",show secs)] []) --- Adds sound to given HTML form. The functions adds two different declarations --- for sound, one invented by Microsoft for the internet explorer, one introduced --- for netscape. As neither is an official part of HTML, addsound might not work --- on all systems and browsers. The greatest chance is by using sound files --- in MID-format. --- @param soundfile - Name of file containing the sound to be played --- @param loop - Should sound go on infinitely? Use with care. --- @param form - The form to add sound to addSound :: String -> Bool -> HtmlForm -> HtmlForm addSound soundfile loop (HtmlForm title params conts) = HtmlForm title (HeadInclude (HtmlStruct "bgsound" [("src",soundfile), ("loop",if loop then "infinite" else "1")] []):params) (HtmlStruct "embed" ((if loop then [("loop","true")] else []) ++ [("src",soundfile),("autostart","true"), ("hidden","true"), ("height","0"), ("width","0")]) []: conts) addSound _ _ (HtmlAnswer _ _) = error "HTML.addSound: unable to add sound to general HTML Answer" ------------------------------------------------------------------------------ --- The data type for representing HTML pages. --- The constructor arguments are the title, the parameters, and --- the contents (body) of the web page. data HtmlPage = HtmlPage String [PageParam] [HtmlExp] --- The possible parameters of an HTML page. --- @cons PageEnc - the encoding scheme of this page --- @cons PageCSS s - a URL for a CSS file for this page --- @cons PageJScript s - a URL for a Javascript file for this page --- @cons PageMeta as - meta information (in form of attributes) for this page --- @cons PageLink as - link information (in form of attributes) for this page --- @cons PageBodyAttr attr - optional attribute for the body element of the --- page (more than one occurrence is allowed) data PageParam = PageEnc String | PageCSS String | PageJScript String | PageMeta [(String,String)] | PageLink [(String,String)] | PageBodyAttr (String,String) --- An encoding scheme for a HTML page. pageEnc :: String -> PageParam pageEnc enc = PageEnc enc --- A URL for a CSS file for a HTML page. pageCSS :: String -> PageParam pageCSS css = PageCSS css --- Meta information for a HTML page. The argument is a list of --- attributes included in the `meta`-tag in the header for this page. pageMetaInfo :: [(String,String)] -> PageParam pageMetaInfo attrs = PageMeta attrs --- Link information for a HTML page. The argument is a list of --- attributes included in the `link`-tag in the header for this page. pageLinkInfo :: [(String,String)] -> PageParam pageLinkInfo attrs = PageLink attrs --- Optional attribute for the body element of the web page. --- More than one occurrence is allowed, i.e., all such attributes are --- collected. pageBodyAttr :: (String,String) -> PageParam pageBodyAttr attr = PageBodyAttr attr --- A basic HTML web page with the default encoding. --- @param title - the title of the page --- @param hexps - the page's body (list of HTML expressions) --- @return an HTML page page :: String -> [HtmlExp] -> HtmlPage page title hexps = HtmlPage title [PageEnc defaultEncoding] hexps --- A standard HTML web page where the title is included --- in the body as the first header. --- @param title - the title of the page --- @param hexps - the page's body (list of HTML expressions) --- @return an HTML page with the title as the first header standardPage :: String -> [HtmlExp] -> HtmlPage standardPage title hexps = page title (h1 [htxt title] : hexps) --- Adds a parameter to an HTML page. --- @param form - a page --- @param param - a page's parameter --- @return an HTML page addPageParam :: HtmlPage -> PageParam -> HtmlPage addPageParam (HtmlPage title params hexps) param = HtmlPage title (param:params) hexps ------------------------------------------------------------------------------ -- some useful abbreviations: --- Basic text as HTML expression. --- The text may contain special HTML chars (like <,>,&,") --- which will be quoted so that they appear as in the parameter string. htxt :: String -> HtmlExp htxt s = HtmlText (htmlQuote s) --- A list of strings represented as a list of HTML expressions. --- The strings may contain special HTML chars that will be quoted. htxts :: [String] -> [HtmlExp] htxts = map htxt --- An empty HTML expression. hempty :: HtmlExp hempty = HtmlText "" --- Non breaking Space nbsp :: HtmlExp nbsp = HtmlText " " --- Header 1 h1 :: [HtmlExp] -> HtmlExp h1 hexps = HtmlStruct "h1" [] hexps --- Header 2 h2 :: [HtmlExp] -> HtmlExp h2 hexps = HtmlStruct "h2" [] hexps --- Header 3 h3 :: [HtmlExp] -> HtmlExp h3 hexps = HtmlStruct "h3" [] hexps --- Header 4 h4 :: [HtmlExp] -> HtmlExp h4 hexps = HtmlStruct "h4" [] hexps --- Header 5 h5 :: [HtmlExp] -> HtmlExp h5 hexps = HtmlStruct "h5" [] hexps --- Paragraph par :: [HtmlExp] -> HtmlExp par hexps = HtmlStruct "p" [] hexps --- Section section :: [HtmlExp] -> HtmlExp section hexps = HtmlStruct "section" [] hexps --- Header header :: [HtmlExp] -> HtmlExp header hexps = HtmlStruct "header" [] hexps --- Footer footer :: [HtmlExp] -> HtmlExp footer hexps = HtmlStruct "footer" [] hexps --- Emphasize emphasize :: [HtmlExp] -> HtmlExp emphasize hexps = HtmlStruct "em" [] hexps --- Strong (more emphasized) text. strong :: [HtmlExp] -> HtmlExp strong hexps = HtmlStruct "strong" [] hexps --- Boldface bold :: [HtmlExp] -> HtmlExp bold hexps = HtmlStruct "b" [] hexps --- Italic italic :: [HtmlExp] -> HtmlExp italic hexps = HtmlStruct "i" [] hexps --- Navigation nav :: [HtmlExp] -> HtmlExp nav doc = HtmlStruct "nav" [] doc --- Program code code :: [HtmlExp] -> HtmlExp code hexps = HtmlStruct "code" [] hexps --- Centered text center :: [HtmlExp] -> HtmlExp center hexps = HtmlStruct "center" [] hexps --- Blinking text blink :: [HtmlExp] -> HtmlExp blink hexps = HtmlStruct "blink" [] hexps --- Teletype font teletype :: [HtmlExp] -> HtmlExp teletype hexps = HtmlStruct "tt" [] hexps --- Unformatted input, i.e., keep spaces and line breaks and --- don't quote special characters. pre :: [HtmlExp] -> HtmlExp pre hexps = HtmlStruct "pre" [] hexps --- Verbatim (unformatted), special characters (<,>,&,") --- are quoted. verbatim :: String -> HtmlExp verbatim s = HtmlStruct "pre" [] [HtmlText (htmlQuote s)] --- Address address :: [HtmlExp] -> HtmlExp address hexps = HtmlStruct "address" [] hexps --- Hypertext reference href :: String -> [HtmlExp] -> HtmlExp href ref hexps = HtmlStruct "a" [("href",ref)] hexps --- An anchored text with a hypertext reference inside a document. anchor :: String -> [HtmlExp] -> HtmlExp anchor anc hexps = HtmlStruct "span" [("id",anc)] hexps --- Unordered list --- @param items - the list items where each item is a list of HTML expressions ulist :: [[HtmlExp]] -> HtmlExp ulist items = HtmlStruct "ul" [] (map litem items) --- Ordered list --- @param items - the list items where each item is a list of HTML expressions olist :: [[HtmlExp]] -> HtmlExp olist items = HtmlStruct "ol" [] (map litem items) --- A single list item (usually not explicitly used) litem :: [HtmlExp] -> HtmlExp litem hexps = HtmlStruct "li" [] hexps --- Description list --- @param items - a list of (title/description) pairs (of HTML expressions) dlist :: [([HtmlExp],[HtmlExp])] -> HtmlExp dlist items = HtmlStruct "dl" [] (concatMap ditem items) where ditem (hexps1,hexps2) = [HtmlStruct "dt" [] hexps1, HtmlStruct "dd" [] hexps2] --- Table with a matrix of items where each item is a list of HTML expressions. table :: [[[HtmlExp]]] -> HtmlExp table items = HtmlStruct "table" [] (map (\row->HtmlStruct "tr" [] (map (\item -> HtmlStruct "td" [] item) row)) items) --- Similar to table but introduces header tags for the first row. headedTable :: [[[HtmlExp]]] -> HtmlExp headedTable = withinTable . table where withinTable (HtmlStruct "table" attrs (HtmlStruct "tr" rowAttrs row:rows)) = HtmlStruct "table" attrs (HtmlStruct "tr" rowAttrs (map addTh row):rows) addTh x = case x of (HtmlStruct "td" attrs conts) -> HtmlStruct "th" attrs conts other -> other --- Add a row of items (where each item is a list of HTML expressions) --- as headings to a table. If the first argument is not a table, --- the headings are ignored. addHeadings :: HtmlExp -> [[HtmlExp]] -> HtmlExp addHeadings htable headings = case htable of HtmlStruct "table" attrs rows -> HtmlStruct "table" attrs (HtmlStruct "tr" [] (map (\item->HtmlStruct "th" [] item) headings):rows) _ -> htable --- Horizontal rule hrule :: HtmlExp hrule = HtmlStruct "hr" [] [] --- Break a line breakline :: HtmlExp breakline = HtmlStruct "br" [] [] --- Image --- @param src - the URL of the image --- @param alt - the alternative text shown instead of the image image :: String -> String -> HtmlExp image src alt = HtmlStruct "img" [("src",src),("alt",htmlQuote alt)] [] -------------- styles and document structuring: --- Defines a style sheet to be used in this HTML document. --- @param css - a string in CSS format styleSheet :: String -> HtmlExp styleSheet css = HtmlStruct "style" [("type","text/css")] [HtmlText css] --- Provides a style for HTML elements. --- The style argument is the name of a style class defined in a --- style definition (see styleSheet) or in an --- external style sheet (see form and page parameters FormCSS --- and PageCSS). --- @param st - name of a style class --- @param hexps - list of HTML expressions style :: String -> [HtmlExp] -> HtmlExp style st hexps = HtmlStruct "span" [("class",st)] hexps --- Provides a style for a basic text. --- The style argument is the name of a style class defined in an --- external style sheet. --- @param st - name of a style class --- @param txt - a string (special characters will be quoted) textstyle :: String -> String -> HtmlExp textstyle st txt = HtmlStruct "span" [("class",st)] [htxt txt] --- Provides a style for a block of HTML elements. --- The style argument is the name of a style class defined in an --- external style sheet. This element is used (in contrast to "style") --- for larger blocks of HTML elements since a line break is placed --- before and after these elements. --- @param st - name of a style class --- @param hexps - list of HTML expressions blockstyle :: String -> [HtmlExp] -> HtmlExp blockstyle st hexps = HtmlStruct "div" [("class",st)] hexps --- Joins a list of HTML elements into a single HTML element. --- Although this construction has no rendering, it is sometimes useful --- for programming when several HTML elements must be put together. --- @param hexps - list of HTML expressions inline :: [HtmlExp] -> HtmlExp inline hexps = HtmlStruct "span" [] hexps --- Joins a list of HTML elements into a block. --- A line break is placed before and after these elements. --- @param hexps - list of HTML expressions block :: [HtmlExp] -> HtmlExp block hexps = HtmlStruct "div" [] hexps -------------- forms and input fields: --- Submit button with a label string and an event handler button :: String -> HtmlHandler -> HtmlExp button label handler = HtmlEvent (HtmlStruct "input" [("type","submit"),("name","EVENT"), ("value",htmlQuote label)] []) handler --- Reset button with a label string resetbutton :: String -> HtmlExp resetbutton label = HtmlStruct "input" [("type","reset"),("value",htmlQuote label)] [] --- Submit button in form of an imag. --- @param src - url of the image --- @param handler - event handler imageButton :: String -> HtmlHandler -> HtmlExp imageButton src handler = HtmlEvent (HtmlStruct "input" [("type","image"),("name","EVENT"),("src",src)] []) handler --- Input text field with a reference and an initial contents textfield :: CgiRef -> String -> HtmlExp textfield cref contents | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "input" [("type","text"),("name",ref), ("value",htmlQuote contents)] []) cref where ref free --- Input text field (where the entered text is obscured) with a reference password :: CgiRef -> HtmlExp password cref | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "input" [("type","password"),("name",ref)] []) cref where ref free --- Input text area with a reference, height/width, and initial contents textarea :: CgiRef -> (Int,Int) -> String -> HtmlExp textarea cref (height,width) contents | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "textarea" [("name",ref), ("rows",show height),("cols",show width)] [htxt contents]) cref where ref free --- A checkbox with a reference and a value. --- The value is returned if checkbox is on, otherwise "" is returned. checkbox :: CgiRef -> String -> HtmlExp checkbox cref value | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "input" [("type","checkbox"),("name",ref), ("value",htmlQuote value)] []) cref where ref free --- A checkbox that is initially checked with a reference and a value. --- The value is returned if checkbox is on, otherwise "" is returned. checkedbox :: CgiRef -> String -> HtmlExp checkedbox cref value | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "input" [("type","checkbox"),("name",ref), ("value",htmlQuote value),("checked","checked")] []) cref where ref free --- A main button of a radio (initially "on") with a reference and a value. --- The value is returned of this button is on. --- A complete radio button suite always consists of a main button --- (radio_main) and some further buttons (radio_others) with the --- same reference. Initially, the main button is selected --- (or nothing is selected if one uses radio_main_off instead of radio_main). --- The user can select another button but always at most one button --- of the radio can be selected. The value corresponding to the --- selected button is returned in the environment for this radio reference. radio_main :: CgiRef -> String -> HtmlExp radio_main cref value | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "input" [("type","radio"),("name",ref), ("value",htmlQuote value),("checked","yes")] []) cref where ref free --- A main button of a radio (initially "off") with a reference and a value. --- The value is returned of this button is on. radio_main_off :: CgiRef -> String -> HtmlExp radio_main_off cref value | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "input" [("type","radio"),("name",ref), ("value",htmlQuote value)] []) cref where ref free --- A further button of a radio (initially "off") with a reference (identical --- to the main button of this radio) and a value. --- The value is returned of this button is on. radio_other :: CgiRef -> String -> HtmlExp radio_other cref value | cref =:= CgiRef ref -- instantiate cref argument = HtmlStruct "input" [("type","radio"),("name",ref),("value",htmlQuote value)] [] where ref free --- A selection button with a reference and a list of name/value pairs. --- The names are shown in the selection and the value is returned --- for the selected name. selection :: CgiRef -> [(String,String)] -> HtmlExp selection cref menue | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "select" [("name",ref)] ((concat . map (\(n,v)->[HtmlStruct "option" [("value",v)] [htxt n]])) menue)) cref where ref free --- A selection button with a reference, a list of name/value pairs, --- and a preselected item in this list. --- The names are shown in the selection and the value is returned --- for the selected name. --- @param ref - a CGI reference --- @param nvs - list of name/value pairs --- @param sel - the index of the initially selected item in the list nvs --- @return an HTML expression representing the selection button selectionInitial :: CgiRef -> [(String,String)] -> Int -> HtmlExp selectionInitial cref sellist sel | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "select" [("name",ref)] (selOption sellist sel)) cref where ref free selOption [] _ = [] selOption ((n,v):nvs) i = HtmlStruct "option" ([("value",v)] ++ if i==0 then [("selected","selected")] else []) [htxt n] : selOption nvs (i-1) --- A selection button with a reference and a list of name/value/flag pairs. --- The names are shown in the selection and the value is returned --- if the corresponding name is selected. If flag is True, the --- corresonding name is initially selected. If more than one name --- has been selected, all values are returned in one string --- where the values are separated by newline (`'\n'`) characters. multipleSelection :: CgiRef -> [(String,String,Bool)] -> HtmlExp multipleSelection cref sellist | cref =:= CgiRef ref -- instantiate cref argument = HtmlCRef (HtmlStruct "select" [("name",ref),("multiple","multiple")] (map selOption sellist)) cref where ref free selOption (n,v,flag) = HtmlStruct "option" ([("value",v)] ++ if flag then [("selected","selected")] else []) [htxt n] --- A hidden field to pass a value referenced by a fixed name. --- This function should be used with care since it may cause --- conflicts with the CGI-based implementation of this library. hiddenfield :: String -> String -> HtmlExp hiddenfield name value = HtmlStruct "input" [("type","hidden"),("name",name),("value",value)] [] ------------------------------------------------------------------------------ --- Quotes special characters (`<`,`>`,`&`,`"`, umlauts) in a string --- as HTML special characters. htmlQuote :: String -> String htmlQuote [] = [] htmlQuote (c:cs) | c=='<' = "<" ++ htmlQuote cs | c=='>' = ">" ++ htmlQuote cs | c=='&' = "&" ++ htmlQuote cs | c=='"' = """ ++ htmlQuote cs | otherwise = htmlIsoUmlauts [c] ++ htmlQuote cs --- Translates umlauts in iso-8859-1 encoding into HTML special characters. htmlIsoUmlauts :: String -> String htmlIsoUmlauts [] = [] htmlIsoUmlauts (c:cs) | oc==228 = "ä" ++ htmlIsoUmlauts cs | oc==246 = "ö" ++ htmlIsoUmlauts cs | oc==252 = "ü" ++ htmlIsoUmlauts cs | oc==196 = "Ä" ++ htmlIsoUmlauts cs | oc==214 = "Ö" ++ htmlIsoUmlauts cs | oc==220 = "Ü" ++ htmlIsoUmlauts cs | oc==223 = "ß" ++ htmlIsoUmlauts cs | oc==197 = "Å" ++ htmlIsoUmlauts cs | oc==250 = "ú"++ htmlIsoUmlauts cs | oc==237 = "í"++ htmlIsoUmlauts cs | oc==225 = "á"++ htmlIsoUmlauts cs | otherwise = c : htmlIsoUmlauts cs where oc = ord c ------------------------------------------------------------------------------ --- Adds an attribute (name/value pair) to an HTML element. addAttr :: HtmlExp -> (String,String) -> HtmlExp addAttr hexp attr = addAttrs hexp [attr] --- Adds a list of attributes (name/value pair) to an HTML element. addAttrs :: HtmlExp -> [(String,String)] -> HtmlExp addAttrs (HtmlText s) _ = HtmlText s -- strings have no attributes addAttrs (HtmlStruct tag attrs hexps) newattrs = HtmlStruct tag (attrs++newattrs) hexps addAttrs (HtmlEvent hexp handler) attrs = HtmlEvent (addAttrs hexp attrs) handler addAttrs (HtmlCRef hexp cref) attrs = HtmlCRef (addAttrs hexp attrs) cref --- Adds a class attribute to an HTML element. addClass :: HtmlExp -> String -> HtmlExp addClass hexp cls = addAttr hexp ("class",cls) ------------------------------------------------------------------------------ -- Auxiliaries for faster show (could be later put into a standard library) type ShowS = String -> String showString :: String -> String -> String showString s = (s++) showChar :: Char -> String -> String showChar c = (c:) nl :: String -> String nl = showChar '\n' concatS :: [a -> a] -> a -> a concatS [] = id concatS xs@(_:_) = foldr1 (\ f g -> f . g) xs ------------------------------------------------------------------------------ --- Transforms a list of HTML expressions into string representation. showHtmlExps :: [HtmlExp] -> String showHtmlExps hexps = showsHtmlExps 0 hexps "" -- get the string contents of an HTML expression: getText :: HtmlExp -> String getText (HtmlText s) = s getText (HtmlStruct _ _ _) = "" getText (HtmlEvent he _) = getText he getText (HtmlCRef he _) = getText he -- get the (last) tag of an HTML expression: getTag :: HtmlExp -> String getTag (HtmlText _) = "" getTag (HtmlStruct tag _ _) = tag getTag (HtmlEvent he _) = getTag he getTag (HtmlCRef he _) = getTag he -- is this a tag where a line break can be safely added? tagWithLn :: String -> Bool tagWithLn t = t/="" && t `elem` ["br","p","li","ul","ol","dl","dt","dd","hr", "h1","h2","h3","h4","h5","h6","div", "html","title","head","body","link","meta","script", "form","table","tr","td"] --- Transforms a single HTML expression into string representation. showHtmlExp :: HtmlExp -> String showHtmlExp hexp = showsHtmlExp 0 hexp "" --- HTML tags that have no end tag in HTML: noEndTags :: [String] noEndTags = ["img","input","link","meta"] showsHtmlExp :: Int -> HtmlExp -> ShowS showsHtmlExp _ (HtmlText s) = showString s showsHtmlExp i (HtmlStruct tag attrs hexps) = let maybeLn j = if tagWithLn tag then nl . showTab j else id in maybeLn i . (if null hexps && (null attrs || tag `elem` noEndTags) then showsHtmlOpenTag tag attrs "/>" else showsHtmlOpenTag tag attrs ">" . maybeLn (i+2) . showExps hexps . maybeLn i . showString "' ) . maybeLn i where showExps = if tag=="pre" then concatS . map (showsHtmlExp 0) else showsHtmlExps (i+2) showsHtmlExp i (HtmlEvent hexp _) = showsHtmlExp i hexp showsHtmlExp i (HtmlCRef hexp _) = showsHtmlExp i hexp showsHtmlExps :: Int -> [HtmlExp] -> ShowS showsHtmlExps _ [] = id showsHtmlExps i (he:hes) = showsWithLnPrefix he . showsHtmlExps i hes where showsWithLnPrefix hexp = let s = getText hexp in if s/="" && isSpace (head s) then nl . showTab i . showString (tail s) else showsHtmlExp i hexp showTab :: Int -> String -> String showTab n = showString (take n (repeat ' ')) showsHtmlOpenTag :: String -> [(String,String)] -> String -> ShowS showsHtmlOpenTag tag attrs close = showChar '<' . showString tag . concatS (map attr2string attrs) . showString close where attr2string (attr,value) = showChar ' ' . showString attr . showString "=\"" . encodeQuotes value . showChar '"' -- encode double quotes as """: encodeQuotes [] = id encodeQuotes (c:cs) | c=='"' = showString """ . encodeQuotes cs | otherwise = showChar c . encodeQuotes cs ------------------------------------------------------------------------------ --- Transforms HTML page into string representation. --- @param page - the HTML page --- @return string representation of the HTML document showHtmlPage :: HtmlPage -> String showHtmlPage (HtmlPage title params html) = htmlPrelude ++ showHtmlExp (HtmlStruct "html" htmlTagAttrs [HtmlStruct "head" [] ([HtmlStruct "title" [] [HtmlText (htmlQuote title)]] ++ concatMap param2html params), HtmlStruct "body" bodyattrs html]) where param2html (PageEnc enc) = [HtmlStruct "meta" [("http-equiv","Content-Type"), ("content","text/html; charset="++enc)] []] param2html (PageCSS css) = [HtmlStruct "link" [("rel","stylesheet"),("type","text/css"),("href",css)] []] param2html (PageJScript js) = [HtmlStruct "script" [("type","text/javascript"),("src",js)] []] param2html (PageMeta attrs) = [HtmlStruct "meta" attrs []] param2html (PageLink attrs) = [HtmlStruct "link" attrs []] param2html (PageBodyAttr _) = [] -- these attributes are separately processed bodyattrs = [attr | (PageBodyAttr attr) <- params] --- Standard header for generated HTML pages. htmlPrelude :: String htmlPrelude = "\n" --- Standard attributes for element "html". htmlTagAttrs :: [(String,String)] htmlTagAttrs = [("lang","en")] ------------------------------------------------------------------------------ --- Gets the parameter attached to the URL of the script. --- For instance, if the script is called with URL --- "http://.../script.cgi?parameter", then "parameter" is --- returned by this I/O action. --- Note that an URL parameter should be "URL encoded" to avoid --- the appearance of characters with a special meaning. --- Use the functions "urlencoded2string" and "string2urlencoded" --- to decode and encode such parameters, respectively. getUrlParameter :: IO String getUrlParameter = getEnviron "QUERY_STRING" --- Translates urlencoded string into equivalent ASCII string. urlencoded2string :: String -> String urlencoded2string [] = [] urlencoded2string (c:cs) | c == '+' = ' ' : urlencoded2string cs | c == '%' = chr (maybe 0 fst (readHex (take 2 cs))) : urlencoded2string (drop 2 cs) | otherwise = c : urlencoded2string cs --- Translates arbitrary strings into equivalent urlencoded string. string2urlencoded :: String -> String string2urlencoded [] = [] string2urlencoded (c:cs) | isAlphaNum c = c : string2urlencoded cs | c == ' ' = '+' : string2urlencoded cs | otherwise = let oc = ord c in '%' : int2hex(oc `div` 16) : int2hex(oc `mod` 16) : string2urlencoded cs where int2hex i = if i<10 then chr (ord '0' + i) else chr (ord 'A' + i - 10) ------------------------------------------------------------------------------ --- Gets the cookies sent from the browser for the current CGI script. --- The cookies are represented in the form of name/value pairs since --- no other components are important here. getCookies :: IO [(String,String)] getCookies = do cookiestring <- getEnviron "HTTP_COOKIE" return $ parseCookies cookiestring -- translate a string of cookies (of the form "NAME1=VAL1; NAME2=VAL") -- into a list of name/value pairs: parseCookies :: String -> [(String,String)] parseCookies str = if str=="" then [] else let (c1,cs) = break (==';') str in parseCookie c1 : parseCookies (dropWhile (==' ') (if cs=="" then "" else tail cs)) where parseCookie s = let (name,evalue) = break (=='=') s in (name,if evalue=="" then "" else urlencoded2string (tail evalue)) --- For image buttons: retrieve the coordinates where the user clicked --- within the image. coordinates :: CgiEnv -> Maybe (Int,Int) coordinates env = let x = env (CgiRef "x") y = env (CgiRef "y") in if x/="" && y/="" then Just (tryReadNat 0 x, tryReadNat 0 y) else Nothing where tryReadNat d s = maybe d (\(i,rs)->if null rs then i else d) (readNat s) ------------------------------------------------------------------------------