-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathentry.hs
224 lines (190 loc) · 6.13 KB
/
entry.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
{-# LANGUAGE PackageImports #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE OverloadedStrings #-}
#ifdef FAY
module Main (main) where
#else
module Fay.Page.Entry (main) where
#endif
#ifdef FAY
import Prelude
#else
import "fay-base" Prelude
#endif
import JQuery as J
import FFI
import Fay.Text as T
import FayRef
-- | Helper ffi's
jLength :: JQuery -> Fay Int
jLength = ffi "%1.length"
sHide :: JQuery -> Fay JQuery
sHide = ffi "%1['hide']()"
-- $(document).ready()
main :: Fay ()
main = ready $ do
print "Hello, from the world of Fay!"
appendTopLinks
setupSourceLink
processCodeBlocks
return ()
-- | Startup subprocedures
--
-- add "top" links to all h2,h3,h4,h5
appendTopLinks :: Fay ()
appendTopLinks = do
mainContent <- select ".main-content"
headings <- childrenMatching "h2,h3,h4,h5" mainContent
J.append topLink headings
topLinks <- select ".top-link"
click (scrollTo 400) topLinks
return ()
where
topLink = "<a href='#title' class='top-link'>top</a>"
-- turn link into a scrollTo
scrollTo :: Double -> Event -> Fay ()
scrollTo duration e = do
preventDefault e
sTarg <- select =<< target e
sTargHref <- getAttr "href" sTarg
case sTargHref of
Undefined -> return ()
Defined targ -> animScroll targ duration "swing"
return ()
-- animate scrolling
animScroll ::
Text -- target href
-> Double -- duration
-> Text -- easing ("swing","linear")
-> Fay ()
animScroll targH dur easing = do
targJ <- select targH
animScrollFFI targJ dur easing targH
where
animScrollFFI :: JQuery -> Double -> Text -> Text -> Fay ()
animScrollFFI = ffi "$('body,html').animate({ scrollTop: %1.offset().top }, %2, %3, function() { location.hash = %4 })"
-- setup source link visiblity behavior at the entry header. When the
-- header is clicked, it should toggle between mostly-off and always-on.
-- In mostly-off mode, a hover reveals the link. This is accomplished
-- using an IORef/FayRef...but maybe there is a better way. Juggling
-- binds/event handlers is kind of the same thing anyway.
setupSourceLink :: Fay ()
setupSourceLink = do
sourceInfo <- select ".source-info"
sourceCount <- jLength sourceInfo
let
hasSource = sourceCount > 0
when hasSource $ do
sourceToggled <- newFayRef False
header <- select ".article header"
flip mouseenter header $ \_ -> do
toggled <- readFayRef sourceToggled
unless toggled (unhide sourceInfo)
flip mouseleave header $ \_ -> do
toggled <- readFayRef sourceToggled
unless toggled (sHide sourceInfo)
flip click header $ \_ -> do
toggled <- readFayRef sourceToggled
if toggled
then sHide sourceInfo
else unhide sourceInfo
modifyFayRef' sourceToggled Prelude.not
-- process code blocks and parse "source" and "interactive" links
processCodeBlocks :: Fay ()
processCodeBlocks = do
blocks <- select ".main-content pre.sourceCode"
flip each blocks $ \_ el -> do
elJ <- select el
processBlock elJ
return True
return ()
where
processBlock :: JQuery -> Fay ()
processBlock blk = do
oldcode <- children blk
newcode <- select "<code />"
oldclasses <- fromDefined "" `mapFay` getAttr "class" oldcode
setAttr "class" oldclasses newcode
codecontents <- contents oldcode
afterProcessed <- newFayRef False
flip each codecontents $ \_ el -> do
ap <- readFayRef afterProcessed
if ap
then writeFayRef afterProcessed False
else do
processed <- processComment el blk
if processed
then writeFayRef afterProcessed True
else void $ J.append el newcode
return True
replaceWithJQuery newcode oldcode
linkBox <- childrenMatching ".code-link-box" blk
flip mouseenter blk $ \_ -> do
void $ unhide linkBox
flip mouseleave blk $ \_ -> do
void $ sHide linkBox
return ()
processComment :: Element -> JQuery -> Fay Bool
processComment el blk = do
elJ <- select el
isComment <- hasClass "co" elJ
if isComment
then do
coText <- getText elJ
processes <-
forM [("-- source: " , handleSource)
,("-- interactive: ", handleInter )] $
\(pref,handler) -> do
let isPre = pref `isPrefixOfT` coText
when isPre (handler blk coText)
return isPre
return (or processes)
else return False
handleSource :: JQuery -> Text -> Fay ()
handleSource blk coText = do
linkBox <- getLinkBox blk
J.append sourceLink linkBox
return ()
where
u = dropT (T.length "-- source: ") coText
sourceLink = T.concat ["<a href='", u, "' class='code-source-link' target='_blank'>Download source</a>"]
handleInter :: JQuery -> Text -> Fay ()
handleInter blk coText = do
linkBox <- getLinkBox blk
J.append interactiveLink linkBox
return ()
where
u = dropT (T.length "-- interactive: ") coText
interactiveLink = T.concat ["<a href='", u, "' class='code-interactive-link' target='_blank'>Interactive</a>"]
getLinkBox :: JQuery -> Fay JQuery
getLinkBox blk = do
already <- childrenMatching ".code-link-box" blk
hasAlready <- (> 0) `mapFay` jLength already
if hasAlready
then return already
else do
linkBox <- select "<div />"
addClass "code-link-box" linkBox
prepend linkBox blk
return linkBox
-- | Util functions
mapFay :: (a -> b) -> Fay a -> Fay b
mapFay f fay = do
res <- fay
return (f res)
fromDefined :: a -> Defined a -> a
fromDefined x Undefined = x
fromDefined _ (Defined x) = x
isPrefixOfT :: Text -> Text -> Bool
isPrefixOfT s1 s2 =
case (uncons s1, uncons s2) of
(Nothing, _) -> True
(_, Nothing) -> False
(Just (x,xs),Just (y,ys)) -> x == y && isPrefixOfT xs ys
dropT :: Int -> Text -> Text
dropT i txt | i <= 0 = txt
| otherwise =
case uncons txt of
Nothing -> txt
Just (_,xs) -> dropT (i-1) xs