1 -----------------------------------------------------------------------------
4 -- Copyright : (c) Andy Gill and OGI, 1999-2001
5 -- License : BSD-style (see the file libraries/base/LICENSE)
7 -- Maintainer : Andy Gill <andy@galconn.com>
8 -- Stability : experimental
9 -- Portability : portable
11 -- An Html combinator library
13 -----------------------------------------------------------------------------
21 import qualified Text.Html.BlockTable as BT
23 infixr 3 </> -- combining table cells
24 infixr 4 <-> -- combining table cells
25 infixr 2 +++ -- combining Html
26 infixr 7 << -- nesting Html
27 infixl 8 ! -- adding optional arguments
30 -- A important property of Html is that all strings inside the
31 -- structure are already in Html friendly format.
32 -- For example, use of >,etc.
36 - ..just..plain..normal..text... but using © and &amb;, etc.
40 - <thetag {..attrs..}> ..content.. </thetag>
42 | HtmlTag { -- tag with internal markup
44 markupAttrs :: [HtmlAttr],
48 {- These are the index-value pairs.
49 - The empty string is a synonym for tags with no arguments.
50 - (not strictly HTML, but anyway).
54 data HtmlAttr = HtmlAttr String String
57 newtype Html = Html { getHtmlElements :: [HtmlElement] }
59 -- Read MARKUP as the class of things that can be validly rendered
60 -- inside MARKUP tag brackets. So this can be one or more Html's,
61 -- or a String, for example.
65 toHtmlFromList :: [a] -> Html
67 toHtmlFromList xs = Html (concat [ x | (Html x) <- map toHtml xs])
69 instance HTML Html where
72 instance HTML Char where
74 toHtmlFromList [] = Html []
75 toHtmlFromList str = Html [HtmlString (stringToHtmlString str)]
77 instance (HTML a) => HTML [a] where
78 toHtml xs = toHtmlFromList xs
80 class ADDATTRS a where
81 (!) :: a -> [HtmlAttr] -> a
83 instance (ADDATTRS b) => ADDATTRS (a -> b) where
84 fn ! attr = \ arg -> fn arg ! attr
86 instance ADDATTRS Html where
87 (Html htmls) ! attr = Html (map addAttrs htmls)
89 addAttrs (html@(HtmlTag { markupAttrs = markupAttrs }) )
90 = html { markupAttrs = markupAttrs ++ attr }
94 (<<) :: (HTML a) => (Html -> b) -> a -> b
95 fn << arg = fn (toHtml arg)
98 concatHtml :: (HTML a) => [a] -> Html
99 concatHtml as = Html (concat (map (getHtmlElements.toHtml) as))
101 (+++) :: (HTML a,HTML b) => a -> b -> Html
102 a +++ b = Html (getHtmlElements (toHtml a) ++ getHtmlElements (toHtml b))
108 isNoHtml (Html xs) = null xs
111 tag :: String -> Html -> Html
112 tag str htmls = Html [
116 markupContent = htmls }]
118 itag :: String -> Html
119 itag str = tag str noHtml
121 emptyAttr :: String -> HtmlAttr
122 emptyAttr s = HtmlAttr s ""
124 intAttr :: String -> Int -> HtmlAttr
125 intAttr s i = HtmlAttr s (show i)
127 strAttr :: String -> String -> HtmlAttr
128 strAttr s t = HtmlAttr s t
132 foldHtml :: (String -> [HtmlAttr] -> [a] -> a)
136 foldHtml f g (HtmlTag str attr fmls)
137 = f str attr (map (foldHtml f g) fmls)
138 foldHtml f g (HtmlString str)
142 -- Processing Strings into Html friendly things.
143 -- This converts a String to a Html String.
144 stringToHtmlString :: String -> String
145 stringToHtmlString = concatMap fixChar
149 fixChar '&' = "&"
150 fixChar '"' = """
153 -- ---------------------------------------------------------------------------
156 instance Show Html where
157 showsPrec _ html = showString (prettyHtml html)
158 showList htmls = showString (concat (map show htmls))
160 instance Show HtmlAttr where
161 showsPrec _ (HtmlAttr str val) =
167 -- ---------------------------------------------------------------------------
172 -- ---------------------------------------------------------------------------
175 -- This is not processed for special chars.
176 -- use stringToHtml or lineToHtml instead, for user strings,
177 -- because they understand special chars, like '<'.
179 primHtml :: String -> Html
180 primHtml x = Html [HtmlString x]
182 -- ---------------------------------------------------------------------------
185 stringToHtml :: String -> Html
186 stringToHtml = primHtml . stringToHtmlString
188 -- This converts a string, but keeps spaces as non-line-breakable
190 lineToHtml :: String -> Html
191 lineToHtml = primHtml . concatMap htmlizeChar2 . stringToHtmlString
193 htmlizeChar2 ' ' = " "
196 -- ---------------------------------------------------------------------------
199 -- (automatically generated)
201 address :: Html -> Html
202 anchor :: Html -> Html
203 applet :: Html -> Html
207 blockquote :: Html -> Html
211 caption :: Html -> Html
212 center :: Html -> Html
215 define :: Html -> Html
216 dlist :: Html -> Html
217 dterm :: Html -> Html
218 emphasize :: Html -> Html
219 fieldset :: Html -> Html
222 frame :: Html -> Html
223 frameset :: Html -> Html
230 header :: Html -> Html
234 italics :: Html -> Html
235 keyboard :: Html -> Html
236 legend :: Html -> Html
239 noframes :: Html -> Html
240 olist :: Html -> Html
241 option :: Html -> Html
242 paragraph :: Html -> Html
245 sample :: Html -> Html
246 select :: Html -> Html
247 small :: Html -> Html
248 strong :: Html -> Html
249 style :: Html -> Html
252 table :: Html -> Html
254 textarea :: Html -> Html
257 thecode :: Html -> Html
258 thediv :: Html -> Html
259 thehtml :: Html -> Html
260 thelink :: Html -> Html
261 themap :: Html -> Html
262 thespan :: Html -> Html
263 thetitle :: Html -> Html
266 ulist :: Html -> Html
267 underline :: Html -> Html
268 variable :: Html -> Html
270 address = tag "ADDRESS"
272 applet = tag "APPLET"
274 basefont = itag "BASEFONT"
276 blockquote = tag "BLOCKQUOTE"
280 caption = tag "CAPTION"
281 center = tag "CENTER"
288 fieldset = tag "FIELDSET"
292 frameset = tag "FRAMESET"
305 legend = tag "LEGEND"
308 noframes = tag "NOFRAMES"
310 option = tag "OPTION"
315 select = tag "SELECT"
317 strong = tag "STRONG"
323 textarea = tag "TEXTAREA"
325 thebase = itag "BASE"
332 thetitle = tag "TITLE"
339 -- ---------------------------------------------------------------------------
342 -- (automatically generated)
344 action :: String -> HtmlAttr
345 align :: String -> HtmlAttr
346 alink :: String -> HtmlAttr
347 alt :: String -> HtmlAttr
348 altcode :: String -> HtmlAttr
349 archive :: String -> HtmlAttr
350 background :: String -> HtmlAttr
351 base :: String -> HtmlAttr
352 bgcolor :: String -> HtmlAttr
353 border :: Int -> HtmlAttr
354 bordercolor :: String -> HtmlAttr
355 cellpadding :: Int -> HtmlAttr
356 cellspacing :: Int -> HtmlAttr
358 clear :: String -> HtmlAttr
359 code :: String -> HtmlAttr
360 codebase :: String -> HtmlAttr
361 color :: String -> HtmlAttr
362 cols :: String -> HtmlAttr
363 colspan :: Int -> HtmlAttr
365 content :: String -> HtmlAttr
366 coords :: String -> HtmlAttr
367 enctype :: String -> HtmlAttr
368 face :: String -> HtmlAttr
369 frameborder :: Int -> HtmlAttr
370 height :: Int -> HtmlAttr
371 href :: String -> HtmlAttr
372 hspace :: Int -> HtmlAttr
373 httpequiv :: String -> HtmlAttr
374 identifier :: String -> HtmlAttr
376 lang :: String -> HtmlAttr
377 link :: String -> HtmlAttr
378 marginheight :: Int -> HtmlAttr
379 marginwidth :: Int -> HtmlAttr
380 maxlength :: Int -> HtmlAttr
381 method :: String -> HtmlAttr
383 name :: String -> HtmlAttr
388 rel :: String -> HtmlAttr
389 rev :: String -> HtmlAttr
390 rows :: String -> HtmlAttr
391 rowspan :: Int -> HtmlAttr
392 rules :: String -> HtmlAttr
393 scrolling :: String -> HtmlAttr
395 shape :: String -> HtmlAttr
396 size :: String -> HtmlAttr
397 src :: String -> HtmlAttr
398 start :: Int -> HtmlAttr
399 target :: String -> HtmlAttr
400 text :: String -> HtmlAttr
401 theclass :: String -> HtmlAttr
402 thestyle :: String -> HtmlAttr
403 thetype :: String -> HtmlAttr
404 title :: String -> HtmlAttr
405 usemap :: String -> HtmlAttr
406 valign :: String -> HtmlAttr
407 value :: String -> HtmlAttr
408 version :: String -> HtmlAttr
409 vlink :: String -> HtmlAttr
410 vspace :: Int -> HtmlAttr
411 width :: String -> HtmlAttr
413 action = strAttr "ACTION"
414 align = strAttr "ALIGN"
415 alink = strAttr "ALINK"
417 altcode = strAttr "ALTCODE"
418 archive = strAttr "ARCHIVE"
419 background = strAttr "BACKGROUND"
420 base = strAttr "BASE"
421 bgcolor = strAttr "BGCOLOR"
422 border = intAttr "BORDER"
423 bordercolor = strAttr "BORDERCOLOR"
424 cellpadding = intAttr "CELLPADDING"
425 cellspacing = intAttr "CELLSPACING"
426 checked = emptyAttr "CHECKED"
427 clear = strAttr "CLEAR"
428 code = strAttr "CODE"
429 codebase = strAttr "CODEBASE"
430 color = strAttr "COLOR"
431 cols = strAttr "COLS"
432 colspan = intAttr "COLSPAN"
433 compact = emptyAttr "COMPACT"
434 content = strAttr "CONTENT"
435 coords = strAttr "COORDS"
436 enctype = strAttr "ENCTYPE"
437 face = strAttr "FACE"
438 frameborder = intAttr "FRAMEBORDER"
439 height = intAttr "HEIGHT"
440 href = strAttr "HREF"
441 hspace = intAttr "HSPACE"
442 httpequiv = strAttr "HTTP-EQUIV"
443 identifier = strAttr "ID"
444 ismap = emptyAttr "ISMAP"
445 lang = strAttr "LANG"
446 link = strAttr "LINK"
447 marginheight = intAttr "MARGINHEIGHT"
448 marginwidth = intAttr "MARGINWIDTH"
449 maxlength = intAttr "MAXLENGTH"
450 method = strAttr "METHOD"
451 multiple = emptyAttr "MULTIPLE"
452 name = strAttr "NAME"
453 nohref = emptyAttr "NOHREF"
454 noresize = emptyAttr "NORESIZE"
455 noshade = emptyAttr "NOSHADE"
456 nowrap = emptyAttr "NOWRAP"
459 rows = strAttr "ROWS"
460 rowspan = intAttr "ROWSPAN"
461 rules = strAttr "RULES"
462 scrolling = strAttr "SCROLLING"
463 selected = emptyAttr "SELECTED"
464 shape = strAttr "SHAPE"
465 size = strAttr "SIZE"
467 start = intAttr "START"
468 target = strAttr "TARGET"
469 text = strAttr "TEXT"
470 theclass = strAttr "CLASS"
471 thestyle = strAttr "STYLE"
472 thetype = strAttr "TYPE"
473 title = strAttr "TITLE"
474 usemap = strAttr "USEMAP"
475 valign = strAttr "VALIGN"
476 value = strAttr "VALUE"
477 version = strAttr "VERSION"
478 vlink = strAttr "VLINK"
479 vspace = intAttr "VSPACE"
480 width = strAttr "WIDTH"
482 -- ---------------------------------------------------------------------------
485 -- (automatically generated)
487 validHtmlTags :: [String]
548 validHtmlITags :: [String]
560 validHtmlAttrs :: [String]
631 -- ---------------------------------------------------------------------------
668 -- ---------------------------------------------------------------------------
671 linesToHtml :: [String] -> Html
673 linesToHtml [] = noHtml
674 linesToHtml (x:[]) = lineToHtml x
675 linesToHtml (x:xs) = lineToHtml x +++ br +++ linesToHtml xs
678 -- ---------------------------------------------------------------------------
679 -- Html abbriviations
681 primHtmlChar :: String -> Html
687 primHtmlChar = \ x -> primHtml ("&" ++ x ++ ";")
688 copyright = primHtmlChar "copy"
689 spaceHtml = primHtmlChar "nbsp"
690 bullet = primHtmlChar "#149"
694 -- ---------------------------------------------------------------------------
697 class HTMLTABLE ht where
698 cell :: ht -> HtmlTable
700 instance HTMLTABLE HtmlTable where
703 instance HTMLTABLE Html where
706 cellFn x y = h ! (add x colspan $ add y rowspan $ [])
708 add n fn rest = fn n : rest
713 -- We internally represent the Cell inside a Table with an
714 -- object of the type
716 -- Int -> Int -> Html
718 -- When we render it later, we find out how many columns
719 -- or rows this cell will span over, and can
720 -- include the correct colspan/rowspan command.
723 = HtmlTable (BT.BlockTable (Int -> Int -> Html))
726 (</>),above,(<->),beside :: (HTMLTABLE ht1,HTMLTABLE ht2)
727 => ht1 -> ht2 -> HtmlTable
728 aboves,besides :: (HTMLTABLE ht) => [ht] -> HtmlTable
729 simpleTable :: [HtmlAttr] -> [HtmlAttr] -> [[Html]] -> Html
732 mkHtmlTable :: BT.BlockTable (Int -> Int -> Html) -> HtmlTable
733 mkHtmlTable r = HtmlTable r
735 -- We give both infix and nonfix, take your pick.
736 -- Notice that there is no concept of a row/column
739 above a b = combine BT.above (cell a) (cell b)
741 beside a b = combine BT.beside (cell a) (cell b)
745 combine fn (HtmlTable a) (HtmlTable b) = mkHtmlTable (a `fn` b)
747 -- Both aboves and besides presume a non-empty list.
748 -- here is no concept of a empty row or column in these
749 -- table combinators.
751 aboves [] = error "aboves []"
752 aboves xs = foldr1 (</>) (map cell xs)
753 besides [] = error "besides []"
754 besides xs = foldr1 (<->) (map cell xs)
756 -- renderTable takes the HtmlTable, and renders it back into
759 renderTable :: BT.BlockTable (Int -> Int -> Html) -> Html
762 [tr << [theCell x y | (theCell,(x,y)) <- theRow ]
763 | theRow <- BT.getMatrix theTable]
765 instance HTML HtmlTable where
766 toHtml (HtmlTable tab) = renderTable tab
768 instance Show HtmlTable where
769 showsPrec _ (HtmlTable tab) = shows (renderTable tab)
772 -- If you can't be bothered with the above, then you
773 -- can build simple tables with simpleTable.
774 -- Just provide the attributes for the whole table,
775 -- attributes for the cells (same for every cell),
776 -- and a list of lists of cell contents,
777 -- and this function will build the table for you.
778 -- It does presume that all the lists are non-empty,
779 -- and there is at least one list.
781 -- Different length lists means that the last cell
782 -- gets padded. If you want more power, then
783 -- use the system above, or build tables explicitly.
785 simpleTable attr cellAttr lst
788 . map (besides . map ((td ! cellAttr) . toHtml))
792 -- ---------------------------------------------------------------------------
793 -- Tree Displaying Combinators
795 -- The basic idea is you render your structure in the form
796 -- of this tree, and then use treeHtml to turn it into a Html
797 -- object with the structure explicit.
801 | HtmlNode Html [HtmlTree] Html
803 treeHtml :: [String] -> HtmlTree -> Html
804 treeHtml colors h = table ! [
807 cellspacing 2] << treeHtml' colors h
809 manycolors = scanr (:) []
811 treeHtmls :: [[String]] -> [HtmlTree] -> HtmlTable
812 treeHtmls c ts = aboves (zipWith treeHtml' c ts)
814 treeHtml' :: [String] -> HtmlTree -> HtmlTable
815 treeHtml' (c:_) (HtmlLeaf leaf) = cell
819 treeHtml' (c:cs@(c2:_)) (HtmlNode hopen ts hclose) =
820 if null ts && isNoHtml hclose
825 hd </> bar `beside` (td ! [bgcolor c2] << spaceHtml)
828 hd </> (bar `beside` treeHtmls morecolors ts)
831 -- This stops a column of colors being the same
832 -- color as the immeduately outside nesting bar.
833 morecolors = filter ((/= c).head) (manycolors cs)
834 bar = td ! [bgcolor c,width "10"] << spaceHtml
835 hd = td ! [bgcolor c] << hopen
836 tl = td ! [bgcolor c] << hclose
837 treeHtml' _ _ = error "The imposible happens"
839 instance HTML HtmlTree where
840 toHtml x = treeHtml treeColors x
842 -- type "length treeColors" to see how many colors are here.
843 treeColors = ["#88ccff","#ffffaa","#ffaaff","#ccffff"] ++ treeColors
846 -- ---------------------------------------------------------------------------
847 -- Html Debugging Combinators
849 -- This uses the above tree rendering function, and displays the
850 -- Html as a tree structure, allowing debugging of what is
851 -- actually getting produced.
853 debugHtml :: (HTML a) => a -> Html
854 debugHtml obj = table ! [border 0] <<
855 ( th ! [bgcolor "#008888"]
857 << "Debugging Output"
858 </> td << (toHtml (debug' (toHtml obj)))
862 debug' :: Html -> [HtmlTree]
863 debug' (Html markups) = map debug markups
865 debug :: HtmlElement -> HtmlTree
866 debug (HtmlString str) = HtmlLeaf (spaceHtml +++
867 linesToHtml (lines str))
869 markupTag = markupTag,
870 markupContent = markupContent,
871 markupAttrs = markupAttrs
873 case markupContent of
874 Html [] -> HtmlNode hd [] noHtml
875 Html xs -> HtmlNode hd (map debug xs) tl
877 args = if null markupAttrs
879 else " " ++ unwords (map show markupAttrs)
880 hd = font ! [size "1"] << ("<" ++ markupTag ++ args ++ ">")
881 tl = font ! [size "1"] << ("</" ++ markupTag ++ ">")
883 -- ---------------------------------------------------------------------------
886 data HotLink = HotLink {
888 hotLinkContents :: [Html],
889 hotLinkAttributes :: [HtmlAttr]
892 instance HTML HotLink where
893 toHtml hl = anchor ! (href (hotLinkURL hl) : hotLinkAttributes hl)
894 << hotLinkContents hl
896 hotlink :: URL -> [Html] -> HotLink
897 hotlink url h = HotLink {
900 hotLinkAttributes = [] }
903 -- ---------------------------------------------------------------------------
906 -- (Abridged from Erik Meijer's Original Html library)
908 ordList :: (HTML a) => [a] -> Html
909 ordList items = olist << map (li <<) items
911 unordList :: (HTML a) => [a] -> Html
912 unordList items = ulist << map (li <<) items
914 defList :: (HTML a,HTML b) => [(a,b)] -> Html
916 = dlist << [ [ dterm << bold << dt, ddef << dd ] | (dt,dd) <- items ]
919 widget :: String -> String -> [HtmlAttr] -> Html
920 widget w n markupAttrs = input ! ([thetype w,name n] ++ markupAttrs)
922 checkbox :: String -> String -> Html
923 hidden :: String -> String -> Html
924 radio :: String -> String -> Html
925 reset :: String -> String -> Html
926 submit :: String -> String -> Html
927 password :: String -> Html
928 textfield :: String -> Html
929 afile :: String -> Html
930 clickmap :: String -> Html
932 checkbox n v = widget "CHECKBOX" n [value v]
933 hidden n v = widget "HIDDEN" n [value v]
934 radio n v = widget "RADIO" n [value v]
935 reset n v = widget "RESET" n [value v]
936 submit n v = widget "SUBMIT" n [value v]
937 password n = widget "PASSWORD" n []
938 textfield n = widget "TEXT" n []
939 afile n = widget "FILE" n []
940 clickmap n = widget "IMAGE" n []
942 menu :: String -> [Html] -> Html
944 = select ! [name n] << [ option << p << choice | choice <- choices ]
946 gui :: String -> Html -> Html
947 gui act = form ! [action act,method "POST"]
949 -- ---------------------------------------------------------------------------
952 -- Uses the append trick to optimize appending.
953 -- The output is quite messy, because space matters in
954 -- HTML, so we must not generate needless spaces.
956 renderHtml :: (HTML html) => html -> String
959 foldr (.) id (map (renderHtml' 0)
960 (getHtmlElements (tag "HTML" << theHtml))) "\n"
963 "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 FINAL//EN\">\n" ++
964 "<!--Rendered using the Haskell Html Library v0.2-->\n"
966 -- Warning: spaces matters in HTML. You are better using renderHtml.
967 -- This is intentually very inefficent to "encorage" this,
968 -- but the neater version in easier when debugging.
971 prettyHtml :: (HTML html) => html -> String
979 renderHtml' :: Int -> HtmlElement -> ShowS
980 renderHtml' _ (HtmlString str) = (++) str
981 renderHtml' n (HtmlTag
983 markupContent = html,
984 markupAttrs = markupAttrs })
985 = if isNoHtml html && elem name validHtmlITags
986 then renderTag True name markupAttrs n
987 else (renderTag True name markupAttrs n
988 . foldr (.) id (map (renderHtml' (n+2)) (getHtmlElements html))
989 . renderTag False name [] n)
991 prettyHtml' :: HtmlElement -> [String]
992 prettyHtml' (HtmlString str) = [str]
995 markupContent = html,
996 markupAttrs = markupAttrs })
997 = if isNoHtml html && elem name validHtmlITags
999 [rmNL (renderTag True name markupAttrs 0 "")]
1001 [rmNL (renderTag True name markupAttrs 0 "")] ++
1002 shift (concat (map prettyHtml' (getHtmlElements html))) ++
1003 [rmNL (renderTag False name [] 0 "")]
1005 shift = map (\x -> " " ++ x)
1006 rmNL = filter (/= '\n')
1008 -- This prints the Tags The lack of spaces in intentunal, because Html is
1009 -- actually space dependant.
1011 renderTag :: Bool -> String -> [HtmlAttr] -> Int -> ShowS
1012 renderTag x name markupAttrs n r
1013 = open ++ name ++ rest markupAttrs ++ ">" ++ r
1015 open = if x then "<" else "</"
1017 nl = "\n" ++ replicate (n `div` 8) '\t'
1018 ++ replicate (n `mod` 8) ' '
1021 rest attr = " " ++ unwords (map showPair attr) ++ nl
1023 showPair :: HtmlAttr -> String
1024 showPair (HtmlAttr tag val)
1025 = tag ++ " = \"" ++ val ++ "\""