{-# LANGUAGE PatternGuards #-} {-| This module converts a list of 'Tag' back into a string. -} module Text.HTML.TagSoup.Render ( renderTags, renderTagsOptions, escapeHTML, RenderOptions(..), renderOptions ) where import Data.Char import qualified Data.IntMap as IntMap import Text.HTML.TagSoup.Entity import Text.HTML.TagSoup.Type import Text.StringLike -- | These options control how 'renderTags' works. -- -- The strange quirk of only minimizing @\@ tags is due to Internet Explorer treating -- @\\<\/br\>@ as @\\@. data RenderOptions str = RenderOptions {optEscape :: str -> str -- ^ Escape a piece of text (default = escape the four characters @&\"\<\>@) ,optMinimize :: str -> Bool -- ^ Minimise \\<\/b\> -> \ (default = minimise only @\@ tags) } -- | Replace the four characters @&\"\<\>@ with their HTML entities (the list from 'xmlEntities'). escapeHTML :: StringLike str => str -> str escapeHTML = fromString . concatMap esc1 . toString where esc = IntMap.fromList [(b, "&"++a++";") | (a,b) <- xmlEntities] esc1 x = IntMap.findWithDefault [x] (ord x) esc -- | The default render options value, described in 'RenderOptions'. renderOptions :: StringLike str => RenderOptions str renderOptions = RenderOptions escapeHTML (\x -> toString x == "br") -- | Show a list of tags, as they might have been parsed, using the default settings given in -- 'RenderOptions'. -- -- > renderTags [TagOpen "hello" [],TagText "my&",TagClose "world"] == "my&" renderTags :: StringLike str => [Tag str] -> str renderTags = renderTagsOptions renderOptions -- | Show a list of tags using settings supplied by the 'RenderOptions' parameter, -- eg. to avoid escaping any characters one could do: -- -- > renderTagsOptions renderOptions{optEscape = id} [TagText "my&"] == "my&" renderTagsOptions :: StringLike str => RenderOptions str -> [Tag str] -> str renderTagsOptions opts = strConcat . tags where s = fromString ss x = [s x] tags (TagOpen name atts:TagClose name2:xs) | name == name2 && optMinimize opts name = open name atts (s " /") ++ tags xs tags (TagOpen name atts:xs) | Just ('?',_) <- uncons name = open name atts (s " ?") ++ tags xs tags (x:xs) = tag x ++ tags xs tags [] = [] tag (TagOpen name atts) = open name atts (s "") tag (TagClose name) = [s ""] tag (TagText text) = [txt text] tag (TagComment text) = ss "" tag _ = ss "" txt = optEscape opts open name atts shut = [s "<",name] ++ concatMap att atts ++ [shut,s ">"] att (x,y) | xnull && ynull = [s " \"\""] | ynull = [s " ", x] | xnull = [s " \"",txt y,s "\""] | otherwise = [s " ",x,s "=\"",txt y,s "\""] where (xnull, ynull) = (strNull x, strNull y) com xs | Just ('-',xs) <- uncons xs, Just ('-',xs) <- uncons xs, Just ('>',xs) <- uncons xs = s "-- >" : com xs com xs = case uncons xs of Nothing -> [] Just (x,xs) -> fromChar x : com xs