-- | Converting markup to HTML
module Text.HMarkup.XHtml (docToHtml,
MarkupXHtmlPrefs(..),
MarkupXHtmlFormat(..),
standardMarkupFormats,
defaultMarkupXHtmlPrefs,
markupURL, markupURI,
useChunkFormat, useRefFormat) where
import Text.HMarkup.Parse
import Text.HMarkup.Types
import Control.Monad
import Data.List
import Data.Maybe
import Network.URI
import Text.XHtml
data MarkupXHtmlPrefs m = MarkupXHtmlPrefs {
markupFormats :: [MarkupXHtmlFormat m],
-- | If set, all relative URIs will be resolved with this as a base.
markupBaseURI :: Maybe URI
}
data MarkupXHtmlFormat m =
ChunkFormat {
formatName :: String,
formatChunk :: MarkupXHtmlPrefs m -> String -> m Html
}
| RefFormat {
formatName :: String,
formatRef :: MarkupXHtmlPrefs m -> URI -> [Text] -> m Html
}
defaultMarkupXHtmlPrefs :: Monad m => MarkupXHtmlPrefs m
defaultMarkupXHtmlPrefs =
MarkupXHtmlPrefs {
markupFormats = standardMarkupFormats,
markupBaseURI = Nothing
}
standardMarkupFormats :: Monad m => [MarkupXHtmlFormat m]
standardMarkupFormats = [
-- chunk formats
preChunk,
quoteChunk,
-- reference formats
imageRef
]
--
-- * Chunk formatters
--
defaultChunkFormat :: Monad m => MarkupXHtmlFormat m
defaultChunkFormat = preChunk
preChunk :: Monad m => MarkupXHtmlFormat m
preChunk = ChunkFormat "pre" f
where f _ s = return $ pre << s
quoteChunk :: Monad m => MarkupXHtmlFormat m
quoteChunk = ChunkFormat "quote" f
where f pref = liftM (blockquote <<) . toHtmlOrPlain pref
--
-- * Ref formatters
--
defaultRefFormat :: Monad m => MarkupXHtmlFormat m
defaultRefFormat = RefFormat "link" f
where
f pref uri ts =
liftM (anchor ! [href (markupURL pref uri)] <<)
(textsToHtml pref ts')
where ts' = if null ts then [Word (show uri)] else ts
imageRef :: Monad m => MarkupXHtmlFormat m
imageRef = RefFormat "image" f
where
f pref uri ts =
do let tit = textsToPlain ts
url = case parseURIReference (uriPath uri) of
Nothing -> show uri
Just u -> markupURL pref u
return $ image ! [src url, alt tit]
--
-- * XHtml generation
--
docToHtml :: Monad m =>
MarkupXHtmlPrefs m -> Doc -> m Html
docToHtml pref (Doc ps) = liftM concatHtml (mapM (blockToHtml pref) ps)
blockToHtml :: Monad m => MarkupXHtmlPrefs m -> Block -> m Html
blockToHtml pref (Chunk n s) = useChunkFormat pref n s
blockToHtml pref (Header n ts) = liftM (h <<) (textsToHtml pref ts)
where h = case n of
_ | n < 1 -> error $ "Header " ++ show n
1 -> h1
2 -> h2
3 -> h3
4 -> h4
5 -> h5
6 -> h6
_ -> h6 -- FIXME: what should we do here?
blockToHtml pref (Para ts) = liftM (paragraph <<) (textsToHtml pref ts)
blockToHtml pref (ItemList tss) = liftM unordList $ mapM (textsToHtml pref) tss
textsToHtml :: Monad m => MarkupXHtmlPrefs m -> [Text] -> m Html
textsToHtml pref = liftM concatHtml . mapM (textToHtml pref)
textToHtml :: Monad m => MarkupXHtmlPrefs m -> Text -> m Html
textToHtml pref (Emph ts) = liftM (emphasize <<) (textsToHtml pref ts)
textToHtml pref (TT ts) = liftM (tt <<) (textsToHtml pref ts)
textToHtml pref (Ref uri ts) = useRefFormat pref n uri ts
where n = removeTrailing (uriScheme uri) ':'
textToHtml _ (Word t) = return $ toHtml t
textToHtml _ WhiteSpace = return $ toHtml " "
markupURI :: Monad m => MarkupXHtmlPrefs m -> URI -> URI
markupURI pref uri = case markupBaseURI pref of
Nothing -> uri
Just baseURI -> uri `relativeTo'` baseURI
where relativeTo' x y =
fromMaybe (error $ "relativeTo " ++ show x ++ " " ++ show y)
(x `relativeTo` y)
markupURL :: Monad m => MarkupXHtmlPrefs m -> URI -> URL
markupURL pref = show . markupURI pref
removeTrailing :: Eq a => [a] -> a -> [a]
removeTrailing xs x = reverse $ dropWhile (==x) $ reverse xs
toHtmlOrPlain :: Monad m =>
MarkupXHtmlPrefs m -> String -> m Html
toHtmlOrPlain pref s =
case parseMarkup "" s of
Nothing -> return $ toHtml s
Just x -> docToHtml pref x
useChunkFormat :: Monad m => MarkupXHtmlPrefs m -> String -> String -> m Html
useChunkFormat pref n s =
do x <- case [f | f@(ChunkFormat n' _) <- markupFormats pref, n' == n] of
[] -> return defaultChunkFormat
[y] -> return y
_ -> fail $ "More than one chunk format called " ++ n
liftM (thediv ! [theclass ("chunk-"++n)] <<) $ formatChunk x pref s
useRefFormat :: Monad m =>
MarkupXHtmlPrefs m -> String -> URI -> [Text] -> m Html
useRefFormat pref n uri ts =
do x <- case [f | f@(RefFormat n' _) <- markupFormats pref, n' == n] of
[] -> return defaultRefFormat
[y] -> return y
_ -> fail $ "More than one ref format called " ++ n
liftM (thespan ! [theclass ("ref-"++n)] <<) $ formatRef x pref uri ts
--
-- * Generating plain text.
--
textsToPlain :: [Text] -> String
textsToPlain = concat . map textToPlain
textToPlain :: Text -> String
textToPlain (Emph ts) = "*" ++ textsToPlain ts ++ "*"
textToPlain (TT ts) = "``" ++ textsToPlain ts ++ "``"
textToPlain (Ref uri ts) = "[" ++ show uri ++ " " ++ textsToPlain ts ++ "]"
textToPlain (Word t) = t
textToPlain WhiteSpace = " "