1 {-# LANGUAGE DataKinds #-}
2 {-# LANGUAGE DuplicateRecordFields #-}
3 {-# LANGUAGE ExistentialQuantification #-}
4 {-# LANGUAGE FlexibleInstances #-}
5 {-# LANGUAGE FlexibleContexts #-}
6 {-# LANGUAGE MultiParamTypeClasses #-}
7 {-# LANGUAGE OverloadedStrings #-}
8 {-# LANGUAGE ScopedTypeVariables #-}
9 {-# OPTIONS_GHC -fno-warn-orphans #-}
10 module Hdoc.DTC.Write.HTML5
11 ( module Hdoc.DTC.Write.HTML5
12 , module Hdoc.DTC.Write.HTML5.Ident
13 , module Hdoc.DTC.Write.HTML5.Base
14 , module Hdoc.DTC.Write.HTML5.Judgment
15 -- , module Hdoc.DTC.Write.HTML5.Error
18 import Control.Applicative (Applicative(..))
19 import Control.Monad (Monad(..), (=<<), forM_, mapM_, sequence_)
21 import Data.Default.Class (Default(..))
22 import Data.Either (Either(..))
23 import Data.Eq (Eq(..))
24 import Data.Foldable (Foldable(..), concat, fold)
25 import Data.Function (($), (.), const, on)
26 import Data.Functor ((<$>), (<$))
27 import Data.Functor.Compose (Compose(..))
28 import Data.List.NonEmpty (NonEmpty(..))
29 import Data.Locale hiding (Index)
30 import Data.Maybe (Maybe(..), maybe, mapMaybe, isNothing, fromMaybe)
31 import Data.Monoid (Monoid(..))
32 import Data.Ord (Ord(..))
34 import Data.Sequence (Seq)
35 import Data.Semigroup (Semigroup(..))
36 import Data.String (String)
37 import Data.TreeSeq.Strict (Tree(..), tree0)
38 import Data.Tuple (snd)
39 import System.FilePath ((</>))
41 import Text.Blaze ((!))
42 import Text.Blaze.Html (Html)
43 import Text.Show (Show(..))
44 import qualified Control.Monad.Trans.RWS.Strict as RWS
45 import qualified Control.Monad.Trans.Reader as R
46 import qualified Data.HashMap.Strict as HM
47 import qualified Data.HashSet as HS
48 import qualified Data.List as List
49 import qualified Data.Map.Strict as Map
50 import qualified Data.Sequence as Seq
51 import qualified Data.Strict.Maybe as Strict
52 import qualified Data.Text as Text
53 import qualified Data.Text.Lazy as TL
54 import qualified Data.TreeMap.Strict as TM
56 import qualified Text.Blaze.Html5 as H
57 import qualified Text.Blaze.Html5.Attributes as HA
58 import qualified Text.Blaze.Internal as H
60 import Control.Monad.Utils
61 import Hdoc.DTC.Document as DTC
62 import Hdoc.DTC.Write.HTML5.Base
63 import Hdoc.DTC.Write.HTML5.Error ()
64 import Hdoc.DTC.Write.HTML5.Ident
65 import Hdoc.DTC.Write.HTML5.Judgment
66 import Hdoc.DTC.Write.Plain (Plainify(..))
67 import Hdoc.DTC.Write.XML ()
69 import Text.Blaze.Utils
70 import Text.Blaze.XML ()
71 import qualified Hdoc.DTC.Analyze.Check as Analyze
72 import qualified Hdoc.DTC.Analyze.Collect as Analyze
73 import qualified Hdoc.DTC.Analyze.Index as Analyze
74 import qualified Hdoc.DTC.Write.Plain as Plain
75 import qualified Hdoc.TCT.Cell as TCT
76 import qualified Hdoc.Utils as FS
77 import qualified Hdoc.XML as XML
78 import qualified Paths_hdoc as Hdoc
81 debug :: Show a => String -> a -> a
82 debug msg a = trace (msg<>": "<>show a) a
83 debugOn :: Show b => String -> (a -> b) -> a -> a
84 debugOn msg get a = trace (msg<>": "<>show (get a)) a
85 debugWith :: String -> (a -> String) -> a -> a
86 debugWith msg get a = trace (msg<>": "<>get a) a
88 writeHTML5 :: Config -> DTC.Document -> IO Html
89 writeHTML5 conf@Config{..} doc_init = do
90 let all_index = Analyze.collectIndex doc_init
91 let (doc@DTC.Document{..}, all_irefs) =
92 Analyze.indexifyDocument (fold all_index) doc_init
93 let all = Analyze.collect doc `R.runReader` def
94 let err = Analyze.errors all
96 { reader_l10n = loqualize config_locale
97 , reader_plainify = def{Plain.reader_l10n = loqualize config_locale}
99 -- , reader_body = body
102 { state_errors = debug "errors" $ Nat1 1 <$ err
103 , state_notes = fold $ toList <$> Analyze.all_notes all
105 (<$> toList all_index) $ \terms ->
107 TM.intersection const all_irefs $
108 Analyze.indexOfTerms terms
110 let (html5Body, _endState, endWriter) =
111 runComposeRWS ro st $ do
115 html5Head <- writeHTML5Head conf ro endWriter doc
118 H.html ! HA.lang (attrify $ countryCode config_locale) $ do
122 unless (null state_scripts) $ do
123 -- NOTE: indicate that JavaScript is active.
124 H.script ! HA.type_ "application/javascript" $
125 "document.body.className = \"script\";"
129 writeHTML5Head :: Config -> Reader -> Writer -> Document -> IO Html
130 writeHTML5Head Config{..} Reader{..} Writer{..} Document{..} = do
132 -- unless (any (\DTC.Link{..} -> link_rel == "stylesheet" && link_url /= URL "") links) $ do
133 (`foldMap` writer_styles) $ \case
135 content <- FS.readFile =<< Hdoc.getDataFileName ("style"</>css)
136 return $ H.style ! HA.type_ "text/css" $
138 Right content -> return $ do
139 H.style ! HA.type_ "text/css" $
140 -- NOTE: as a special case, H.style wraps its content into an External,
141 -- so it does not HTML-escape its content.
144 (`foldMap` writer_scripts) $ \script -> do
145 content <- FS.readFile =<< Hdoc.getDataFileName ("style"</>script)
146 return $ H.script ! HA.type_ "application/javascript" $
149 if not (any (\DTC.Link{link_rel} -> link_rel == "script") links)
155 Left js -> H.script ! HA.src (attrify js)
156 ! HA.type_ "application/javascript"
158 Right js -> H.script ! HA.type_ "application/javascript"
163 H.meta ! HA.httpEquiv "Content-Type"
164 ! HA.content "text/html; charset=UTF-8"
165 unless (TL.null config_generator) $ do
166 H.meta ! HA.name "generator"
167 ! HA.content (attrify config_generator)
168 case document_head of
170 Just Head{head_section=Section{section_about=About{..}}, ..} -> do
172 title:_ -> H.title $ H.toMarkup $ Plain.text reader_plainify title
174 forM_ about_links $ \Link{..} ->
176 "stylesheet" | URL "" <- link_url ->
177 H.style ! HA.type_ "text/css" $
178 H.toMarkup $ Plain.text def link_plain
180 H.link ! HA.rel (attrify link_rel)
181 ! HA.href (attrify link_url)
182 unless (null about_tags) $
183 H.meta ! HA.name "keywords"
184 ! HA.content (attrify $ TL.intercalate ", " about_tags)
186 (`mapMaybe` toList document_body) $ \case
187 Tree (BodySection s) _ -> Just s
189 forM_ chapters $ \Section{..} ->
190 H.link ! HA.rel "Chapter"
191 ! HA.title (attrify $ plainify $ Safe.headDef def about_titles)
192 ! HA.href (refIdent $ identify section_posXML)
196 H.link ! HA.rel "stylesheet"
197 ! HA.type_ "text/css"
198 ! HA.href (attrify css)
200 H.style ! HA.type_ "text/css" $
205 instance Html5ify Document where
206 html5ify Document{document_head=Nothing, ..} =
207 html5ify document_body
208 html5ify Document{document_head=Just Head{..}, ..} = do
209 localComposeRWS (\ro -> ro{reader_section = [head_section], reader_body = body}) $ do
210 ro <- composeLift RWS.ask
211 unless (null about_authors) $ do
212 H.div ! HA.class_ "document-head" $$
216 H.td ! HA.class_ "left" $$ html5Headers
217 H.td ! HA.class_ "right" $$ html5Roles
218 unless (null about_titles) $ do
219 H.div ! HA.class_ "title"
220 ! HA.id "document-title." $$ do
221 forM_ about_titles $ \title ->
222 H.h1 ! HA.id (attrify $ identifyTitle (Plain.reader_l10n $ reader_plainify ro) title) $$
224 html5SectionJudgments
227 body = head_body <> document_body
228 Section{section_about=About{..}, ..} = head_section
230 H.table ! HA.class_ "document-headers" $$
232 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
233 forM_ about_series $ \s@Serie{..} ->
237 headerName $ html5ify serie_name
238 headerValue $ html5ify serie_id
240 headerName $ html5ify serie_name
242 H.a ! HA.href (attrify href) $$
244 forM_ about_links $ \Link{..} ->
245 unless (TL.null $ unName link_role) $
247 headerName $ html5ify link_role
248 headerValue $ html5ify $ Tree PlainEref{eref_href=link_url} link_plain
249 forM_ about_dates $ \d@Date{..} ->
252 if TL.null $ unName date_role
253 then l10n_Header_Date l10n
254 else html5ify date_role
255 headerValue $ html5ify d
257 forM_ about_headers $ \Header{..} ->
259 headerName $ html5ify header_name
260 headerValue $ html5ify header_value
263 H.table ! HA.class_ "document-authors" $$
265 forM_ about_authors $ \a ->
267 H.td ! HA.class_ "author" $$
269 header :: HTML5 -> HTML5
270 header hdr = H.tr ! HA.class_ "header" $$ hdr
271 headerName :: HTML5 -> HTML5
273 H.td ! HA.class_ "header-name" $$ do
275 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
276 Plain.l10n_Colon l10n
277 headerValue :: HTML5 -> HTML5
279 H.td ! HA.class_ "header-value" $$ do
281 instance Html5ify Body where
283 localComposeRWS (\ro -> ro{reader_body = body}) $ go body
288 popNotes >>= html5Notes
289 curr Seq.:< next -> do
291 Tree BodySection{} _ -> popNotes >>= html5Notes
295 instance Html5ify (Tree BodyNode) where
296 html5ify (Tree b bs) = do
298 BodyBlock blk -> html5ify blk
299 BodySection section@Section{section_about=About{..}, ..} -> do
300 localComposeRWS (\ro -> ro
301 { reader_section = section : reader_section ro
304 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
306 html5CommonAttrs section_attrs
307 { attrs_classes = "section":attrs_classes section_attrs
310 H.section ! HA.id (attrify $ identify section_posXML) $$ do
311 forM_ about_aliases html5ify
312 html5SectionJudgments
314 case attrs_id section_attrs of
315 Just ident | Just [_] <- toList <$> HM.lookup ident all_section ->
316 Just $ identifyTag "" ident Nothing
319 ! HA.class_ "section-header"
320 !?? mayAttr HA.id mayId $$
325 H.td ! HA.class_ "section-number" $$ do
326 html5SectionAnchor section
328 let hN = case List.length $ XML.pos_ancestors section_posXML of
337 H.td ! HA.class_ "section-number" $$ do
338 html5SectionAnchor section
339 H.td ! HA.class_ "section-title" $$ do
345 H.td ! HA.class_ "section-title" $$ do
352 notes <- composeLift $ S.gets state_notes
353 maybe mempty html5Notes $
354 Map.lookup (XML.pos_ancestors section_posXML) notes
356 instance Html5ify Block where
358 BlockPara para -> html5ify para
360 html5CommonAttrs attrs
361 { attrs_classes = "page-break":"print-only":attrs_classes attrs } $
363 H.p $$ " " -- NOTE: force page break
365 H.nav ! HA.class_ "toc"
366 ! HA.id (attrify $ identify posXML) $$ do
367 H.span ! HA.class_ "toc-name" $$
368 H.a ! HA.href (refIdent $ identify posXML) $$ do
369 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
370 Plain.l10n_Table_of_Contents l10n
372 Reader{reader_body} <- composeLift RWS.ask
373 forM_ reader_body $ html5ifyToC depth
375 H.nav ! HA.class_ "tof"
376 ! HA.id (attrify $ identify posXML) $$
377 H.table ! HA.class_ "tof" $$
381 html5CommonAttrs attrs $
382 H.aside ! HA.class_ "aside" $$ do
383 forM_ blocks html5ify
385 html5CommonAttrs attrs
386 { attrs_classes = "figure":("figure-"<>type_):attrs_classes attrs
387 , attrs_id = Just $ identify $ XML.pos_ancestorsWithFigureNames posXML
390 H.table ! HA.class_ "figure-caption" $$
394 then H.a ! HA.href (refIdent $ identify posXML) $$ mempty
396 H.td ! HA.class_ "figure-number" $$ do
397 H.a ! HA.href (refIdent $ identify $ XML.pos_ancestorsWithFigureNames posXML) $$ do
399 html5ify $ XML.pos_ancestorsWithFigureNames posXML
400 forM_ mayTitle $ \title -> do
401 H.td ! HA.class_ "figure-colon" $$ do
402 unless (TL.null type_) $ do
403 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
404 Plain.l10n_Colon l10n
405 H.td ! HA.class_ "figure-title" $$ do
407 H.div ! HA.class_ "figure-content" $$ do
409 BlockIndex{posXML} -> do
410 State{..} <- composeLift RWS.get
413 { writer_styles = HS.singleton $ Left "dtc-index.css" }
414 RWS.modify $ \s -> s{state_indices=List.tail state_indices}
415 let (allTerms,refsByTerm) = List.head state_indices
416 let chars = Analyze.termsByChar allTerms
417 H.div ! HA.class_ "index"
418 ! HA.id (attrify $ identify posXML) $$ do
419 H.nav ! HA.class_ "index-nav" $$ do
420 forM_ (Map.keys chars) $ \char ->
421 H.a ! HA.href (refIdent (identify posXML <> "." <> identify char)) $$
423 H.dl ! HA.class_ "index-chars" $$
424 forM_ (Map.toList chars) $ \(char,terms) -> do
426 let i = identify posXML <> "." <> identify char
427 H.a ! HA.id (attrify i)
428 ! HA.href (refIdent i) $$
431 H.dl ! HA.class_ "index-term" $$ do
432 forM_ terms $ \aliases -> do
434 H.ul ! HA.class_ "index-aliases" $$
435 forM_ (List.take 1 aliases) $ \term -> do
436 H.li ! HA.id (attrify $ identifyIref term Nothing) $$
440 List.sortBy (compare `on` snd) $
441 (`foldMap` aliases) $ \term ->
443 path <- DTC.pathFromWords term
444 refs <- Strict.maybe Nothing Just $ TM.lookup path refsByTerm
446 Seq.foldrWithIndex (\num ref acc -> ((term, succ num), ref):acc) [] $
449 (<$> sortedRefs) $ \((term, num), section) ->
450 H.a ! HA.class_ "index-iref"
451 ! HA.href (refIdent $ identifyIref term $ Just $ Nat1 num) $$
452 html5ify $ XML.pos_ancestors $ section_posXML section
453 BlockReferences{..} ->
454 html5CommonAttrs attrs
455 { attrs_classes = "references":attrs_classes attrs
456 , attrs_id = Just $ identify $ XML.pos_ancestors posXML
462 html5CommonAttrs attrs
463 { attrs_classes = "grades":attrs_classes attrs
464 , attrs_id = Just $ identify $ XML.pos_ancestors posXML
467 -- let dg = List.head $ List.filter default_ scale
468 -- let sc = MJ.Scale (Set.fromList scale) dg
469 -- o :: Map choice grade
470 -- os :: Opinions (Map judge (Opinion choice grade))
473 BlockJudges js -> html5ify js
474 instance Html5ify Para where
478 { attrs_classes = "para":cls item
482 html5CommonAttrs attrs
483 { attrs_classes = "para":attrs_classes attrs
484 , attrs_id = id_ posXML
487 forM_ items $ \item ->
488 html5AttrClass (cls item) $
491 id_ = Just . identify . XML.pos_ancestors
494 ParaArtwork{..} -> ["artwork", "artwork-"<>type_]
495 ParaQuote{..} -> ["quote", "quote-"<>type_]
499 ParaJudgment Judgment{..} -> ["judgment"] <> when (null judgment_opinionsByChoice) ["judgment-error"]
500 instance Html5ify ParaItem where
502 ParaPlain p -> H.p $$ html5ify p
503 ParaArtwork{..} -> H.pre $$ do html5ify text
504 ParaQuote{..} -> H.div $$ do html5ify paras
505 ParaComment t -> html5ify $ H.Comment (H.String $ TL.unpack t) ()
508 forM_ items $ \ListItem{..} -> do
509 H.dt ! HA.class_ "name" $$ do
512 H.dd ! HA.class_ "value" $$
516 forM_ items $ \item -> do
518 H.dd $$ html5ify item
519 ParaJudgment j -> html5ify j
520 instance Html5ify [Para] where
521 html5ify = mapM_ html5ify
522 instance Html5ify Plain where
528 -- NOTE: gather adjacent PlainNotes
530 | (notes, rest) <- Seq.spanl (\case {Tree PlainNote{} _ -> True; _ -> False}) next -> do
531 H.sup ! HA.class_ "note-numbers" $$ do
533 forM_ notes $ \note -> do
542 instance Html5ify (Tree PlainNode)
543 where html5ify (Tree n ps) =
545 PlainBreak -> html5ify H.br
546 PlainText t -> html5ify t
547 PlainGroup -> html5ify ps
548 PlainB -> H.strong $$ html5ify ps
549 PlainCode -> H.code $$ html5ify ps
550 PlainDel -> H.del $$ html5ify ps
552 i <- composeLift $ RWS.asks reader_italic
553 H.em ! HA.class_ (if i then "even" else "odd") $$
554 localComposeRWS (\ro -> ro{reader_italic=not i}) $
557 html5CommonAttrs attrs $
558 H.span $$ html5ify ps
559 PlainSub -> H.sub $$ html5ify ps
560 PlainSup -> H.sup $$ html5ify ps
561 PlainSC -> H.span ! HA.class_ "smallcaps" $$ html5ify ps
562 PlainU -> H.span ! HA.class_ "underline" $$ html5ify ps
564 num <- composeLift $ do
565 num <- RWS.gets state_note_num_ref
566 RWS.modify $ \s -> s{state_note_num_ref=succNat1 num}
568 H.a ! HA.class_ "note-ref"
569 ! HA.id ("note-ref."<>attrify num)
570 ! HA.href ("#note."<>attrify num) $$
573 H.span ! HA.class_ "q" $$ do
574 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
575 Plain.l10n_Quote (html5ify $ Tree PlainI ps) l10n
578 H.a ! HA.class_ "eref no-print"
579 ! HA.href (attrify eref_href) $$
581 then html5ify $ unURL eref_href
583 H.span ! HA.class_ "eref print-only" $$ do
584 unless (null ps) $ do
594 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
595 State{state_errors=Analyze.Errors{..}} <- composeLift RWS.get
596 case HM.lookup tag_ident all_tag of
599 H.span ! HA.class_ "tag-backs" $$
601 (<$> List.zip (toList anchs) [1..]) $ \((_loc, maySection),idNum) ->
602 H.a ! HA.class_ "tag-back"
603 ! HA.href (refIdent $ identifyTag "-back" tag_ident $ Just $ Nat1 idNum) $$
604 html5SectionNumber maySection
607 State{state_tag} <- composeLift RWS.get
608 let idNum = HM.lookupDefault (Nat1 1) tag_ident state_tag
609 composeLift $ RWS.modify $ \s -> s
610 { state_tag = HM.insert tag_ident (succNat1 idNum) state_tag }
611 H.span ! HA.class_ "tag"
612 ! HA.id (attrify $ identifyTag "-back" tag_ident $ Just idNum) $$
618 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
619 State{state_errors=Analyze.Errors{..}} <- composeLift RWS.get
620 case HM.lookup at_ident all_at of
623 H.span ! HA.class_ "at-backs" $$
625 (<$> List.zip (toList anchs) [1..]) $ \((_loc, maySection),idNum) ->
626 H.a ! HA.class_ "at-back"
627 ! HA.href (refIdent $ identifyAt "-back" at_ident $ Just $ Nat1 idNum) $$
628 html5SectionNumber maySection
631 Reader{..} <- composeLift RWS.ask
632 State{state_errors=errs@Analyze.Errors{..}, ..} <- composeLift RWS.get
633 let idNum = HM.lookupDefault (Nat1 1) at_ident state_at
634 composeLift $ RWS.modify $ \s -> s
635 { state_at = HM.insert at_ident (succNat1 idNum) state_at }
638 _ | Just errNum <- HM.lookup at_ident errors_at_unknown -> do
639 composeLift $ RWS.modify $ \s -> s
640 { state_errors = errs
641 { Analyze.errors_at_unknown =
642 HM.adjust succNat1 at_ident errors_at_unknown } }
644 ! HA.class_ "at at-unknown"
645 ! HA.id (attrify $ identifyAt "-unknown" at_ident (Just errNum)) $$
647 ! HA.class_ "at at-unknown"
648 ! HA.id (attrify $ identifyAt "-back" at_ident $ Just idNum) $$
651 | Just errNum <- HM.lookup at_ident errors_at_ambiguous -> do
652 composeLift $ RWS.modify $ \s -> s
653 { state_errors = errs
654 { Analyze.errors_at_ambiguous =
655 HM.adjust succNat1 at_ident errors_at_ambiguous } }
657 ! HA.class_ "at at-ambiguous"
658 ! HA.id (attrify $ identifyAt "-ambiguous" at_ident (Just errNum)) $$
660 ! HA.class_ "at at-ambiguous"
661 ! HA.id (attrify $ identifyAt "-back" at_ident $ Just idNum) $$
667 ! HA.href (refIdent $ identifyAt "" at_ident Nothing)
668 ! HA.id (attrify $ identifyAt "-back" at_ident $ Just idNum) $$
672 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
673 State{state_errors=errs@Analyze.Errors{..}, ..} <- composeLift RWS.get
674 let idNum = HM.lookupDefault (Nat1 1) ref_ident state_ref
675 composeLift $ RWS.modify $ \s -> s
676 { state_ref = HM.insert ref_ident (succNat1 idNum) state_ref }
677 case toList $ HM.lookupDefault def ref_ident all_reference of
680 let errNum = HM.lookup ref_ident errors_ref_unknown
681 composeLift $ RWS.modify $ \s -> s
682 { state_errors = errs
683 { Analyze.errors_ref_unknown =
684 HM.adjust succNat1 ref_ident errors_ref_unknown } }
686 ! HA.class_ "reference reference-unknown"
687 ! HA.id (attrify $ identifyReference "-unknown" ref_ident errNum) $$ do
692 [Reference{..}] -> do
693 let a = H.a ! HA.href (refIdent $ identifyReference "" ref_ident Nothing)
696 ! HA.class_ "reference"
697 ! HA.id (attrify $ identifyReference "" ref_ident $ Just idNum) $$ do
699 a $$ html5ify ref_ident
703 [Tree (PlainText "") _] -> do
704 refs <- composeLift $ RWS.asks $ Analyze.all_reference . reader_all
705 case toList <$> HM.lookup ref_ident refs of
706 Just [Reference{reference_about=About{..}}] -> do
707 forM_ (List.take 1 about_titles) $ \(Title title) -> do
708 html5ify $ Tree PlainQ $
709 case List.filter ((\rel -> rel == "" || rel == "self") . link_rel) about_links of
711 Link{..}:_ -> pure $ Tree (PlainEref link_url) title
717 H.span ! HA.class_ "print-only" $$ do
724 [Tree (PlainText "") _] -> mempty
728 H.span ! HA.class_ "reference reference-ambiguous" $$ do
734 case pathFromWords iref_term of
735 Nothing -> html5ify ps
737 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
738 State{state_irefs} <- composeLift RWS.get
739 let num = Strict.fromMaybe (Nat1 1) $ TM.lookup path state_irefs
740 composeLift $ RWS.modify $ \s -> s
741 { state_irefs = TM.insert const path (succNat1 num) state_irefs }
742 H.span ! HA.class_ "iref"
743 ! HA.id (attrify $ identifyIref iref_term $ Just num) $$
745 instance Html5ify [Title] where
747 html5ify . fold . List.intersperse sep . toList
748 where sep = Title $ Seq.singleton $ tree0 $ PlainText " — "
749 instance Html5ify Title where
750 html5ify (Title t) = html5ify t
751 instance Html5ify About where
752 html5ify About{..} = do
753 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
755 html5CommasDot $ concat
756 [ html5Titles about_titles
757 , html5ify <$> about_authors
758 , html5ify <$> about_dates
759 , html5ify <$> about_series
761 forM_ about_links $ \Link{..} ->
764 || link_rel == "self" ->
765 H.p ! HA.class_ "reference-url print-only" $$ do
766 html5ify $ Tree PlainEref{eref_href=link_url} link_plain
768 H.p ! HA.class_ "reference-url" $$ do
770 Plain.l10n_Colon l10n :: HTML5
771 html5ify $ Tree PlainEref{eref_href=link_url} link_plain
772 forM_ about_description $ \description -> do
773 H.div ! HA.class_ "reference-description" $$ do
776 html5Titles :: [Title] -> [HTML5]
777 html5Titles ts | null ts = []
778 html5Titles ts = [html5Title $ joinTitles ts]
780 joinTitles = fold . List.intersperse sep . toList
781 sep = Title $ Seq.singleton $ tree0 $ PlainText " — "
782 html5Title (Title title) = do
783 H.span ! HA.class_ "no-print" $$
784 html5ify $ Tree PlainQ $
785 case List.filter ((\rel -> rel == "" || rel == "self") . link_rel) about_links of
787 Link{..}:_ -> pure $ Tree (PlainEref link_url) title
788 H.span ! HA.class_ "print-only" $$
789 html5ify $ Tree PlainQ title
790 instance Html5ify Serie where
791 html5ify s@Serie{..} = do
792 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
796 Plain.l10n_Colon l10n :: HTML5
800 Tree PlainEref{eref_href=href} $
802 [ tree0 $ PlainText $ unName serie_name
803 , tree0 $ PlainText $ Plain.l10n_Colon l10n
804 , tree0 $ PlainText serie_id
806 instance Html5ify Entity where
807 html5ify Entity{..} = do
809 _ | not (TL.null entity_email) -> do
810 H.span ! HA.class_ "no-print" $$ do
812 Tree (PlainEref $ URL $ "mailto:"<>entity_email) $
813 pure $ tree0 $ PlainText entity_name
814 html5ify $ orgs entity_org
815 H.span ! HA.class_ "print-only" $$
817 Tree (PlainEref $ URL entity_email) $
818 pure $ tree0 $ PlainText $
819 entity_name <> orgs entity_org
821 orgs = foldMap $ \Entity{entity_name=name, entity_org=org} -> " ("<>name<>orgs org<>")"
822 _ | Just u <- entity_url ->
825 pure $ tree0 $ PlainText entity_name
828 tree0 $ PlainText entity_name
829 instance Html5ify Words where
830 html5ify = html5ify . Analyze.plainifyWords
831 instance Html5ify Alias where
832 html5ify Alias{..} = do
833 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
835 case attrs_id alias_attrs of
836 Just ident | Just [_] <- toList <$> HM.lookup ident all_section ->
837 Just $ identifyTag "" ident Nothing
839 H.a ! HA.class_ "alias"
840 !?? mayAttr HA.id mayId $$
842 instance Html5ify URL where
844 H.a ! HA.class_ "url"
845 ! HA.href (attrify url) $$
847 instance Html5ify Date where
848 html5ify date@Date{..} = do
849 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
850 case (date_rel, date_role) of
851 ("", "") -> ""::HTML5
854 Plain.l10n_Colon l10n
857 Plain.l10n_Colon l10n
858 Plain.l10n_Date date l10n
859 instance Html5ify Reference where
860 html5ify Reference{..} = do
861 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
862 State{state_errors=errs@Analyze.Errors{..}, ..} <- composeLift RWS.get
864 H.td ! HA.class_ "reference-key" $$ do
866 case HM.lookup reference_id errors_reference_ambiguous of
868 H.a ! HA.class_ "reference"
869 ! HA.href (refIdent $ identifyReference "" reference_id Nothing)
870 ! HA.id (attrify $ identifyReference "" reference_id Nothing) $$
871 html5ify reference_id
873 composeLift $ RWS.modify $ \s -> s
874 { state_errors = errs
875 { Analyze.errors_reference_ambiguous =
876 HM.insert reference_id (succNat1 errNum) errors_reference_ambiguous } }
877 H.span ! HA.class_ "reference reference-ambiguous"
878 ! HA.id (attrify $ identifyReference "-ambiguous" reference_id $ Just errNum) $$
879 html5ify reference_id
881 H.td ! HA.class_ "reference-content" $$ do
882 html5ify reference_about
883 case HM.lookup reference_id all_ref of
886 when (isNothing $ HM.lookup reference_id errors_reference_ambiguous) $ do
887 H.p ! HA.class_ "ref-backs" $$
889 (<$> List.zip (toList anchs) [1..]) $ \((_loc, maySection),num) ->
890 H.a ! HA.class_ "ref-back"
891 ! HA.href (refIdent $ identifyReference "" reference_id $ Just $ Nat1 num) $$
892 html5SectionNumber maySection
893 instance Html5ify XML.Ancestors where
901 Text.intercalate "." $
902 Text.pack . show . snd <$> as
903 instance Html5ify Plain.Plain where
905 rp <- composeLift $ RWS.asks reader_plainify
906 html5ify $ Plain.runPlain p rp
907 instance Html5ify TCT.Location where
910 H.span ! HA.class_ "tct-location" $$
913 H.ul ! HA.class_ "tct-location" $$
918 instance Html5ify SVG.Element where
921 B.preEscapedLazyText $
923 instance Semigroup SVG.Element where
927 html5Commas :: [HTML5] -> HTML5
928 html5Commas [] = pure ()
930 sequence_ $ List.intersperse ", " hs
932 html5CommasDot :: [HTML5] -> HTML5
933 html5CommasDot [] = pure ()
934 html5CommasDot hs = do
938 html5Lines :: [HTML5] -> HTML5
939 html5Lines hs = sequence_ $ List.intersperse (html5ify H.br) hs
941 html5Words :: [HTML5] -> HTML5
942 html5Words hs = sequence_ $ List.intersperse " " hs
944 html5SectionAnchor :: Section -> HTML5
945 html5SectionAnchor = go mempty . XML.pos_ancestors . section_posXML
947 go :: XML.Ancestors -> XML.Ancestors -> HTML5
949 case Seq.viewl next of
950 Seq.EmptyL -> pure ()
951 a@(_n,rank) Seq.:< as -> do
952 H.a ! HA.href (refIdent $ identify $ prev Seq.|> a) $$
954 when (not (null as) || null prev) $ do
958 html5SectionTo :: Section -> HTML5
959 html5SectionTo Section{..} =
960 H.a ! HA.href (refIdent $ identify ancestors) $$
962 where ancestors = XML.pos_ancestors section_posXML
964 html5SectionNumber :: Section -> HTML5
965 html5SectionNumber Section{..} =
966 html5ify $ XML.pos_ancestors section_posXML
968 popNotes :: ComposeRWS Reader Writer State H.MarkupM (Seq [Para])
970 st <- composeLift RWS.get
971 case {-debug "state_notes" $-} state_notes st of
974 composeLift $ RWS.modify $ \s -> s{state_notes=next}
977 html5Notes :: Seq [Para] -> HTML5
978 html5Notes notes = do
979 unless (null notes) $ do
980 H.aside ! HA.class_ "notes" $$ do
984 forM_ notes $ \content -> do
985 num <- composeLift $ do
986 n <- RWS.gets state_note_num_content
987 RWS.modify $ \s -> s{state_note_num_content=succNat1 n}
990 H.td ! HA.class_ "note-ref" $$ do
991 H.a ! HA.class_ "note-number"
992 ! HA.id ("note."<>attrify num)
993 ! HA.href ("#note."<>attrify num) $$ do
996 H.a ! HA.href ("#note-ref."<>attrify num) $$ do
1001 html5ifyToC :: Maybe DTC.Nat -> Tree BodyNode -> HTML5
1002 html5ifyToC depth (Tree b bs) =
1004 BodySection section@Section{section_about=About{..}, ..} -> do
1006 H.table ! HA.class_ "toc-entry" $$
1008 case about_titles of
1011 H.td ! HA.class_ "section-number" $$
1012 html5SectionTo section
1015 H.td ! HA.class_ "section-number" $$
1016 html5SectionTo section
1017 H.td ! HA.class_ "section-title" $$
1018 html5ify $ cleanPlain $ unTitle title
1019 forM_ titles $ \t ->
1021 H.td ! HA.class_ "section-title" $$
1022 html5ify $ cleanPlain $ unTitle t
1023 when (maybe True (> Nat 1) depth && not (null sections)) $
1026 html5ifyToC (depth >>= predNat)
1030 (`Seq.filter` bs) $ \case
1031 Tree BodySection{} _ -> True
1034 html5ifyToF :: [TL.Text] -> HTML5
1035 html5ifyToF types = do
1036 figuresByType <- composeLift $ RWS.asks $ Analyze.all_figure . reader_all
1039 ((\(ty,ts) -> (ty,) <$> ts) <$>) $
1044 HM.intersection figuresByType $
1045 HM.fromList [(ty,()) | ty <- types]
1046 forM_ (Map.toList figures) $ \(posXML, (type_, title)) ->
1048 H.td ! HA.class_ "figure-number" $$
1049 H.a ! HA.href (refIdent $ identify posXML) $$ do
1051 html5ify $ XML.pos_ancestors posXML
1052 forM_ title $ \ti ->
1053 H.td ! HA.class_ "figure-title" $$
1054 html5ify $ cleanPlain $ unTitle ti
1057 instance Attrify Plain.Plain where
1058 attrify p = attrify $ Plain.runPlain p def