~alcinnz/amphiarao

ref: e7e16d8e1a667715d0bda0332254d6d6e05f399b amphiarao/src/Main.hs -rw-r--r-- 7.9 KiB
e7e16d8e — Adrian Cochrane Implement WebDriver forms, viewsource, & noops. 2 years ago
                                                                                
a6104355 Adrian Cochrane
505d1225 Adrian Cochrane
d4c10f89 Adrian Cochrane
e7e16d8e Adrian Cochrane
505d1225 Adrian Cochrane
d4c10f89 Adrian Cochrane
a6104355 Adrian Cochrane
d4c10f89 Adrian Cochrane
a6104355 Adrian Cochrane
d4c10f89 Adrian Cochrane
a6104355 Adrian Cochrane
505d1225 Adrian Cochrane
d4c10f89 Adrian Cochrane
70de37ce Adrian Cochrane
a6104355 Adrian Cochrane
70de37ce Adrian Cochrane
d4c10f89 Adrian Cochrane
8e195046 Adrian Cochrane
e7e16d8e Adrian Cochrane
d4c10f89 Adrian Cochrane
505d1225 Adrian Cochrane
d4c10f89 Adrian Cochrane
e7e16d8e Adrian Cochrane
d4c10f89 Adrian Cochrane
a6104355 Adrian Cochrane
d4c10f89 Adrian Cochrane
e7e16d8e Adrian Cochrane
d4c10f89 Adrian Cochrane
e7e16d8e Adrian Cochrane
d4c10f89 Adrian Cochrane
afb3d55d Adrian Cochrane
d4c10f89 Adrian Cochrane
e7e16d8e Adrian Cochrane
d4c10f89 Adrian Cochrane
a6104355 Adrian Cochrane
d4c10f89 Adrian Cochrane
70de37ce Adrian Cochrane
d4c10f89 Adrian Cochrane
9efe611b Adrian Cochrane
d4c10f89 Adrian Cochrane
8a39949f Adrian Cochrane
d4c10f89 Adrian Cochrane
7a6334be Adrian Cochrane
afb3d55d Adrian Cochrane
70de37ce Adrian Cochrane
6a894a2a Adrian Cochrane
e7e16d8e Adrian Cochrane
505d1225 Adrian Cochrane
9efe611b Adrian Cochrane
d4c10f89 Adrian Cochrane
8a39949f Adrian Cochrane
9efe611b Adrian Cochrane
e7e16d8e Adrian Cochrane
d4c10f89 Adrian Cochrane
a6104355 Adrian Cochrane
d4c10f89 Adrian Cochrane
8a39949f Adrian Cochrane
d4c10f89 Adrian Cochrane
58648b62 Adrian Cochrane
7b26fe8f Adrian Cochrane
58648b62 Adrian Cochrane
7b26fe8f Adrian Cochrane
a5210952 Adrian Cochrane
d4c10f89 Adrian Cochrane
58648b62 Adrian Cochrane
d4c10f89 Adrian Cochrane
a6104355 Adrian Cochrane
d4c10f89 Adrian Cochrane
505d1225 Adrian Cochrane
d4c10f89 Adrian Cochrane
8a39949f Adrian Cochrane
7a6334be Adrian Cochrane
afb3d55d Adrian Cochrane
7a6334be Adrian Cochrane
70de37ce Adrian Cochrane
9efe611b Adrian Cochrane
70de37ce Adrian Cochrane
9efe611b Adrian Cochrane
70de37ce Adrian Cochrane
a6104355 Adrian Cochrane
9efe611b Adrian Cochrane
e7e16d8e Adrian Cochrane
70de37ce Adrian Cochrane
9efe611b Adrian Cochrane
70de37ce Adrian Cochrane
a6104355 Adrian Cochrane
9efe611b Adrian Cochrane
e7e16d8e Adrian Cochrane
6a894a2a Adrian Cochrane
e7e16d8e Adrian Cochrane
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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, TemplateHaskell #-}
module Main where

import Happstack.Lite
import Happstack.Server.RqData
import Happstack.Server.Monads (askRq)
import Happstack.Server.Types (rqUri)
import Control.Concurrent.MVar
import Data.HashMap.Strict as M
import Data.FileEmbed

