1 {-# LANGUAGE FlexibleInstances #-}
2 {-# LANGUAGE OverloadedStrings #-}
3 {-# LANGUAGE ViewPatterns #-}
4 module Language.TCT.Write.HTML5 where
6 import Control.Applicative (Applicative(..))
7 import Control.Monad (Monad(..), forM_, mapM_, when)
9 import Data.Char (Char)
10 import Data.Default.Class (Default(..))
11 import Data.Eq (Eq(..))
12 import Data.Foldable (Foldable(..))
13 import Data.Function (($), (.), id)
14 import Data.Functor ((<$>))
15 import Data.Functor.Compose (Compose(..))
16 import Data.Maybe (Maybe(..))
17 import Data.Monoid (Monoid(..))
18 import Data.Ord (Ord(..))
19 import Data.Semigroup (Semigroup(..))
20 import Data.Sequence (ViewL(..))
21 import Data.String (String, IsString(..))
22 import Data.Text (Text)
23 import Data.TreeSeq.Strict (Tree(..),Trees)
24 import Prelude (Num(..), undefined, error)
25 import Text.Blaze ((!))
26 import Text.Blaze.Html (Html)
27 import Text.Show (Show(..))
28 import qualified Control.Monad.Trans.State as S
29 import qualified Data.List as List
30 import qualified Data.Sequence as Seq
31 import qualified Data.Text as Text
32 import qualified Data.Text.Lazy as TL
33 import qualified Text.Blaze.Html5 as H
34 import qualified Text.Blaze.Html5.Attributes as HA
35 -- import Debug.Trace (trace)
37 import Text.Blaze.Utils
39 import qualified Language.TCT.Write.Plain as Plain
41 html5Document :: TCTs -> Html
42 html5Document body = do
46 H.meta ! HA.httpEquiv "Content-Type"
47 ! HA.content "text/html; charset=UTF-8"
48 whenJust (tokensTitle body) $ \ts ->
50 H.toMarkup $ Plain.text def $ List.head $ toList ts
51 -- link ! rel "Chapter" ! title "SomeTitle">
52 H.link ! HA.rel "stylesheet"
54 ! HA.href "style/tct-html5.css"
55 let (html5Body, State{}) =
59 H.a ! HA.id ("line-1") $ return ()
63 type Html5 = StateMarkup State ()
70 instance Default State where
76 class Html5ify a where
77 html5ify :: a -> Html5
78 instance Html5ify H.Markup where
79 html5ify = Compose . return
80 instance Html5ify Html5 where
82 instance Html5ify () where
84 instance Html5ify Char where
85 html5ify = html5ify . H.toMarkup
86 instance Html5ify Text where
87 html5ify = html5ify . H.toMarkup
88 instance Html5ify TL.Text where
89 html5ify = html5ify . H.toMarkup
90 instance Html5ify String where
91 html5ify = html5ify . H.toMarkup
92 instance Html5ify (Trees (Cell Key) Tokens) where
93 html5ify = mapM_ html5ify
94 instance Html5ify (Tree (Cell Key) Tokens) where
96 TreeN (Cell bp ep k) ts -> html5ify (Cell bp ep (k,ts))
97 Tree0 ts -> html5ify ts
98 instance Html5ify a => Html5ify (Cell a) where
99 html5ify (Cell next@(Pos line col) ep a) = do
100 prev@(Pos lineLast colLast) <- liftStateMarkup $ S.gets state_pos
102 _ | lineLast < line -> do
103 forM_ [lineLast+1..line] $ \lnum -> do
105 H.a ! HA.id ("line-"<>attrify lnum) $$ return ()
106 html5ify $ Text.replicate (col - 1) " "
107 _ | lineLast == line && colLast <= col -> do
108 html5ify $ Text.replicate (col - colLast) " "
109 _ -> error $ "html5ify: non-ascending positions: "
110 <> "\n prev: " <> show prev
111 <> "\n next: " <> show next
112 -- liftStateMarkup $ S.modify $ \s -> s{state_pos=bp}
113 liftStateMarkup $ S.modify $ \s -> s{state_pos=ep}
115 instance Html5ify (Key, Trees (Cell Key) Tokens) where
118 KeyPara -> html5ify ts
119 KeyColon n wh -> html5Key "" "" n wh ":" "" "colon"
120 KeyGreat n wh -> html5Key "" "" n wh ">" "" "great"
121 KeyEqual n wh -> html5Key "" "" n wh "=" "" "equal"
122 KeyBar n wh -> html5Key "" "" n wh "|" "" "bar"
123 KeyDot n -> html5Key "" "" n "" "." "" "dot"
124 KeyDash -> html5Key "" "" "" "" "-" " " "dash"
125 KeyDashDash -> html5Key "" "" "" "" "--" " " "dashdash"
126 KeyBrackets n -> html5Key "[" "" n "" "]" "" "dashdash"
127 KeyLower name attrs -> do
128 H.span ! HA.class_ (mconcat ["key key-lower"," key-name-",attrify name]) $$ do
129 H.span ! HA.class_ "key-mark" $$ html5ify '<'
130 H.span ! HA.class_ "key-name" $$ html5ify name
135 H.span ! HA.class_ "section-title" $$ do
136 H.span ! HA.class_ "section-mark" $$ do
137 html5ify $ Text.replicate lvl "#"
139 Tree0 title :< _ -> h lvl $$ html5ify title
143 Tree0{} :< ts' -> ts'
152 h n | n > 6 = H.span ! HA.class_ ("h h"<>attrify n)
155 html5Key :: Text -> White -> Text -> White -> Text -> White -> H.AttributeValue -> Html5
156 html5Key markBegin whmb name whn markEnd whme cl = do
157 H.span ! HA.class_ (mconcat ["key key-",cl," key-name-",attrify name]) $$ do
158 when (markBegin/="") $
159 H.span ! HA.class_ "key-mark" $$ html5ify markBegin
162 H.span ! HA.class_ "key-name" $$ html5ify name
165 H.span ! HA.class_ "key-mark" $$ html5ify markEnd
167 H.span ! HA.class_ "key-value" $$
169 instance Html5ify Tokens where
170 html5ify = mapM_ html5ify
171 instance Html5ify Token where
172 html5ify (TreeN (Cell bp ep p) ts) = do
174 PairElem name attrs -> do
175 H.span ! HA.class_ ("pair-PairElem" <> " pair-elem-"<>attrify name) $$ do
176 html5ify $ Cell bp bp{columnPos = columnPos bp + lenO} ()
178 H.span ! HA.class_ "pair-open" $$ o
179 when (not $ Seq.null ts) $
180 H.span ! HA.class_ "pair-content" $$ html5ify ts
181 html5ify $ Cell ep{columnPos = columnPos ep - lenC} ep ()
183 H.span ! HA.class_ "pair-close" $$ c
186 H.span ! HA.class_ "elem-name" $$
188 lenName = Text.length name
189 lenAttrs = sum $ (<$> attrs) $ \(elemAttr_white,ElemAttr{..}) ->
190 Text.length elemAttr_white +
191 Text.length elemAttr_name +
192 Text.length elemAttr_open +
193 Text.length elemAttr_value +
194 Text.length elemAttr_close
195 (lenO,lenC) | Seq.null ts = (1+lenName+lenAttrs+2,0)
196 | otherwise = (1+lenName+lenAttrs+1,2+lenName+1)
198 (o,c) | Seq.null ts =
199 ( "<"<>html5Name<>html5ify attrs<>"/>"
202 ( "<"<>html5Name<>html5ify attrs<>">"
203 , "</"<>html5Name<>">" )
205 let (o,c) = pairBorders p ts
206 H.span ! HA.class_ ("pair-"<>fromString (show p)) $$ do
207 html5ify $ Cell bp bp{columnPos = columnPos bp + Text.length o} ()
208 H.span ! HA.class_ "pair-open" $$ html5ify o
209 H.span ! HA.class_ "pair-content" $$ html5ify ts
210 html5ify $ Cell ep{columnPos = columnPos ep - Text.length c} ep ()
211 H.span ! HA.class_ "pair-close" $$ html5ify c
212 html5ify (Tree0 tok) = do
213 -- html5ify $ Cell bp ep ()
215 TokenPhrases ps -> html5ify ps
216 TokenRaw t -> html5ify t
219 let lines = Text.splitOn "\n" txt
220 let lnums = html5ify :
223 H.a ! HA.id ("line-"<>attrify lnum) $$ return ()
228 S.put (lin - 1 + List.length lines)
229 return $ mconcat $ List.zipWith ($) lnums lines
232 H.span ! HA.class_ "tag" $$ do
233 H.span ! HA.class_ "tag-open" $$
236 TokenEscape c -> html5ify $ ('\\' :) . pure <$> c
237 TokenLink (Cell bp ep lnk) -> do
238 html5ify $ Cell bp ep ()
239 H.a ! HA.href (attrify lnk) $$
241 instance Html5ify Phrases where
242 html5ify = mapM_ html5ify
243 instance Html5ify Phrase where
246 PhraseWord t -> html5ify t
247 PhraseWhite t -> html5ify t
248 PhraseOther t -> html5ify t
249 instance Html5ify ElemAttrs where
250 html5ify = mapM_ html5ify
251 instance Html5ify (White,ElemAttr) where
252 html5ify (elemAttr_white,ElemAttr{..}) = do
253 html5ify elemAttr_white
254 H.span ! HA.class_ "attr-name" $$
255 html5ify elemAttr_name
256 html5ify elemAttr_open
257 H.span ! HA.class_ "attr-value" $$
258 html5ify elemAttr_value
259 html5ify elemAttr_close
263 tokensTitle :: Trees (Cell Key) Tokens -> Maybe Tokens
266 TreeN (unCell -> KeySection{}) _ts -> True
269 TreeN (unCell -> KeySection _lvl) (Seq.viewl -> Tree0 title:<_) -> Just title
272 html5Spaces :: Column -> Html5
273 html5Spaces 0 = return ()
274 html5Spaces sp = H.span $$ html5ify $ Text.replicate sp " "