{-# LANGUAGE Safe #-} module Text.Show.Html ( HtmlOpts(..), defaultHtmlOpts , valToHtml, valToHtmlPage, htmlPage , Html(..) ) where import Text.Show.Value import Prelude hiding (span) -- | Make an Html page representing the given value. valToHtmlPage :: HtmlOpts -> Value -> String valToHtmlPage opts = htmlPage opts . valToHtml opts -- | Options on how to generate Html (more to come). data HtmlOpts = HtmlOpts { dataDir :: FilePath -- ^ Path for extra files. If empty, we look in -- directory `style`, relative to document. , wideListWidth :: Int -- ^ Max. number of columns in wide lists. } deriving Show -- | Default options. defaultHtmlOpts :: HtmlOpts defaultHtmlOpts = HtmlOpts { dataDir = "" , wideListWidth = 80 } -- | Convert a value into an Html fragment. valToHtml :: HtmlOpts -> Value -> Html valToHtml opts = loop where loop val = case val of Con con [] -> span "con" (text con) Con con vs -> tallRecord con (map conLab vs) (map loop vs) Rec con fs -> tallRecord con (map fst fs) (map (loop . snd) fs) Tuple vs -> wideTuple (map loop vs) InfixCons v ms -> table "infix tallRecord" [ tr $ (th "label" 1 (text " ") :) $ map td $ loop v : [ h | (op,u) <- ms , h <- [ text op, loop u ] ] ] List [] -> span "list" (text "[]") List vs@(v : vs1) -> case v of Con c fs | all (isCon c) vs1 -> recordList c (map conLab fs) [ map loop xs | Con _ xs <- vs ] | otherwise -> tallList $ map (loop) vs Rec c fs | all (isRec c) vs1 -> recordList c (map fst fs) [ map (loop . snd) xs | Rec _ xs <- vs ] | otherwise -> tallList $ map (loop) vs Tuple fs -> tupleList (length fs) [ map (loop) xs | Tuple xs <- vs ] List {} -> tallList $ map loop vs Neg {} -> wideList (wideListWidth opts) $ map loop vs Ratio {} -> wideList (wideListWidth opts) $ map loop vs Integer {} -> wideList (wideListWidth opts) $ map loop vs Float {} -> wideList (wideListWidth opts) $ map loop vs Char {} -> wideList (wideListWidth opts) $ map loop vs String {} -> tallList $ map loop vs InfixCons {} -> tallList $ map loop vs Neg v -> case v of Integer txt -> span "integer" $ text ('-' : txt) Float txt -> span "float" $ text ('-' : txt) _ -> neg (loop v) Ratio v1 v2 -> ratio (loop v1) (loop v2) Integer txt -> span "integer" (text txt) Float txt -> span "float" (text txt) Char txt -> span "char" (text txt) String txt -> span "string" (text txt) conLab _ = " " isCon c (Con d _) = c == d isCon _ _ = False isRec c (Rec d _) = c == d isRec _ _ = False neg :: Html -> Html neg e = table "negate" [ tr [td (text "-"), td e] ] ratio :: Html -> Html -> Html ratio e1 e2 = table "ratio" [ tr [ td' "numerator" e1 ], tr [td e2] ] wideTuple :: [Html] -> Html wideTuple els = table "wideTuple" [ tr $ map td els ] tallTuple :: [Html] -> Html tallTuple els = table "tallTuple" $ map (tr . return . td) els tallRecord :: Name -> [Name] -> [Html] -> Html tallRecord con labs els = table "tallRecord" $ topHs : zipWith row labs els where topHs = tr [ th "con" 2 (text con) ] row l e = tr [ th "label" 1 (text l), td e ] recordList :: Name -> [Name] -> [[Html]] -> Html recordList con labs els = table "recordList" $ topHs : zipWith row [0..] els where topHs = tr $ th "con" 1 (text con) : map (th "label" 1 . text) labs row n es = tr $ th "ix" 1 (int n) : map td es tupleList :: Int -> [[Html]] -> Html tupleList n els = recordList " " (replicate n " ") els tallList :: [Html] -> Html tallList els = table "tallList" $ top : zipWith row [0..] els where top = tr [ th "con" 2 (text " ")] row n e = tr [ th "ix" 1 (int n), td e ] wideList :: Int -> [Html] -> Html wideList w els = table "wideList" $ topHs : zipWith row [0..] (chop els) where elNum = length els pad = elNum > w chop [] = [] chop xs = let (as,bs) = splitAt w xs in take w (as ++ if pad then repeat empty else []) : chop bs topHs = tr $ th "con" 1 (text " ") : map (th "label" 1 . int) [ 0 .. min elNum w - 1 ] row n es = tr $ (th "ix" 1 (int (n*w))) : map td es -------------------------------------------------------------------------------- newtype Html = Html { exportHtml :: String } table :: String -> [Html] -> Html table cl body = Html $ "" ++ concatMap exportHtml body ++ "
" tr :: [Html] -> Html tr body = Html $ "" ++ concatMap exportHtml body ++ "" th :: String -> Int -> Html -> Html th cl n body = Html $ "" ++ exportHtml body ++ "" td :: Html -> Html td body = Html $ "" ++ exportHtml body ++ "" td' :: String -> Html -> Html td' cl body = Html $ "" ++ exportHtml body ++ "" span :: String -> Html -> Html span cl body = Html $ "" ++ exportHtml body ++ "" empty :: Html empty = Html "" int :: Int -> Html int = Html . show text :: String -> Html text = Html . concatMap esc where esc '<' = "<" esc '>' = ">" esc '&' = "&" esc ' ' = " " esc c = [c] -- | Wrap an Html fragment to make an Html page. htmlPage :: HtmlOpts -> Html -> String htmlPage opts body = unlines [ "" , "" , "" , "" , "" , "" , exportHtml body , "" , "" ] where -- XXX: slashes on Windows? dir = case dataDir opts of "" -> "" d -> d ++ "/" jquery = dir ++ "style/jquery.js" pjs = dir ++ "style/pretty-show.js" pstyle = dir ++ "style/pretty-show.css"