diff options
Diffstat (limited to 'src/Text/Pandoc/Writers/HTML.hs')
-rw-r--r-- | src/Text/Pandoc/Writers/HTML.hs | 549 |
1 files changed, 295 insertions, 254 deletions
diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs index 573adbf4a..f35b29370 100644 --- a/src/Text/Pandoc/Writers/HTML.hs +++ b/src/Text/Pandoc/Writers/HTML.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE OverloadedStrings #-} {-# OPTIONS_GHC -fno-warn-deprecations #-} {- Copyright (C) 2006-2010 John MacFarlane <jgm@berkeley.edu> @@ -30,168 +31,186 @@ Conversion of 'Pandoc' documents to HTML. -} module Text.Pandoc.Writers.HTML ( writeHtml , writeHtmlString ) where import Text.Pandoc.Definition -import Text.Pandoc.CharacterReferences ( decodeCharacterReferences ) import Text.Pandoc.Shared import Text.Pandoc.Templates +import Text.Pandoc.Generic import Text.Pandoc.Readers.TeXMath -import Text.Pandoc.Highlighting ( highlightHtml, defaultHighlightingCss ) -import Text.Pandoc.XML (stripTags, escapeStringForXML) +import Text.Pandoc.Slides +import Text.Pandoc.Highlighting ( highlight, styleToCss, + formatHtmlInline, formatHtmlBlock ) +import Text.Pandoc.XML (stripTags, escapeStringForXML, fromEntities) import Network.HTTP ( urlEncode ) import Numeric ( showHex ) import Data.Char ( ord, toLower ) import Data.List ( isPrefixOf, intersperse ) +import Data.String ( fromString ) import Data.Maybe ( catMaybes ) import Control.Monad.State -import Text.XHtml.Transitional hiding ( stringToHtml, unordList, ordList ) -import qualified Text.XHtml.Transitional as XHtml +import Text.Blaze +import qualified Text.Blaze.Html5 as H5 +import qualified Text.Blaze.XHtml1.Transitional as H +import qualified Text.Blaze.XHtml1.Transitional.Attributes as A +import Text.Blaze.Renderer.String (renderHtml) import Text.TeXMath import Text.XML.Light.Output import System.FilePath (takeExtension) +import Data.Monoid (mempty, mconcat) data WriterState = WriterState { stNotes :: [Html] -- ^ List of notes , stMath :: Bool -- ^ Math is used in document , stHighlighting :: Bool -- ^ Syntax highlighting is used , stSecNum :: [Int] -- ^ Number of current section - } deriving Show + } defaultWriterState :: WriterState defaultWriterState = WriterState {stNotes= [], stMath = False, stHighlighting = False, stSecNum = []} -- Helpers to render HTML with the appropriate function. --- | Modified version of Text.XHtml's stringToHtml. --- Use unicode characters wherever possible. -stringToHtml :: WriterOptions -> String -> Html -stringToHtml opts = if writerAscii opts - then XHtml.stringToHtml - else primHtml . escapeStringForXML +strToHtml :: String -> Html +strToHtml = preEscapedString . escapeStringForXML +-- strToHtml = toHtml -- | Hard linebreak. nl :: WriterOptions -> Html nl opts = if writerWrapText opts - then primHtml "\n" - else noHtml + then preEscapedString "\n" + else mempty -- | Convert Pandoc document to Html string. writeHtmlString :: WriterOptions -> Pandoc -> String writeHtmlString opts d = - let (tit, auths, date, toc, body', newvars) = evalState (pandocToHtml opts d) - defaultWriterState + let (tit, auths, authsMeta, date, toc, body', newvars) = evalState (pandocToHtml opts d) + defaultWriterState in if writerStandalone opts - then inTemplate opts tit auths date toc body' newvars - else dropWhile (=='\n') $ showHtmlFragment body' + then inTemplate opts tit auths authsMeta date toc body' newvars + else renderHtml body' -- | Convert Pandoc document to Html structure. writeHtml :: WriterOptions -> Pandoc -> Html writeHtml opts d = - let (tit, auths, date, toc, body', newvars) = evalState (pandocToHtml opts d) - defaultWriterState + let (tit, auths, authsMeta, date, toc, body', newvars) = evalState (pandocToHtml opts d) + defaultWriterState in if writerStandalone opts - then inTemplate opts tit auths date toc body' newvars + then inTemplate opts tit auths authsMeta date toc body' newvars else body' -- result is (title, authors, date, toc, body, new variables) pandocToHtml :: WriterOptions -> Pandoc - -> State WriterState (Html, [Html], Html, Maybe Html, Html, [(String,String)]) + -> State WriterState (Html, [Html], [Html], Html, Maybe Html, Html, [(String,String)]) pandocToHtml opts (Pandoc (Meta title' authors' date') blocks) = do let standalone = writerStandalone opts tit <- if standalone then inlineListToHtml opts title' - else return noHtml + else return mempty auths <- if standalone then mapM (inlineListToHtml opts) authors' else return [] + authsMeta <- if standalone + then mapM (inlineListToHtml opts . prepForMeta) authors' + else return [] date <- if standalone then inlineListToHtml opts date' - else return noHtml + else return mempty + let slideLevel = maybe (getSlideLevel blocks) id $ writerSlideLevel opts let sects = hierarchicalize $ if writerSlideVariant opts == NoSlides then blocks - else case blocks of - (Header 1 _ : _) -> blocks - _ -> - let isL1 (Header 1 _) = True - isL1 _ = False - (preBlocks, rest) = break isL1 blocks - in (RawBlock "html" "<div class=\"slide\">" : - preBlocks) ++ (RawBlock "html" "</div>" : - rest) + else prepSlides slideLevel blocks toc <- if writerTableOfContents opts then tableOfContents opts sects else return Nothing - blocks' <- liftM (toHtmlFromList . intersperse (nl opts)) $ - mapM (elementToHtml opts) sects + blocks' <- liftM (mconcat . intersperse (nl opts)) $ + mapM (elementToHtml slideLevel opts) sects st <- get let notes = reverse (stNotes st) - let thebody = blocks' +++ footnoteSection opts notes + let thebody = blocks' >> footnoteSection opts notes let math = if stMath st then case writerHTMLMathMethod opts of LaTeXMathML (Just url) -> - script ! - [src url, thetype "text/javascript"] $ noHtml + H.script ! A.src (toValue url) + ! A.type_ "text/javascript" + $ mempty MathML (Just url) -> - script ! - [src url, thetype "text/javascript"] $ noHtml + H.script ! A.src (toValue url) + ! A.type_ "text/javascript" + $ mempty MathJax url -> - script ! [src url, thetype "text/javascript"] $ noHtml + H.script ! A.src (toValue url) + ! A.type_ "text/javascript" + $ mempty JsMath (Just url) -> - script ! - [src url, thetype "text/javascript"] $ noHtml + H.script ! A.src (toValue url) + ! A.type_ "text/javascript" + $ mempty _ -> case lookup "mathml-script" (writerVariables opts) of - Just s -> - script ! [thetype "text/javascript"] << - primHtml ("/*<![CDATA[*/\n" ++ s ++ - "/*]]>*/\n") - Nothing -> noHtml - else noHtml - let newvars = [("highlighting-css", defaultHighlightingCss) | + Just s | not (writerHtml5 opts) -> + H.script ! A.type_ "text/javascript" + $ preEscapedString + ("/*<![CDATA[*/\n" ++ s ++ "/*]]>*/\n") + | otherwise -> mempty + Nothing -> mempty + else mempty + let newvars = [("highlighting-css", + styleToCss $ writerHighlightStyle opts) | stHighlighting st] ++ - [("math", showHtmlFragment math) | stMath st] - return (tit, auths, date, toc, thebody, newvars) + [("math", renderHtml math) | stMath st] + return (tit, auths, authsMeta, date, toc, thebody, newvars) + +-- | Prepare author for meta tag, converting notes into +-- bracketed text and removing links. +prepForMeta :: [Inline] -> [Inline] +prepForMeta = bottomUp (concatMap fixInline) + where fixInline (Note [Para xs]) = [Str " ["] ++ xs ++ [Str "]"] + fixInline (Note [Plain xs]) = [Str " ["] ++ xs ++ [Str "]"] + fixInline (Link lab _) = lab + fixInline (Image lab _) = lab + fixInline x = [x] inTemplate :: TemplateTarget a => WriterOptions -> Html -> [Html] + -> [Html] -> Html -> Maybe Html -> Html -> [(String,String)] -> a -inTemplate opts tit auths date toc body' newvars = - let renderedTit = showHtmlFragment tit - topTitle' = stripTags renderedTit - authors = map (stripTags . showHtmlFragment) auths - date' = stripTags $ showHtmlFragment date +inTemplate opts tit auths authsMeta date toc body' newvars = + let title' = renderHtml tit + date' = renderHtml date + dateMeta = maybe [] (\x -> [("date-meta",x)]) $ normalizeDate date' variables = writerVariables opts ++ newvars - context = variables ++ - [ ("body", dropWhile (=='\n') $ showHtmlFragment body') - , ("pagetitle", topTitle') - , ("title", dropWhile (=='\n') $ showHtmlFragment tit) + context = variables ++ dateMeta ++ + [ ("body", dropWhile (=='\n') $ renderHtml body') + , ("pagetitle", stripTags title') + , ("title", title') , ("date", date') , ("idprefix", writerIdentifierPrefix opts) , ("slidy-url", "http://www.w3.org/Talks/Tools/Slidy2") - , ("s5-url", "ui/default") ] ++ + , ("s5-url", "s5/default") ] ++ [ ("html5","true") | writerHtml5 opts ] ++ (case toc of - Just t -> [ ("toc", showHtmlFragment t)] + Just t -> [ ("toc", renderHtml t)] Nothing -> []) ++ - [ ("author", a) | a <- authors ] + [ ("author", renderHtml a) | a <- auths ] ++ + [ ("author-meta", stripTags $ renderHtml a) | a <- authsMeta ] in renderTemplate context $ writerTemplate opts -- | Like Text.XHtml's identifier, but adds the writerIdentifierPrefix -prefixedId :: WriterOptions -> String -> HtmlAttr -prefixedId opts s = identifier $ writerIdentifierPrefix opts ++ s +prefixedId :: WriterOptions -> String -> Attribute +prefixedId opts s = A.id $ toValue $ writerIdentifierPrefix opts ++ s -- | Replacement for Text.XHtml's unordList. unordList :: WriterOptions -> ([Html] -> Html) -unordList opts items = ulist << toListItems opts items +unordList opts items = H.ul $ mconcat $ toListItems opts items -- | Replacement for Text.XHtml's ordList. ordList :: WriterOptions -> ([Html] -> Html) -ordList opts items = olist << toListItems opts items +ordList opts items = H.ol $ mconcat $ toListItems opts items -- | Construct table of contents from list of elements. tableOfContents :: WriterOptions -> [Element] -> State WriterState (Maybe Html) @@ -214,52 +233,66 @@ elementToListItem :: WriterOptions -> Element -> State WriterState (Maybe Html) elementToListItem _ (Blk _) = return Nothing elementToListItem opts (Sec _ num id' headerText subsecs) = do let sectnum = if writerNumberSections opts - then (thespan ! [theclass "toc-section-number"] << showSecNum num) +++ - stringToHtml opts" " - else noHtml - txt <- liftM (sectnum +++) $ inlineListToHtml opts headerText + then (H.span ! A.class_ "toc-section-number" $ toHtml $ showSecNum num) >> + preEscapedString " " + else mempty + txt <- liftM (sectnum >>) $ inlineListToHtml opts headerText subHeads <- mapM (elementToListItem opts) subsecs >>= return . catMaybes let subList = if null subHeads - then noHtml + then mempty else unordList opts subHeads - return $ Just $ (anchor ! [href ("#" ++ writerIdentifierPrefix opts ++ id')] $ txt) +++ subList + return $ Just $ (H.a ! A.href (toValue $ "#" ++ writerIdentifierPrefix opts ++ id') + $ toHtml txt) >> subList -- | Convert an Element to Html. -elementToHtml :: WriterOptions -> Element -> State WriterState Html -elementToHtml opts (Blk HorizontalRule) | writerSlideVariant opts /= NoSlides = - return $ primHtml "</div>" +++ nl opts +++ primHtml "<div class=\"slide\">" -elementToHtml opts (Blk block) = blockToHtml opts block -elementToHtml opts (Sec level num id' title' elements) = do +elementToHtml :: Int -> WriterOptions -> Element -> State WriterState Html +elementToHtml _slideLevel opts (Blk block) = blockToHtml opts block +elementToHtml slideLevel opts (Sec level num id' title' elements) = do + let slide = writerSlideVariant opts /= NoSlides && level <= slideLevel modify $ \st -> st{stSecNum = num} -- update section number - header' <- blockToHtml opts (Header level title') - innerContents <- mapM (elementToHtml opts) elements - let header'' = header' ! [prefixedId opts id' | - not (writerStrictMarkdown opts || - writerSectionDivs opts || - writerSlideVariant opts == S5Slides)] - let stuff = header'' : innerContents - let slide = writerSlideVariant opts /= NoSlides && level == 1 - let stuff' = if slide - then [thediv ! [theclass "slide"] << - (nl opts : intersperse (nl opts) stuff ++ [nl opts])] - else intersperse (nl opts) stuff - let inNl x = nl opts : x ++ [nl opts] - return $ if writerSectionDivs opts - then if writerHtml5 opts - then tag "section" ! [prefixedId opts id'] << inNl stuff' - else thediv ! [prefixedId opts id'] << inNl stuff' - else toHtmlFromList stuff' + -- always use level 1 for slide titles + let level' = if slide then 1 else level + let titleSlide = slide && level < slideLevel + header' <- blockToHtml opts (Header level' title') + let isSec (Sec _ _ _ _ _) = True + isSec (Blk _) = False + innerContents <- mapM (elementToHtml slideLevel opts) + $ if titleSlide + -- title slides have no content of their own + then filter isSec elements + else elements + let header'' = if (writerStrictMarkdown opts || + writerSectionDivs opts || + writerSlideVariant opts == S5Slides) + then header' + else header' ! prefixedId opts id' + let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts] + let classes = ["titleslide" | titleSlide] ++ ["slide" | slide] ++ + ["level" ++ show level] + let secttag = if writerHtml5 opts + then H5.section ! A.class_ (toValue $ unwords classes) + else H.div ! A.class_ (toValue $ unwords ("section":classes)) + return $ if titleSlide + then mconcat $ (secttag ! prefixedId opts id' $ header'') : innerContents + else if writerSectionDivs opts || slide + then secttag ! prefixedId opts id' $ inNl $ header'' : innerContents + else mconcat $ intersperse (nl opts) $ header'' : innerContents -- | Convert list of Note blocks to a footnote <div>. -- Assumes notes are sorted. footnoteSection :: WriterOptions -> [Html] -> Html footnoteSection opts notes = if null notes - then noHtml - else nl opts +++ (thediv ! [theclass "footnotes"] - $ nl opts +++ hr +++ nl opts +++ - (olist << (notes ++ [nl opts])) +++ nl opts) - + then mempty + else nl opts >> (container + $ nl opts >> hrtag >> nl opts >> + H.ol (mconcat notes >> nl opts) >> nl opts) + where container x = if writerHtml5 opts + then H5.section ! A.class_ "footnotes" $ x + else if writerSlideVariant opts /= NoSlides + then H.div ! A.class_ "footnotes slide" $ x + else H.div ! A.class_ "footnotes" $ x + hrtag = if writerHtml5 opts then H5.hr else H.hr -- | Parse a mailto link; return Just (name, domain) or Nothing. parseMailto :: String -> Maybe (String, String) @@ -272,7 +305,7 @@ parseMailto _ = Nothing -- | Obfuscate a "mailto:" link. obfuscateLink :: WriterOptions -> String -> String -> Html obfuscateLink opts txt s | writerEmailObfuscation opts == NoObfuscation = - anchor ! [href s] << txt + H.a ! A.href (toValue s) $ toHtml txt obfuscateLink opts txt s = let meth = writerEmailObfuscation opts s' = map toLower s @@ -287,19 +320,19 @@ obfuscateLink opts txt s = domain' ++ ")") in case meth of ReferenceObfuscation -> - -- need to use primHtml or &'s are escaped to & in URL - primHtml $ "<a href=\"" ++ (obfuscateString s') + -- need to use preEscapedString or &'s are escaped to & in URL + preEscapedString $ "<a href=\"" ++ (obfuscateString s') ++ "\">" ++ (obfuscateString txt) ++ "</a>" JavascriptObfuscation -> - (script ! [thetype "text/javascript"] $ - primHtml ("\n<!--\nh='" ++ + (H.script ! A.type_ "text/javascript" $ + preEscapedString ("\n<!--\nh='" ++ obfuscateString domain ++ "';a='" ++ at' ++ "';n='" ++ obfuscateString name' ++ "';e=n+a+h;\n" ++ "document.write('<a h'+'ref'+'=\"ma'+'ilto'+':'+e+'\">'+" ++ - linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) +++ - noscript (primHtml $ obfuscateString altText) + linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) >> + H.noscript (preEscapedString $ obfuscateString altText) _ -> error $ "Unknown obfuscation method: " ++ show meth - _ -> anchor ! [href s] $ stringToHtml opts txt -- malformed email + _ -> H.a ! A.href (toValue s) $ toHtml txt -- malformed email -- | Obfuscate character as entity. obfuscateChar :: Char -> String @@ -310,13 +343,13 @@ obfuscateChar char = -- | Obfuscate string using entities. obfuscateString :: String -> String -obfuscateString = concatMap obfuscateChar . decodeCharacterReferences +obfuscateString = concatMap obfuscateChar . fromEntities -attrsToHtml :: WriterOptions -> Attr -> [HtmlAttr] +attrsToHtml :: WriterOptions -> Attr -> [Attribute] attrsToHtml opts (id',classes',keyvals) = - [theclass (unwords classes') | not (null classes')] ++ + [A.class_ (toValue $ unwords classes') | not (null classes')] ++ [prefixedId opts id' | not (null id')] ++ - map (\(x,y) -> strAttr x y) keyvals + map (\(x,y) -> customAttribute (fromString x) (toValue y)) keyvals imageExts :: [String] imageExts = [ "art", "bmp", "cdr", "cdt", "cpt", "cr2", "crw", "djvu", "erf", @@ -331,40 +364,41 @@ treatAsImage fp = -- | Convert Pandoc block element to HTML. blockToHtml :: WriterOptions -> Block -> State WriterState Html -blockToHtml _ Null = return noHtml +blockToHtml _ Null = return mempty blockToHtml opts (Plain lst) = inlineListToHtml opts lst blockToHtml opts (Para [Image txt (s,tit)]) = do img <- inlineToHtml opts (Image txt (s,tit)) capt <- inlineListToHtml opts txt return $ if writerHtml5 opts - then tag "figure" << - [nl opts, img, tag "figcaption" << capt, nl opts] - else thediv ! [theclass "figure"] << - [nl opts, img, paragraph ! [theclass "caption"] << capt, + then H5.figure $ mconcat + [nl opts, img, H5.figcaption capt, nl opts] + else H.div ! A.class_ "figure" $ mconcat + [nl opts, img, H.p ! A.class_ "caption" $ capt, nl opts] blockToHtml opts (Para lst) = do contents <- inlineListToHtml opts lst - return $ paragraph contents -blockToHtml _ (RawBlock "html" str) = return $ primHtml str -blockToHtml _ (RawBlock _ _) = return noHtml -blockToHtml _ (HorizontalRule) = return hr + return $ H.p contents +blockToHtml _ (RawBlock "html" str) = return $ preEscapedString str +blockToHtml _ (RawBlock _ _) = return mempty +blockToHtml opts (HorizontalRule) = return $ if writerHtml5 opts then H5.hr else H.hr blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do - let classes' = if writerLiterateHaskell opts - then classes + let tolhs = writerLiterateHaskell opts && + any (\c -> map toLower c == "haskell") classes && + any (\c -> map toLower c == "literate") classes + classes' = if tolhs + then map (\c -> if map toLower c == "haskell" + then "literatehaskell" + else c) classes else filter (/= "literate") classes - case highlightHtml False (id',classes',keyvals) rawCode of - Left _ -> -- change leading newlines into <br /> tags, because some - -- browsers ignore leading newlines in pre blocks - let (leadingBreaks, rawCode') = span (=='\n') rawCode - attrs = attrsToHtml opts (id', classes', keyvals) - addBird = if "literate" `elem` classes' - then unlines . map ("> " ++) . lines - else unlines . lines - in return $ pre ! attrs $ thecode << - (replicate (length leadingBreaks) br +++ - [stringToHtml opts $ addBird rawCode']) - Right h -> modify (\st -> st{ stHighlighting = True }) >> - return h + adjCode = if tolhs + then unlines . map ("> " ++) . lines $ rawCode + else rawCode + case highlight formatHtmlBlock (id',classes,keyvals) adjCode of + Nothing -> let attrs = attrsToHtml opts (id', classes', keyvals) + in return $ foldl (!) H.pre attrs $ H.code + $ toHtml adjCode + Just h -> modify (\st -> st{ stHighlighting = True }) >> + return (foldl (!) h (attrsToHtml opts (id',[],keyvals))) blockToHtml opts (BlockQuote blocks) = -- in S5, treat list in blockquote specially -- if default is incremental, make it nonincremental; @@ -378,47 +412,48 @@ blockToHtml opts (BlockQuote blocks) = blockToHtml (opts {writerIncremental = inc}) (OrderedList attribs lst) _ -> do contents <- blockListToHtml opts blocks - return $ blockquote (nl opts +++ - contents +++ nl opts) + return $ H.blockquote + $ nl opts >> contents >> nl opts else do contents <- blockListToHtml opts blocks - return $ blockquote (nl opts +++ contents +++ nl opts) + return $ H.blockquote $ nl opts >> contents >> nl opts blockToHtml opts (Header level lst) = do contents <- inlineListToHtml opts lst secnum <- liftM stSecNum get let contents' = if writerNumberSections opts - then (thespan ! [theclass "header-section-number"] << showSecNum secnum) +++ - stringToHtml opts " " +++ contents + then (H.span ! A.class_ "header-section-number" $ toHtml $ showSecNum secnum) >> + strToHtml " " >> contents else contents let contents'' = if writerTableOfContents opts - then anchor ! [href $ "#" ++ writerIdentifierPrefix opts ++ "TOC"] $ contents' + then H.a ! A.href (toValue $ "#" ++ writerIdentifierPrefix opts ++ "TOC") $ contents' else contents' return $ (case level of - 1 -> h1 contents'' - 2 -> h2 contents'' - 3 -> h3 contents'' - 4 -> h4 contents'' - 5 -> h5 contents'' - 6 -> h6 contents'' - _ -> paragraph contents'') + 1 -> H.h1 contents'' + 2 -> H.h2 contents'' + 3 -> H.h3 contents'' + 4 -> H.h4 contents'' + 5 -> H.h5 contents'' + 6 -> H.h6 contents'' + _ -> H.p contents'') blockToHtml opts (BulletList lst) = do contents <- mapM (blockListToHtml opts) lst - let attribs = if writerIncremental opts - then [theclass "incremental"] - else [] - return $ (unordList opts contents) ! attribs + let lst' = unordList opts contents + let lst'' = if writerIncremental opts + then lst' ! A.class_ "incremental" + else lst' + return lst'' blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do contents <- mapM (blockListToHtml opts) lst let numstyle' = camelCaseToHyphenated $ show numstyle let attribs = (if writerIncremental opts - then [theclass "incremental"] + then [A.class_ "incremental"] else []) ++ (if startnum /= 1 - then [start startnum] + then [A.start $ toValue startnum] else []) ++ (if numstyle /= DefaultStyle then if writerHtml5 opts - then [strAttr "type" $ + then [A.type_ $ case numstyle of Decimal -> "1" LowerAlpha -> "a" @@ -426,44 +461,44 @@ blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do LowerRoman -> "i" UpperRoman -> "I" _ -> "1"] - else [thestyle $ "list-style-type: " ++ + else [A.style $ toValue $ "list-style-type: " ++ numstyle'] else []) - return $ (ordList opts contents) ! attribs + return $ foldl (!) (ordList opts contents) attribs blockToHtml opts (DefinitionList lst) = do contents <- mapM (\(term, defs) -> - do term' <- liftM (dterm <<) $ inlineListToHtml opts term - defs' <- mapM ((liftM (\x -> ddef << (x +++ nl opts))) . + do term' <- liftM (H.dt) $ inlineListToHtml opts term + defs' <- mapM ((liftM (\x -> H.dd $ (x >> nl opts))) . blockListToHtml opts) defs - return $ nl opts : term' : nl opts : defs') lst - let attribs = if writerIncremental opts - then [theclass "incremental"] - else [] - return $ dlist ! attribs << (concat contents +++ nl opts) + return $ mconcat $ nl opts : term' : nl opts : defs') lst + let lst' = H.dl $ mconcat contents >> nl opts + let lst'' = if writerIncremental opts + then lst' ! A.class_ "incremental" + else lst' + return lst'' blockToHtml opts (Table capt aligns widths headers rows') = do captionDoc <- if null capt - then return noHtml + then return mempty else do cs <- inlineListToHtml opts capt - return $ caption cs +++ nl opts + return $ H.caption cs >> nl opts let percent w = show (truncate (100*w) :: Integer) ++ "%" - let widthAttrs w = if writerHtml5 opts - then [thestyle $ "width: " ++ percent w] - else [width $ percent w] let coltags = if all (== 0.0) widths - then noHtml - else concatHtml $ map - (\w -> (col ! (widthAttrs w)) noHtml +++ nl opts) - widths + then mempty + else mconcat $ map (\w -> + if writerHtml5 opts + then H.col ! A.style (toValue $ "width: " ++ percent w) + else H.col ! A.width (toValue $ percent w) >> nl opts) + widths head' <- if all null headers - then return noHtml + then return mempty else do contents <- tableRowToHtml opts aligns 0 headers - return $ thead << (nl opts +++ contents) +++ nl opts - body' <- liftM (\x -> tbody << (nl opts +++ x)) $ + return $ H.thead (nl opts >> contents) >> nl opts + body' <- liftM (\x -> H.tbody (nl opts >> mconcat x)) $ zipWithM (tableRowToHtml opts aligns) [1..] rows' - return $ table $ nl opts +++ captionDoc +++ coltags +++ head' +++ - body' +++ nl opts + return $ H.table $ nl opts >> captionDoc >> coltags >> head' >> + body' >> nl opts tableRowToHtml :: WriterOptions -> [Alignment] @@ -471,7 +506,7 @@ tableRowToHtml :: WriterOptions -> [[Block]] -> State WriterState Html tableRowToHtml opts aligns rownum cols' = do - let mkcell = if rownum == 0 then th else td + let mkcell = if rownum == 0 then H.th else H.td let rowclass = case rownum of 0 -> "header" x | x `rem` 2 == 1 -> "odd" @@ -479,8 +514,8 @@ tableRowToHtml opts aligns rownum cols' = do cols'' <- sequence $ zipWith (\alignment item -> tableItemToHtml opts mkcell alignment item) aligns cols' - return $ (tr ! [theclass rowclass] $ nl opts +++ toHtmlFromList cols'') - +++ nl opts + return $ (H.tr ! A.class_ rowclass $ nl opts >> mconcat cols'') + >> nl opts alignmentToString :: Alignment -> [Char] alignmentToString alignment = case alignment of @@ -496,84 +531,87 @@ tableItemToHtml :: WriterOptions -> State WriterState Html tableItemToHtml opts tag' align' item = do contents <- blockListToHtml opts item - let alignAttrs = if writerHtml5 opts - then [thestyle $ "align: " ++ alignmentToString align'] - else [align $ alignmentToString align'] - return $ (tag' ! alignAttrs) contents +++ nl opts + let alignStr = alignmentToString align' + let attribs = if writerHtml5 opts + then A.style (toValue $ "text-align: " ++ alignStr ++ ";") + else A.align (toValue alignStr) + return $ (tag' ! attribs $ contents) >> nl opts toListItems :: WriterOptions -> [Html] -> [Html] toListItems opts items = map (toListItem opts) items ++ [nl opts] toListItem :: WriterOptions -> Html -> Html -toListItem opts item = nl opts +++ li item +toListItem opts item = nl opts >> H.li item blockListToHtml :: WriterOptions -> [Block] -> State WriterState Html blockListToHtml opts lst = mapM (blockToHtml opts) lst >>= - return . toHtmlFromList . intersperse (nl opts) + return . mconcat . intersperse (nl opts) -- | Convert list of Pandoc inline elements to HTML. inlineListToHtml :: WriterOptions -> [Inline] -> State WriterState Html inlineListToHtml opts lst = - mapM (inlineToHtml opts) lst >>= return . toHtmlFromList + mapM (inlineToHtml opts) lst >>= return . mconcat -- | Convert Pandoc inline element to HTML. inlineToHtml :: WriterOptions -> Inline -> State WriterState Html inlineToHtml opts inline = case inline of - (Str str) -> return $ stringToHtml opts str - (Space) -> return $ stringToHtml opts " " - (LineBreak) -> return br - (EmDash) -> return $ stringToHtml opts "—" - (EnDash) -> return $ stringToHtml opts "–" - (Ellipses) -> return $ stringToHtml opts "…" - (Apostrophe) -> return $ stringToHtml opts "’" - (Emph lst) -> inlineListToHtml opts lst >>= return . emphasize - (Strong lst) -> inlineListToHtml opts lst >>= return . strong - (Code attr str) -> case highlightHtml True attr str of - Left _ -> return - $ thecode ! (attrsToHtml opts attr) - $ stringToHtml opts str - Right h -> return h + (Str str) -> return $ strToHtml str + (Space) -> return $ strToHtml " " + (LineBreak) -> return $ if writerHtml5 opts then H5.br else H.br + (Emph lst) -> inlineListToHtml opts lst >>= return . H.em + (Strong lst) -> inlineListToHtml opts lst >>= return . H.strong + (Code attr str) -> case highlight formatHtmlInline attr str of + Nothing -> return + $ foldl (!) H.code (attrsToHtml opts attr) + $ strToHtml str + Just h -> return $ foldl (!) h $ + attrsToHtml opts (id',[],keyvals) + where (id',_,keyvals) = attr (Strikeout lst) -> inlineListToHtml opts lst >>= - return . (thespan ! [thestyle "text-decoration: line-through;"]) + return . H.del (SmallCaps lst) -> inlineListToHtml opts lst >>= - return . (thespan ! [thestyle "font-variant: small-caps;"]) - (Superscript lst) -> inlineListToHtml opts lst >>= return . sup - (Subscript lst) -> inlineListToHtml opts lst >>= return . sub + return . (H.span ! A.style "font-variant: small-caps;") + (Superscript lst) -> inlineListToHtml opts lst >>= return . H.sup + (Subscript lst) -> inlineListToHtml opts lst >>= return . H.sub (Quoted quoteType lst) -> let (leftQuote, rightQuote) = case quoteType of - SingleQuote -> (stringToHtml opts "‘", - stringToHtml opts "’") - DoubleQuote -> (stringToHtml opts "“", - stringToHtml opts "”") + SingleQuote -> (strToHtml "‘", + strToHtml "’") + DoubleQuote -> (strToHtml "“", + strToHtml "”") in do contents <- inlineListToHtml opts lst - return $ leftQuote +++ contents +++ rightQuote + return $ leftQuote >> contents >> rightQuote (Math t str) -> modify (\st -> st {stMath = True}) >> (case writerHTMLMathMethod opts of LaTeXMathML _ -> -- putting LaTeXMathML in container with class "LaTeX" prevents -- non-math elements on the page from being treated as math by -- the javascript - return $ thespan ! [theclass "LaTeX"] $ + return $ H.span ! A.class_ "LaTeX" $ case t of - InlineMath -> primHtml ("$" ++ str ++ "$") - DisplayMath -> primHtml ("$$" ++ str ++ "$$") + InlineMath -> toHtml ("$" ++ str ++ "$") + DisplayMath -> toHtml ("$$" ++ str ++ "$$") JsMath _ -> do - let m = primHtml str + let m = preEscapedString str return $ case t of - InlineMath -> thespan ! [theclass "math"] $ m - DisplayMath -> thediv ! [theclass "math"] $ m + InlineMath -> H.span ! A.class_ "math" $ m + DisplayMath -> H.div ! A.class_ "math" $ m WebTeX url -> do - let m = image ! [src (url ++ urlEncode str), - alt str, title str] + let imtag = if writerHtml5 opts then H5.img else H.img + let m = imtag ! A.style "vertical-align:middle" + ! A.src (toValue $ url ++ urlEncode str) + ! A.alt (toValue str) + ! A.title (toValue str) + let brtag = if writerHtml5 opts then H5.br else H.br return $ case t of InlineMath -> m - DisplayMath -> br +++ m +++ br + DisplayMath -> brtag >> m >> brtag GladTeX -> return $ case t of - InlineMath -> primHtml $ "<EQ ENV=\"math\">" ++ str ++ "</EQ>" - DisplayMath -> primHtml $ "<EQ ENV=\"displaymath\">" ++ str ++ "</EQ>" + InlineMath -> preEscapedString "<EQ ENV=\"math\">" >> toHtml str >> preEscapedString "</EQ>" + DisplayMath -> preEscapedString "<EQ ENV=\"displaymath\">" >> toHtml str >> preEscapedString "</EQ>" MathML _ -> do let dt = if t == InlineMath then DisplayInline @@ -581,54 +619,57 @@ inlineToHtml opts inline = let conf = useShortEmptyTags (const False) defaultConfigPP case texMathToMathML dt str of - Right r -> return $ primHtml $ + Right r -> return $ preEscapedString $ ppcElement conf r Left _ -> inlineListToHtml opts (readTeXMath str) >>= return . - (thespan ! [theclass "math"]) - MathJax _ -> return $ primHtml $ + (H.span ! A.class_ "math") + MathJax _ -> return $ toHtml $ case t of InlineMath -> "\\(" ++ str ++ "\\)" DisplayMath -> "\\[" ++ str ++ "\\]" PlainMath -> do x <- inlineListToHtml opts (readTeXMath str) - let m = thespan ! [theclass "math"] $ x + let m = H.span ! A.class_ "math" $ x + let brtag = if writerHtml5 opts then H5.br else H.br return $ case t of InlineMath -> m - DisplayMath -> br +++ m +++ br ) + DisplayMath -> brtag >> m >> brtag ) (RawInline "latex" str) -> case writerHTMLMathMethod opts of LaTeXMathML _ -> do modify (\st -> st {stMath = True}) - return $ primHtml str - _ -> return noHtml - (RawInline "html" str) -> return $ primHtml str - (RawInline _ _) -> return noHtml + return $ toHtml str + _ -> return mempty + (RawInline "html" str) -> return $ preEscapedString str + (RawInline _ _) -> return mempty (Link [Code _ str] (s,_)) | "mailto:" `isPrefixOf` s -> return $ obfuscateLink opts str s (Link txt (s,_)) | "mailto:" `isPrefixOf` s -> do linkText <- inlineListToHtml opts txt - return $ obfuscateLink opts (show linkText) s + return $ obfuscateLink opts (renderHtml linkText) s (Link txt (s,tit)) -> do linkText <- inlineListToHtml opts txt - return $ anchor ! ([href s] ++ - if null tit then [] else [title tit]) $ - linkText + let link = H.a ! A.href (toValue s) $ linkText + return $ if null tit + then link + else link ! A.title (toValue tit) (Image txt (s,tit)) | treatAsImage s -> do let alternate' = stringify txt - let attributes = [src s] ++ + let attributes = [A.src $ toValue s] ++ (if null tit then [] - else [title tit]) ++ + else [A.title $ toValue tit]) ++ if null txt then [] - else [alt alternate'] - return $ image ! attributes + else [A.alt $ toValue alternate'] + let tag = if writerHtml5 opts then H5.img else H.img + return $ foldl (!) tag attributes -- note: null title included, as in Markdown.pl (Image _ (s,tit)) -> do - let attributes = [src s] ++ + let attributes = [A.src $ toValue s] ++ (if null tit then [] - else [title tit]) - return $ itag "embed" ! attributes + else [A.title $ toValue tit]) + return $ foldl (!) H5.embed attributes -- note: null title included, as in Markdown.pl (Note contents) -> do st <- get @@ -638,19 +679,19 @@ inlineToHtml opts inline = htmlContents <- blockListToNote opts ref contents -- push contents onto front of notes put $ st {stNotes = (htmlContents:notes)} - return $ sup << - anchor ! [href ("#" ++ writerIdentifierPrefix opts ++ "fn" ++ ref), - theclass "footnoteRef", - prefixedId opts ("fnref" ++ ref)] << ref - (Cite _ il) -> inlineListToHtml opts il + return $ H.sup $ + H.a ! A.href (toValue $ "#" ++ writerIdentifierPrefix opts ++ "fn" ++ ref) + ! A.class_ "footnoteRef" + ! prefixedId opts ("fnref" ++ ref) + $ toHtml ref + (Cite _ il) -> do contents <- inlineListToHtml opts il + return $ H.span ! A.class_ "citation" $ contents blockListToNote :: WriterOptions -> String -> [Block] -> State WriterState Html blockListToNote opts ref blocks = -- If last block is Para or Plain, include the backlink at the end of -- that block. Otherwise, insert a new Plain block with the backlink. - let backlink = [RawInline "html" $ " <a href=\"#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref ++ - "\" class=\"footnoteBackLink\">" ++ - (if writerAscii opts then "↩" else "↩") ++ "</a>"] + let backlink = [Link [Str "↩"] ("#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref,[])] blocks' = if null blocks then [] else let lastBlock = last blocks @@ -663,4 +704,4 @@ blockListToNote opts ref blocks = _ -> otherBlocks ++ [lastBlock, Plain backlink] in do contents <- blockListToHtml opts blocks' - return $ nl opts +++ (li ! [prefixedId opts ("fn" ++ ref)]) contents + return $ nl opts >> (H.li ! (prefixedId opts ("fn" ++ ref)) $ contents) |