import qualified Text.Blaze.Html5 as H
import qualified Text.Blaze.Html5.Attributes as A
import Text.Blaze.Html (text, string, contents)
import Text.Blaze.Renderer.Text (renderMarkup)
import qualified Data.Text as Txt
import Data.Text.Lazy (toStrict)

import Webdriver
import Data.UUID as ID

import qualified Text.XML.Cursor as XC
import Data.Maybe

import Messages
import Happstack.Server.I18N

import Internal
import Internal.Load as Load
import Internal.Elements as El
import Internal.Forms as Forms
import Control.Monad.IO.Class (liftIO)
import Control.Monad (forM)

import qualified UI.Templates as Tpl
import qualified UI.Search as Q

main :: IO ()
main = do
  sessions <- initSessions
  putStrLn "Serving http://localhost:8000/"
  serve Nothing $ do
    req <- askRq
    liftIO $ putStrLn $ rqUri req
    msum [
        dir "assets" $ path $ ok . toResponse . fromMaybe "Not Found" . flip Prelude.lookup $(embedDir "assets"),
        dir "webdriver" $ dir "v1" $ serveWebdriver sessions,
        postHome sessions,
        serveHome,
        dir "preview-prompt" servePreviewPrompt,
        dir "close" $ path $ deleteSession sessions,
        path $ serveSession sessions
      ]

serveHome :: ServerPart Response
serveHome = do
    nullDir
    method GET
    Tpl.page ok ["Amphiarao"] "" $ \langs -> do
        l langs AmphiaraoIntro
        Tpl.sessionForm langs

