1 {-# LANGUAGE DuplicateRecordFields #-}
2 {-# LANGUAGE FlexibleInstances #-}
3 {-# LANGUAGE FlexibleContexts #-}
4 {-# LANGUAGE MultiParamTypeClasses #-}
5 {-# LANGUAGE OverloadedStrings #-}
6 {-# LANGUAGE ScopedTypeVariables #-}
7 {-# LANGUAGE TypeApplications #-}
8 {-# LANGUAGE ViewPatterns #-}
9 {-# OPTIONS_GHC -fno-warn-orphans #-}
10 -- | Render a DTC source file in HTML5.
11 module Language.DTC.Write.HTML5 where
13 -- import Control.Monad.Trans.Class (MonadTrans(..))
14 -- import Data.Functor.Identity (Identity(..))
15 -- import qualified Data.Map.Strict as Map
16 -- import qualified Data.TreeSeq.Strict as Tree
17 -- import qualified Data.Sequence as Seq
18 import Control.Applicative (Applicative(..))
19 import Control.Monad (Monad(..), forM_, mapM_, when)
20 import Data.Char (Char)
21 import Data.Eq (Eq(..))
22 import Data.Foldable (Foldable(..))
23 import Data.Function (($), (.))
24 import Data.Functor ((<$>))
25 import Data.Functor.Compose (Compose(..))
27 import Data.Map.Strict (Map)
28 import Data.Maybe (Maybe(..), mapMaybe)
29 import Data.Monoid (Monoid(..))
30 import Data.Ord (Ord(..))
31 import Data.Semigroup (Semigroup(..))
32 import Data.Sequence (Seq)
33 import Data.String (String)
34 import Data.Text (Text)
35 import Data.TreeSeq.Strict (Tree(..))
36 import Data.Tuple (snd)
37 import Prelude (Num(..))
38 import Text.Blaze ((!))
39 import Text.Blaze.Html (Html)
40 import Text.Show (Show(..))
41 import qualified Control.Monad.Trans.State as S
42 import qualified Data.List as List
43 import qualified Data.Text as Text
44 import qualified Data.Text.Lazy as TL
45 import qualified Data.TreeSeq.Strict.Zipper as Tree
46 import qualified Text.Blaze.Html5 as H
47 import qualified Text.Blaze.Html5.Attributes as HA
48 import qualified Text.Blaze.Internal as H
49 import System.FilePath (FilePath)
51 import Text.Blaze.Utils
52 import Data.Locale hiding (localize)
53 import qualified Data.Locale as Locale
55 import Language.DTC.Document (Document)
56 import Language.DTC.Write.XML ()
57 import Language.XML (XmlName(..), XmlPos(..))
58 import qualified Language.DTC.Document as DTC
59 -- import Debug.Trace (trace)
62 type Html5 = StateMarkup StateHtml5 ()
64 -- ** Type 'StateHtml5'
67 { styles :: Map FilePath CSS
68 , scripts :: Map FilePath Script
69 , localize :: MsgHtml5 -> Html5
71 stateHtml5 :: StateHtml5
72 stateHtml5 = StateHtml5
75 , localize = html5ify . show
80 -- ** Class 'Html5ify'
81 class Html5ify a where
82 html5ify :: a -> Html5
83 instance Html5ify Char where
84 html5ify = html5ify . H.toMarkup
85 instance Html5ify Text where
86 html5ify = html5ify . H.toMarkup
87 instance Html5ify String where
88 html5ify = html5ify . H.toMarkup
89 instance Html5ify H.Markup where
90 html5ify = Compose . return
91 instance Html5ify DTC.Title where
92 html5ify (DTC.Title t) = html5ify t
93 instance Html5ify DTC.Ident where
94 html5ify (DTC.Ident i) = html5ify i
97 Localize ls Html5 MsgHtml5 =>
99 LocaleIn ls -> Document -> Html
100 html5Document locale DTC.Document{..} = do
101 let (html5Body, StateHtml5{..}) =
102 runStateMarkup stateHtml5 $ do
103 liftStateMarkup $ S.modify $ \s -> s{localize = Locale.localize locale}
106 H.html ! HA.lang (attrValue $ countryCode locale) $ do
108 H.meta ! HA.httpEquiv "Content-Type"
109 ! HA.content "text/html; charset=UTF-8"
110 whenSome (DTC.titles $ DTC.about (head :: DTC.Head)) $ \ts ->
112 H.toMarkup $ plainify $ List.head ts
113 forM_ (DTC.links $ DTC.about (head :: DTC.Head)) $ \DTC.Link{rel, href} ->
114 H.link ! HA.rel (attrValue rel)
115 ! HA.href (attrValue href)
116 H.meta ! HA.name "generator"
119 (`mapMaybe` toList body) $ \case
122 forM_ chapters $ \DTC.Section{..} ->
123 H.link ! HA.rel "Chapter"
124 ! HA.title (attrValue $ plainify title)
125 ! HA.href ("#"<>attrValue pos)
126 H.link ! HA.rel "stylesheet"
127 ! HA.type_ "text/css"
128 ! HA.href "style/dtc-html5.css"
129 forM_ styles $ \style ->
130 H.style ! HA.type_ "text/css" $
132 forM_ scripts $ \script ->
133 H.script ! HA.type_ "application/javascript" $
138 -- * Type 'BodyCursor'
139 -- | Cursor to navigate within a 'DTC.Body' according to many axis (like in XSLT).
140 type BodyCursor = Tree.Zipper DTC.BodyKey (Seq DTC.BodyValue)
141 instance Html5ify DTC.Body where
143 forM_ (Tree.zippers body >>= Tree.axis_following_sibling) $
146 instance Html5ify BodyCursor where
148 case Tree.current z of
149 TreeN k _ts -> html5BodyKey z k
150 Tree0 vs -> forM_ vs $ html5BodyValue z
152 html5BodyKey :: BodyCursor -> DTC.BodyKey -> Html5
153 html5BodyKey z = \case
155 H.section ! HA.class_ "section"
156 ! HA.id (attrValue pos) $$ do
157 html5CommonAttrs attrs $
158 H.table ! HA.class_ "section-header" $$
161 H.td ! HA.class_ "section-number" $$ do
164 H.td ! HA.class_ "section-title" $$ do
166 forM_ (Tree.axis_child z) $
168 html5BodyValue :: BodyCursor -> DTC.BodyValue -> Html5
169 html5BodyValue z = \case
173 H.nav ! HA.class_ "toc"
174 ! HA.id (attrValue pos) $$ do
175 H.span ! HA.class_ "toc-name" $$
176 H.a ! HA.href (attrValue pos) $$
177 html5ify MsgHTML5_Table_of_Contents
179 forM_ (Tree.axis_following_sibling z) $
181 where d = case depth of { Just (DTC.Nat n) -> n; _ -> 0 }
183 H.nav ! HA.class_ "tof"
184 ! HA.id (attrValue pos) $$
185 H.table ! HA.class_ "tof" $$
187 forM_ (Tree.axis_preceding z) $
189 where d = case depth of { Just (DTC.Nat n) -> n; _ -> 0 }
191 html5CommonAttrs attrs $
192 H.div ! HA.class_ (attrValue $ "figure-"<>type_)
193 ! HA.id (attrValue pos) $$ do
194 H.table ! HA.class_ "figure-caption" $$
197 H.td ! HA.class_ "figure-number" $$ do
198 H.a ! HA.href ("#"<>attrValue pos) $$
201 H.td ! HA.class_ "figure-name" $$
203 H.div ! HA.class_ "figure-content" $$ do
205 html5ToC :: Int -> BodyCursor -> Html5
207 case Tree.current z of
208 TreeN DTC.Section{..} _ts -> do
210 H.table ! HA.class_ "toc-entry" $$
214 html5SectionRef $ xmlPosAncestors pos
219 forM_ (Tree.axis_child z) $
222 html5ToF :: Int -> BodyCursor -> Html5
224 case Tree.current z of
229 H.td ! HA.class_ "figure-number" $$
230 H.a ! HA.href ("#"<>attrValue pos) $$
232 H.td ! HA.class_ "figure-name" $$
237 instance Html5ify [DTC.Vertical] where
238 html5ify = mapM_ html5ify
239 instance Html5ify DTC.Vertical where
242 html5CommonAttrs attrs $
243 H.div ! HA.class_ "para"
244 ! HA.id (attrValue pos) $$ do
247 html5CommonAttrs attrs $
248 H.ol ! HA.class_ "ol"
249 ! HA.id (attrValue pos) $$ do
250 forM_ items $ \item ->
251 H.li $$ html5ify item
253 html5CommonAttrs attrs $
254 H.ul ! HA.class_ "ul"
255 ! HA.id (attrValue pos) $$ do
256 forM_ items $ \item ->
257 H.li $$ html5ify item
259 html5CommonAttrs attrs $
260 H.div ! HA.class_ "rl"
261 ! HA.id (attrValue pos) $$ do
265 html5ify $ H.Comment (H.Text t) ()
270 instance Html5ify DTC.Horizontal where
272 DTC.BR -> html5ify H.br
273 DTC.B hs -> H.strong $$ html5ify hs
274 DTC.Code hs -> H.code $$ html5ify hs
275 DTC.Del hs -> H.del $$ html5ify hs
276 DTC.I hs -> H.i $$ html5ify hs
282 DTC.SC hs -> html5ify hs
283 DTC.Sub hs -> H.sub $$ html5ify hs
284 DTC.Sup hs -> H.sup $$ html5ify hs
285 DTC.U hs -> H.span ! HA.class_ "underline" $$ html5ify hs
287 H.a ! HA.class_ "eref"
288 ! HA.href (attrValue href) $$
291 H.a ! HA.class_ "iref"
292 ! HA.href (attrValue to) $$
295 H.a ! HA.class_ "ref"
296 ! HA.href ("#"<>attrValue to) $$
301 H.a ! HA.class_ "rref"
302 ! HA.href (attrValue to) $$
304 DTC.Plain t -> Compose $ return $ H.toMarkup t
305 instance Html5ify [DTC.Horizontal] where
306 html5ify = mapM_ html5ify
307 instance Html5ify DTC.About where
308 html5ify DTC.About{..} =
309 forM_ titles $ \(DTC.Title title) ->
310 html5ify $ DTC.Q title
311 instance Html5ify DTC.Reference where
312 html5ify DTC.Reference{..} =
314 H.td ! HA.class_ "reference-key" $$
316 H.td ! HA.class_ "reference-content" $$
319 html5CommonAttrs :: DTC.CommonAttrs -> Html5 -> Html5
320 html5CommonAttrs DTC.CommonAttrs{..} =
321 Compose . (addClass . addId <$>) . getCompose
326 _ -> H.AddCustomAttribute "class" (H.Text $ Text.unwords classes)
330 Just (DTC.Ident i) ->
331 H.AddCustomAttribute "id" (H.Text i)
333 html5SectionNumber :: [(XmlName,Int)] -> Html5
334 html5SectionNumber = go [] . List.reverse
336 go :: [(XmlName,Int)] -> [(XmlName,Int)] -> Html5
338 go rs (a@(_n,cnt):as) = do
339 H.a ! HA.href (attrValue $ "#"<>textXmlPosAncestors (a:rs)) $$
344 html5SectionRef :: [(XmlName,Int)] -> Html5
346 H.a ! HA.href (attrValue $ "#"<>textXmlPosAncestors as) $$
353 Text.intercalate "." $
354 Text.pack . show . snd <$> as
356 textXmlPosAncestors :: [(XmlName,Int)] -> Text
357 textXmlPosAncestors =
358 snd . foldr (\(n,c) (nParent,acc) ->
371 -- * Class 'Plainify'
372 class Plainify a where
373 plainify :: a -> TL.Text
374 instance Plainify DTC.Horizontal where
377 DTC.B hs -> "*"<>plainify hs<>"*"
378 DTC.Code hs -> "`"<>plainify hs<>"`"
379 DTC.Del hs -> "-"<>plainify hs<>"-"
380 DTC.I hs -> "/"<>plainify hs<>"/"
382 DTC.Q hs -> "« "<>plainify hs<>" »"
383 DTC.SC hs -> plainify hs
384 DTC.Sub hs -> plainify hs
385 DTC.Sup hs -> plainify hs
386 DTC.U hs -> "_"<>plainify hs<>"_"
387 DTC.Eref{..} -> plainify text
388 DTC.Iref{..} -> plainify text
389 DTC.Ref{..} -> plainify text
390 DTC.Rref{..} -> plainify text
391 DTC.Plain t -> TL.fromStrict t
392 instance Plainify [DTC.Horizontal] where
393 plainify = foldMap plainify
394 instance Plainify DTC.Title where
395 plainify (DTC.Title t) = plainify t
397 instance AttrValue XmlPos where
398 attrValue = attrValue . textXmlPosAncestors . xmlPosAncestors
402 = MsgHTML5_Table_of_Contents
404 instance Html5ify MsgHtml5 where
406 loc <- liftStateMarkup $ S.gets localize
408 instance LocalizeIn FR Html5 MsgHtml5 where
410 MsgHTML5_Table_of_Contents -> "Sommaire"
411 instance LocalizeIn EN Html5 MsgHtml5 where
413 MsgHTML5_Table_of_Contents -> "Summary"