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 Textphile.DTC.Write.HTML5
11 ( module Textphile.DTC.Write.HTML5
12 , module Textphile.DTC.Write.HTML5.Ident
13 , module Textphile.DTC.Write.HTML5.Base
14 , module Textphile.DTC.Write.HTML5.Judgment
15 -- , module Textphile.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 Textphile.DTC.Document as DTC
62 import Textphile.DTC.Write.HTML5.Base
63 import Textphile.DTC.Write.HTML5.Error ()
64 import Textphile.DTC.Write.HTML5.Ident
65 import Textphile.DTC.Write.HTML5.Judgment
66 import Textphile.DTC.Write.Plain (Plainify(..))
67 import Textphile.DTC.Write.XML ()
68 import Textphile.Utils
69 import Text.Blaze.Utils
70 import Text.Blaze.XML ()
71 import qualified Textphile.DTC.Analyze.Check as Analyze
72 import qualified Textphile.DTC.Analyze.Collect as Analyze
73 import qualified Textphile.DTC.Analyze.Index as Analyze
74 import qualified Textphile.DTC.Write.Plain as Plain
75 import qualified Textphile.TCT.Cell as TCT
76 import qualified Textphile.Utils as FS
77 import qualified Textphile.XML as XML
78 import qualified Paths_textphile as Textphile
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 =<< Textphile.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 =<< Textphile.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 } }
687 ! HA.class_ "reference reference-unknown"
688 ! HA.id (attrify $ identifyReference "-unknown" ref_ident errNum) $$ do
694 [Tree (PlainText "") _] -> ref
700 [Reference{..}] -> do
701 let a = H.a ! HA.href (refIdent $ identifyReference "" ref_ident Nothing)
704 ! HA.class_ "reference"
705 ! HA.id (attrify $ identifyReference "" ref_ident $ Just idNum) $$ do
707 a $$ html5ify ref_ident
711 [Tree (PlainText "") _] -> do
712 let About{..} = reference_about
713 forM_ (List.take 1 about_titles) $ \(Title title) -> do
714 html5ify $ Tree PlainQ $
715 case List.filter ((\rel -> rel == "" || rel == "self") . link_rel) about_links of
717 Link{..}:_ -> pure $ Tree (PlainEref link_url) title
722 H.span ! HA.class_ "print-only" $$ do
729 [Tree (PlainText "") _] -> mempty
733 H.span ! HA.class_ "reference reference-ambiguous" $$ do
738 PlainPageRef{..} -> do
739 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
740 State{..} <- composeLift RWS.get
741 let idNum = HM.lookupDefault (Nat1 1) pageRef_path state_pageRef
742 composeLift $ RWS.modify $ \s -> s
743 { state_pageRef = HM.insert pageRef_path (succNat1 idNum) state_pageRef }
744 let href_at = attrify pageRef_path <>
745 maybe mempty (\at -> refIdent (identifyAt "" at Nothing)) pageRef_at
747 H.sup ! HA.class_ "page-path"
748 ! HA.id (attrify $ identifyPage "" pageRef_path $ Just idNum) $$ do
750 H.a ! HA.href (attrify pageRef_path) $$
751 html5ify pageRef_path
753 H.span ! HA.class_ "page-ref" $$ do
757 Nothing -> html5ify pageRef_path
758 Just at -> html5ify at
759 H.span ! HA.class_ "print-only" $$ do
764 case pathFromWords iref_term of
765 Nothing -> html5ify ps
767 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
768 State{state_irefs} <- composeLift RWS.get
769 let num = Strict.fromMaybe (Nat1 1) $ TM.lookup path state_irefs
770 composeLift $ RWS.modify $ \s -> s
771 { state_irefs = TM.insert const path (succNat1 num) state_irefs }
772 H.span ! HA.class_ "iref"
773 ! HA.id (attrify $ identifyIref iref_term $ Just num) $$
775 instance Html5ify [Title] where
777 html5ify . fold . List.intersperse sep . toList
778 where sep = Title $ Seq.singleton $ tree0 $ PlainText " — "
779 instance Html5ify Title where
780 html5ify (Title t) = html5ify t
781 instance Html5ify About where
782 html5ify About{..} = do
783 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
785 html5CommasDot $ concat
786 [ html5Titles about_titles
787 , html5ify <$> about_authors
788 , html5ify <$> about_dates
789 , html5ify <$> about_series
791 forM_ about_links $ \Link{..} ->
794 || link_rel == "self" ->
795 H.p ! HA.class_ "reference-url print-only" $$ do
796 html5ify $ Tree PlainEref{eref_href=link_url} link_plain
798 H.p ! HA.class_ "reference-url" $$ do
800 Plain.l10n_Colon l10n :: HTML5
801 html5ify $ Tree PlainEref{eref_href=link_url} link_plain
802 forM_ about_description $ \description -> do
803 H.div ! HA.class_ "reference-description" $$ do
806 html5Titles :: [Title] -> [HTML5]
807 html5Titles ts | null ts = []
808 html5Titles ts = [html5Title $ joinTitles ts]
810 joinTitles = fold . List.intersperse sep . toList
811 sep = Title $ Seq.singleton $ tree0 $ PlainText " — "
812 html5Title (Title title) = do
813 H.span ! HA.class_ "no-print" $$
814 html5ify $ Tree PlainQ $
815 case List.filter ((\rel -> rel == "" || rel == "self") . link_rel) about_links of
817 Link{..}:_ -> pure $ Tree (PlainEref link_url) title
818 H.span ! HA.class_ "print-only" $$
819 html5ify $ Tree PlainQ title
820 instance Html5ify Serie where
821 html5ify s@Serie{..} = do
822 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
826 Plain.l10n_Colon l10n :: HTML5
830 Tree PlainEref{eref_href=href} $
832 [ tree0 $ PlainText $ unName serie_name
833 , tree0 $ PlainText $ Plain.l10n_Colon l10n
834 , tree0 $ PlainText serie_id
836 instance Html5ify Entity where
837 html5ify Entity{..} = do
839 _ | not (TL.null entity_email) -> do
840 H.span ! HA.class_ "no-print" $$ do
842 Tree (PlainEref $ URL $ "mailto:"<>entity_email) $
843 pure $ tree0 $ PlainText entity_name
844 html5ify $ orgs entity_org
845 H.span ! HA.class_ "print-only" $$
847 Tree (PlainEref $ URL entity_email) $
848 pure $ tree0 $ PlainText $
849 entity_name <> orgs entity_org
851 orgs = foldMap $ \Entity{entity_name=name, entity_org=org} -> " ("<>name<>orgs org<>")"
852 _ | Just u <- entity_url ->
855 pure $ tree0 $ PlainText entity_name
858 tree0 $ PlainText entity_name
859 instance Html5ify Words where
860 html5ify = html5ify . Analyze.plainifyWords
861 instance Html5ify Alias where
862 html5ify Alias{..} = do
863 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
865 case attrs_id alias_attrs of
866 Just ident | Just [_] <- toList <$> HM.lookup ident all_section ->
867 Just $ identifyTag "" ident Nothing
869 H.a ! HA.class_ "alias"
870 !?? mayAttr HA.id mayId $$
872 instance Html5ify URL where
874 H.a ! HA.class_ "url"
875 ! HA.href (attrify url) $$
877 instance Html5ify Date where
878 html5ify date@Date{..} = do
879 Loqualization l10n <- composeLift $ RWS.asks reader_l10n
880 case (date_rel, date_role) of
881 ("", "") -> ""::HTML5
884 Plain.l10n_Colon l10n
887 Plain.l10n_Colon l10n
888 Plain.l10n_Date date l10n
889 instance Html5ify Reference where
890 html5ify Reference{..} = do
891 Reader{reader_all=Analyze.All{..}} <- composeLift RWS.ask
892 State{state_errors=errs@Analyze.Errors{..}, ..} <- composeLift RWS.get
894 H.td ! HA.class_ "reference-key" $$ do
896 case HM.lookup reference_id errors_reference_ambiguous of
898 H.a ! HA.class_ "reference"
899 ! HA.href (refIdent $ identifyReference "" reference_id Nothing)
900 ! HA.id (attrify $ identifyReference "" reference_id Nothing) $$
901 html5ify reference_id
903 composeLift $ RWS.modify $ \s -> s
904 { state_errors = errs
905 { Analyze.errors_reference_ambiguous =
906 HM.insert reference_id (succNat1 errNum) errors_reference_ambiguous } }
907 H.span ! HA.class_ "reference reference-ambiguous"
908 ! HA.id (attrify $ identifyReference "-ambiguous" reference_id $ Just errNum) $$
909 html5ify reference_id
911 H.td ! HA.class_ "reference-content" $$ do
912 html5ify reference_about
913 case HM.lookup reference_id all_ref of
916 when (isNothing $ HM.lookup reference_id errors_reference_ambiguous) $ do
917 H.p ! HA.class_ "ref-backs" $$
919 (<$> List.zip (toList anchs) [1..]) $ \((_loc, maySection),num) ->
920 H.a ! HA.class_ "ref-back"
921 ! HA.href (refIdent $ identifyReference "" reference_id $ Just $ Nat1 num) $$
922 html5SectionNumber maySection
923 instance Html5ify XML.Ancestors where
931 Text.intercalate "." $
932 Text.pack . show . snd <$> as
933 instance Html5ify Plain.Plain where
935 rp <- composeLift $ RWS.asks reader_plainify
936 html5ify $ Plain.runPlain p rp
937 instance Html5ify TCT.Location where
940 H.span ! HA.class_ "tct-location" $$
943 H.ul ! HA.class_ "tct-location" $$
948 instance Html5ify SVG.Element where
951 B.preEscapedLazyText $
953 instance Semigroup SVG.Element where
957 html5Commas :: [HTML5] -> HTML5
958 html5Commas [] = pure ()
960 sequence_ $ List.intersperse ", " hs
962 html5CommasDot :: [HTML5] -> HTML5
963 html5CommasDot [] = pure ()
964 html5CommasDot hs = do
968 html5Lines :: [HTML5] -> HTML5
969 html5Lines hs = sequence_ $ List.intersperse (html5ify H.br) hs
971 html5Words :: [HTML5] -> HTML5
972 html5Words hs = sequence_ $ List.intersperse " " hs
974 html5SectionAnchor :: Section -> HTML5
975 html5SectionAnchor = go mempty . XML.pos_ancestors . section_posXML
977 go :: XML.Ancestors -> XML.Ancestors -> HTML5
979 case Seq.viewl next of
980 Seq.EmptyL -> pure ()
981 a@(_n,rank) Seq.:< as -> do
982 H.a ! HA.href (refIdent $ identify $ prev Seq.|> a) $$
984 when (not (null as) || null prev) $ do
988 html5SectionTo :: Section -> HTML5
989 html5SectionTo Section{..} =
990 H.a ! HA.href (refIdent $ identify ancestors) $$
992 where ancestors = XML.pos_ancestors section_posXML
994 html5SectionNumber :: Section -> HTML5
995 html5SectionNumber Section{..} =
996 html5ify $ XML.pos_ancestors section_posXML
998 popNotes :: ComposeRWS Reader Writer State H.MarkupM (Seq [Para])
1000 st <- composeLift RWS.get
1001 case {-debug "state_notes" $-} state_notes st of
1004 composeLift $ RWS.modify $ \s -> s{state_notes=next}
1007 html5Notes :: Seq [Para] -> HTML5
1008 html5Notes notes = do
1009 unless (null notes) $ do
1010 H.aside ! HA.class_ "notes" $$ do
1014 forM_ notes $ \content -> do
1015 num <- composeLift $ do
1016 n <- RWS.gets state_note_num_content
1017 RWS.modify $ \s -> s{state_note_num_content=succNat1 n}
1020 H.td ! HA.class_ "note-ref" $$ do
1021 H.a ! HA.class_ "note-number"
1022 ! HA.id ("note."<>attrify num)
1023 ! HA.href ("#note."<>attrify num) $$ do
1026 H.a ! HA.href ("#note-ref."<>attrify num) $$ do
1031 html5ifyToC :: Maybe DTC.Nat -> Tree BodyNode -> HTML5
1032 html5ifyToC depth (Tree b bs) =
1034 BodySection section@Section{section_about=About{..}, ..} -> do
1036 H.table ! HA.class_ "toc-entry" $$
1038 case about_titles of
1041 H.td ! HA.class_ "section-number" $$
1042 html5SectionTo section
1045 H.td ! HA.class_ "section-number" $$
1046 html5SectionTo section
1047 H.td ! HA.class_ "section-title" $$
1048 html5ify $ cleanPlain $ unTitle title
1049 forM_ titles $ \t ->
1051 H.td ! HA.class_ "section-title" $$
1052 html5ify $ cleanPlain $ unTitle t
1053 when (maybe True (> Nat 1) depth && not (null sections)) $
1056 html5ifyToC (depth >>= predNat)
1060 (`Seq.filter` bs) $ \case
1061 Tree BodySection{} _ -> True
1064 html5ifyToF :: [TL.Text] -> HTML5
1065 html5ifyToF types = do
1066 figuresByType <- composeLift $ RWS.asks $ Analyze.all_figure . reader_all
1069 ((\(ty,ts) -> (ty,) <$> ts) <$>) $
1074 HM.intersection figuresByType $
1075 HM.fromList [(ty,()) | ty <- types]
1076 forM_ (Map.toList figures) $ \(posXML, (type_, title)) ->
1078 H.td ! HA.class_ "figure-number" $$
1079 H.a ! HA.href (refIdent $ identify posXML) $$ do
1081 html5ify $ XML.pos_ancestors posXML
1082 forM_ title $ \ti ->
1083 H.td ! HA.class_ "figure-title" $$
1084 html5ify $ cleanPlain $ unTitle ti
1087 instance Attrify Plain.Plain where
1088 attrify p = attrify $ Plain.runPlain p def