Fix XML rendition of PairFrenchquote.
[doclang.git] / Language / DTC / Write / HTML5.hs
index b43b36248a2f19a25c26119c39514ec9e7266182..f074487bf0ea97619608d0923f019eac2a6d73e5 100644 (file)
@@ -1,30 +1,26 @@
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE FlexibleContexts #-}
 {-# LANGUAGE MultiParamTypeClasses #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# LANGUAGE TypeApplications #-}
-{-# LANGUAGE ViewPatterns #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Language.DTC.Write.HTML5 where
 
--- import Control.Monad.Trans.Class (MonadTrans(..))
--- import Data.Functor.Identity (Identity(..))
--- import Data.Sequence (Seq)
--- import Data.Set (Set)
--- import Data.Traversable (Traversable(..))
--- import qualified Data.Sequence as Seq
--- import qualified Data.TreeSeq.Strict as Tree
 import Control.Applicative (Applicative(..))
-import Control.Category
+import Control.Category as Cat
 import Control.Monad
 import Data.Bool
 import Data.Char (Char)
 import Data.Default.Class (Default(..))
-import Data.Foldable (Foldable(..), concat)
-import Data.Function (($), const, flip, on)
-import Data.Functor (Functor(..), (<$>))
+import Data.Either (Either(..))
+import Data.Eq (Eq(..))
+import Data.Foldable (Foldable(..), concat, any)
+import Data.Function (($), const, on)
+import Data.Functor ((<$>))
 import Data.Functor.Compose (Compose(..))
 import Data.Int (Int)
 import Data.Map.Strict (Map)
@@ -32,16 +28,16 @@ import Data.Maybe (Maybe(..), maybe, mapMaybe, fromJust, maybeToList)
 import Data.Monoid (Monoid(..))
 import Data.Ord (Ord(..))
 import Data.Semigroup (Semigroup(..))
-import Data.String (String)
+import Data.String (String, IsString(..))
 import Data.Text (Text)
-import Data.TreeSeq.Strict (Tree(..), Trees)
+import Data.TreeSeq.Strict (Tree(..), tree0)
 import Data.Tuple (snd)
+import Prelude (mod)
 import System.FilePath (FilePath)
 import Text.Blaze ((!))
 import Text.Blaze.Html (Html)
 import Text.Show (Show(..))
 import qualified Control.Monad.Trans.State as S
-import qualified Data.Char as Char
 import qualified Data.List as List
 import qualified Data.Map.Strict as Map
 import qualified Data.Sequence as Seq
@@ -49,83 +45,263 @@ import qualified Data.Strict.Maybe as Strict
 import qualified Data.Text as Text
 import qualified Data.Text.Lazy as TL
 import qualified Data.TreeMap.Strict as TreeMap
-import qualified Data.TreeSeq.Strict as Tree
 import qualified Data.TreeSeq.Strict.Zipper as Tree
 import qualified Text.Blaze.Html5 as H
 import qualified Text.Blaze.Html5.Attributes  as HA
 import qualified Text.Blaze.Internal as H
 
 import Text.Blaze.Utils
-import Data.Locale hiding (localize, Index)
-import qualified Data.Locale as Locale
+import Data.Locale hiding (Index)
 
+import Language.DTC.Document as DTC
+import Language.DTC.Write.Plain (Plainify(..))
 import Language.DTC.Write.XML ()
-import Language.DTC.Write.Plain (Plain, Plainify(..))
-import qualified Language.DTC.Write.Plain as Plain
-import qualified Language.DTC.Document as DTC
 import qualified Language.DTC.Anchor as Anchor
+import qualified Language.DTC.Write.Plain as Plain
+
+writeHTML5 :: Config -> DTC.Document -> Html
+writeHTML5 conf@Config{..} DTC.Document{..} = do
+       let Keys{..} = keys body `S.execState` def
+       let (body',state_rrefs,state_notes,state_indexs) =
+               let irefs = foldMap Anchor.irefsOfTerms keys_index in
+               let (body0, Anchor.State{state_irefs, state_rrefs=rrefs, state_notes=notes}) =
+                       Anchor.anchorify body `S.runState`
+                       def{Anchor.state_irefs=irefs} in
+               (body0,rrefs,notes,) $
+               (<$> keys_index) $ \terms ->
+                       (terms,) $
+                       TreeMap.intersection const state_irefs $
+                       Anchor.irefsOfTerms terms
+       let state_plainify = def{ Plain.state_l10n = loqualize config_locale}
+       let (html5Body, endState) =
+               runStateMarkup def
+                { state_indexs
+                , state_rrefs
+                , state_notes
+                , state_figures    = keys_figure
+                , state_references = keys_reference
+                , state_plainify
+                , state_l10n = loqualize config_locale
+                } $ do
+                       html5DocumentHead head
+                       html5ify body'
+       H.docType
+       H.html ! HA.lang (attrify $ countryCode config_locale) $ do
+               html5Head conf endState head body
+               H.body $ html5Body
+
+html5Head :: Config -> State -> Head -> Body -> Html
+html5Head Config{..} State{..} Head{DTC.about=About{..}} body = do
+       H.head $ do
+               H.meta ! HA.httpEquiv "Content-Type"
+                      ! HA.content "text/html; charset=UTF-8"
+               unless (null titles) $ do
+                       H.title $
+                               H.toMarkup $ Plain.text state_plainify $ List.head titles
+               forM_ links $ \Link{rel, href} ->
+                       H.link ! HA.rel (attrify rel)
+                              ! HA.href (attrify href)
+               forM_ url $ \href ->
+                       H.link ! HA.rel "self"
+                              ! HA.href (attrify href)
+               unless (TL.null config_generator) $ do
+                       H.meta ! HA.name "generator"
+                              ! HA.content (attrify config_generator)
+               unless (null tags) $
+                       H.meta ! HA.name "keywords"
+                              ! HA.content (attrify $ TL.intercalate ", " tags)
+               let chapters =
+                       (`mapMaybe` toList body) $ \case
+                        Tree k@BodySection{} _ -> Just k
+                        _ -> Nothing
+               forM_ chapters $ \case
+                BodySection{..} ->
+                       H.link ! HA.rel "Chapter"
+                              ! HA.title (attrify $ plainify title)
+                              ! HA.href ("#"<>attrify pos)
+                _ -> mempty
+               unless (any (\DTC.Link{rel} -> rel == "stylesheet") links) $ do
+                       case config_css of
+                        Left "" -> mempty
+                        Left css ->
+                               H.link ! HA.rel "stylesheet"
+                                      ! HA.type_ "text/css"
+                                      ! HA.href (attrify css)
+                        Right css ->
+                               H.style ! HA.type_ "text/css" $
+                                       -- NOTE: as a special case, H.style wraps its content into an External,
+                                       -- so it does not HTML-escape its content.
+                                       H.toMarkup css
+                       forM_ state_styles $ \style ->
+                               H.style ! HA.type_ "text/css" $
+                                       H.toMarkup style
+               unless (any (\DTC.Link{rel} -> rel == "script") links) $ do
+                       forM_ state_scripts $ \script ->
+                               H.script ! HA.type_ "application/javascript" $
+                                       H.toMarkup script
 
-(<&>) :: Functor f => f a -> (a -> b) -> f b
-(<&>) = flip (<$>)
-infixl 4 <&>
+html5DocumentHead :: Head -> Html5
+html5DocumentHead Head{DTC.about=About{..}} = do
+       H.div ! HA.class_ "document-head" $$
+               H.table $$ do
+                       H.tbody $$ do
+                               H.tr $$ do
+                                       H.td ! HA.class_ "left"  $$ docHeaders
+                                       H.td ! HA.class_ "right" $$ docAuthors
+       unless (null titles) $
+               H.div ! HA.class_ "title" $$ do
+                       forM_ titles $ \title ->
+                               H.h1 $$ html5ify title
+       where
+       docHeaders =
+               H.table ! HA.class_ "document-headers" $$
+                       H.tbody $$ do
+                               Loqualization loc <- liftStateMarkup $ S.gets state_l10n
+                               forM_ series $ \s@Serie{id=id_, name} ->
+                                       header $
+                                               case urlSerie s of
+                                                Nothing -> do
+                                                       headerName  $ html5ify name
+                                                       headerValue $ html5ify id_
+                                                Just href -> do
+                                                       headerName  $ html5ify name
+                                                       headerValue $
+                                                               H.a ! HA.href (attrify href) $$
+                                                                       html5ify id_
+                               forM_ date $ \d ->
+                                       header $ do
+                                               headerName  $ l10n_Header_Date loc
+                                               headerValue $ html5ify d
+                               forM_ url $ \href ->
+                                       header $ do
+                                               headerName  $ l10n_Header_Address loc
+                                               headerValue $ html5ify $ tree0 $ PlainEref{href}
+                               forM_ links $ \Link{..} ->
+                                       unless (TL.null name) $
+                                               header $ do
+                                                       headerName  $ html5ify name
+                                                       headerValue $ html5ify $ Tree PlainEref{href} plain
+                               forM_ headers $ \Header{..} ->
+                                       header $ do
+                                               headerName  $ html5ify name
+                                               headerValue $ html5ify value
+       docAuthors =
+               H.table ! HA.class_ "document-authors" $$
+                       H.tbody $$ do
+                               forM_ authors $ \a ->
+                                       H.tr $$
+                                               H.td ! HA.class_ "author" $$
+                                                       html5ify a
+       header :: Html5 -> Html5
+       header h = H.tr ! HA.class_ "header" $$ h
+       headerName :: Html5 -> Html5
+       headerName h =
+               H.td ! HA.class_ "header-name" $$ do
+                       h
+                       Loqualization loc <- liftStateMarkup $ S.gets state_l10n
+                       Plain.l10n_Colon loc
+       headerValue :: Html5 -> Html5
+       headerValue h =
+               H.td ! HA.class_ "header-value" $$ do
+                       h
+
+-- * Type 'Config'
+data Config
+ =   forall locales.
+     ( Locales   locales
+     , Loqualize locales (L10n Html5)
+     , Loqualize locales (Plain.L10n Plain.Plain)
+     ) =>
+     Config
+ {   config_css       :: Either FilePath TL.Text
+ ,   config_locale    :: LocaleIn locales
+ ,   config_generator :: TL.Text
+ }
+instance Default Config where
+       def = Config
+        { config_css       = Right "style/dtc-html5.css"
+        , config_locale    = LocaleIn @'[EN] en_US
+        , config_generator = "https://hackage.haskell.org/package/hdoc"
+        }
 
 -- * Type 'Html5'
 type Html5 = StateMarkup State ()
+instance IsString Html5 where
+       fromString = html5ify
 
--- ** Type 'State'
+-- * Type 'State'
 data State
  =   State
- { state_styles     :: Map FilePath CSS
- , state_scripts    :: Map FilePath Script
- , state_indexs     :: Map DTC.Pos (DTC.Terms, Anchor.Irefs)
- , state_rrefs      :: Anchor.Rrefs
- , state_figures    :: Map Text (Map DTC.Pos DTC.Title)
- , state_references :: Map DTC.Ident DTC.About
- , state_plainify   :: Plain.State
+ {   state_styles     :: Map FilePath TL.Text
+ ,   state_scripts    :: Map FilePath TL.Text
+ ,   state_indexs     :: Map DTC.Pos (Terms, Anchor.Irefs)
+ ,   state_rrefs      :: Anchor.Rrefs
+ ,   state_figures    :: Map TL.Text (Map DTC.Pos (Maybe Title))
+ ,   state_references :: Map Ident About
+ ,   state_notes      :: Anchor.Notes
+ ,   state_plainify   :: Plain.State
+ ,   state_l10n       :: Loqualization (L10n Html5)
  }
 instance Default State where
        def = State
-        { state_styles     = mempty
-        , state_scripts    = mempty
-        , state_indexs     = mempty
-        , state_rrefs      = mempty
-        , state_figures    = mempty
-        , state_references = mempty
+        { state_styles     = def
+        , state_scripts    = def
+        , state_indexs     = def
+        , state_rrefs      = def
+        , state_figures    = def
+        , state_references = def
+        , state_notes      = def
         , state_plainify   = def
+        , state_l10n       = Loqualization EN_US
         }
-type CSS = Text
-type Script = Text
 
--- ** Type 'Keys'
+-- * Type 'Keys'
 data Keys
  = Keys
- { keys_index     :: Map DTC.Pos DTC.Terms
- , keys_figure    :: Map Text (Map DTC.Pos DTC.Title)
- , keys_reference :: Map DTC.Ident DTC.About
+ { keys_index     :: Map DTC.Pos Terms
+ , keys_figure    :: Map TL.Text (Map DTC.Pos (Maybe Title))
+ , keys_reference :: Map Ident About
  } deriving (Show)
+instance Default Keys where
+       def = Keys mempty mempty mempty
 
-keys :: Trees DTC.BodyKey DTC.BodyValue -> Keys
-keys body = foldl' flt (Keys mempty mempty mempty) (Compose body)
-       where
-       flt acc = \case
-        DTC.Index{..} -> acc{keys_index =
-               Map.insert pos terms $ keys_index acc}
-        DTC.Figure{..} -> acc{keys_figure =
-               Map.insertWith (<>)
-                type_ (Map.singleton pos title) $
-               keys_figure acc}
-        DTC.References{..} -> acc{keys_reference =
-               foldr
-                (\r -> Map.insert
-                        (DTC.id    (r::DTC.Reference))
-                        (DTC.about (r::DTC.Reference)))
-                (keys_reference acc)
-                refs}
-        _ -> acc
+-- ** Class 'KeysOf'
+class KeysOf a where
+       keys :: a -> S.State Keys ()
+instance KeysOf Body where
+       keys = mapM_ keys
+instance KeysOf (Tree BodyNode) where
+       keys (Tree n ts) =
+               case n of
+                BodySection{..} -> keys ts
+                BodyBlock b -> keys b
+instance KeysOf DTC.Block where
+       keys = \case
+        BlockPara{} -> return ()
+        BlockToC{}  -> return ()
+        BlockToF{}  -> return ()
+        BlockIndex{..} ->
+               S.modify $ \s -> s{keys_index=
+                       Map.insert pos terms $ keys_index s}
+        BlockFigure{..} ->
+               S.modify $ \s -> s{keys_figure=
+                       Map.insertWith (<>)
+                        type_ (Map.singleton pos mayTitle) $
+                       keys_figure s}
+        BlockReferences{..} ->
+               S.modify $ \s -> s{keys_reference=
+                       foldr
+                        (\r -> Map.insert
+                                (DTC.id    (r::DTC.Reference))
+                                (DTC.about (r::DTC.Reference)))
+                        (keys_reference s)
+                        refs}
 
--- ** Class 'Html5ify'
+-- * Class 'Html5ify'
 class Html5ify a where
        html5ify :: a -> Html5
+instance Html5ify H.Markup where
+       html5ify = Compose . return
 instance Html5ify Char where
        html5ify = html5ify . H.toMarkup
 instance Html5ify Text where
@@ -134,103 +310,59 @@ instance Html5ify TL.Text where
        html5ify = html5ify . H.toMarkup
 instance Html5ify String where
        html5ify = html5ify . H.toMarkup
-instance Html5ify H.Markup where
-       html5ify = Compose . return
-instance Html5ify DTC.Title where
-       html5ify (DTC.Title t) = html5ify t
-instance Html5ify DTC.Para where
-       html5ify = mapM_ html5ify
-instance Html5ify DTC.Ident where
-       html5ify (DTC.Ident i) = html5ify i
+instance Html5ify Title where
+       html5ify (Title t) = html5ify t
+instance Html5ify Ident where
+       html5ify (Ident i) = html5ify i
 instance Html5ify Int where
        html5ify = html5ify . show
-instance Html5ify DTC.Nat where
-       html5ify (DTC.Nat n) = html5ify n
-instance Html5ify DTC.Nat1 where
-       html5ify (DTC.Nat1 n) = html5ify n
-
-html5Document ::
- Localize ls Plain Plain.L10n =>
- Locales ls =>
- LocaleIn ls -> DTC.Document -> Html
-html5Document locale DTC.Document{..} = do
-       let Keys{..} = keys body
-       let (body',state_rrefs,state_indexs) =
-               let irefs = foldMap Anchor.irefsOfTerms keys_index in
-               let (body0, Anchor.State{state_irefs, state_rrefs=rrefs}) = 
-                       Anchor.anchorify body `S.runState`
-                       Anchor.state{Anchor.state_irefs=irefs} in
-               (body0,rrefs,) $
-               (<$> keys_index) $ \terms ->
-                       (terms,) $
-                       TreeMap.intersection const state_irefs $
-                       Anchor.irefsOfTerms terms
-       let state_plainify = def
-               { Plain.state_localize = Locale.localize locale }
-       let (html5Body, State{state_styles,state_scripts}) =
-               runStateMarkup def
-                { state_indexs
-                , state_rrefs
-                , state_figures    = keys_figure
-                , state_references = keys_reference
-                , state_plainify
-                } $ html5ify body'
-       
-       H.docType
-       H.html ! HA.lang (attrify $ countryCode locale) $ do
-               H.head $ do
-                       H.meta ! HA.httpEquiv "Content-Type"
-                              ! HA.content "text/html; charset=UTF-8"
-                       whenSome (DTC.titles $ DTC.about (head :: DTC.Head)) $ \ts ->
-                               H.title $
-                                       H.toMarkup $ Plain.text state_plainify $ List.head ts
-                       forM_ (DTC.links $ DTC.about (head :: DTC.Head)) $ \DTC.Link{rel, href} ->
-                               H.link ! HA.rel (attrify rel)
-                                      ! HA.href (attrify href)
-                       H.meta ! HA.name "generator"
-                              ! HA.content "tct"
-                       let chapters =
-                               (`mapMaybe` toList body) $ \case
-                                TreeN k@DTC.Section{} _ -> Just k
-                                _ -> Nothing
-                       forM_ chapters $ \DTC.Section{..} ->
-                               H.link ! HA.rel "Chapter"
-                                      ! HA.title (attrify $ plainify title)
-                                      ! HA.href ("#"<>attrify pos)
-                       H.link ! HA.rel "stylesheet"
-                              ! HA.type_ "text/css"
-                              ! HA.href "style/dtc-html5.css"
-                       forM_ state_styles $ \style ->
-                               H.style ! HA.type_ "text/css" $
-                                       H.toMarkup style
-                       forM_ state_scripts $ \script ->
-                               H.script ! HA.type_ "application/javascript" $
-                                       H.toMarkup script
-               H.body
-                       html5Body
+instance Html5ify Nat where
+       html5ify (Nat n) = html5ify n
+instance Html5ify Nat1 where
+       html5ify (Nat1 n) = html5ify n
+instance Html5ify a => Html5ify (Maybe a) where
+       html5ify = foldMap html5ify
 
 -- * Type 'BodyCursor'
--- | Cursor to navigate within a 'DTC.Body' according to many axis (like in XSLT).
-type BodyCursor = Tree.Zipper DTC.BodyKey DTC.BodyValue
-instance Html5ify DTC.Body where
+-- | Cursor to navigate within a 'Body' according to many axis (like in XSLT).
+type BodyCursor = Tree.Zipper BodyNode
+instance Html5ify Body where
        html5ify body =
                forM_ (Tree.zippers body) $ \z ->
                        forM_ (Tree.axis_repeat Tree.axis_following_sibling_nearest `Tree.runAxis` z) $
                                html5ify
-
-instance Html5ify BodyCursor where
-       html5ify z =
-               case Tree.current z of
-                TreeN k _ts -> html5BodyKey z k
-                Tree0 v     -> html5BodyValue z v
-
-html5BodyKey :: BodyCursor -> DTC.BodyKey -> Html5
-html5BodyKey z = \case
-        DTC.Section{..} ->
+instance Html5ify BodyCursor
+ where html5ify z =
+       let Tree n _ts = Tree.current z in
+       case n of
+        BodyBlock BlockToC{..} -> do
+               H.nav ! HA.class_ "toc"
+                     ! HA.id (attrify pos) $$ do
+                       H.span ! HA.class_ "toc-name" $$
+                               H.a ! HA.href (attrify pos) $$ do
+                                       Loqualization loc <- liftStateMarkup $ S.gets state_l10n
+                                       Plain.l10n_Table_of_Contents loc
+                       H.ul $$
+                               forM_ (Tree.axis_following_sibling `Tree.runAxis` z) $
+                                       html5ifyToC depth
+        BodyBlock b -> html5ify b
+        BodySection{..} -> do
+               do
+                       notes <- liftStateMarkup $ S.gets state_notes
+                       let mayNotes = do
+                               p <- posParent $ posAncestors pos
+                               let (ns, as) = Map.updateLookupWithKey (\_ _ -> Nothing) p notes
+                               (,as) <$> ns
+                       case mayNotes of
+                        Nothing -> mempty
+                        Just (secNotes, state_notes) -> do
+                               liftStateMarkup $ S.modify' $ \s -> s{state_notes}
+                               html5ify secNotes
                H.section ! HA.class_ "section"
                          ! HA.id (attrify pos) $$ do
-                       html5CommonAttrs attrs $
-                               H.table ! HA.class_ "section-header" $$
+                       forM_ aliases html5ify
+                       html5CommonAttrs attrs{classes="section-header":classes attrs} $
+                               H.table $$
                                        H.tbody $$
                                                H.tr $$ do
                                                        H.td ! HA.class_ "section-number" $$ do
@@ -247,41 +379,62 @@ html5BodyKey z = \case
                                                                        html5ify title
                        forM_ (Tree.axis_child `Tree.runAxis` z) $
                                html5ify
-html5BodyValue :: BodyCursor -> DTC.BodyValue -> Html5
-html5BodyValue z = \case
-        DTC.Block b -> html5ify b
-        DTC.ToC{..} -> do
-               H.nav ! HA.class_ "toc"
-                     ! HA.id (attrify pos) $$ do
-                       H.span ! HA.class_ "toc-name" $$
-                               H.a ! HA.href (attrify pos) $$
-                                       html5ify Plain.L10n_Table_of_Contents
-                       H.ul $$
-                               forM_ (Tree.axis_following_sibling `Tree.runAxis` z) $
-                                       html5ifyToC depth
-        DTC.ToF{..} -> do
+                       notes <- liftStateMarkup $ S.gets state_notes
+                       html5ify $ Map.lookup (posAncestors pos) notes
+instance Html5ify [Anchor.Note] where
+       html5ify notes =
+               H.aside ! HA.class_ "notes" $$ do
+                       Compose $ pure H.hr
+                       H.table $$
+                               H.tbody $$
+                                       forM_ (List.reverse notes) $ \Anchor.Note{..} ->
+                                               H.tr $$ do
+                                                       H.td ! HA.class_ "note-ref" $$ do
+                                                               H.a ! HA.class_ "note-number"
+                                                                   ! HA.id   ("note."<>attrify note_number)
+                                                                   ! HA.href ("#note."<>attrify note_number) $$ do
+                                                                       html5ify note_number
+                                                               ". "::Html5
+                                                               H.a ! HA.href ("#note-ref."<>attrify note_number) $$ do
+                                                                       "↑"
+                                                       H.td $$
+                                                               html5ify note_content
+instance Html5ify Block where
+       html5ify = \case
+        BlockPara para -> html5ify para
+        BlockToC{..} -> mempty -- NOTE: done in Html5ify BodyCursor
+        BlockToF{..} -> do
                H.nav ! HA.class_ "tof"
                      ! HA.id (attrify pos) $$
                        H.table ! HA.class_ "tof" $$
                                H.tbody $$
                                        html5ifyToF types
-        DTC.Figure{..} ->
-               html5CommonAttrs attrs $
-               H.div ! HA.class_ ("figure " <> attrify ("figure-"<>type_))
-                     ! HA.id (attrify pos) $$ do
+        BlockFigure{..} ->
+               html5CommonAttrs attrs
+                { classes = "figure":("figure-"<>type_):classes attrs
+                , DTC.id  = Just $ Ident $ Plain.text def $ DTC.posAncestors pos
+                } $
+               H.div $$ do
                        H.table ! HA.class_ "figure-caption" $$
                                H.tbody $$
                                        H.tr $$ do
-                                               H.td ! HA.class_ "figure-number" $$ do
-                                                       H.a ! HA.href ("#"<>attrify pos) $$ do
-                                                               html5ify type_
-                                                               html5ify $ DTC.posAncestors pos
-                                                       html5ify $ Plain.L10n_Colon
-                                               H.td ! HA.class_ "figure-name" $$
-                                                       html5ify title
+                                               if TL.null type_
+                                                then H.a ! HA.href ("#"<>attrify pos) $$ mempty
+                                                else
+                                                       H.td ! HA.class_ "figure-number" $$ do
+                                                               H.a ! HA.href ("#"<>attrify (DTC.posAncestorsWithFigureNames pos)) $$ do
+                                                                       html5ify type_
+                                                                       html5ify $ DTC.posAncestorsWithFigureNames pos
+                                               forM_ mayTitle $ \title -> do
+                                                       H.td ! HA.class_ "figure-colon" $$ do
+                                                               unless (TL.null type_) $ do
+                                                                       Loqualization loc <- liftStateMarkup $ S.gets state_l10n
+                                                                       Plain.l10n_Colon loc
+                                                       H.td ! HA.class_ "figure-title" $$ do
+                                                               html5ify title
                        H.div ! HA.class_ "figure-content" $$ do
-                               html5ify blocks
-        DTC.Index{pos} -> do
+                               html5ify paras
+        BlockIndex{pos} -> do
                (allTerms,refsByTerm) <- liftStateMarkup $ S.gets $ (Map.! pos) . state_indexs
                let chars = Anchor.termsByChar allTerms
                H.div ! HA.class_ "index"
@@ -303,7 +456,7 @@ html5BodyValue z = \case
                                                        H.dt $$
                                                                H.ul ! HA.class_ "index-aliases" $$
                                                                        forM_ (List.take 1 aliases) $ \term ->
-                                                                               H.li ! HA.id (attrify term) $$
+                                                                               H.li ! HA.id (attrifyIref term) $$
                                                                                        html5ify term
                                                        H.dd $$
                                                                let anchs =
@@ -316,29 +469,22 @@ html5BodyValue z = \case
                                                                html5CommasDot $
                                                                (<$> anchs) $ \(term,DTC.Anchor{..}) ->
                                                                        H.a ! HA.class_ "index-iref"
-                                                                           ! HA.href ("#"<>attrify (term,count)) $$
+                                                                           ! HA.href ("#"<>attrifyIrefCount term count) $$
                                                                                html5ify $ DTC.posAncestors section
-        DTC.References{..} ->
-               html5CommonAttrs attrs $
-               H.div ! HA.class_ "references"
-                     ! HA.id (attrify pos) $$ do
+        BlockReferences{..} ->
+               html5CommonAttrs attrs
+                { classes = "references":classes attrs
+                , DTC.id  = Just $ Ident $ Plain.text def $ DTC.posAncestors pos
+                } $
+               H.div $$ do
                        H.table $$
                                forM_ refs html5ify
 
-instance Html5ify DTC.Words where
-       html5ify = html5ify . Anchor.plainifyWords
-
-cleanPara :: DTC.Para -> DTC.Para
-cleanPara p =
-       p >>= (`Tree.bindTrees` \case
-        TreeN DTC.Iref{} ls -> ls
-        TreeN DTC.Note{} _  -> mempty
-        h -> pure h)
-
 html5ifyToC :: Maybe DTC.Nat -> BodyCursor -> Html5
 html5ifyToC depth z =
-       case Tree.current z of
-        TreeN DTC.Section{..} _ts -> do
+       let Tree n _ts = Tree.current z in
+       case n of
+        BodySection{..} -> do
                H.li $$ do
                        H.table ! HA.class_ "toc-entry" $$
                                H.tbody $$
@@ -346,21 +492,21 @@ html5ifyToC depth z =
                                                H.td ! HA.class_ "section-number" $$
                                                        html5SectionRef $ DTC.posAncestors pos
                                                H.td ! HA.class_ "section-title" $$
-                                                       html5ify $ cleanPara $ DTC.unTitle title
-                       when (maybe True (> DTC.Nat 1) depth && not (null sections)) $
+                                                       html5ify $ cleanPlain $ unTitle title
+                       when (maybe True (> Nat 1) depth && not (null sections)) $
                                H.ul $$
                                        forM_ sections $
-                                               html5ifyToC (depth >>= DTC.predNat)
+                                               html5ifyToC (depth >>= predNat)
         _ -> pure ()
        where
        sections =
                (`Tree.runAxis` z) $
                Tree.axis_child
                `Tree.axis_filter_current` \case
-                TreeN DTC.Section{} _ -> True
+                Tree BodySection{} _ -> True
                 _ -> False
 
-html5ifyToF :: [Text] -> Html5
+html5ifyToF :: [TL.Text] -> Html5
 html5ifyToF types = do
        figsByType <- liftStateMarkup $ S.gets state_figures
        let figs =
@@ -376,188 +522,244 @@ html5ifyToF types = do
                                H.a ! HA.href ("#"<>attrify pos) $$ do
                                        html5ify type_
                                        html5ify $ DTC.posAncestors pos
-                       H.td ! HA.class_ "figure-name" $$
-                               html5ify $ cleanPara $ DTC.unTitle title
+                       forM_ title $ \ti ->
+                               H.td ! HA.class_ "figure-title" $$
+                                       html5ify $ cleanPlain $ unTitle ti
 
-instance Html5ify [DTC.Block] where
-       html5ify = mapM_ html5ify
-instance Html5ify DTC.Block where
+cleanPlain :: Plain -> Plain
+cleanPlain ps =
+       ps >>= \case
+        Tree PlainIref{} ls -> cleanPlain ls
+        Tree PlainNote{} _  -> mempty
+        Tree n ts -> pure $ Tree n $ cleanPlain ts
+
+instance Html5ify Para where
        html5ify = \case
-        DTC.Para{..} ->
-               html5CommonAttrs attrs $
-               H.p ! HA.class_ "para"
-                   ! HA.id (attrify pos) $$ do
-                       html5ify para
-        DTC.OL{..} ->
-               html5CommonAttrs attrs $
-               H.ol ! HA.class_ "ol"
-                    ! HA.id (attrify pos) $$ do
+        ParaItem{..} ->
+               html5CommonAttrs def
+                { classes="para":cls item
+                } $
+                       html5ify item
+        ParaItems{..} ->
+               html5CommonAttrs attrs
+                { classes = "para":classes attrs
+                , DTC.id  = id_ pos
+                } $
+               H.div $$
                        forM_ items $ \item ->
-                               H.li $$ html5ify item
-        DTC.UL{..} ->
-               html5CommonAttrs attrs $
-               H.ul ! HA.class_ "ul"
-                    ! HA.id (attrify pos) $$ do
-                       forM_ items $ \item ->
-                               H.li $$ html5ify item
-        DTC.Comment t ->
-               html5ify $ H.Comment (H.Text t) ()
-instance Html5ify DTC.Lines where
+                               html5AttrClass (cls item) $
+                               html5ify item
+        where
+               id_ = Just . Ident . Plain.text def . DTC.posAncestors
+               cls = \case
+                ParaPlain{}     -> []
+                ParaArtwork{..} -> ["artwork", "artwork-"<>type_]
+                ParaQuote{..}   -> ["quote", "quote-"<>type_]
+                ParaComment{}   -> []
+                ParaOL{}        -> ["ol"]
+                ParaUL{}        -> ["ul"]
+instance Html5ify ParaItem where
        html5ify = \case
-        Tree0 v ->
-               case v of
-                DTC.BR      -> html5ify H.br
-                DTC.Plain t -> html5ify t
-        TreeN k ls ->
-               case k of
-                DTC.B    -> H.strong $$ html5ify ls
-                DTC.Code -> H.code   $$ html5ify ls
-                DTC.Del  -> H.del    $$ html5ify ls
-                DTC.I    -> do
-                       i <- liftStateMarkup $ do
-                               i <- S.gets $ Plain.state_italic . state_plainify
-                               S.modify $ \s ->
-                                       s{state_plainify=
-                                               (state_plainify s){Plain.state_italic=
-                                                       not i}}
-                               return i
-                       H.em ! HA.class_ (if i then "even" else "odd") $$
-                               html5ify ls
-                       liftStateMarkup $
-                               S.modify $ \s ->
-                                       s{state_plainify=
-                                               (state_plainify s){Plain.state_italic=i}}
-                DTC.Sub  -> H.sub    $$ html5ify ls
-                DTC.Sup  -> H.sup    $$ html5ify ls
-                DTC.SC   -> H.span ! HA.class_ "smallcaps" $$ html5ify ls
-                DTC.U    -> H.span ! HA.class_ "underline" $$ html5ify ls
-                DTC.Note -> ""
-                DTC.Q -> do
-                       d <- liftStateMarkup $ do
-                               d <- S.gets $ Plain.state_quote . state_plainify
-                               S.modify $ \s -> s{state_plainify=
-                                       (state_plainify s){Plain.state_quote=
-                                               DTC.succNat d}}
-                               return d
-                       H.span ! HA.class_ "q" $$ do
-                               html5ify $ Plain.L10n_QuoteOpen d
-                               html5ify $ TreeN DTC.I ls
-                               html5ify $ Plain.L10n_QuoteClose d
-                       liftStateMarkup $
-                               S.modify $ \s ->
-                                       s{state_plainify=
-                                               (state_plainify s){Plain.state_quote = d}}
-                DTC.Eref{..} ->
-                       H.a ! HA.class_ "eref"
-                           ! HA.href (attrify href) $$
-                               if null ls
-                               then html5ify $ DTC.unURL href
-                               else html5ify ls
-                DTC.Iref{..} ->
-                       case anchor of
-                        Nothing -> html5ify ls
-                        Just DTC.Anchor{..} ->
-                               H.span ! HA.class_ "iref"
-                                      ! HA.id (attrify (term,count)) $$
-                                       html5ify ls
-                DTC.Ref{..} ->
-                       H.a ! HA.class_ "ref"
-                           ! HA.href ("#"<>attrify to) $$
+        ParaPlain p -> H.p $$ html5ify p
+        ParaArtwork{..} -> H.pre $$ do html5ify text
+        ParaQuote{..} -> H.div $$ do html5ify paras
+        ParaComment t -> html5ify $ H.Comment (H.String $ TL.unpack t) ()
+        ParaOL items ->
+               H.table $$ do
+                       H.tbody $$
+                               forM_ items $ \ListItem{..} -> do
+                                       H.tr $$ do
+                                               H.td ! HA.class_ "name" $$ do
+                                                       html5ify name
+                                                       "."::Html5
+                                               H.td ! HA.class_ "value" $$
+                                                       html5ify paras
+        ParaUL items ->
+               H.dl $$ do
+                       forM_ items $ \item -> do
+                               H.dt $$ "—"
+                               H.dd $$ html5ify item
+instance Html5ify [Para] where
+       html5ify = mapM_ html5ify
+
+instance Html5ify Plain where
+       html5ify ps =
+               case Seq.viewl ps of
+                Seq.EmptyL -> mempty
+                curr Seq.:< next ->
+                       case curr of
+                       -- NOTE: gather adjacent PlainNotes
+                        Tree PlainNote{} _
+                         | (notes, rest) <- Seq.spanl (\case {Tree PlainNote{} _ -> True; _ -> False}) next -> do
+                               H.sup ! HA.class_ "note-numbers" $$ do
+                                       html5ify curr
+                                       forM_ notes $ \note -> do
+                                               ", "::Html5
+                                               html5ify note
+                               " "::Html5
+                               html5ify rest
+                       --
+                        _ -> do
+                               html5ify curr
+                               html5ify next
+instance Html5ify (Tree PlainNode)
+ where html5ify (Tree n ls) =
+       case n of
+        PlainBR     -> html5ify H.br
+        PlainText t -> html5ify t
+        PlainGroup  -> html5ify ls
+        PlainB      -> H.strong $$ html5ify ls
+        PlainCode   -> H.code   $$ html5ify ls
+        PlainDel    -> H.del    $$ html5ify ls
+        PlainI -> do
+               i <- liftStateMarkup $ do
+                       i <- S.gets $ Plain.state_italic . state_plainify
+                       S.modify $ \s ->
+                               s{state_plainify=
+                                       (state_plainify s){Plain.state_italic=
+                                               not i}}
+                       return i
+               H.em ! HA.class_ (if i then "even" else "odd") $$
+                       html5ify ls
+               liftStateMarkup $
+                       S.modify $ \s ->
+                               s{state_plainify=
+                                       (state_plainify s){Plain.state_italic=i}}
+        PlainSub  -> H.sub    $$ html5ify ls
+        PlainSup  -> H.sup    $$ html5ify ls
+        PlainSC   -> H.span ! HA.class_ "smallcaps" $$ html5ify ls
+        PlainU    -> H.span ! HA.class_ "underline" $$ html5ify ls
+        PlainNote{..} ->
+               case number of
+                Nothing -> mempty
+                Just num ->
+                       H.a ! HA.class_ "note-ref"
+                           ! HA.id ("note-ref."<>attrify num)
+                           ! HA.href ("#note."<>attrify num) $$
+                               html5ify num
+        PlainQ -> do
+               H.span ! HA.class_ "q" $$ do
+                       Loqualization loc <- liftStateMarkup $ S.gets state_l10n
+                       Plain.l10n_Quote (html5ify $ Tree PlainI ls) loc
+        PlainEref{..} ->
+               H.a ! HA.class_ "eref"
+                   ! HA.href (attrify href) $$
                        if null ls
-                       then html5ify to
+                       then html5ify $ unURL href
                        else html5ify ls
-                DTC.Rref{..} -> do
-                       refs <- liftStateMarkup $ S.gets state_references
-                       case Map.lookup to refs of
-                        Nothing -> do
-                               "["::Html5
-                               H.span ! HA.class_ "rref-broken" $$
-                                       html5ify to
-                               "]"
-                        Just DTC.About{..} -> do
-                               when (not $ null ls) $
-                                       forM_ (List.take 1 titles) $ \(DTC.Title title) -> do
-                                               html5ify $ TreeN DTC.Q $
-                                                       case url of
-                                                        Nothing -> title
-                                                        Just u -> pure $ TreeN (DTC.Eref u) title
-                                               " "::Html5
-                               "["::Html5
-                               H.a ! HA.class_ "rref"
-                                   ! HA.href ("#rref."<>attrify to)
-                                   ! HA.id ("rref."<>attrify to<>maybe "" (\DTC.Anchor{..} -> "."<>attrify count) anchor) $$
-                                       html5ify to
-                               "]"
-instance Html5ify DTC.URL where
-       html5ify (DTC.URL url) =
-               H.a ! HA.class_ "eref"
-                   ! HA.href (attrify url) $$
-                       html5ify url
+        PlainIref{..} ->
+               case anchor of
+                Nothing -> html5ify ls
+                Just Anchor{..} ->
+                       H.span ! HA.class_ "iref"
+                              ! HA.id (attrifyIrefCount term count) $$
+                               html5ify ls
+        PlainRef{..} ->
+               H.a ! HA.class_ "ref"
+                   ! HA.href ("#"<>attrify to) $$
+               if null ls
+               then html5ify to
+               else html5ify ls
+        PlainRref{..} -> do
+               refs <- liftStateMarkup $ S.gets state_references
+               case Map.lookup to refs of
+                Nothing -> do
+                       "["::Html5
+                       H.span ! HA.class_ "rref-broken" $$
+                               html5ify to
+                       "]"
+                Just About{..} -> do
+                       unless (null ls) $
+                               forM_ (List.take 1 titles) $ \(Title title) -> do
+                                       html5ify $ Tree PlainQ $
+                                               case url of
+                                                Nothing -> title
+                                                Just u -> pure $ Tree (PlainEref u) title
+                                       " "::Html5
+                       "["::Html5
+                       H.a ! HA.class_ "rref"
+                           ! HA.href ("#rref."<>attrify to)
+                           ! HA.id ("rref."<>attrify to<>maybe "" (\Anchor{..} -> "."<>attrify count) anchor) $$
+                               html5ify to
+                       "]"
 
-instance Attrify DTC.Words where
-       attrify term =
-               "iref" <> "." <> attrify (Anchor.plainifyWords term)
-instance Attrify (DTC.Words,DTC.Nat1) where
-       attrify (term,count) =
-               "iref"
-                <> "." <> attrify (Anchor.plainifyWords term)
-                <> "." <> attrify count
-instance Html5ify DTC.Date where
-       html5ify = html5ify . Plain.L10n_Date
-instance Html5ify DTC.About where
-       html5ify DTC.About{..} =
+instance Html5ify [Title] where
+       html5ify =
+               html5ify . fold . List.intersperse sep . toList
+               where sep = Title $ Seq.singleton $ tree0 $ PlainText " — "
+instance Html5ify About where
+       html5ify About{..} =
                html5CommasDot $ concat $
                 [ html5Titles titles
-                , html5Entity <$> authors
-                , html5ify    <$> maybeToList date
-                , html5Entity <$> maybeToList editor
-                , html5Serie  <$> series
+                , html5ify <$> authors
+                , html5ify <$> maybeToList date
+                , html5ify <$> maybeToList editor
+                , html5ify <$> series
                 ]
                where
-               html5Titles :: [DTC.Title] -> [Html5]
+               html5Titles :: [Title] -> [Html5]
                html5Titles ts | null ts = []
-               html5Titles ts = [html5Title $ fold $ List.intersperse (DTC.Title " — ") $ toList ts]
-               html5Title (DTC.Title title) =
-                       html5ify $ TreeN DTC.Q $
+               html5Titles ts = [html5Title $ joinTitles ts]
+                       where
+                       joinTitles = fold . List.intersperse sep . toList
+                       sep = Title $ Seq.singleton $ tree0 $ PlainText " — "
+               html5Title (Title title) =
+                       html5ify $ Tree PlainQ $
                                case url of
                                 Nothing -> title
-                                Just u -> pure $ TreeN (DTC.Eref u) title
-               html5SerieHref href DTC.Serie{..} = do
-                       sp <- liftStateMarkup $ S.gets state_plainify
+                                Just u -> pure $ Tree (PlainEref u) title
+instance Html5ify Serie where
+       html5ify s@Serie{id=id_, name} = do
+               Loqualization loc <- liftStateMarkup $ S.gets state_l10n
+               case urlSerie s of
+                Nothing -> do
+                       html5ify name
+                       Plain.l10n_Colon loc :: Html5
+                       html5ify id_
+                Just href -> do
                        html5ify $
-                               TreeN DTC.Eref{href} $
+                               Tree PlainEref{href} $
                                Seq.fromList
-                                [ Tree0 $ DTC.Plain $ name
-                                , Tree0 $ DTC.Plain $ TL.toStrict $ Plain.text sp Plain.L10n_Colon
-                                , Tree0 $ DTC.Plain key
+                                [ tree0 $ PlainText $ name
+                                , tree0 $ PlainText $ Plain.l10n_Colon loc
+                                , tree0 $ PlainText id_
                                 ]
-               html5Serie s@DTC.Serie{name="RFC", key} | Text.all Char.isDigit key =
-                       html5SerieHref (DTC.URL $ "https://tools.ietf.org/html/rfc"<>key) s
-               html5Serie s@DTC.Serie{name="DOI", key} =
-                       html5SerieHref (DTC.URL $ "https://dx.doi.org/"<>key) s
-               html5Serie DTC.Serie{..} = do
-                       html5ify name
-                       html5ify Plain.L10n_Colon
-                       html5ify key
-               html5Entity DTC.Entity{url=mu, ..} = do
-                       html5ify @DTC.Lines $
-                               case () of
-                                _ | not (Text.null email) ->
-                                       TreeN (DTC.Eref $ DTC.URL $ "mailto:"<>email) $
-                                               pure $ Tree0 $ DTC.Plain name
-                                _ | Just u <- mu  ->
-                                       TreeN (DTC.Eref u) $
-                                               pure $ Tree0 $ DTC.Plain name
-                                _ -> Tree0 $ DTC.Plain name
-                       forM_ org $ \o -> do
-                               " ("::Html5
-                               html5Entity o
-                               ")"::Html5
-instance Html5ify DTC.Reference where
-       html5ify DTC.Reference{id=id_, ..} =
+instance Html5ify Entity where
+       html5ify Entity{..} = do
+               html5ify $
+                       case () of
+                        _ | not (TL.null email) ->
+                               Tree (PlainEref $ URL $ "mailto:"<>email) $
+                                       pure $ tree0 $ PlainText name
+                        _ | Just u <- url ->
+                               Tree (PlainEref u) $
+                                       pure $ tree0 $ PlainText name
+                        _ -> tree0 $ PlainText name
+               forM_ org $ \o -> do
+                       " ("::Html5
+                       html5ify o
+                       ")"::Html5
+instance Html5ify Words where
+       html5ify = html5ify . Anchor.plainifyWords
+instance Html5ify Alias where
+       html5ify Alias{id=id_, ..} = do
+               H.a ! HA.class_ "alias"
+                   ! HA.id (attrify id_) $$
+                       mempty
+instance Html5ify URL where
+       html5ify (URL url) =
+               H.a ! HA.class_ "eref"
+                   ! HA.href (attrify url) $$
+                       html5ify url
+instance Html5ify Date where
+       html5ify date = do
+               Loqualization loc <- liftStateMarkup $ S.gets state_l10n
+               Plain.l10n_Date date loc
+instance Html5ify Reference where
+       html5ify Reference{id=id_, ..} =
                H.tr $$ do
                        H.td ! HA.class_ "reference-key" $$
-                               html5ify @DTC.Lines $ TreeN DTC.Rref{anchor=Nothing, to=id_} Seq.empty
+                               html5ify $ Tree PlainRref{anchor=Nothing, to=id_} Seq.empty
                        H.td ! HA.class_ "reference-content" $$ do
                                html5ify about
                                rrefs <- liftStateMarkup $ S.gets state_rrefs
@@ -566,10 +768,26 @@ instance Html5ify DTC.Reference where
                                 Just anchs ->
                                        H.span ! HA.class_ "reference-rrefs" $$
                                                html5CommasDot $
-                                               (<$> List.reverse anchs) $ \DTC.Anchor{..} ->
+                                               (<$> List.reverse anchs) $ \Anchor{..} ->
                                                        H.a ! HA.class_ "reference-rref"
                                                            ! HA.href ("#rref."<>attrify id_<>"."<>attrify count) $$
                                                                html5ify $ DTC.posAncestors section
+instance Html5ify PosPath where
+       html5ify ancs =
+               case toList ancs of
+                [(_n,c)] -> do
+                       html5ify $ show c
+                       html5ify '.'
+                as ->
+                       html5ify $
+                               Text.intercalate "." $
+                               Text.pack . show . snd <$> as
+instance Html5ify Plain.Plain where
+       html5ify p = do
+               sp <- liftStateMarkup $ S.gets state_plainify
+               let (t,sp') = Plain.runPlain p sp
+               html5ify t
+               liftStateMarkup $ S.modify $ \s -> s{state_plainify=sp'}
 
 html5CommasDot :: [Html5] -> Html5
 html5CommasDot [] = pure ()
@@ -577,21 +795,31 @@ html5CommasDot hs = do
        sequence_ $ List.intersperse ", " hs
        "."
 
-html5CommonAttrs :: DTC.CommonAttrs -> Html5 -> Html5
-html5CommonAttrs DTC.CommonAttrs{id=id_, ..} =
-       Compose . (addClass . addId <$>) . getCompose
-       where
-       addClass =
-               case classes of
-                [] -> id
-                _  -> H.AddCustomAttribute "class" (H.Text $ Text.unwords classes)
-       addId = maybe id (\(DTC.Ident i) ->
-               H.AddCustomAttribute "id" (H.Text i)) id_
+html5AttrClass :: [TL.Text] -> Html5 -> Html5
+html5AttrClass = \case
+ [] -> Cat.id
+ cls  ->
+       Compose .
+       (H.AddCustomAttribute "class"
+        (H.String $ TL.unpack $ TL.unwords cls) <$>) .
+       getCompose
+
+html5AttrId :: Ident -> Html5 -> Html5
+html5AttrId (Ident id_) =
+       Compose .
+       (H.AddCustomAttribute "id"
+        (H.String $ TL.unpack id_) <$>) .
+       getCompose
+
+html5CommonAttrs :: CommonAttrs -> Html5 -> Html5
+html5CommonAttrs CommonAttrs{id=id_, ..} =
+       html5AttrClass classes .
+       maybe Cat.id html5AttrId id_
 
-html5SectionNumber :: DTC.PosPath -> Html5
+html5SectionNumber :: PosPath -> Html5
 html5SectionNumber = go mempty
        where
-       go :: DTC.PosPath -> DTC.PosPath -> Html5
+       go :: PosPath -> PosPath -> Html5
        go prev next =
                case Seq.viewl next of
                 Seq.EmptyL -> pure ()
@@ -602,43 +830,91 @@ html5SectionNumber = go mempty
                                html5ify '.'
                                go (prev Seq.|>a) as
 
-html5SectionRef :: DTC.PosPath -> Html5
+html5SectionRef :: PosPath -> Html5
 html5SectionRef as =
        H.a ! HA.href ("#"<>attrify as) $$
                html5ify as
 
-instance Html5ify DTC.PosPath where
-       html5ify ancs =
-               case toList ancs of
-                [(_n,c)] -> do
-                       html5ify $ show c
-                       html5ify '.'
-                as ->
-                       html5ify $
-                               Text.intercalate "." $
-                               Text.pack . show . snd <$> as
-instance Html5ify Plain where
-       html5ify p = do
-               sp <- liftStateMarkup $ S.gets state_plainify
-               let (t,sp') = Plain.runPlain p sp
-               html5ify t
-               liftStateMarkup $ S.modify $ \s -> s{state_plainify=sp'}
-instance Attrify Plain where
-       attrify p =
-               let (t,_) = Plain.runPlain p def in
-               attrify t
-
-instance Attrify DTC.PosPath where
+-- * 'Attrify'
+instance Attrify Anchor where
+       attrify Anchor{..} = attrify section <> "." <> attrify count
+instance Attrify Plain.Plain where
+       attrify p = attrify t
+               where (t,_) = Plain.runPlain p def
+instance Attrify PosPath where
        attrify = attrify . plainify
 instance Attrify DTC.Pos where
        attrify = attrify . DTC.posAncestors
 
--- * Type 'L10n'
-instance Html5ify Plain.L10n where
-       html5ify = html5ify . plainify
-instance Localize ls Plain Plain.L10n => Localize ls Html5 Plain.L10n where
-       localize loc a = html5ify (Locale.localize loc a::Plain)
-instance LocalizeIn FR Html5 Plain.L10n where
-       localizeIn loc = html5ify @Plain . localizeIn loc
-instance LocalizeIn EN Html5 Plain.L10n where
-       localizeIn loc = html5ify @Plain . localizeIn loc
+attrifyIref :: Words -> H.AttributeValue
+attrifyIref term =
+       "iref" <> "." <> attrify (Anchor.plainifyWords term)
+attrifyIrefCount :: Words -> Nat1 -> H.AttributeValue
+attrifyIrefCount term count =
+       "iref"
+        <> "." <> attrify (Anchor.plainifyWords term)
+        <> "." <> attrify count
+
+-- * Class 'L10n'
+class
+ ( Plain.L10n msg lang
+ , Plain.L10n TL.Text lang
+ ) => L10n msg lang where
+       l10n_Header_Address :: FullLocale lang -> msg
+       l10n_Header_Date    :: FullLocale lang -> msg
+       l10n_Header_Version :: FullLocale lang -> msg
+       l10n_Header_Origin  :: FullLocale lang -> msg
+       l10n_Header_Source  :: FullLocale lang -> msg
+instance L10n Html5 EN where
+       l10n_Header_Address _loc = "Address"
+       l10n_Header_Date    _loc = "Date"
+       l10n_Header_Origin  _loc = "Origin"
+       l10n_Header_Source  _loc = "Source"
+       l10n_Header_Version _loc = "Version"
+instance L10n Html5 FR where
+       l10n_Header_Address _loc = "Adresse"
+       l10n_Header_Date    _loc = "Date"
+       l10n_Header_Origin  _loc = "Origine"
+       l10n_Header_Source  _loc = "Source"
+       l10n_Header_Version _loc = "Version"
+
+instance Plain.L10n Html5 EN where
+       l10n_Colon             loc = html5ify (Plain.l10n_Colon loc :: TL.Text)
+       l10n_Table_of_Contents loc = html5ify (Plain.l10n_Table_of_Contents loc :: TL.Text)
+       l10n_Date date         loc = html5ify (Plain.l10n_Date date loc :: TL.Text)
+       l10n_Quote msg _loc = do
+               depth <- liftStateMarkup $ S.gets $ Plain.state_quote . state_plainify
+               let (o,c) :: (Html5, Html5) =
+                       case unNat depth `mod` 3 of
+                        0 -> ("“","”")
+                        1 -> ("« "," »")
+                        _ -> ("‟","„")
+               o
+               setDepth $ succNat depth
+               msg
+               setDepth $ depth
+               c
+               where
+               setDepth d =
+                       liftStateMarkup $ S.modify' $ \s ->
+                               s{state_plainify=(state_plainify s){Plain.state_quote=d}}
+instance Plain.L10n Html5 FR where
+       l10n_Colon             loc = html5ify (Plain.l10n_Colon loc :: TL.Text)
+       l10n_Table_of_Contents loc = html5ify (Plain.l10n_Table_of_Contents loc :: TL.Text)
+       l10n_Date date         loc = html5ify (Plain.l10n_Date date loc :: TL.Text)
+       l10n_Quote msg _loc = do
+               depth <- liftStateMarkup $ S.gets $ Plain.state_quote . state_plainify
+               let (o,c) :: (Html5, Html5) =
+                       case unNat depth `mod` 3 of
+                        0 -> ("« "," »")
+                        1 -> ("“","”")
+                        _ -> ("‟","„")
+               o
+               setDepth $ succNat depth
+               msg
+               setDepth $ depth
+               c
+               where
+               setDepth d =
+                       liftStateMarkup $ S.modify' $ \s ->
+                               s{state_plainify=(state_plainify s){Plain.state_quote=d}}