diff options
Diffstat (limited to 'src/Text/Pandoc/Writers/HTML.hs')
-rw-r--r-- | src/Text/Pandoc/Writers/HTML.hs | 1069 |
1 files changed, 0 insertions, 1069 deletions
diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs deleted file mode 100644 index 99f8c5b42..000000000 --- a/src/Text/Pandoc/Writers/HTML.hs +++ /dev/null @@ -1,1069 +0,0 @@ -{-# LANGUAGE OverloadedStrings, CPP, ViewPatterns, ScopedTypeVariables #-} -{- -Copyright (C) 2006-2015 John MacFarlane <jgm@berkeley.edu> - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA --} - -{- | - Module : Text.Pandoc.Writers.HTML - Copyright : Copyright (C) 2006-2015 John MacFarlane - License : GNU GPL, version 2 or above - - Maintainer : John MacFarlane <jgm@berkeley.edu> - Stability : alpha - Portability : portable - -Conversion of 'Pandoc' documents to HTML. --} -module Text.Pandoc.Writers.HTML ( - writeHtml4, - writeHtml4String, - writeHtml5, - writeHtml5String, - writeHtmlStringForEPUB, - writeS5, - writeSlidy, - writeSlideous, - writeDZSlides, - writeRevealJs - ) where -import Text.Pandoc.Definition -import Text.Pandoc.Walk -import Data.Monoid ((<>)) -import Text.Pandoc.Shared -import Text.Pandoc.Writers.Shared -import Text.Pandoc.Options -import Text.Pandoc.ImageSize -import Text.Pandoc.Templates -import Text.Pandoc.Writers.Math -import Text.Pandoc.Slides -import Text.Pandoc.Highlighting ( highlight, styleToCss, - formatHtmlInline, formatHtmlBlock ) -import Text.Pandoc.XML (fromEntities, escapeStringForXML) -import Network.URI ( parseURIReference, URI(..), unEscapeString ) -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, fromMaybe, isJust ) -import Control.Monad.State -import Text.Blaze.Html hiding(contents) -#if MIN_VERSION_blaze_markup(0,6,3) -#else -import Text.Blaze.Internal(preEscapedString) -#endif -#if MIN_VERSION_blaze_html(0,5,1) -import qualified Text.Blaze.XHtml5 as H5 -#else -import qualified Text.Blaze.Html5 as H5 -#endif -import qualified Text.Blaze.XHtml1.Transitional as H -import qualified Text.Blaze.XHtml1.Transitional.Attributes as A -import Text.Blaze.Html.Renderer.String (renderHtml) -import Text.TeXMath -import Text.XML.Light.Output -import Text.XML.Light (unode, elChildren, unqual) -import qualified Text.XML.Light as XML -import System.FilePath (takeExtension) -import Data.Aeson (Value) -import Control.Monad.Except (throwError) -import Text.Pandoc.Error -import Text.Pandoc.Class (PandocMonad, report) -import Text.Pandoc.Logging - -data WriterState = WriterState - { stNotes :: [Html] -- ^ List of notes - , stMath :: Bool -- ^ Math is used in document - , stQuotes :: Bool -- ^ <q> tag is used - , stHighlighting :: Bool -- ^ Syntax highlighting is used - , stSecNum :: [Int] -- ^ Number of current section - , stElement :: Bool -- ^ Processing an Element - , stHtml5 :: Bool -- ^ Use HTML5 - , stEPUBVersion :: Maybe EPUBVersion -- ^ EPUB version if for epub - , stSlideVariant :: HTMLSlideVariant - } - -defaultWriterState :: WriterState -defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False, - stHighlighting = False, stSecNum = [], - stElement = False, stHtml5 = False, - stEPUBVersion = Nothing, - stSlideVariant = NoSlides} - --- Helpers to render HTML with the appropriate function. - -strToHtml :: String -> Html -strToHtml ('\'':xs) = preEscapedString "\'" `mappend` strToHtml xs -strToHtml xs@(_:_) = case break (=='\'') xs of - (_ ,[]) -> toHtml xs - (ys,zs) -> toHtml ys `mappend` strToHtml zs -strToHtml [] = "" - --- | Hard linebreak. -nl :: WriterOptions -> Html -nl opts = if writerWrapText opts == WrapNone - then mempty - else preEscapedString "\n" - --- | Convert Pandoc document to Html 5 string. -writeHtml5String :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeHtml5String = writeHtmlString' - defaultWriterState{ stHtml5 = True } - --- | Convert Pandoc document to Html 5 structure. -writeHtml5 :: PandocMonad m => WriterOptions -> Pandoc -> m Html -writeHtml5 = writeHtml' defaultWriterState{ stHtml5 = True } - --- | Convert Pandoc document to Html 4 string. -writeHtml4String :: PandocMonad m => WriterOptions -> Pandoc -> m String -writeHtml4String = writeHtmlString' - defaultWriterState{ stHtml5 = False } - --- | Convert Pandoc document to Html 4 structure. -writeHtml4 :: PandocMonad m => WriterOptions -> Pandoc -> m Html -writeHtml4 = writeHtml' defaultWriterState{ stHtml5 = False } - --- | Convert Pandoc document to Html appropriate for an epub version. -writeHtmlStringForEPUB :: PandocMonad m - => EPUBVersion -> WriterOptions -> Pandoc -> m String -writeHtmlStringForEPUB version = writeHtmlString' - defaultWriterState{ stHtml5 = version == EPUB3, - stEPUBVersion = Just version } - --- | Convert Pandoc document to Reveal JS HTML slide show. -writeRevealJs :: PandocMonad m - => WriterOptions -> Pandoc -> m String -writeRevealJs = writeHtmlSlideShow' RevealJsSlides - --- | Convert Pandoc document to S5 HTML slide show. -writeS5 :: PandocMonad m - => WriterOptions -> Pandoc -> m String -writeS5 = writeHtmlSlideShow' S5Slides - --- | Convert Pandoc document to Slidy HTML slide show. -writeSlidy :: PandocMonad m - => WriterOptions -> Pandoc -> m String -writeSlidy = writeHtmlSlideShow' SlidySlides - --- | Convert Pandoc document to Slideous HTML slide show. -writeSlideous :: PandocMonad m - => WriterOptions -> Pandoc -> m String -writeSlideous = writeHtmlSlideShow' SlideousSlides - --- | Convert Pandoc document to DZSlides HTML slide show. -writeDZSlides :: PandocMonad m - => WriterOptions -> Pandoc -> m String -writeDZSlides = writeHtmlSlideShow' DZSlides - -writeHtmlSlideShow' :: PandocMonad m - => HTMLSlideVariant -> WriterOptions -> Pandoc -> m String -writeHtmlSlideShow' variant = writeHtmlString' - defaultWriterState{ stSlideVariant = variant - , stHtml5 = case variant of - RevealJsSlides -> True - S5Slides -> False - SlidySlides -> False - DZSlides -> True - SlideousSlides -> False - NoSlides -> False - } - -writeHtmlString' :: PandocMonad m - => WriterState -> WriterOptions -> Pandoc -> m String -writeHtmlString' st opts d = do - (body, context) <- evalStateT (pandocToHtml opts d) st - return $ case writerTemplate opts of - Nothing -> renderHtml body - Just tpl -> renderTemplate' tpl $ - defField "body" (renderHtml body) context - -writeHtml' :: PandocMonad m => WriterState -> WriterOptions -> Pandoc -> m Html -writeHtml' st opts d = do - (body, context) <- evalStateT (pandocToHtml opts d) st - return $ case writerTemplate opts of - Nothing -> body - Just tpl -> renderTemplate' tpl $ - defField "body" (renderHtml body) context - --- result is (title, authors, date, toc, body, new variables) -pandocToHtml :: PandocMonad m - => WriterOptions - -> Pandoc - -> StateT WriterState m (Html, Value) -pandocToHtml opts (Pandoc meta blocks) = do - metadata <- metaToJSON opts - (fmap renderHtml . blockListToHtml opts) - (fmap renderHtml . inlineListToHtml opts) - meta - let stringifyHTML = escapeStringForXML . stringify - let authsMeta = map stringifyHTML $ docAuthors meta - let dateMeta = stringifyHTML $ docDate meta - let slideLevel = fromMaybe (getSlideLevel blocks) $ writerSlideLevel opts - slideVariant <- gets stSlideVariant - let sects = hierarchicalize $ - if slideVariant == NoSlides - then blocks - else prepSlides slideLevel blocks - toc <- if writerTableOfContents opts && slideVariant /= S5Slides - then tableOfContents opts sects - else return Nothing - blocks' <- liftM (mconcat . intersperse (nl opts)) $ - mapM (elementToHtml slideLevel opts) sects - st <- get - notes <- footnoteSection opts (reverse (stNotes st)) - let thebody = blocks' >> notes - let math = case writerHTMLMathMethod opts of - LaTeXMathML (Just url) -> - H.script ! A.src (toValue url) - ! A.type_ "text/javascript" - $ mempty - MathJax url -> - H.script ! A.src (toValue url) - ! A.type_ "text/javascript" - $ case slideVariant of - SlideousSlides -> - preEscapedString - "MathJax.Hub.Queue([\"Typeset\",MathJax.Hub]);" - _ -> mempty - JsMath (Just url) -> - H.script ! A.src (toValue url) - ! A.type_ "text/javascript" - $ mempty - KaTeX js css -> - (H.script ! A.src (toValue js) $ mempty) <> - (H.link ! A.rel "stylesheet" ! A.href (toValue css)) <> - (H.script ! A.type_ "text/javascript" $ toHtml renderKaTeX) - _ -> case lookup "mathml-script" (writerVariables opts) of - Just s | not (stHtml5 st) -> - H.script ! A.type_ "text/javascript" - $ preEscapedString - ("/*<![CDATA[*/\n" ++ s ++ "/*]]>*/\n") - | otherwise -> mempty - Nothing -> mempty - let context = (if stHighlighting st - then case writerHighlightStyle opts of - Just sty -> defField "highlighting-css" - (styleToCss sty) - Nothing -> id - else id) $ - (if stMath st - then defField "math" (renderHtml math) - else id) $ - defField "quotes" (stQuotes st) $ - maybe id (defField "toc" . renderHtml) toc $ - defField "author-meta" authsMeta $ - maybe id (defField "date-meta") (normalizeDate dateMeta) $ - defField "pagetitle" (stringifyHTML $ docTitle meta) $ - defField "idprefix" (writerIdentifierPrefix opts) $ - -- these should maybe be set in pandoc.hs - defField "slidy-url" - ("http://www.w3.org/Talks/Tools/Slidy2" :: String) $ - defField "slideous-url" ("slideous" :: String) $ - defField "revealjs-url" ("reveal.js" :: String) $ - defField "s5-url" ("s5/default" :: String) $ - defField "html5" (stHtml5 st) $ - metadata - return (thebody, context) - --- | Like Text.XHtml's identifier, but adds the writerIdentifierPrefix -prefixedId :: WriterOptions -> String -> Attribute -prefixedId opts s = - case s of - "" -> mempty - _ -> A.id $ toValue $ writerIdentifierPrefix opts ++ s - -toList :: PandocMonad m - => (Html -> Html) - -> WriterOptions - -> [Html] - -> StateT WriterState m Html -toList listop opts items = do - slideVariant <- gets stSlideVariant - return $ - if (writerIncremental opts) - then if (slideVariant /= RevealJsSlides) - then (listop $ mconcat items) ! A.class_ "incremental" - else listop $ mconcat $ map (! A.class_ "fragment") items - else listop $ mconcat items - -unordList :: PandocMonad m - => WriterOptions -> [Html] -> StateT WriterState m Html -unordList opts = toList H.ul opts . toListItems opts - -ordList :: PandocMonad m - => WriterOptions -> [Html] -> StateT WriterState m Html -ordList opts = toList H.ol opts . toListItems opts - -defList :: PandocMonad m - => WriterOptions -> [Html] -> StateT WriterState m Html -defList opts items = toList H.dl opts (items ++ [nl opts]) - --- | Construct table of contents from list of elements. -tableOfContents :: PandocMonad m => WriterOptions -> [Element] -> StateT WriterState m (Maybe Html) -tableOfContents _ [] = return Nothing -tableOfContents opts sects = do - contents <- mapM (elementToListItem opts) sects - let tocList = catMaybes contents - if null tocList - then return Nothing - else Just <$> unordList opts tocList - --- | Convert section number to string -showSecNum :: [Int] -> String -showSecNum = concat . intersperse "." . map show - --- | Converts an Element to a list item for a table of contents, --- retrieving the appropriate identifier from state. -elementToListItem :: PandocMonad m => WriterOptions -> Element -> StateT WriterState m (Maybe Html) --- Don't include the empty headers created in slide shows --- shows when an hrule is used to separate slides without a new title: -elementToListItem _ (Sec _ _ _ [Str "\0"] _) = return Nothing -elementToListItem opts (Sec lev num (id',classes,_) headerText subsecs) - | lev <= writerTOCDepth opts = do - let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0) - let sectnum = if writerNumberSections opts && not (null num) && - "unnumbered" `notElem` classes - then (H.span ! A.class_ "toc-section-number" - $ toHtml $ showSecNum num') >> preEscapedString " " - else mempty - txt <- liftM (sectnum >>) $ inlineListToHtml opts $ walk deNote headerText - subHeads <- mapM (elementToListItem opts) subsecs >>= return . catMaybes - subList <- if null subHeads - then return mempty - else unordList opts subHeads - -- in reveal.js, we need #/apples, not #apples: - slideVariant <- gets stSlideVariant - let revealSlash = ['/' | slideVariant== RevealJsSlides] - return $ Just - $ if null id' - then (H.a $ toHtml txt) >> subList - else (H.a ! A.href (toValue $ "#" ++ revealSlash ++ - writerIdentifierPrefix opts ++ id') - $ toHtml txt) >> subList -elementToListItem _ _ = return Nothing - --- | Convert an Element to Html. -elementToHtml :: PandocMonad m => Int -> WriterOptions -> Element -> StateT WriterState m Html -elementToHtml _slideLevel opts (Blk block) = blockToHtml opts block -elementToHtml slideLevel opts (Sec level num (id',classes,keyvals) title' elements) = do - slideVariant <- gets stSlideVariant - let slide = slideVariant /= NoSlides && level <= slideLevel - let num' = zipWith (+) num (writerNumberOffset opts ++ repeat 0) - modify $ \st -> st{stSecNum = num'} -- update section number - html5 <- gets stHtml5 - let titleSlide = slide && level < slideLevel - header' <- if title' == [Str "\0"] -- marker for hrule - then return mempty - else do - modify (\st -> st{ stElement = True}) - res <- blockToHtml opts - (Header level (id',classes,keyvals) title') - modify (\st -> st{ stElement = False}) - return res - - let isSec (Sec _ _ _ _ _) = True - isSec (Blk _) = False - let isPause (Blk x) = x == Para [Str ".",Space,Str ".",Space,Str "."] - isPause _ = False - let fragmentClass = case slideVariant of - RevealJsSlides -> "fragment" - _ -> "incremental" - let inDiv xs = Blk (RawBlock (Format "html") ("<div class=\"" - ++ fragmentClass ++ "\">")) : - (xs ++ [Blk (RawBlock (Format "html") "</div>")]) - innerContents <- mapM (elementToHtml slideLevel opts) - $ if titleSlide - -- title slides have no content of their own - then filter isSec elements - else case splitBy isPause elements of - [] -> [] - (x:xs) -> x ++ concatMap inDiv xs - let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts] - let classes' = ["titleslide" | titleSlide] ++ ["slide" | slide] ++ - ["section" | (slide || writerSectionDivs opts) && - not html5 ] ++ - ["level" ++ show level | slide || writerSectionDivs opts ] - ++ classes - let secttag = if html5 - then H5.section - else H.div - let attr = (id',classes',keyvals) - return $ if titleSlide - then (if slideVariant == RevealJsSlides - then H5.section - else id) $ mconcat $ - (addAttrs opts attr $ secttag $ header') : innerContents - else if writerSectionDivs opts || slide - then addAttrs opts attr - $ secttag $ inNl $ header' : innerContents - else mconcat $ intersperse (nl opts) - $ addAttrs opts attr header' : innerContents - --- | Convert list of Note blocks to a footnote <div>. --- Assumes notes are sorted. -footnoteSection :: PandocMonad m - => WriterOptions -> [Html] -> StateT WriterState m Html -footnoteSection opts notes = do - html5 <- gets stHtml5 - slideVariant <- gets stSlideVariant - let hrtag = if html5 then H5.hr else H.hr - let container x = if html5 - then H5.section ! A.class_ "footnotes" $ x - else if slideVariant /= NoSlides - then H.div ! A.class_ "footnotes slide" $ x - else H.div ! A.class_ "footnotes" $ x - return $ - if null notes - then mempty - else nl opts >> (container - $ nl opts >> hrtag >> nl opts >> - H.ol (mconcat notes >> nl opts) >> nl opts) - --- | Parse a mailto link; return Just (name, domain) or Nothing. -parseMailto :: String -> Maybe (String, String) -parseMailto s = do - case break (==':') s of - (xs,':':addr) | map toLower xs == "mailto" -> do - let (name', rest) = span (/='@') addr - let domain = drop 1 rest - return (name', domain) - _ -> fail "not a mailto: URL" - --- | Obfuscate a "mailto:" link. -obfuscateLink :: PandocMonad m => WriterOptions -> Attr -> Html -> String -> m Html -obfuscateLink opts attr txt s | writerEmailObfuscation opts == NoObfuscation = - return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ txt -obfuscateLink opts attr (renderHtml -> txt) s = - let meth = writerEmailObfuscation opts - s' = map toLower (take 7 s) ++ drop 7 s - in case parseMailto s' of - (Just (name', domain)) -> - let domain' = substitute "." " dot " domain - at' = obfuscateChar '@' - (linkText, altText) = - if txt == drop 7 s' -- autolink - then ("e", name' ++ " at " ++ domain') - else ("'" ++ obfuscateString txt ++ "'", - txt ++ " (" ++ name' ++ " at " ++ domain' ++ ")") - in case meth of - ReferenceObfuscation -> - -- need to use preEscapedString or &'s are escaped to & in URL - return $ - preEscapedString $ "<a href=\"" ++ (obfuscateString s') - ++ "\" class=\"email\">" ++ (obfuscateString txt) ++ "</a>" - JavascriptObfuscation -> - return $ - (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+'\" clas'+'s=\"em' + 'ail\">'+" ++ - linkText ++ "+'<\\/'+'a'+'>');\n// -->\n")) >> - H.noscript (preEscapedString $ obfuscateString altText) - _ -> throwError $ PandocSomeError $ "Unknown obfuscation method: " ++ show meth - _ -> return $ addAttrs opts attr $ H.a ! A.href (toValue s) $ toHtml txt -- malformed email - --- | Obfuscate character as entity. -obfuscateChar :: Char -> String -obfuscateChar char = - let num = ord char - numstr = if even num then show num else "x" ++ showHex num "" - in "&#" ++ numstr ++ ";" - --- | Obfuscate string using entities. -obfuscateString :: String -> String -obfuscateString = concatMap obfuscateChar . fromEntities - -addAttrs :: WriterOptions -> Attr -> Html -> Html -addAttrs opts attr h = foldl (!) h (attrsToHtml opts attr) - -toAttrs :: [(String, String)] -> [Attribute] -toAttrs kvs = map (\(x,y) -> customAttribute (fromString x) (toValue y)) kvs - -attrsToHtml :: WriterOptions -> Attr -> [Attribute] -attrsToHtml opts (id',classes',keyvals) = - [prefixedId opts id' | not (null id')] ++ - [A.class_ (toValue $ unwords classes') | not (null classes')] ++ toAttrs keyvals - -imgAttrsToHtml :: WriterOptions -> Attr -> [Attribute] -imgAttrsToHtml opts attr = - attrsToHtml opts (ident,cls,kvs') ++ - toAttrs (dimensionsToAttrList opts attr) - where - (ident,cls,kvs) = attr - kvs' = filter isNotDim kvs - isNotDim ("width", _) = False - isNotDim ("height", _) = False - isNotDim _ = True - -dimensionsToAttrList :: WriterOptions -> Attr -> [(String, String)] -dimensionsToAttrList opts attr = (go Width) ++ (go Height) - where - go dir = case (dimension dir attr) of - (Just (Percent a)) -> [("style", show dir ++ ":" ++ show (Percent a))] - (Just dim) -> [(show dir, showInPixel opts dim)] - _ -> [] - - -imageExts :: [String] -imageExts = [ "art", "bmp", "cdr", "cdt", "cpt", "cr2", "crw", "djvu", "erf", - "gif", "ico", "ief", "jng", "jpg", "jpeg", "nef", "orf", "pat", "pbm", - "pcx", "pgm", "png", "pnm", "ppm", "psd", "ras", "rgb", "svg", "tiff", - "wbmp", "xbm", "xpm", "xwd" ] - -treatAsImage :: FilePath -> Bool -treatAsImage fp = - let path = case uriPath `fmap` parseURIReference fp of - Nothing -> fp - Just up -> up - ext = map toLower $ drop 1 $ takeExtension path - in null ext || ext `elem` imageExts - --- | Convert Pandoc block element to HTML. -blockToHtml :: PandocMonad m => WriterOptions -> Block -> StateT WriterState m Html -blockToHtml _ Null = return mempty -blockToHtml opts (Plain lst) = inlineListToHtml opts lst --- title beginning with fig: indicates that the image is a figure -blockToHtml opts (Para [Image attr txt (s,'f':'i':'g':':':tit)]) = do - img <- inlineToHtml opts (Image attr txt (s,tit)) - html5 <- gets stHtml5 - let tocapt = if html5 - then H5.figcaption - else H.p ! A.class_ "caption" - capt <- if null txt - then return mempty - else tocapt `fmap` inlineListToHtml opts txt - return $ if html5 - then H5.figure $ mconcat - [nl opts, img, capt, nl opts] - else H.div ! A.class_ "figure" $ mconcat - [nl opts, img, nl opts, capt, nl opts] -blockToHtml opts (Para lst) - | isEmptyRaw lst = return mempty - | otherwise = do - contents <- inlineListToHtml opts lst - return $ H.p contents - where - isEmptyRaw [RawInline f _] = f /= (Format "html") - isEmptyRaw _ = False -blockToHtml opts (LineBlock lns) = - if writerWrapText opts == WrapNone - then blockToHtml opts $ linesToPara lns - else do - let lf = preEscapedString "\n" - htmlLines <- mconcat . intersperse lf <$> mapM (inlineListToHtml opts) lns - return $ H.div ! A.style "white-space: pre-line;" $ htmlLines -blockToHtml opts (Div attr@(ident, classes, kvs) bs) = do - html5 <- gets stHtml5 - let speakerNotes = "notes" `elem` classes - -- we don't want incremental output inside speaker notes, see #1394 - let opts' = if speakerNotes then opts{ writerIncremental = False } else opts - contents <- blockListToHtml opts' bs - let contents' = nl opts >> contents >> nl opts - let (divtag, classes') = if html5 && "section" `elem` classes - then (H5.section, filter (/= "section") classes) - else (H.div, classes) - slideVariant <- gets stSlideVariant - return $ - if speakerNotes - then case slideVariant of - RevealJsSlides -> addAttrs opts' attr $ H5.aside $ contents' - DZSlides -> (addAttrs opts' attr $ H5.div $ contents') - ! (H5.customAttribute "role" "note") - NoSlides -> addAttrs opts' attr $ H.div $ contents' - _ -> mempty - else addAttrs opts (ident, classes', kvs) $ divtag $ contents' -blockToHtml opts (RawBlock f str) - | f == Format "html" = return $ preEscapedString str - | (f == Format "latex" || f == Format "tex") && - allowsMathEnvironments (writerHTMLMathMethod opts) && - isMathEnvironment str = blockToHtml opts $ Plain [Math DisplayMath str] - | otherwise = do - report $ BlockNotRendered (RawBlock f str) - return mempty -blockToHtml _ (HorizontalRule) = do - html5 <- gets stHtml5 - return $ if html5 then H5.hr else H.hr -blockToHtml opts (CodeBlock (id',classes,keyvals) rawCode) = do - let tolhs = isEnabled Ext_literate_haskell 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 classes - adjCode = if tolhs - then unlines . map ("> " ++) . lines $ rawCode - else rawCode - hlCode = if isJust (writerHighlightStyle opts) - then highlight formatHtmlBlock - (id',classes',keyvals) adjCode - else Nothing - case hlCode of - Nothing -> return $ addAttrs opts (id',classes,keyvals) - $ H.pre $ H.code $ toHtml adjCode - Just h -> modify (\st -> st{ stHighlighting = True }) >> - return (addAttrs opts (id',[],keyvals) h) -blockToHtml opts (BlockQuote blocks) = do - -- in S5, treat list in blockquote specially - -- if default is incremental, make it nonincremental; - -- otherwise incremental - slideVariant <- gets stSlideVariant - if slideVariant /= NoSlides - then let inc = not (writerIncremental opts) in - case blocks of - [BulletList lst] -> blockToHtml (opts {writerIncremental = inc}) - (BulletList lst) - [OrderedList attribs lst] -> - blockToHtml (opts {writerIncremental = inc}) - (OrderedList attribs lst) - [DefinitionList lst] -> - blockToHtml (opts {writerIncremental = inc}) - (DefinitionList lst) - _ -> do contents <- blockListToHtml opts blocks - return $ H.blockquote - $ nl opts >> contents >> nl opts - else do - contents <- blockListToHtml opts blocks - return $ H.blockquote $ nl opts >> contents >> nl opts -blockToHtml opts (Header level attr@(_,classes,_) lst) = do - contents <- inlineListToHtml opts lst - secnum <- liftM stSecNum get - let contents' = if writerNumberSections opts && not (null secnum) - && "unnumbered" `notElem` classes - then (H.span ! A.class_ "header-section-number" $ toHtml - $ showSecNum secnum) >> strToHtml " " >> contents - else contents - inElement <- gets stElement - return $ (if inElement then id else addAttrs opts attr) - $ case level of - 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 - unordList opts contents -blockToHtml opts (OrderedList (startnum, numstyle, _) lst) = do - contents <- mapM (blockListToHtml opts) lst - html5 <- gets stHtml5 - let numstyle' = case numstyle of - Example -> "decimal" - _ -> camelCaseToHyphenated $ show numstyle - let attribs = (if startnum /= 1 - then [A.start $ toValue startnum] - else []) ++ - (if numstyle == Example - then [A.class_ "example"] - else []) ++ - (if numstyle /= DefaultStyle - then if html5 - then [A.type_ $ - case numstyle of - Decimal -> "1" - LowerAlpha -> "a" - UpperAlpha -> "A" - LowerRoman -> "i" - UpperRoman -> "I" - _ -> "1"] - else [A.style $ toValue $ "list-style-type: " ++ - numstyle'] - else []) - l <- ordList opts contents - return $ foldl (!) l attribs -blockToHtml opts (DefinitionList lst) = do - contents <- mapM (\(term, defs) -> - do term' <- if null term - then return mempty - else liftM H.dt $ inlineListToHtml opts term - defs' <- mapM ((liftM (\x -> H.dd $ (x >> nl opts))) . - blockListToHtml opts) defs - return $ mconcat $ nl opts : term' : nl opts : - intersperse (nl opts) defs') lst - defList opts contents -blockToHtml opts (Table capt aligns widths headers rows') = do - captionDoc <- if null capt - then return mempty - else do - cs <- inlineListToHtml opts capt - return $ H.caption cs >> nl opts - html5 <- gets stHtml5 - let percent w = show (truncate (100*w) :: Integer) ++ "%" - let coltags = if all (== 0.0) widths - then mempty - else do - H.colgroup $ do - nl opts - mapM_ (\w -> do - if html5 - then H.col ! A.style (toValue $ "width: " ++ - percent w) - else H.col ! A.width (toValue $ percent w) - nl opts) widths - nl opts - head' <- if all null headers - then return mempty - else do - contents <- tableRowToHtml opts aligns 0 headers - return $ H.thead (nl opts >> contents) >> nl opts - body' <- liftM (\x -> H.tbody (nl opts >> mconcat x)) $ - zipWithM (tableRowToHtml opts aligns) [1..] rows' - let tbl = H.table $ - nl opts >> captionDoc >> coltags >> head' >> body' >> nl opts - let totalWidth = sum widths - -- When widths of columns are < 100%, we need to set width for the whole - -- table, or some browsers give us skinny columns with lots of space between: - return $ if totalWidth == 0 || totalWidth == 1 - then tbl - else tbl ! A.style (toValue $ "width:" ++ - show (round (totalWidth * 100) :: Int) ++ "%;") - -tableRowToHtml :: PandocMonad m - => WriterOptions - -> [Alignment] - -> Int - -> [[Block]] - -> StateT WriterState m Html -tableRowToHtml opts aligns rownum cols' = do - let mkcell = if rownum == 0 then H.th else H.td - let rowclass = case rownum of - 0 -> "header" - x | x `rem` 2 == 1 -> "odd" - _ -> "even" - cols'' <- sequence $ zipWith - (\alignment item -> tableItemToHtml opts mkcell alignment item) - aligns cols' - return $ (H.tr ! A.class_ rowclass $ nl opts >> mconcat cols'') - >> nl opts - -alignmentToString :: Alignment -> [Char] -alignmentToString alignment = case alignment of - AlignLeft -> "left" - AlignRight -> "right" - AlignCenter -> "center" - AlignDefault -> "" - -tableItemToHtml :: PandocMonad m - => WriterOptions - -> (Html -> Html) - -> Alignment - -> [Block] - -> StateT WriterState m Html -tableItemToHtml opts tag' align' item = do - contents <- blockListToHtml opts item - html5 <- gets stHtml5 - let alignStr = alignmentToString align' - let attribs = if html5 - then A.style (toValue $ "text-align: " ++ alignStr ++ ";") - else A.align (toValue alignStr) - let tag'' = if null alignStr - then tag' - else tag' ! attribs - return $ (tag'' $ 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 >> H.li item - -blockListToHtml :: PandocMonad m => WriterOptions -> [Block] -> StateT WriterState m Html -blockListToHtml opts lst = - fmap (mconcat . intersperse (nl opts)) $ mapM (blockToHtml opts) lst - --- | Convert list of Pandoc inline elements to HTML. -inlineListToHtml :: PandocMonad m => WriterOptions -> [Inline] -> StateT WriterState m Html -inlineListToHtml opts lst = - mapM (inlineToHtml opts) lst >>= return . mconcat - --- | Annotates a MathML expression with the tex source -annotateMML :: XML.Element -> String -> XML.Element -annotateMML e tex = math (unode "semantics" [cs, unode "annotation" (annotAttrs, tex)]) - where - cs = case elChildren e of - [] -> unode "mrow" () - [x] -> x - xs -> unode "mrow" xs - math childs = XML.Element q as [XML.Elem childs] l - where - (XML.Element q as _ l) = e - annotAttrs = [XML.Attr (unqual "encoding") "application/x-tex"] - - --- | Convert Pandoc inline element to HTML. -inlineToHtml :: PandocMonad m - => WriterOptions -> Inline -> StateT WriterState m Html -inlineToHtml opts inline = do - html5 <- gets stHtml5 - case inline of - (Str str) -> return $ strToHtml str - (Space) -> return $ strToHtml " " - (SoftBreak) -> return $ case writerWrapText opts of - WrapNone -> preEscapedString " " - WrapAuto -> preEscapedString " " - WrapPreserve -> preEscapedString "\n" - (LineBreak) -> return $ (if html5 then H5.br else H.br) - <> strToHtml "\n" - (Span (id',classes,kvs) ils) - -> inlineListToHtml opts ils >>= - return . addAttrs opts attr' . H.span - where attr' = (id',classes',kvs') - classes' = filter (`notElem` ["csl-no-emph", - "csl-no-strong", - "csl-no-smallcaps"]) classes - kvs' = if null styles - then kvs - else (("style", concat styles) : kvs) - styles = ["font-style:normal;" - | "csl-no-emph" `elem` classes] - ++ ["font-weight:normal;" - | "csl-no-strong" `elem` classes] - ++ ["font-variant:normal;" - | "csl-no-smallcaps" `elem` classes] - (Emph lst) -> inlineListToHtml opts lst >>= return . H.em - (Strong lst) -> inlineListToHtml opts lst >>= return . H.strong - (Code attr str) -> case hlCode of - Nothing -> return - $ addAttrs opts attr - $ H.code $ strToHtml str - Just h -> do - modify $ \st -> st{ stHighlighting = True } - return $ addAttrs opts (id',[],keyvals) h - where (id',_,keyvals) = attr - hlCode = if isJust (writerHighlightStyle opts) - then highlight formatHtmlInline - attr str - else Nothing - (Strikeout lst) -> inlineListToHtml opts lst >>= - return . H.del - (SmallCaps lst) -> inlineListToHtml opts lst >>= - 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 -> (strToHtml "‘", - strToHtml "’") - DoubleQuote -> (strToHtml "“", - strToHtml "”") - in if writerHtmlQTags opts - then do - modify $ \st -> st{ stQuotes = True } - H.q `fmap` inlineListToHtml opts lst - else (\x -> leftQuote >> x >> rightQuote) - `fmap` inlineListToHtml opts lst - (Math t str) -> do - modify (\st -> st {stMath = True}) - let mathClass = toValue $ ("math " :: String) ++ - if t == InlineMath then "inline" else "display" - 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 $ H.span ! A.class_ "LaTeX" $ - case t of - InlineMath -> toHtml ("$" ++ str ++ "$") - DisplayMath -> toHtml ("$$" ++ str ++ "$$") - JsMath _ -> do - let m = preEscapedString str - return $ case t of - InlineMath -> H.span ! A.class_ mathClass $ m - DisplayMath -> H.div ! A.class_ mathClass $ m - WebTeX url -> do - let imtag = if html5 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 html5 then H5.br else H.br - return $ case t of - InlineMath -> m - DisplayMath -> brtag >> m >> brtag - GladTeX -> - return $ case t of - InlineMath -> preEscapedString $ "<EQ ENV=\"math\">" ++ str ++ "</EQ>" - DisplayMath -> preEscapedString $ "<EQ ENV=\"displaymath\">" ++ str ++ "</EQ>" - MathML -> do - let conf = useShortEmptyTags (const False) - defaultConfigPP - res <- lift $ convertMath writeMathML t str - case res of - Right r -> return $ preEscapedString $ - ppcElement conf (annotateMML r str) - Left il -> (H.span ! A.class_ mathClass) <$> - inlineToHtml opts il - MathJax _ -> return $ H.span ! A.class_ mathClass $ toHtml $ - case t of - InlineMath -> "\\(" ++ str ++ "\\)" - DisplayMath -> "\\[" ++ str ++ "\\]" - KaTeX _ _ -> return $ H.span ! A.class_ mathClass $ - toHtml (case t of - InlineMath -> str - DisplayMath -> "\\displaystyle " ++ str) - PlainMath -> do - x <- lift (texMathToInlines t str) >>= inlineListToHtml opts - let m = H.span ! A.class_ mathClass $ x - let brtag = if html5 then H5.br else H.br - return $ case t of - InlineMath -> m - DisplayMath -> brtag >> m >> brtag - (RawInline f str) - | f == Format "html" -> return $ preEscapedString str - | otherwise -> do - report $ InlineNotRendered inline - return mempty - (Link attr txt (s,_)) | "mailto:" `isPrefixOf` s -> do - linkText <- inlineListToHtml opts txt - lift $ obfuscateLink opts attr linkText s - (Link attr txt (s,tit)) -> do - linkText <- inlineListToHtml opts txt - slideVariant <- gets stSlideVariant - let s' = case s of - '#':xs -> let prefix = if slideVariant == RevealJsSlides - then "/" - else writerIdentifierPrefix opts - in '#' : prefix ++ xs - _ -> s - let link = H.a ! A.href (toValue s') $ linkText - let link' = if txt == [Str (unEscapeString s)] - then link ! A.class_ "uri" - else link - let link'' = addAttrs opts attr link' - return $ if null tit - then link'' - else link'' ! A.title (toValue tit) - (Image attr txt (s,tit)) | treatAsImage s -> do - let alternate' = stringify txt - let attributes = [A.src $ toValue s] ++ - [A.title $ toValue tit | not (null tit)] ++ - [A.alt $ toValue alternate' | not (null txt)] ++ - imgAttrsToHtml opts attr - let tag = if html5 then H5.img else H.img - return $ foldl (!) tag attributes - -- note: null title included, as in Markdown.pl - (Image attr _ (s,tit)) -> do - let attributes = [A.src $ toValue s] ++ - [A.title $ toValue tit | not (null tit)] ++ - imgAttrsToHtml opts attr - return $ foldl (!) H5.embed attributes - -- note: null title included, as in Markdown.pl - (Note contents) -> do - notes <- gets stNotes - let number = (length notes) + 1 - let ref = show number - htmlContents <- blockListToNote opts ref contents - epubVersion <- gets stEPUBVersion - -- push contents onto front of notes - modify $ \st -> st {stNotes = (htmlContents:notes)} - slideVariant <- gets stSlideVariant - let revealSlash = ['/' | slideVariant == RevealJsSlides] - let link = H.a ! A.href (toValue $ "#" ++ - revealSlash ++ - writerIdentifierPrefix opts ++ "fn" ++ ref) - ! A.class_ "footnoteRef" - ! prefixedId opts ("fnref" ++ ref) - $ (if isJust epubVersion - then id - else H.sup) - $ toHtml ref - return $ case epubVersion of - Just EPUB3 -> link ! customAttribute "epub:type" "noteref" - _ -> link - (Cite cits il)-> do contents <- inlineListToHtml opts il - let citationIds = unwords $ map citationId cits - let result = H.span ! A.class_ "citation" $ contents - return $ if html5 - then result ! customAttribute "data-cites" (toValue citationIds) - else result - -blockListToNote :: PandocMonad m => WriterOptions -> String -> [Block] -> StateT WriterState m 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 = [Link nullAttr [Str "↩"] ("#" ++ writerIdentifierPrefix opts ++ "fnref" ++ ref,[])] - blocks' = if null blocks - then [] - else let lastBlock = last blocks - otherBlocks = init blocks - in case lastBlock of - (Para lst) -> otherBlocks ++ - [Para (lst ++ backlink)] - (Plain lst) -> otherBlocks ++ - [Plain (lst ++ backlink)] - _ -> otherBlocks ++ [lastBlock, - Plain backlink] - in do contents <- blockListToHtml opts blocks' - let noteItem = H.li ! (prefixedId opts ("fn" ++ ref)) $ contents - epubVersion <- gets stEPUBVersion - let noteItem' = case epubVersion of - Just EPUB3 -> noteItem ! customAttribute "epub:type" "footnote" - _ -> noteItem - return $ nl opts >> noteItem' - --- Javascript snippet to render all KaTeX elements -renderKaTeX :: String -renderKaTeX = unlines [ - "window.onload = function(){var mathElements = document.getElementsByClassName(\"math\");" - , "for (var i=0; i < mathElements.length; i++)" - , "{" - , " var texText = mathElements[i].firstChild" - , " katex.render(texText.data, mathElements[i])" - , "}}" - ] - -isMathEnvironment :: String -> Bool -isMathEnvironment s = "\\begin{" `isPrefixOf` s && - envName `elem` mathmlenvs - where envName = takeWhile (/= '}') (drop 7 s) - mathmlenvs = [ "align" - , "align*" - , "alignat" - , "alignat*" - , "aligned" - , "alignedat" - , "array" - , "Bmatrix" - , "bmatrix" - , "cases" - , "CD" - , "eqnarray" - , "eqnarray*" - , "equation" - , "equation*" - , "gather" - , "gather*" - , "gathered" - , "matrix" - , "multline" - , "multline*" - , "pmatrix" - , "smallmatrix" - , "split" - , "subarray" - , "Vmatrix" - , "vmatrix" ] - -allowsMathEnvironments :: HTMLMathMethod -> Bool -allowsMathEnvironments (MathJax _) = True -allowsMathEnvironments (MathML) = True -allowsMathEnvironments (WebTeX _) = True -allowsMathEnvironments _ = False |