aboutsummaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Writers/HTML.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Text/Pandoc/Writers/HTML.hs')
-rw-r--r--src/Text/Pandoc/Writers/HTML.hs233
1 files changed, 100 insertions, 133 deletions
diff --git a/src/Text/Pandoc/Writers/HTML.hs b/src/Text/Pandoc/Writers/HTML.hs
index 38b0e1974..52825fb09 100644
--- a/src/Text/Pandoc/Writers/HTML.hs
+++ b/src/Text/Pandoc/Writers/HTML.hs
@@ -32,9 +32,10 @@ import Prelude
import Control.Monad.State.Strict
import Data.Char (ord, toLower)
import Data.List (intercalate, intersperse, isPrefixOf, partition)
-import Data.Maybe (catMaybes, fromMaybe, isJust, isNothing, mapMaybe)
+import Data.Maybe (fromMaybe, isJust, isNothing, mapMaybe)
import qualified Data.Set as Set
import Data.String (fromString)
+import Data.List.Split (splitWhen)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Lazy as TL
@@ -90,20 +91,20 @@ data WriterState = WriterState
, 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
+ , stSlideLevel :: Int -- ^ Slide level
, stCodeBlockNum :: Int -- ^ Number of code block
}
defaultWriterState :: WriterState
defaultWriterState = WriterState {stNotes= [], stMath = False, stQuotes = False,
- stHighlighting = False, stSecNum = [],
- stElement = False, stHtml5 = False,
+ stHighlighting = False,
+ stHtml5 = False,
stEPUBVersion = Nothing,
stSlideVariant = NoSlides,
+ stSlideLevel = 1,
stCodeBlockNum = 0}
-- Helpers to render HTML with the appropriate function.
@@ -243,6 +244,8 @@ pandocToHtml :: PandocMonad m
-> Pandoc
-> StateT WriterState m (Html, Context Text)
pandocToHtml opts (Pandoc meta blocks) = do
+ let slideLevel = fromMaybe (getSlideLevel blocks) $ writerSlideLevel opts
+ modify $ \st -> st{ stSlideLevel = slideLevel }
metadata <- metaToContext opts
(fmap renderHtml' . blockListToHtml opts)
(fmap renderHtml' . inlineListToHtml opts)
@@ -250,17 +253,15 @@ pandocToHtml opts (Pandoc meta blocks) = do
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 $
+ let sects = makeSections (writerNumberSections opts) Nothing $
if slideVariant == NoSlides
then blocks
else prepSlides slideLevel blocks
toc <- if writerTableOfContents opts && slideVariant /= S5Slides
then fmap renderHtml' <$> tableOfContents opts sects
else return Nothing
- blocks' <- liftM (mconcat . intersperse (nl opts)) $
- mapM (elementToHtml Nothing slideLevel opts) sects
+ blocks' <- blockListToHtml opts sects
st <- get
notes <- footnoteSection opts (reverse (stNotes st))
let thebody = blocks' >> notes
@@ -380,130 +381,20 @@ listItemToHtml opts bls
return $ constr (checkbox >> isContents) >> bsContents
-- | Construct table of contents from list of elements.
-tableOfContents :: PandocMonad m => WriterOptions -> [Element]
+tableOfContents :: PandocMonad m => WriterOptions -> [Block]
-> 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 = intercalate "." . 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 (deLink . 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
-
-deLink :: Inline -> Inline
-deLink (Link _ ils _) = Span nullAttr ils
-deLink x = x
-
--- | Convert an Element to Html.
-elementToHtml :: PandocMonad m => Maybe Int -> Int -> WriterOptions -> Element
- -> StateT WriterState m Html
-elementToHtml _ _ opts (Blk block) = blockToHtml opts block
-elementToHtml mbparentlevel slideLevel opts
- (Sec level num (id',classes,keyvals) title' elements)
- = do
- slideVariant <- gets stSlideVariant
- let slide = slideVariant /= NoSlides &&
- (level <= slideLevel ||
- -- we're missing a header at slide level (see #5168)
- maybe False (< slideLevel) mbparentlevel)
- 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})
- let level' = if level <= slideLevel &&
- slideVariant == SlidySlides
- then 1 -- see #3566
- else level
- 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>")])
- let (titleBlocks, innerSecs) =
- if titleSlide
- -- title slides have no content of their own
- then ([x | Blk x <- elements],
- filter isSec elements)
- else case splitBy isPause elements of
- [] -> ([],[])
- (x:xs) -> ([],x ++ concatMap inDiv xs)
- titleContents <- blockListToHtml opts titleBlocks
- innerContents <- mapM (elementToHtml (Just level) slideLevel opts) innerSecs
- let inNl x = mconcat $ nl opts : intersperse (nl opts) x ++ [nl opts]
- let classes' = ["title-slide" | 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)
- if titleSlide
- then do
- t <- addAttrs opts attr $ secttag $ header' <> titleContents
- return $
- (if slideVariant == RevealJsSlides && not (null innerContents)
- -- revealjs doesn't like more than one level of section nesting:
- && isNothing mbparentlevel
- then H5.section
- else id) $ mconcat $ t : innerContents
- else if writerSectionDivs opts || slide
- then addAttrs opts attr
- $ secttag $ inNl $ header' : innerContents
- else do
- t <- addAttrs opts attr header'
- return $ mconcat $ intersperse (nl opts) (t : innerContents)
+ let opts' = case slideVariant of
+ RevealJsSlides ->
+ opts{ writerIdentifierPrefix =
+ '/' : writerIdentifierPrefix opts }
+ _ -> opts
+ case toTableOfContents opts sects of
+ bl@(BulletList (_:_)) -> Just <$> blockToHtml opts' bl
+ _ -> return Nothing
-- | Convert list of Note blocks to a footnote <div>.
-- Assumes notes are sorted.
@@ -686,6 +577,16 @@ figure opts attr txt (s,tit) = do
else H.div ! A.class_ "figure" $ mconcat
[nl opts, img, nl opts, capt, nl opts]
+showSecNum :: [Int] -> String
+showSecNum = intercalate "." . map show
+
+getNumber :: WriterOptions -> Attr -> String
+getNumber opts (_,_,kvs) =
+ showSecNum $ zipWith (+) num (writerNumberOffset opts ++ repeat 0)
+ where
+ num = maybe [] (map (fromMaybe 0 . safeRead) . splitWhen (=='.')) $
+ lookup "number" kvs
+
-- | Convert Pandoc block element to HTML.
blockToHtml :: PandocMonad m => WriterOptions -> Block -> StateT WriterState m Html
blockToHtml _ Null = return mempty
@@ -713,6 +614,73 @@ blockToHtml opts (LineBlock lns) =
else do
htmlLines <- inlineListToHtml opts $ intercalate [LineBreak] lns
return $ H.div ! A.class_ "line-block" $ htmlLines
+blockToHtml opts (Div (ident, "section":dclasses, dkvs)
+ (Header level hattr ils : xs)) = do
+ slideVariant <- gets stSlideVariant
+ slideLevel <- gets stSlideLevel
+ let slide = slideVariant /= NoSlides &&
+ level <= slideLevel {- DROPPED old fix for #5168 here -}
+ html5 <- gets stHtml5
+ let titleSlide = slide && level < slideLevel
+ let level' = if level <= slideLevel && slideVariant == SlidySlides
+ then 1 -- see #3566
+ else level
+ header' <- if ils == [Str "\0"] -- marker for hrule
+ then return mempty
+ else blockToHtml opts (Header level' hattr ils)
+ let isSec (Div (_,"section":_,_) _) = True
+ isSec (Div _ zs) = any isSec zs
+ isSec _ = False
+ let isPause (Para [Str ".",Space,Str ".",Space,Str "."]) = True
+ isPause _ = False
+ let fragmentClass = case slideVariant of
+ RevealJsSlides -> "fragment"
+ _ -> "incremental"
+ let inDiv zs = (RawBlock (Format "html") ("<div class=\""
+ ++ fragmentClass ++ "\">")) :
+ (zs ++ [RawBlock (Format "html") "</div>"])
+ let (titleBlocks, innerSecs) =
+ if titleSlide
+ -- title slides have no content of their own
+ then break isSec xs
+ else case splitBy isPause xs of
+ [] -> ([],[])
+ (z:zs) -> ([],z ++ concatMap inDiv zs)
+ titleContents <- blockListToHtml opts titleBlocks
+ innerContents <- blockListToHtml opts innerSecs
+ let classes' = ["title-slide" | titleSlide] ++ ["slide" | slide] ++
+ ["section" | (slide || writerSectionDivs opts) &&
+ not html5 ] ++
+ ["level" ++ show level | slide || writerSectionDivs opts ]
+ ++ dclasses
+ let secttag = if html5
+ then H5.section
+ else H.div
+ let attr = (ident, classes', dkvs)
+ if titleSlide
+ then do
+ t <- addAttrs opts attr $ secttag $ header' <> titleContents
+ return $
+ (if slideVariant == RevealJsSlides && not (null innerSecs)
+ -- revealjs doesn't like more than one level of section nesting:
+ {- REMOVED && isNothing mbparentlevel -}
+ then H5.section
+ else id) $ t <> if null innerSecs
+ then mempty
+ else nl opts <> innerContents
+ else if writerSectionDivs opts || slide || not (null dclasses) ||
+ not (null dkvs)
+ then addAttrs opts attr
+ $ secttag
+ $ nl opts <> header' <> nl opts <>
+ if null innerSecs
+ then mempty
+ else innerContents <> nl opts
+ else do
+ t <- addAttrs opts attr header'
+ return $ t <> if null innerSecs
+ then mempty
+ else nl opts <> innerContents
blockToHtml opts (Div attr@(ident, classes, kvs') bs) = do
html5 <- gets stHtml5
slideVariant <- gets stSlideVariant
@@ -826,14 +794,13 @@ blockToHtml opts (BlockQuote blocks) = do
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 secnum = getNumber opts attr
let contents' = if writerNumberSections opts && not (null secnum)
&& "unnumbered" `notElem` classes
- then (H.span ! A.class_ "header-section-number" $ toHtml
- $ showSecNum secnum) >> strToHtml " " >> contents
+ then (H.span ! A.class_ "header-section-number"
+ $ toHtml secnum) >> strToHtml " " >> contents
else contents
- inElement <- gets stElement
- (if inElement then return else addAttrs opts attr)
+ addAttrs opts attr
$ case level of
1 -> H.h1 contents'
2 -> H.h2 contents'