postHome sessions = do
    nullDir
    method POST
    liftIO $ putStrLn "Creating new session!"
    (uuid, session) <- liftIO $ createSession sessions M.empty
    target <- looks "target"
    -- Not much point of a blank session, so allow loading here.
    liftIO $ putStrLn "Allocated new session!"
    case target of
        (target':_) | Just url <- Load.parseAbsoluteURI target' -> liftIO $ Load.load' session url
        _ -> return ()
    liftIO $ putStrLn "Created new session!"
    seeOther ('/' : ID.toString uuid) $ toResponse ()

deleteSession sessions uuid = do
    nullDir
    method POST
    case ID.fromString uuid of
        Just id -> liftIO $ delSession id sessions
        Nothing -> return ()
    seeOther ['/'] $ toResponse ()

servePreviewPrompt = do
    nullDir
    method GET
    Tpl.page ok ["?", "Amphiarao"] "" $ \langs -> H.p $ l langs PromptPreview

---

serveSession :: Sessions -> String -> ServerPart Response
serveSession = withSession session404 $ \uuid session -> msum [
        sessionHome uuid session,
        dir "timeout" $ setTimeout uuid session,
        dir "search" $ searchSession session,
        dir "nav" $ msum [
            dir "load" $ loadPage uuid session,
            dir "reload" $ reloadPage uuid session,
            dir "back" $ sessionAction' uuid session Load.back,
            dir "next" $ sessionAction' uuid session Load.next
        ],
        dir "el" $ path $ serveEl uuid session,
        dir "click" $ clickElement uuid session,
        dir "type" $ typeElement uuid session
    ]

sessionHome uuid session = do
    nullDir
    method GET

    session' <- liftIO $ readMVar session
    let el = XC.fromDocument $ document session'
    related <- liftIO $ getRelatedEls session el
    Tpl.inspector ok ":root" session' $ Tpl.elPage uuid el related ""

session404 uuid = do
    Tpl.page notFound ["404", "Amphiarao"] "" $ \langs -> do
        H.h1 $ l langs SessionNotFound
        Tpl.sessionForm langs

setTimeout uuid session = do
    nullDir
    method POST
    timeout <- lookRead "pageLoad"
    let inner s = return $ s {timeouts = (timeouts s) {pageLoad = Just timeout}}
    liftIO $ modifyMVar_ session inner
    seeOther ('/':ID.toString uuid) $ toResponse ()

searchSession session = do
    nullDir
    method GET
    q <- look "q"
    session' <- liftIO $ readMVar session
    el <- looks "el"
    let (root, ancestors) = case el of {
        (el':_) | Just root <- ID.fromString el' >>= getEl session' ->
            (root, Prelude.reverse $ (XC.orSelf XC.ancestor) root);
        _ -> (XC.fromDocument $ document session', [])
    }

    results <- forM Q.engines $ \(header, engine) -> do
        results' <- liftIO $ engine q session root
        return (header, labelEmpty results')
    Tpl.inspector' q ok (Txt.pack ('🔎':q)) session' $ \langs -> H.main $ do
        H.aside $ do
            H.p $ do
                forM ancestors $ \el -> do
                    Tpl.xmlNode' $ XC.node el -- Should all be elements...
                    H.text " > "
                return ()
            H.dl $ do
                forM results $ \(header, results') -> do
                    H.dt $ header langs
                    forM results' $ \result -> H.dd $ result langs
                    return ()
                return ()
        H.iframe H.! A.src "/preview-prompt" H.! A.name "preview" $ ""
  where
    labelEmpty [] = [\langs -> l langs NoResults]
    labelEmpty x = x

loadPage uuid session = do
    nullDir
    method POST
    target <- look "url"
    case Load.parseAbsoluteURI target of
        Just url -> do
            liftIO $ Load.load session url
            seeOther ('/':ID.toString uuid) $ toResponse ()
        Nothing -> do
            session' <- liftIO $ readMVar session
            Tpl.inspector ok "400" session' $ \langs -> l langs ErrURL

reloadPage uuid session = do
    nullDir
    method POST
    session' <- liftIO $ readMVar session
    liftIO $ Load.load' session $ currentURL session'
    seeOther ('/':ID.toString uuid) $ toResponse ()

sessionAction' uuid session cb = do
    nullDir
    method POST
    liftIO $ cb session
    seeOther ('/':ID.toString uuid) $ toResponse ()

---

serveEl :: UUID -> Session -> String -> ServerPart Response
serveEl uuid session el = do
    session' <- liftIO $ readMVar session
    case getEl session' =<< ID.fromString el of
        Just el' -> msum [
            dir "preview" $ serveElPreview uuid session el',
            serveEl' uuid session session' el'
          ]
        Nothing -> Tpl.inspector notFound "404" session' $ \langs ->
            H.h1 $ l langs ElementNotFound

serveEl' uuid session session' el = do
    nullDir
    method GET
    let title = toStrict $ renderMarkup $ contents $ Tpl.xmlNode' $ XC.node el
    related <- liftIO $ getRelatedEls session el
    elValue <- liftIO $ Forms.readInput' session el
    Tpl.inspector ok title session' $ Tpl.elPage uuid el related elValue

serveElPreview uuid session el = do
    nullDir
    method GET
    let title = toStrict $ renderMarkup $ contents $ Tpl.xmlNode' $ XC.node el
    related <- liftIO $ getRelatedEls session el
    elValue <- liftIO $ Forms.readInput' session el
    Tpl.page ok [title, Txt.pack $ ID.toString uuid, "Amphiarao"] "fill" $
        Tpl.elPage uuid el related elValue

clickElement uuid session = do
    nullDir
    method POST
    elUUID <- look "el"
    session' <- liftIO $ readMVar session
    case getEl session' =<< ID.fromString elUUID of
        Just el -> do
            liftIO $ Load.clickEl session el
            seeOther ('/':ID.toString uuid) $ toResponse ()
        Nothing -> Tpl.inspector notFound "404" session' $ \langs ->
            H.h1 $ l langs ElementNotFound

typeElement uuid session = do
    nullDir
    method POST
    elUUID <- look "el"
    newValue <- lookText' "text"
    isReset <- looks "reset"
    session' <- liftIO $ readMVar session
    case getEl session' =<< ID.fromString elUUID of
        Just el | Prelude.null isReset -> do
            liftIO $ Forms.sendText (Forms.SendText newValue) el session
            seeOther ('/':ID.toString uuid) $ toResponse ()
        Just el -> do
            liftIO $ Forms.clearForm session el
            seeOther ('/':ID.toString uuid) $ toResponse ()
        Nothing -> Tpl.inspector notFound "404" session' $ \langs ->
            H.h1 $ l langs ElementNotFound