on develop help sign in
Refactor everything
commit532c64d97f02f99b2c5ccb58bf6302a7b52f0d69 authorRyan Trinkle <ryan.trinkle@gmail.com> dateFri 20 May 2016, 09:32 (10 years ago) parent76d1e7c
29 files changed+2578−1361
.gitignore+1 default.nix+2−1 reflex-dom.cabal+18−9 src-ghc/Reflex/Dom/Internal/Foreign.hs+16−14 src-ghc/Reflex/Dom/WebSocket/Foreign.hs+4−3 src-ghc/Reflex/Dom/Xhr/Foreign.hs+13−14 src-ghcjs/Reflex/Dom/WebSocket/Foreign.hs+4−3 src/Foreign/JavaScript/TH.hs+80−7 src/Reflex/Dom.hs+18−10 src/Reflex/Dom/Builder/Class.hs+363 src/Reflex/Dom/Builder/Class/Events.hs+159 src/Reflex/Dom/Builder/Immediate.hs+723 src/Reflex/Dom/Class.hs+8−136 src/Reflex/Dom/Deletable/Class.hs+14 src/Reflex/Dom/DynamicWriter.hs+90−36 src/Reflex/Dom/Internal.hs+71−253 src/Reflex/Dom/Location.hs+3−2 src/Reflex/Dom/Old.hs+44 src/Reflex/Dom/PerformEvent/Base.hs+166 src/Reflex/Dom/PerformEvent/Class.hs+38 src/Reflex/Dom/PostBuild/Class.hs+133 src/Reflex/Dom/Storage.hs+31 src/Reflex/Dom/Time.hs+13−12 src/Reflex/Dom/WebSocket.hs+13−20 src/Reflex/Dom/Widget/Basic.hs+185−701 src/Reflex/Dom/Widget/Input.hs+283−58 src/Reflex/Dom/Widget/Lazy.hs+28−25 src/Reflex/Dom/Widget/Resize.hs+10−5 src/Reflex/Dom/Xhr.hs+47−52
.gitignore+1
@@ -8,6 +8,7 @@ cabal-dev
88*.dyn_o
99*.p_hi
1010*.p_o
11+*.prof
1112*.js_dyn_hi
1213*.js_dyn_o
1314*.js_p_hi
default.nix+2−1
@@ -3,7 +3,7 @@
33, transformers, data-default, semigroups, aeson
44, ghc, webkitgtk3-javascriptcore, exception-transformers
55, webkitgtk24x, dependent-sum-template, bifunctors, bimap
6−, raw-strings-qq, zenc, random
6+, raw-strings-qq, zenc, random, monad-control
77}:
88
99mkDerivation {
@@ -29,6 +29,7 @@ mkDerivation {
2929 zenc
3030 random
3131 bimap
32+ monad-control
3233 ] ++ (if (ghc.pname or null) == "ghcjs" then [ ] else [ webkitgtk3-javascriptcore raw-strings-qq ]);
3334 pkgconfigDepends = if (ghc.pname or null) == "ghcjs" then [ ] else [ webkitgtk24x webkitgtk3-javascriptcore raw-strings-qq ];
3435 license = null;
reflex-dom.cabal+18−9
@@ -23,7 +23,7 @@ extra-source-files: src-ghc/Reflex/Dom/Internal/Foreign.hs
2323library
2424 hs-source-dirs: src
2525 build-depends:
26− base >= 4.7 && < 4.9,
26+ base >= 4.7 && < 4.10,
2727 template-haskell,
2828 stm == 2.4.*,
2929 reflex == 0.4.*,
@@ -36,19 +36,20 @@ library
3636 ref-tf == 0.4.*,
3737 random == 1.1.*,
3838 ghcjs-dom >= 0.2.1 && < 0.3,
39− transformers == 0.3.* || == 0.4.*,
40− lens >= 4.7 && < 4.14,
39+ transformers >= 0.3 || < 0.6,
40+ lens >= 4.7 && < 4.15,
4141 text == 1.2.*,
4242 bytestring == 0.10.*,
4343 data-default == 0.5.*,
44− aeson >= 0.8 && < 0.11,
45− time == 1.4.* || == 1.5.*,
44+ aeson >= 0.8 && < 0.12,
45+ time == 1.4.* || ( >= 1.5 && < 1.7 ),
4646 exception-transformers == 0.4.*,
4747 directory == 1.2.*,
4848 dependent-sum-template >= 0.0.0.4 && < 0.1,
4949 zenc == 0.1.*,
5050 bifunctors >= 4.2 && < 6,
51− bimap >= 0.3 && < 0.4
51+ bimap >= 0.3 && < 0.4,
52+ monad-control >= 1.0.1 && < 1.1
5253
5354 if impl(ghcjs)
5455 hs-source-dirs: src-ghcjs
@@ -69,10 +70,21 @@ library
6970 build-depends: unix == 2.7.*
7071
7172 exposed-modules:
73+ Foreign.JavaScript.TH
7274 Reflex.Dom
75+ Reflex.Dom.Builder.Class
76+ Reflex.Dom.Builder.Class.Events
77+ Reflex.Dom.Builder.Immediate
7378 Reflex.Dom.Class
79+ Reflex.Dom.Deletable.Class
7480 Reflex.Dom.DynamicWriter
7581 Reflex.Dom.Internal
82+ Reflex.Dom.Internal.Foreign
83+ Reflex.Dom.Location
84+ Reflex.Dom.PerformEvent.Base
85+ Reflex.Dom.PerformEvent.Class
86+ Reflex.Dom.PostBuild.Class
87+ Reflex.Dom.Old
7688 Reflex.Dom.Time
7789 Reflex.Dom.WebSocket
7890 Reflex.Dom.Widget
@@ -81,10 +93,7 @@ library
8193 Reflex.Dom.Widget.Lazy
8294 Reflex.Dom.Widget.Resize
8395 Reflex.Dom.Xhr
84− Reflex.Dom.Location
85− Foreign.JavaScript.TH
8696 other-modules:
87− Reflex.Dom.Internal.Foreign
8897 Reflex.Dom.WebSocket.Foreign
8998 Reflex.Dom.Xhr.Foreign
9099 Reflex.Dom.Xhr.ResponseType
src-ghc/Reflex/Dom/Internal/Foreign.hs+16−14
@@ -1,4 +1,4 @@
1−{-# LANGUAGE CPP, ForeignFunctionInterface, ScopedTypeVariables, LambdaCase #-}
1+{-# LANGUAGE CPP, ForeignFunctionInterface, ScopedTypeVariables, LambdaCase, OverloadedStrings #-}
22module Reflex.Dom.Internal.Foreign where
33
44import Control.Concurrent
@@ -7,7 +7,6 @@ import Control.Lens hiding (set)
77import Control.Monad
88import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_, get)
99import Data.ByteString (ByteString)
10−import Data.List
1110import Foreign.Marshal hiding (void)
1211import Foreign.Ptr
1312import GHCJS.DOM hiding (runWebGUI)
@@ -19,7 +18,7 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
1918import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
2019import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
2120import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
22−import Graphics.UI.Gtk.WebKit.Types hiding (Event, Widget)
21+import Graphics.UI.Gtk.WebKit.Types hiding (Event, Widget, Text)
2322import Graphics.UI.Gtk.WebKit.WebFrame
2423import Graphics.UI.Gtk.WebKit.WebInspector
2524import Graphics.UI.Gtk.WebKit.WebSettings
@@ -27,23 +26,26 @@ import Graphics.UI.Gtk.WebKit.WebView
2726import System.Directory
2827import System.Glib.FFI hiding (void)
2928import qualified Data.ByteString as BS
29+import Data.Monoid
30+import Data.Text (Text)
31+import qualified Data.Text as T
3032
3133#ifndef mingw32_HOST_OS
3234import System.Posix.Signals
3335#endif
3436
3537quitWebView :: WebView -> IO ()
36−quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv
38+quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv --TODO: Shouldn't this be postGUISync?
3739 widgetDestroy w
3840
3941installQuitHandler :: WebView -> IO ()
4042#ifdef mingw32_HOST_OS
4143installQuitHandler wv = return () -- TODO: Maybe figure something out here for Windows users.
4244#else
43−installQuitHandler wv = installHandler keyboardSignal (Catch (quitWebView wv)) Nothing >> return ()
45+installQuitHandler wv = void $ installHandler keyboardSignal (Catch (quitWebView wv)) Nothing
4446#endif
4547
46−makeDefaultWebView :: String -> (WebView -> IO ()) -> IO ()
48+makeDefaultWebView :: Text -> (WebView -> IO ()) -> IO ()
4749makeDefaultWebView userAgentKey main = do
4850 _ <- initGUI
4951 window <- windowNew
@@ -54,7 +56,7 @@ makeDefaultWebView userAgentKey main = do
5456 webView <- webViewNew
5557 settings <- webViewGetWebSettings webView
5658 userAgent <- settings `get` webSettingsUserAgent
57− settings `set` [ webSettingsUserAgent := userAgent ++ " " ++ userAgentKey
59+ settings `set` [ webSettingsUserAgent := userAgent <> " " <> userAgentKey
5860 , webSettingsEnableUniversalAccessFromFileUris := True
5961 , webSettingsEnableDeveloperExtras := True
6062 ]
@@ -77,14 +79,14 @@ makeDefaultWebView userAgentKey main = do
7779 return inspectorWebView
7880 wf <- webViewGetMainFrame webView
7981 pwd <- getCurrentDirectory
80− webFrameLoadString wf "" Nothing $ "file://" ++ pwd ++ "/"
82+ webFrameLoadString wf "" Nothing $ "file://" <> pwd <> "/"
8183 installQuitHandler webView
8284 mainGUI
8385
8486runWebGUI :: (WebView -> IO ()) -> IO ()
8587runWebGUI = runWebGUI' "GHCJS"
8688
87−runWebGUI' :: String -> (WebView -> IO ()) -> IO ()
89+runWebGUI' :: Text -> (WebView -> IO ()) -> IO ()
8890runWebGUI' userAgentKey main = do
8991 -- Are we in a java script inside some kind of browser
9092 mbWindow <- currentWindow
@@ -93,7 +95,7 @@ runWebGUI' userAgentKey main = do
9395 -- Check if we are running in javascript inside the the native version
9496 Just n <- getNavigator window
9597 agent <- getUserAgent n
96− unless ((" " ++ userAgentKey) `isSuffixOf` agent) $ main (castToWebView window)
98+ unless ((" " <> userAgentKey) `T.isSuffixOf` agent) $ main (castToWebView window)
9799 Nothing -> do
98100 makeDefaultWebView userAgentKey main
99101
@@ -108,7 +110,7 @@ toJSObject ctx args = do
108110 jsobjectsetproperty ctx o prop a 1 nullPtr
109111 return o
110112
111−fromJSStringMaybe :: JSContextRef -> JSValueRef -> IO (Maybe String)
113+fromJSStringMaybe :: JSContextRef -> JSValueRef -> IO (Maybe Text)
112114fromJSStringMaybe c t = do
113115 isNull <- jsvalueisnull c t
114116 case isNull of
@@ -119,16 +121,16 @@ fromJSStringMaybe c t = do
119121 s <- allocaBytes (fromIntegral l) $ \ps -> do
120122 _ <- jsstringgetutf8cstring'_ j ps (fromIntegral l)
121123 peekCString ps
122− return $ Just s
124+ return $ Just $ T.pack s
123125
124−getLocationHost :: WebView -> IO String
126+getLocationHost :: WebView -> IO Text
125127getLocationHost wv = withWebViewContext wv $ \c -> do
126128 script <- jsstringcreatewithutf8cstring "location.host"
127129 lh <- jsevaluatescript c script nullPtr nullPtr 1 nullPtr
128130 lh' <- fromJSStringMaybe c lh
129131 return $ maybe "" id lh'
130132
131−getLocationProtocol :: WebView -> IO String
133+getLocationProtocol :: WebView -> IO Text
132134getLocationProtocol wv = withWebViewContext wv $ \c -> do
133135 script <- jsstringcreatewithutf8cstring "location.protocol"
134136 lp <- jsevaluatescript c script nullPtr nullPtr 1 nullPtr
src-ghc/Reflex/Dom/WebSocket/Foreign.hs+4−3
@@ -17,6 +17,7 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
1717import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
1818import Graphics.UI.Gtk.WebKit.WebView
1919import qualified Data.ByteString as BS
20+import Data.Text (Text)
2021import qualified Data.Text as T
2122
2223import Reflex.Dom.Internal.Foreign
@@ -25,9 +26,9 @@ data JSWebSocket = JSWebSocket { wsValue :: JSValueRef
2526 , wsContext :: JSContextRef
2627 }
2728
28−newWebSocket :: WebView -> String -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
29+newWebSocket :: WebView -> Text -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
2930newWebSocket wv url onMessage onOpen onClose = withWebViewContext wv $ \c -> do
30− url' <- jsvaluemakestring c =<< jsstringcreatewithutf8cstring url
31+ url' <- jsvaluemakestring c =<< jsstringcreatewithutf8cstring (T.unpack url)
3132 newWSArgs <- toJSObject c [url']
3233 newWS <- jsstringcreatewithutf8cstring "(function(that) { var ws = new WebSocket(that[0]); ws['binaryType'] = 'arraybuffer'; return ws; })(this)"
3334 ws <- jsevaluatescript c newWS newWSArgs nullPtr 1 nullPtr
@@ -38,7 +39,7 @@ newWebSocket wv url onMessage onOpen onClose = withWebViewContext wv $ \c -> do
3839 msg' <- fromJSStringMaybe c msg
3940 case msg' of
4041 Nothing -> return ()
41− Just m -> onMessage $ encodeUtf8 $ T.pack m
42+ Just m -> onMessage $ encodeUtf8 m
4243 jsvaluemakeundefined c
4344 onMessageCb <- jsobjectmakefunctionwithcallback c nullPtr onMessage'
4445 onOpen' <- wrapper $ \_ _ _ _ _ _ -> do
src-ghc/Reflex/Dom/Xhr/Foreign.hs+13−14
@@ -1,4 +1,4 @@
1−{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, QuasiQuotes, ForeignFunctionInterface #-}
1+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances, QuasiQuotes, ForeignFunctionInterface, OverloadedStrings #-}
22module Reflex.Dom.Xhr.Foreign ( module Reflex.Dom.Xhr.Foreign
33 , XhrResponseType
44 ) where
@@ -12,7 +12,6 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
1212import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
1313import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
1414import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
15−import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
1615import GHCJS.DOM.File
1716import Reflex.Dom.Xhr.ResponseType
1817import Reflex.Dom.Xhr.Exception
@@ -36,22 +35,22 @@ class IsXhrPayload a where
3635instance IsXhrPayload () where
3736 xmlHttpRequestSend xhr _ = xmlHttpRequestSendPayload xhr Nothing
3837
39−instance IsXhrPayload String where
38+instance IsXhrPayload Text where
4039 xmlHttpRequestSend xhr = xmlHttpRequestSendPayload xhr . Just
4140
4241instance IsXhrPayload File where
4342 xmlHttpRequestSend = error "xmlHttpRequestSend{File}: not implemented"
4443
45−stringToJSValue :: JSContextRef -> String -> IO JSValueRef
46−stringToJSValue ctx s = jsvaluemakestring ctx =<< jsstringcreatewithutf8cstring s
44+stringToJSValue :: JSContextRef -> Text -> IO JSValueRef
45+stringToJSValue ctx s = jsvaluemakestring ctx =<< jsstringcreatewithutf8cstring (T.unpack s)
4746
48−fromResponseType :: XhrResponseType -> String
47+fromResponseType :: XhrResponseType -> Text
4948fromResponseType XhrResponseType_Default = ""
5049fromResponseType XhrResponseType_ArrayBuffer = "arraybuffer"
5150fromResponseType XhrResponseType_Blob = "blob"
5251fromResponseType XhrResponseType_Text = "text"
5352
54−toResponseType :: String -> Maybe XhrResponseType
53+toResponseType :: Text -> Maybe XhrResponseType
5554toResponseType "" = Just XhrResponseType_Default
5655toResponseType "arraybuffer" = Just XhrResponseType_ArrayBuffer
5756toResponseType "blob" = Just XhrResponseType_Blob
@@ -65,7 +64,7 @@ xmlHttpRequestNew wv = withWebViewContext wv $ \jsContext -> do
6564 jsvalueprotect jsContext xhr'
6665 return $ XMLHttpRequest xhr' jsContext
6766
68−xmlHttpRequestOpen :: XMLHttpRequest -> String -> String -> Bool -> String -> String -> IO ()
67+xmlHttpRequestOpen :: XMLHttpRequest -> Text -> Text -> Bool -> Text -> Text -> IO ()
6968xmlHttpRequestOpen xhr method url async user password = do
7069 let c = xhrContext xhr
7170 method' <- stringToJSValue c method
@@ -117,8 +116,8 @@ xmlHttpRequestGetResponse xhr = do
117116 False -> case mrt of
118117 Just XhrResponseType_ArrayBuffer -> Just . XhrResponseBody_ArrayBuffer <$> bsFromArrayBuffer c t
119118 Just XhrResponseType_Blob -> Just . XhrResponseBody_Blob . Blob . castForeignPtr <$> newForeignPtr_ t
120− Just XhrResponseType_Default -> fmap (XhrResponseBody_Default . T.pack) <$> fromJSStringMaybe c t
121− Just XhrResponseType_Text -> fmap (XhrResponseBody_Text . T.pack) <$> fromJSStringMaybe c t
119+ Just XhrResponseType_Default -> fmap (XhrResponseBody_Default) <$> fromJSStringMaybe c t
120+ Just XhrResponseType_Text -> fmap (XhrResponseBody_Text) <$> fromJSStringMaybe c t
122121 _ -> return Nothing
123122
124123xmlHttpRequestGetResponseText :: XMLHttpRequest -> IO (Maybe Text)
@@ -126,9 +125,9 @@ xmlHttpRequestGetResponseText xhr = do
126125 let c = xhrContext xhr
127126 script <- jsstringcreatewithutf8cstring "this.responseText"
128127 t <- jsevaluatescript c script (xhrValue xhr) nullPtr 1 nullPtr
129− fmap (fmap T.pack) $ fromJSStringMaybe c t
128+ fromJSStringMaybe c t
130129
131−xmlHttpRequestSendPayload :: XMLHttpRequest -> Maybe String -> IO ()
130+xmlHttpRequestSendPayload :: XMLHttpRequest -> Maybe Text -> IO ()
132131xmlHttpRequestSendPayload xhr payload = do
133132 let c = xhrContext xhr
134133 result <- newEmptyMVar
@@ -181,7 +180,7 @@ xmlHttpRequestSendPayload xhr payload = do
181180 })(this[0], this[1], this[2], this[3], this[4])
182181 |]
183182
184−xmlHttpRequestSetRequestHeader :: XMLHttpRequest -> String -> String -> IO ()
183+xmlHttpRequestSetRequestHeader :: XMLHttpRequest -> Text -> Text -> IO ()
185184xmlHttpRequestSetRequestHeader xhr header value = do
186185 let c = xhrContext xhr
187186 header' <- stringToJSValue c header
@@ -191,7 +190,7 @@ xmlHttpRequestSetRequestHeader xhr header value = do
191190 _ <- jsevaluatescript c script o nullPtr 1 nullPtr
192191 return ()
193192
194−xmlHttpRequestSetResponseType :: XMLHttpRequest -> String -> IO ()
193+xmlHttpRequestSetResponseType :: XMLHttpRequest -> Text -> IO ()
195194xmlHttpRequestSetResponseType xhr t = do
196195 let c = xhrContext xhr
197196 t' <- stringToJSValue c t
src-ghcjs/Reflex/Dom/WebSocket/Foreign.hs+4−3
@@ -11,7 +11,7 @@ import GHCJS.DOM.WebSocket (message, open, closeEvent)
1111import qualified GHCJS.DOM.WebSocket as GD
1212import GHCJS.DOM.MessageEvent
1313import GHCJS.DOM.EventM (on)
14−import GHCJS.DOM.Types
14+import GHCJS.DOM.Types hiding (Text)
1515import Control.Monad.IO.Class
1616import Control.Monad.Reader
1717import GHCJS.Buffer
@@ -19,12 +19,13 @@ import JavaScript.TypedArray.ArrayBuffer as JS
1919import GHCJS.Marshal.Pure
2020import GHCJS.Foreign.Internal
2121import Data.Text.Encoding
22+import Data.Text (Text)
2223
2324data JSWebSocket = JSWebSocket { unWebSocket :: WebSocket }
2425
25−newWebSocket :: a -> String -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
26+newWebSocket :: a -> Text -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
2627newWebSocket _ url onMessage onOpen onClose = do
27− ws <- GD.newWebSocket url (Just [] :: Maybe [String])
28+ ws <- GD.newWebSocket url (Just [] :: Maybe [Text])
2829 _ <- on ws open $ liftIO onOpen
2930 GD.setBinaryType ws "arraybuffer"
3031 _ <- on ws message $ do
src/Foreign/JavaScript/TH.hs+80−7
@@ -6,6 +6,11 @@ module Foreign.JavaScript.TH ( module Foreign.JavaScript.TH
66 , Safety (..)
77 ) where
88
9+import Reflex.Class
10+import Reflex.Host.Class
11+import Reflex.Dom.PerformEvent.Class
12+import Reflex.Dom.Deletable.Class
13+
914import Language.Haskell.TH
1015
1116#ifdef __GHCJS__
@@ -16,10 +21,9 @@ import qualified GHCJS.Foreign.Callback as JS
1621import qualified GHCJS.Types as JS
1722import qualified GHCJS.Buffer as JS
1823import GHCJS.DOM
19−import GHCJS.DOM.Types hiding (fromJSString)
24+import GHCJS.DOM.Types hiding (fromJSString, Text)
2025import qualified GHCJS.DOM.Types as JS
2126import qualified JavaScript.Array as JS
22−import qualified JavaScript.Cast as JS
2327import qualified JavaScript.Object as JS
2428import qualified JavaScript.Object.Internal (Object (..))
2529import qualified GHCJS.Foreign.Callback.Internal (Callback (..))
@@ -40,21 +44,25 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
4044import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
4145import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
4246import Graphics.UI.Gtk.WebKit.DOM.Node
47+import Foreign.Marshal
4348#endif
4449
4550import Control.Monad
51+import Control.Monad.Ref
4652import Control.Monad.Fix
4753import Control.Monad.IO.Class
4854import Control.Monad.Exception
4955import Control.Monad.Reader
5056import Control.Monad.State
57+import Control.Monad.Trans.Control
5158import qualified Control.Monad.State.Strict as Strict
52−import Foreign.Marshal
5359import Data.ByteString (ByteString)
5460import qualified Data.ByteString as BS
5561import Control.Concurrent
5662import Data.Coerce
5763import Data.Monoid
64+import Data.Text (Text)
65+import qualified Data.Text as T
5866
5967class Monad m => HasWebView m where
6068 type WebViewPhantom m :: *
@@ -72,7 +80,54 @@ instance HasWebView m => HasWebView (Strict.StateT r m) where
7280 type WebViewPhantom (Strict.StateT r m) = WebViewPhantom m
7381 askWebView = lift askWebView
7482
75−newtype WithWebView x m a = WithWebView { unWithWebView :: ReaderT (WebViewSingleton x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
83+newtype WithWebView x m a = WithWebView { unWithWebView :: ReaderT (WebViewSingleton x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadTrans, MonadException, MonadAsyncException)
84+
85+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView x m) where
86+ {-# INLINABLE newEventWithTrigger #-}
87+ newEventWithTrigger = lift . newEventWithTrigger
88+ {-# INLINABLE newFanEventWithTrigger #-}
89+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
90+
91+instance MonadSubscribeEvent t m => MonadSubscribeEvent t (WithWebView x m) where
92+ {-# INLINABLE subscribeEvent #-}
93+ subscribeEvent = lift . subscribeEvent
94+
95+instance MonadReflexHost t m => MonadReflexHost t (WithWebView x m) where
96+ type ReadPhase (WithWebView x m) = ReadPhase m
97+ {-# INLINABLE fireEventsAndRead #-}
98+ fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
99+ {-# INLINABLE runHostFrame #-}
100+ runHostFrame = lift . runHostFrame
101+
102+instance MonadSample t m => MonadSample t (WithWebView x m) where
103+ {-# INLINABLE sample #-}
104+ sample = lift . sample
105+
106+instance MonadHold t m => MonadHold t (WithWebView x m) where
107+ {-# INLINABLE hold #-}
108+ hold v0 = lift . hold v0
109+ {-# INLINABLE holdDyn #-}
110+ holdDyn v0 = lift . holdDyn v0
111+ {-# INLINABLE holdIncremental #-}
112+ holdIncremental v0 = lift . holdIncremental v0
113+
114+instance MonadTransControl (WithWebView x) where
115+ type StT (WithWebView x) a = StT (ReaderT (WebViewSingleton x)) a
116+ {-# INLINABLE liftWith #-}
117+ liftWith = defaultLiftWith WithWebView unWithWebView
118+ {-# INLINABLE restoreT #-}
119+ restoreT = defaultRestoreT WithWebView
120+
121+instance PerformEvent t m => PerformEvent t (WithWebView x m) where
122+ type Performable (WithWebView x m) = WithWebView x (Performable m)
123+ {-# INLINABLE performEvent_ #-}
124+ performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
125+ {-# INLINABLE performEvent #-}
126+ performEvent e = liftWith $ \run -> performEvent $ fmap run e
127+
128+instance Deletable t m => Deletable t (WithWebView x m) where
129+ {-# INLINABLE deletable #-}
130+ deletable e = liftThrough $ deletable e
76131
77132runWithWebView :: WithWebView x m a -> WebViewSingleton x -> m a
78133runWithWebView = runReaderT . unWithWebView
@@ -81,6 +136,15 @@ instance (Monad m) => HasWebView (WithWebView x m) where
81136 type WebViewPhantom (WithWebView x m) = x
82137 askWebView = WithWebView ask
83138
139+instance MonadRef m => MonadRef (WithWebView x m) where
140+ type Ref (WithWebView x m) = Ref m
141+ newRef = lift . newRef
142+ readRef = lift . readRef
143+ writeRef r = lift . writeRef r
144+
145+instance MonadAtomicRef m => MonadAtomicRef (WithWebView x m) where
146+ atomicModifyRef r = lift . atomicModifyRef r
147+
84148withWebViewSingleton :: WebView -> (forall x. WebViewSingleton x -> r) -> r
85149withWebViewSingleton wv f = f $ WebViewSingleton wv
86150
@@ -129,7 +193,7 @@ class Monad m => MonadJS x m | m -> x where
129193 withJSString :: String -> (JSRef x -> m r) -> m r
130194 withJSNumber :: Double -> (JSRef x -> m r) -> m r
131195 withJSArray :: [JSRef x] -> (JSRef x -> m r) -> m r
132− withJSUint8Array :: MonadJS x m => ByteString -> (JSUint8Array x -> m r) -> m r
196+ withJSUint8Array :: ByteString -> (JSUint8Array x -> m r) -> m r
133197 -- | Create a JSFun with zero arguments; should be equilvant to `syncCallback AlwaysRetain True` in GHCJS
134198 mkJSFun :: ([JSRef x] -> m (JSRef x)) -> m (JSFun x) --TODO: Support 'this', exceptions
135199 freeJSFun :: JSFun x -> m ()
@@ -141,6 +205,10 @@ class Monad m => MonadJS x m | m -> x where
141205
142206data JSCtx_IO
143207
208+instance MonadIO m => HasJS JSCtx_IO (WithWebView x m) where
209+ type JSM (WithWebView x m) = IO
210+ liftJS = liftIO
211+
144212instance IsJSContext JSCtx_IO where
145213 newtype JSRef JSCtx_IO = JSRef_IO { unJSRef_IO :: JS.JSVal }
146214
@@ -181,7 +249,7 @@ instance MonadJS JSCtx_IO IO where
181249 return $ JSRef_IO r
182250 withJSNode n f = f $ JSRef_IO $ unNode n
183251
184−foreign import javascript unsafe "new Uint8Array($1_1.buf, $1_2, $2)" extractByteArray :: Ptr CChar -> Int -> IO (JS.JSRef (JSRef JSCtx_IO))
252+foreign import javascript unsafe "new Uint8Array($1_1.buf, $1_2, $2)" extractByteArray :: Ptr CChar -> Int -> IO JS.JSVal
185253
186254foreign import javascript unsafe "function(){ return $1(arguments); }" funWithArguments :: JS.Callback (JS.MutableJSArray -> IO a) -> IO JS.JSVal
187255
@@ -203,7 +271,6 @@ fromJSNumberRaw val = do
203271 jsContext <- askJSContext
204272 liftIO $ jsvaluetonumber jsContext val nullPtr --TODO: Exceptions
205273
206−foreign import ccall "wrapper" mkJSObjectCallAsFunctionCallback :: JSObjectCallAsFunctionCallback' -> IO (FunPtr JSObjectCallAsFunctionCallback')
207274
208275data JSCtx_JavaScriptCore x
209276
@@ -326,6 +393,9 @@ instance ToJS x Bool where
326393instance FromJS x String where
327394 fromJS = fromJSString
328395
396+instance FromJS x Text where
397+ fromJS s = T.pack <$> fromJSString s
398+
329399instance FromJS x a => FromJS x (Maybe a) where
330400 fromJS x = do
331401 n <- isJSNull x
@@ -343,6 +413,9 @@ instance FromJS x (JSRef x) where
343413instance ToJS x String where
344414 withJS = withJSString
345415
416+instance ToJS x Text where
417+ withJS = withJSString . T.unpack
418+
346419newtype JSArray a = JSArray { unJSArray :: [a] }
347420
348421instance ToJS x a => ToJS x (JSArray a) where
src/Reflex/Dom.hs+18−10
@@ -1,22 +1,30 @@
11module Reflex.Dom ( module Reflex
2+ , module Reflex.Dom.Builder.Class
3+ , module Reflex.Dom.Builder.Immediate
24 , module Reflex.Dom.Class
5+ , module Reflex.Dom.DynamicWriter
36 , module Reflex.Dom.Internal
4− , module Reflex.Dom.Widget
5− , module Reflex.Dom.Xhr
7+ , module Reflex.Dom.Location
8+ , module Reflex.Dom.Old
9+ , module Reflex.Dom.PerformEvent.Class
10+ , module Reflex.Dom.PostBuild.Class
611 , module Reflex.Dom.Time
7− , module Reflex.Dom.DynamicWriter
812 , module Reflex.Dom.WebSocket
9− , module Reflex.Dom.DynamicWriter
10− , module Reflex.Dom.Location
13+ , module Reflex.Dom.Widget
14+ , module Reflex.Dom.Xhr
1115 ) where
1216
1317import Reflex
18+import Reflex.Dom.Builder.Class
19+import Reflex.Dom.Builder.Immediate
1420import Reflex.Dom.Class
21+import Reflex.Dom.DynamicWriter
1522import Reflex.Dom.Internal
16−import Reflex.Dom.Widget
17−import Reflex.Dom.Xhr
23+import Reflex.Dom.Location
24+import Reflex.Dom.Old
25+import Reflex.Dom.PerformEvent.Class
26+import Reflex.Dom.PostBuild.Class
1827import Reflex.Dom.Time
19−import Reflex.Dom.DynamicWriter
2028import Reflex.Dom.WebSocket
21−import Reflex.Dom.DynamicWriter
22−import Reflex.Dom.Location
29+import Reflex.Dom.Widget
30+import Reflex.Dom.Xhr
src/Reflex/Dom/Builder/Class.hsnew file+363
@@ -0,0 +1,363 @@
1+{-# LANGUAGE OverloadedStrings, TemplateHaskell, MultiParamTypeClasses, FunctionalDependencies, TypeFamilies, DeriveFunctor, FlexibleInstances, DataKinds, LambdaCase, PolyKinds, RankNTypes, ScopedTypeVariables, PolyKinds, FlexibleContexts, UndecidableInstances, ConstraintKinds, DefaultSignatures #-}
2+module Reflex.Dom.Builder.Class
3+ ( module Reflex.Dom.Builder.Class
4+ , module Reflex.Dom.Builder.Class.Events
5+ , module Reflex.Dom.Deletable.Class
6+ ) where
7+
8+import Reflex
9+import Reflex.Dom.Builder.Class.Events
10+import Reflex.Dom.Deletable.Class
11+
12+import Control.Lens hiding (element)
13+import Control.Monad.Reader
14+import Control.Monad.Trans.Control
15+import Data.Default
16+import Data.Functor.Misc
17+import Data.Map (Map)
18+import Data.Text (Text)
19+import Data.Dependent.Map (DMap)
20+import qualified Data.Dependent.Map as DMap
21+import Data.Semigroup
22+import Data.Proxy
23+import GHC.Exts (Constraint)
24+import Data.Type.Coercion
25+import qualified Control.Category
26+
27+data Pair1 (f :: k -> *) (g :: k -> *) (a :: k) = Pair1 (f a) (g a)
28+
29+data Maybe1 f a = Nothing1 | Just1 (f a)
30+
31+class DomSpace d where
32+ type DomHandler d :: * -> * -> *
33+ type DomHandler1 d :: (EventTag -> *) -> (EventTag -> *) -> * --TODO: Why can't this be k -> * ?
34+ type RawEvent d :: EventTag -> *
35+ type RawElement d :: *
36+ defaultEventHandler :: proxy d -> DomHandler1 d (Pair1 EventName (RawEvent d)) (Maybe1 EventResult)
37+
38+-- | @'DomBuilder' t m@ indicates that @m@ is a 'Monad' capable of building dynamic DOM in the 'Reflex' timeline @t@
39+class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBuilder t m | m -> t where
40+ type DomBuilderSpace m :: *
41+ textNode :: TextNodeConfig t -> m (TextNode t)
42+ element :: Text -> ElementConfig er t m -> m a -> m (Element er (DomBuilderSpace m) t, a)
43+ -- | Create a section of DOM that can be completely deleted (no trace will be left once it is deleted)
44+ fragment :: FragmentConfig above t m -> m a -> m (Fragment above t, a)
45+ -- | Create a placeholder in the DOM, with the ability to insert new DOM before it
46+ -- The provided DOM will be executed after the current frame, so it will not be affected by any occurrences that are concurrent with the occurrence that created it
47+ placeholder :: PlaceholderConfig above t m -> m (Placeholder above t)
48+ inputElement :: InputElementConfig er t m -> m (InputElement er (DomBuilderSpace m) t)
49+ textAreaElement :: TextAreaElementConfig er t m -> m (TextAreaElement er (DomBuilderSpace m) t)
50+
51+type Namespace = Text
52+
53+data TextNodeConfig t
54+ = TextNodeConfig { _textNodeConfig_contents :: Text
55+ }
56+
57+instance Default (TextNodeConfig t) where
58+ {-# INLINABLE def #-}
59+ def = TextNodeConfig
60+ { _textNodeConfig_contents = mempty
61+ }
62+
63+data TextNode t = TextNode
64+
65+type AttributeName = (Maybe Namespace, Text)
66+
67+data Propagation
68+ = Propagation_Continue
69+ | Propagation_Stop
70+ | Propagation_StopImmediate
71+ deriving (Show, Read, Eq, Ord)
72+
73+instance Semigroup Propagation where
74+ {-# INLINABLE (<>) #-}
75+ (<>) = max
76+
77+instance Monoid Propagation where
78+ {-# INLINABLE mempty #-}
79+ mempty = Propagation_Continue
80+ {-# INLINABLE mappend #-}
81+ mappend = (<>)
82+
83+data EventFlags = EventFlags --TODO: Monoid; ways of building each flag
84+ { _eventFlags_propagation :: Propagation
85+ , _eventFlags_preventDefault :: Bool
86+ }
87+
88+instance Semigroup EventFlags where
89+ {-# INLINABLE (<>) #-}
90+ EventFlags p pd <> EventFlags p' pd' = EventFlags (p <> p') (pd || pd')
91+
92+instance Monoid EventFlags where
93+ {-# INLINABLE mempty #-}
94+ mempty = EventFlags Propagation_Continue False
95+ {-# INLINABLE mappend #-}
96+ mappend = (<>)
97+
98+preventDefault :: EventFlags
99+preventDefault = mempty { _eventFlags_preventDefault = True }
100+
101+newtype EventFilter d er en = EventFilter (DomHandler d (RawEvent d en) (EventFlags, DomHandler d () (Maybe (er en))))
102+
103+data ElementConfig er t m
104+ = ElementConfig { _elementConfig_namespace :: Maybe Namespace
105+ , _elementConfig_initialAttributes :: Map AttributeName Text
106+ , _elementConfig_modifyAttributes :: Event t (Map AttributeName (Maybe Text))
107+ , _elementConfig_eventFilters :: DMap EventName (EventFilter (DomBuilderSpace m) er)
108+ , _elementConfig_eventHandler :: DomHandler1 (DomBuilderSpace m) (Pair1 EventName (RawEvent (DomBuilderSpace m))) (Maybe1 er)
109+ }
110+
111+instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (ElementConfig er t m) where
112+ {-# INLINABLE def #-}
113+ def = ElementConfig
114+ { _elementConfig_namespace = Nothing
115+ , _elementConfig_initialAttributes = mempty
116+ , _elementConfig_modifyAttributes = never
117+ , _elementConfig_eventFilters = DMap.empty
118+ , _elementConfig_eventHandler = defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))
119+ }
120+
121+data Element er d t
122+ = Element { _element_events :: EventSelector t (WrapArg er EventName) --TODO: EventSelector should have two arguments
123+ , _element_raw :: RawElement d
124+ }
125+
126+data PlaceholderConfig above t m
127+ = PlaceholderConfig { _placeholderConfig_insertAbove :: Event t (m above)
128+ , _placeholderConfig_deleteSelf :: Event t ()
129+ }
130+
131+instance Reflex t => Default (PlaceholderConfig above t m) where
132+ {-# INLINABLE def #-}
133+ def = PlaceholderConfig
134+ { _placeholderConfig_insertAbove = never
135+ , _placeholderConfig_deleteSelf = never
136+ }
137+
138+data Placeholder above t
139+ = Placeholder { _placeholder_insertedAbove :: Event t above
140+ , _placeholder_deleted :: Event t ()
141+ }
142+
143+data FragmentConfig above t m
144+ = FragmentConfig { _fragmentConfig_deleteSelf :: Event t ()
145+ , _fragmentConfig_insertAbove :: Event t (m above)
146+ }
147+
148+instance (Reflex t, above ~ ()) => Default (FragmentConfig above t m) where
149+ {-# INLINABLE def #-}
150+ def = FragmentConfig
151+ { _fragmentConfig_deleteSelf = never
152+ , _fragmentConfig_insertAbove = never
153+ }
154+
155+data Fragment above t
156+ = Fragment { _fragment_insertedAbove :: Event t above
157+ , _fragment_deleted :: Event t ()
158+ }
159+
160+data InputElementConfig er t m
161+ = InputElementConfig { _inputElementConfig_initialValue :: Text
162+ , _inputElementConfig_setValue :: Event t Text
163+ , _inputElementConfig_initialChecked :: Bool
164+ , _inputElementConfig_setChecked :: Event t Bool
165+ , _inputElementConfig_elementConfig :: ElementConfig er t m
166+ }
167+
168+instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (InputElementConfig er t m) where
169+ {-# INLINABLE def #-}
170+ def = InputElementConfig
171+ { _inputElementConfig_initialValue = ""
172+ , _inputElementConfig_setValue = never
173+ , _inputElementConfig_initialChecked = False
174+ , _inputElementConfig_setChecked = never
175+ , _inputElementConfig_elementConfig = def
176+ }
177+
178+data InputElement er d t
179+ = InputElement { _inputElement_value :: Dynamic t Text
180+ , _inputElement_checked :: Dynamic t Bool
181+ , _inputElement_input :: Event t Text
182+ , _inputElement_hasFocus :: Dynamic t Bool
183+ , _inputElement_element :: Element er d t
184+ }
185+
186+data TextAreaElementConfig er t m
187+ = TextAreaElementConfig { _textAreaElementConfig_initialValue :: Text
188+ , _textAreaElementConfig_setValue :: Event t Text
189+ , _textAreaElementConfig_elementConfig :: ElementConfig er t m
190+ }
191+
192+instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (TextAreaElementConfig er t m) where
193+ {-# INLINABLE def #-}
194+ def = TextAreaElementConfig
195+ { _textAreaElementConfig_initialValue = ""
196+ , _textAreaElementConfig_setValue = never
197+ , _textAreaElementConfig_elementConfig = def
198+ }
199+
200+data TextAreaElement er d t
201+ = TextAreaElement { _textAreaElement_value :: Dynamic t Text
202+ , _textAreaElement_input :: Event t Text
203+ , _textAreaElement_hasFocus :: Dynamic t Bool
204+ , _textAreaElement_element :: Element er d t
205+ }
206+
207+makeLenses ''TextNodeConfig
208+makeLenses ''ElementConfig
209+makeLenses ''FragmentConfig
210+makeLenses ''PlaceholderConfig
211+makeLenses ''InputElementConfig
212+makeLenses ''TextAreaElementConfig
213+
214+class CanDeleteSelf t a | a -> t where
215+ deleteSelf :: Lens' a (Event t ())
216+
217+instance CanDeleteSelf t (FragmentConfig above t m) where
218+ {-# INLINABLE deleteSelf #-}
219+ deleteSelf = fragmentConfig_deleteSelf
220+
221+instance CanDeleteSelf t (PlaceholderConfig above t m) where
222+ {-# INLINABLE deleteSelf #-}
223+ deleteSelf = placeholderConfig_deleteSelf
224+
225+class InsertAbove t m above above' a a' | a -> t m above, a' -> t m above', a above' -> a', a' above -> a where
226+ insertAbove :: Lens a a' (Event t (m above)) (Event t (m above'))
227+
228+instance InsertAbove t m above above' (FragmentConfig above t m) (FragmentConfig above' t m) where
229+ insertAbove = fragmentConfig_insertAbove
230+
231+class InitialAttributes a where
232+ initialAttributes :: Lens' a (Map AttributeName Text)
233+
234+instance InitialAttributes (ElementConfig er t m) where
235+ {-# INLINABLE initialAttributes #-}
236+ initialAttributes = elementConfig_initialAttributes
237+
238+class InitialAttributes a => ModifyAttributes t a | a -> t where
239+ modifyAttributes :: Lens' a (Event t (Map AttributeName (Maybe Text)))
240+
241+instance ModifyAttributes t (ElementConfig er t m) where
242+ {-# INLINABLE modifyAttributes #-}
243+ modifyAttributes = elementConfig_modifyAttributes
244+
245+class HasNamespace a where
246+ namespace :: Lens' a (Maybe Namespace)
247+
248+instance HasNamespace (ElementConfig er t m) where
249+ {-# INLINABLE namespace #-}
250+ namespace = elementConfig_namespace
251+
252+-- * Convenience functions
253+
254+--TODO: Move/replace
255+class Functor1 (f :: (k -> *) -> *) where
256+ type Functor1Constraint f (a :: k -> *) (b :: k -> *) :: Constraint
257+ type Functor1Constraint f a b = ()
258+ fmap1 :: Functor1Constraint f a b => (forall x. a x -> b x) -> f a -> f b
259+
260+instance Functor1 (ElementConfig er t) where
261+ type Functor1Constraint (ElementConfig er t) a b = DomBuilderSpace a ~ DomBuilderSpace b
262+ {-# INLINABLE fmap1 #-}
263+ fmap1 _ cfg = cfg
264+ { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
265+ , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
266+ }
267+
268+instance Reflex t => Functor1 (FragmentConfig above t) where
269+ {-# INLINABLE fmap1 #-}
270+ fmap1 f cfg = cfg
271+ { _fragmentConfig_insertAbove = fmap f $ _fragmentConfig_insertAbove cfg
272+ }
273+
274+instance Reflex t => Functor1 (PlaceholderConfig above t) where
275+ {-# INLINABLE fmap1 #-}
276+ fmap1 f cfg = cfg
277+ { _placeholderConfig_insertAbove = fmap f $ _placeholderConfig_insertAbove cfg
278+ }
279+
280+instance Functor1 (InputElementConfig er t) where
281+ type Functor1Constraint (InputElementConfig er t) a b = Functor1Constraint (ElementConfig er t) a b
282+ fmap1 f cfg = cfg & inputElementConfig_elementConfig %~ fmap1 f
283+
284+instance Functor1 (TextAreaElementConfig er t) where
285+ type Functor1Constraint (TextAreaElementConfig er t) a b = Functor1Constraint (ElementConfig er t) a b
286+ fmap1 f cfg = cfg & textAreaElementConfig_elementConfig %~ fmap1 f
287+
288+{-# INLINABLE deletableFragment #-}
289+deletableFragment :: DomBuilder t m => Event t () -> m a -> m a
290+deletableFragment d c = fmap snd $ fragment (def & deleteSelf .~ d) c
291+
292+class HasDomEvent t target eventName where
293+ type DomEventType target eventName :: *
294+ domEvent :: EventName eventName -> target -> Event t (DomEventType target eventName)
295+
296+instance Reflex t => HasDomEvent t (Element EventResult d t) en where
297+ type DomEventType (Element EventResult d t) en = EventResultType en
298+ {-# INLINABLE domEvent #-}
299+ domEvent en e = fmap unEventResult $ Reflex.select (_element_events e) (WrapArg en)
300+
301+instance Reflex t => HasDomEvent t (InputElement EventResult d t) en where
302+ type DomEventType (InputElement EventResult d t) en = EventResultType en
303+ {-# INLINABLE domEvent #-}
304+ domEvent en = domEvent en . _inputElement_element
305+
306+instance Reflex t => HasDomEvent t (TextAreaElement EventResult d t) en where
307+ type DomEventType (TextAreaElement EventResult d t) en = EventResultType en
308+ {-# INLINABLE domEvent #-}
309+ domEvent en = domEvent en . _textAreaElement_element
310+
311+instance DomBuilder t m => DomBuilder t (ReaderT r m) where
312+ type DomBuilderSpace (ReaderT r m) = DomBuilderSpace m
313+ {-# INLINABLE textNode #-}
314+ textNode = liftTextNode
315+ element = liftElement
316+ fragment = liftFragment
317+ placeholder = liftPlaceholder
318+ inputElement = liftInputElement
319+ textAreaElement = liftTextAreaElement
320+
321+type LiftDomBuilder t f m =
322+ ( Reflex t
323+ , MonadTransControlStateless f
324+ , Monad m
325+ , DomBuilder t m
326+ , DomBuilderSpace (f m) ~ DomBuilderSpace m
327+ )
328+
329+class MonadTransControl t => MonadTransControlStateless t where
330+ stTCoercion :: proxy t -> Coercion (StT t a) a
331+ default stTCoercion :: proxy t -> Coercion a a
332+ stTCoercion _ = Control.Category.id
333+
334+toStT :: MonadTransControlStateless t => proxy t -> a -> StT t a
335+toStT = coerceWith . sym . stTCoercion
336+
337+fromStT :: MonadTransControlStateless t => proxy t -> StT t a -> a
338+fromStT = coerceWith . stTCoercion
339+
340+instance MonadTransControlStateless (ReaderT r)
341+
342+type RunStateless t = forall n b. Monad n => t n b -> n b
343+
344+liftWithStateless :: forall m t a. (Monad m, MonadTransControlStateless t) => (RunStateless t -> m a) -> t m a
345+liftWithStateless a = liftWith $ \run -> a $ \x -> liftM (fromStT (Proxy :: Proxy t)) $ run x
346+
347+liftTextNode :: (MonadTrans f, DomBuilder t m) => TextNodeConfig t -> f m (TextNode t)
348+liftTextNode = lift . textNode
349+
350+liftElement :: LiftDomBuilder t f m => Text -> ElementConfig er t (f m) -> f m a -> f m (Element er (DomBuilderSpace m) t, a)
351+liftElement elementTag cfg child = liftWithStateless $ \run -> element elementTag (fmap1 run cfg) $ run child
352+
353+liftFragment :: LiftDomBuilder t f m => FragmentConfig above t (f m) -> f m a -> f m (Fragment above t, a)
354+liftFragment cfg child = liftWithStateless $ \run -> fragment (fmap1 run cfg) $ run child
355+
356+liftPlaceholder :: LiftDomBuilder t f m => PlaceholderConfig above t (f m) -> f m (Placeholder above t)
357+liftPlaceholder cfg = liftWithStateless $ \run -> placeholder $ fmap1 run cfg
358+
359+liftInputElement :: LiftDomBuilder t f m => InputElementConfig er t (f m) -> f m (InputElement er (DomBuilderSpace m) t)
360+liftInputElement cfg = liftWithStateless $ \run -> inputElement $ fmap1 run cfg
361+
362+liftTextAreaElement :: LiftDomBuilder t f m => TextAreaElementConfig er t (f m) -> f m (TextAreaElement er (DomBuilderSpace m) t)
363+liftTextAreaElement cfg = liftWithStateless $ \run -> textAreaElement $ fmap1 run cfg
src/Reflex/Dom/Builder/Class/Events.hsnew file+159
@@ -0,0 +1,159 @@
1+{-# LANGUAGE TemplateHaskell, DataKinds, GADTs, KindSignatures, TypeFamilies, PolyKinds #-}
2+module Reflex.Dom.Builder.Class.Events where
3+
4+import Data.GADT.Compare.TH
5+
6+data EventTag
7+ = AbortTag
8+ | BlurTag
9+ | ChangeTag
10+ | ClickTag
11+ | ContextmenuTag
12+ | DblclickTag
13+ | DragTag
14+ | DragendTag
15+ | DragenterTag
16+ | DragleaveTag
17+ | DragoverTag
18+ | DragstartTag
19+ | DropTag
20+ | ErrorTag
21+ | FocusTag
22+ | InputTag
23+ | InvalidTag
24+ | KeydownTag
25+ | KeypressTag
26+ | KeyupTag
27+ | LoadTag
28+ | MousedownTag
29+ | MouseenterTag
30+ | MouseleaveTag
31+ | MousemoveTag
32+ | MouseoutTag
33+ | MouseoverTag
34+ | MouseupTag
35+ | MousewheelTag
36+ | ScrollTag
37+ | SelectTag
38+ | SubmitTag
39+ | WheelTag
40+ {-
41+ | BeforecutTag
42+ | CutTag
43+ | BeforecopyTag
44+ | CopyTag
45+ | BeforepasteTag
46+ | PasteTag
47+-}
48+ | ResetTag
49+ | SearchTag
50+-- | SelectstartTag
51+ | TouchstartTag
52+ | TouchmoveTag
53+ | TouchendTag
54+ | TouchcancelTag
55+
56+data EventName :: EventTag -> * where
57+ Abort :: EventName 'AbortTag
58+ Blur :: EventName 'BlurTag
59+ Change :: EventName 'ChangeTag
60+ Click :: EventName 'ClickTag
61+ Contextmenu :: EventName 'ContextmenuTag
62+ Dblclick :: EventName 'DblclickTag
63+ Drag :: EventName 'DragTag
64+ Dragend :: EventName 'DragendTag
65+ Dragenter :: EventName 'DragenterTag
66+ Dragleave :: EventName 'DragleaveTag
67+ Dragover :: EventName 'DragoverTag
68+ Dragstart :: EventName 'DragstartTag
69+ Drop :: EventName 'DropTag
70+ Error :: EventName 'ErrorTag
71+ Focus :: EventName 'FocusTag
72+ Input :: EventName 'InputTag
73+ Invalid :: EventName 'InvalidTag
74+ Keydown :: EventName 'KeydownTag
75+ Keypress :: EventName 'KeypressTag
76+ Keyup :: EventName 'KeyupTag
77+ Load :: EventName 'LoadTag
78+ Mousedown :: EventName 'MousedownTag
79+ Mouseenter :: EventName 'MouseenterTag
80+ Mouseleave :: EventName 'MouseleaveTag
81+ Mousemove :: EventName 'MousemoveTag
82+ Mouseout :: EventName 'MouseoutTag
83+ Mouseover :: EventName 'MouseoverTag
84+ Mouseup :: EventName 'MouseupTag
85+ Mousewheel :: EventName 'MousewheelTag
86+ Scroll :: EventName 'ScrollTag
87+ Select :: EventName 'SelectTag
88+ Submit :: EventName 'SubmitTag
89+ Wheel :: EventName 'WheelTag
90+ {-
91+ Beforecut :: EventName 'BeforecutTag
92+ Cut :: EventName 'CutTag
93+ Beforecopy :: EventName 'BeforecopyTag
94+ Copy :: EventName 'CopyTag
95+ Beforepaste :: EventName 'BeforepasteTag
96+ Paste :: EventName 'PasteTag
97+-}
98+ Reset :: EventName 'ResetTag
99+ Search :: EventName 'SearchTag
100+-- Selectstart :: EventName 'SelectstartTag
101+ Touchstart :: EventName 'TouchstartTag
102+ Touchmove :: EventName 'TouchmoveTag
103+ Touchend :: EventName 'TouchendTag
104+ Touchcancel :: EventName 'TouchcancelTag
105+
106+newtype EventResult en = EventResult { unEventResult :: EventResultType en }
107+
108+type family EventResultType (en :: EventTag) :: * where
109+ EventResultType 'ClickTag = ()
110+ EventResultType 'DblclickTag = ()
111+ EventResultType 'KeypressTag = Int
112+ EventResultType 'KeydownTag = Int
113+ EventResultType 'KeyupTag = Int
114+ EventResultType 'ScrollTag = Int
115+ EventResultType 'MousemoveTag = (Int, Int)
116+ EventResultType 'MousedownTag = (Int, Int)
117+ EventResultType 'MouseupTag = (Int, Int)
118+ EventResultType 'MouseenterTag = ()
119+ EventResultType 'MouseleaveTag = ()
120+ EventResultType 'FocusTag = ()
121+ EventResultType 'BlurTag = ()
122+ EventResultType 'ChangeTag = ()
123+ EventResultType 'DragTag = ()
124+ EventResultType 'DragendTag = ()
125+ EventResultType 'DragenterTag = ()
126+ EventResultType 'DragleaveTag = ()
127+ EventResultType 'DragoverTag = ()
128+ EventResultType 'DragstartTag = ()
129+ EventResultType 'DropTag = ()
130+ EventResultType 'AbortTag = ()
131+ EventResultType 'ContextmenuTag = ()
132+ EventResultType 'ErrorTag = ()
133+ EventResultType 'InputTag = ()
134+ EventResultType 'InvalidTag = ()
135+ EventResultType 'LoadTag = ()
136+ EventResultType 'MouseoutTag = ()
137+ EventResultType 'MouseoverTag = ()
138+ EventResultType 'MousewheelTag = ()
139+ EventResultType 'SelectTag = ()
140+ EventResultType 'SubmitTag = ()
141+ {-
142+ EventResultType 'BeforecutTag = ()
143+ EventResultType 'CutTag = ()
144+ EventResultType 'BeforecopyTag = ()
145+ EventResultType 'CopyTag = ()
146+ EventResultType 'BeforepasteTag = ()
147+ EventResultType 'PasteTag = ()
148+-}
149+ EventResultType 'ResetTag = ()
150+ EventResultType 'SearchTag = ()
151+-- EventResultType 'SelectstartTag = ()
152+ EventResultType 'TouchstartTag = ()
153+ EventResultType 'TouchmoveTag = ()
154+ EventResultType 'TouchendTag = ()
155+ EventResultType 'TouchcancelTag = ()
156+ EventResultType 'WheelTag = ()
157+
158+deriveGEq ''EventName
159+deriveGCompare ''EventName
src/Reflex/Dom/Builder/Immediate.hsnew file+723
@@ -0,0 +1,723 @@
1+{-# LANGUAGE OverloadedStrings, MultiParamTypeClasses, FlexibleInstances, GeneralizedNewtypeDeriving, UndecidableInstances, FunctionalDependencies, DataKinds, TypeFamilies, RankNTypes, ConstraintKinds, TypeOperators, FlexibleContexts, LambdaCase, ScopedTypeVariables, PolyKinds, EmptyDataDecls #-}
2+module Reflex.Dom.Builder.Immediate where
3+
4+import Reflex
5+import Reflex.Host.Class
6+import Reflex.Dom.Builder.Class
7+import Reflex.Dom.PerformEvent.Class
8+import Reflex.Dom.PostBuild.Class
9+import Foreign.JavaScript.TH
10+
11+import GHCJS.DOM.Document (Document, createTextNode, createElement, createElementNS, createDocumentFragment)
12+import GHCJS.DOM.Element (setAttribute, setAttributeNS, removeAttribute, removeAttributeNS, getScrollTop)
13+import qualified GHCJS.DOM.HTMLInputElement as Input
14+import qualified GHCJS.DOM.Element as Element
15+import qualified GHCJS.DOM.Window as Window
16+import GHCJS.DOM.EventM (on, event, EventM)
17+import qualified GHCJS.DOM.Event as Event
18+import GHCJS.DOM.MouseEvent
19+import GHCJS.DOM.Node (appendChild, getParentNode, getPreviousSibling, removeChild, toNode, getOwnerDocument, insertBefore)
20+import GHCJS.DOM.Types (Node, IsNode, ToDOMString, FocusEvent, KeyboardEvent, WheelEvent, TouchEvent, IsElement, IsEvent, castToHTMLInputElement)
21+import qualified GHCJS.DOM.Types as DOM
22+import qualified GHCJS.DOM.EventM as DOM
23+import GHCJS.DOM.UIEvent
24+import Control.Monad.Reader
25+import Control.Monad.Trans.Control
26+import Control.Lens hiding (element)
27+import Control.Concurrent.Chan
28+import Data.Dependent.Sum
29+import Data.Functor.Misc
30+import Data.Bitraversable
31+import Data.Text (Text)
32+import Data.Maybe
33+import Data.IORef
34+import Control.Monad.Ref
35+import Control.Monad.Exception
36+import Data.Dependent.Map (DMap)
37+import qualified Data.Dependent.Map as DMap
38+import Data.Default
39+
40+import Debug.Trace hiding (traceEvent)
41+
42+data TriggerRef t a = TriggerRef { unTriggerRef :: IORef (Maybe (EventTrigger t a)) }
43+
44+data TriggerInvocation a = TriggerInvocation a (IO ())
45+
46+data ImmediateDomBuilderEnv t
47+ = ImmediateDomBuilderEnv { _immediateDomBuilderEnv_document :: Document
48+ , _immediateDomBuilderEnv_parent :: Node
49+ , _immediateDomBuilderEnv_events :: Chan [DSum (TriggerRef t) TriggerInvocation]
50+ }
51+
52+newtype ImmediateDomBuilderT t m a = ImmediateDomBuilderT { unImmediateDomBuilderT :: ReaderT (ImmediateDomBuilderEnv t) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
53+
54+instance MonadTransControl (ImmediateDomBuilderT t) where
55+ type StT (ImmediateDomBuilderT t) a = StT (ReaderT (ImmediateDomBuilderEnv t)) a
56+ liftWith = defaultLiftWith ImmediateDomBuilderT unImmediateDomBuilderT
57+ restoreT = defaultRestoreT ImmediateDomBuilderT
58+
59+instance MonadTrans (ImmediateDomBuilderT t) where
60+ lift = ImmediateDomBuilderT . lift
61+
62+{-# INLINABLE runImmediateDomBuilderT #-}
63+runImmediateDomBuilderT :: ImmediateDomBuilderT t m a -> ImmediateDomBuilderEnv t -> m a
64+runImmediateDomBuilderT (ImmediateDomBuilderT a) env = runReaderT a env
65+
66+{-# INLINABLE askDocument #-}
67+askDocument :: Monad m => ImmediateDomBuilderT t m Document
68+askDocument = ImmediateDomBuilderT $ asks _immediateDomBuilderEnv_document
69+
70+{-# INLINABLE askParent #-}
71+askParent :: Monad m => ImmediateDomBuilderT t m Node
72+askParent = ImmediateDomBuilderT $ asks _immediateDomBuilderEnv_parent
73+
74+{-# INLINABLE askEvents #-}
75+askEvents :: Monad m => ImmediateDomBuilderT t m (Chan [DSum (TriggerRef t) TriggerInvocation])
76+askEvents = ImmediateDomBuilderT $ asks _immediateDomBuilderEnv_events
77+
78+{-# INLINABLE append #-}
79+append :: (IsNode n, MonadIO m) => n -> ImmediateDomBuilderT t m ()
80+append n = do
81+ p <- askParent
82+ _ <- liftIO $ appendChild p $ Just n
83+ return ()
84+
85+{-# INLINABLE textNodeInternal #-}
86+textNodeInternal :: (MonadIO m, ToDOMString contents) => contents -> ImmediateDomBuilderT t m DOM.Text
87+textNodeInternal t = do
88+ doc <- askDocument
89+ Just n <- liftIO $ createTextNode doc t
90+ append n
91+ return n
92+
93+-- | s and e must both be children of the same node and s must precede e
94+{-# INLINABLE deleteBetweenInclusive #-}
95+deleteBetweenInclusive :: (MonadIO m, IsNode start, IsNode end) => start -> end -> m ()
96+deleteBetweenInclusive s e = do
97+ mCurrentParent <- getParentNode e -- May be different than it was at initial construction, e.g., because the parent may have dumped us in from a DocumentFragment
98+ case mCurrentParent of
99+ Nothing -> return () --TODO: Is this the right behavior?
100+ Just currentParent -> do
101+ let go = do
102+ Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
103+ _ <- removeChild currentParent $ Just x
104+ when (toNode s /= toNode x) go
105+ go
106+ _ <- removeChild currentParent $ Just e
107+ return ()
108+
109+type SupportsImmediateDomBuilder t m = (Reflex t, MonadIO m, MonadHold t m, MonadFix m, PerformEvent t m, Performable m ~ m, MonadReflexCreateTrigger t m, Deletable t m, MonadRef m, Ref m ~ Ref IO)
110+
111+newtype EventFilterTriggerRef t er (en :: EventTag) = EventFilterTriggerRef (IORef (Maybe (EventTrigger t (er en))))
112+
113+{-# INLINABLE makeElement #-}
114+makeElement :: forall er t m a. SupportsImmediateDomBuilder t m => Text -> ElementConfig er t (ImmediateDomBuilderT t m) -> ImmediateDomBuilderT t m a -> ImmediateDomBuilderT t m ((Element er GhcjsDomSpace t, a), DOM.Element)
115+makeElement elementTag cfg child = do
116+ doc <- askDocument
117+ Just e <- ImmediateDomBuilderT $ case cfg ^. namespace of
118+ Nothing -> createElement doc (Just elementTag)
119+ Just ens -> createElementNS doc (Just ens) (Just elementTag)
120+ ImmediateDomBuilderT $ iforM_ (cfg ^. initialAttributes) $ \(mAttrNamespace, n) v -> case mAttrNamespace of
121+ Nothing -> setAttribute e n v
122+ Just ans -> setAttributeNS e (Just ans) n v
123+ events <- askEvents
124+ result <- lift $ runImmediateDomBuilderT child $ ImmediateDomBuilderEnv
125+ { _immediateDomBuilderEnv_parent = toNode e
126+ , _immediateDomBuilderEnv_document = doc
127+ , _immediateDomBuilderEnv_events = events
128+ }
129+ append e
130+ lift $ performEvent_ $ ffor (cfg ^. modifyAttributes) $ imapM_ $ \(mAttrNamespace, n) mv -> case mAttrNamespace of
131+ Nothing -> maybe (removeAttribute e n) (setAttribute e n) mv
132+ Just ns -> maybe (removeAttributeNS e (Just ns) n) (setAttributeNS e (Just ns) n) mv
133+ eventTriggerRefs :: DMap EventName (EventFilterTriggerRef t er) <- liftIO $ fmap DMap.fromList $ forM (DMap.toList $ _elementConfig_eventFilters cfg) $ \(en :=> EventFilter (GhcjsDomHandler f)) -> do
134+ triggerRef <- newIORef Nothing
135+ _ <- elementOnEventName en e $ do
136+ evt <- DOM.event
137+ (flags, GhcjsDomHandler k) <- liftIO $ f $ GhcjsDomEvent evt
138+ when (_eventFlags_preventDefault flags) $ withIsEvent en DOM.preventDefault
139+ case _eventFlags_propagation flags of
140+ Propagation_Continue -> return ()
141+ Propagation_Stop -> withIsEvent en DOM.stopPropagation
142+ Propagation_StopImmediate -> withIsEvent en DOM.stopImmediatePropagation
143+ mv <- liftIO $ k ()
144+ liftIO $ forM_ mv $ \v -> writeChan events [TriggerRef triggerRef :=> TriggerInvocation v (return ())]
145+ return $ en :=> EventFilterTriggerRef triggerRef
146+ es <- newFanEventWithTrigger $ \(WrapArg en) t -> do
147+ case DMap.lookup en eventTriggerRefs of
148+ Just (EventFilterTriggerRef r) -> do
149+ writeIORef r $ Just t
150+ return $ do
151+ writeIORef r Nothing
152+ Nothing -> elementOnEventName en e $ do
153+ evt <- DOM.event
154+ let GhcjsDomHandler1 h = _elementConfig_eventHandler cfg
155+ mv <- liftIO $ h $ Pair1 en (GhcjsDomEvent evt)
156+ case mv of
157+ Nothing1 -> return ()
158+ Just1 v -> liftIO $ do
159+ --TODO: I don't think this is quite right: if a new trigger is created between when this is enqueued and when it fires, this may not work quite right
160+ ref <- newIORef $ Just t
161+ writeChan events [TriggerRef ref :=> TriggerInvocation v (return ())]
162+ return ((Element es e, result), e)
163+
164+newtype GhcjsDomHandler a b = GhcjsDomHandler { unGhcjsDomHandler :: a -> IO b }
165+
166+newtype GhcjsDomHandler1 a b = GhcjsDomHandler1 { unGhcjsDomHandler1 :: forall (x :: EventTag). a x -> IO (b x) }
167+
168+newtype GhcjsDomEvent en = GhcjsDomEvent { unGhcjsDomEvent :: EventType en }
169+
170+data GhcjsDomSpace
171+
172+instance DomSpace GhcjsDomSpace where
173+ type DomHandler GhcjsDomSpace = GhcjsDomHandler
174+ type DomHandler1 GhcjsDomSpace = GhcjsDomHandler1
175+ type RawEvent GhcjsDomSpace = GhcjsDomEvent
176+ type RawElement GhcjsDomSpace = DOM.Element
177+ {-# INLINABLE defaultEventHandler #-}
178+ defaultEventHandler _ = GhcjsDomHandler1 $ \(Pair1 en (GhcjsDomEvent evt)) -> do
179+ Just t <- withIsEvent en $ Event.getTarget evt
180+ mr <- runReaderT (defaultDomEventHandler (Element.castToElement t) en) evt
181+ return $ case mr of
182+ Nothing -> Nothing1
183+ Just r -> Just1 r
184+
185+instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t m) where
186+ type DomBuilderSpace (ImmediateDomBuilderT t m) = GhcjsDomSpace
187+ {-# INLINABLE textNode #-}
188+ textNode (TextNodeConfig contents) = do
189+ _ <- textNodeInternal contents
190+ return TextNode
191+ {-# INLINABLE element #-}
192+ element elementTag cfg child = fst <$> makeElement elementTag cfg child
193+ {-# INLINABLE fragment #-}
194+ fragment cfg child = liftThrough (deletable $ cfg ^. deleteSelf) $ do
195+ top <- textNodeInternal ("" :: Text)
196+ result <- child
197+ bottom <- textNodeInternal ("" :: Text)
198+ insertedAbove <- insertImmediateAbove top $ cfg ^. fragmentConfig_insertAbove
199+ deleted <- lift $ performEvent $ ffor (cfg ^. deleteSelf) $ \_ -> do
200+ deleteBetweenInclusive top bottom
201+ return (Fragment insertedAbove deleted, result)
202+ {-# INLINABLE placeholder #-}
203+ placeholder (PlaceholderConfig toInsertAbove delete) = liftThrough (deletable delete) $ do
204+ n <- textNodeInternal ("" :: Text)
205+ insertedAbove <- insertImmediateAbove n toInsertAbove
206+ --Note: "deleteSelf" must come after "insertAbove", because we need to be able to insert above in the same frame that we delete
207+ deleted <- lift $ performEvent $ ffor delete $ \_ -> do
208+ mp <- getParentNode n
209+ forM_ mp $ \p -> removeChild p $ Just n
210+ return $ Placeholder insertedAbove deleted
211+ {-# INLINABLE inputElement #-}
212+ inputElement cfg = do
213+ ((e, _), domElement) <- makeElement "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
214+ let domInputElement = castToHTMLInputElement domElement
215+ Just v0 <- Input.getValue domInputElement
216+ let getMyValue = fromMaybe "" <$> Input.getValue domInputElement
217+ valueChangedByUI <- performEvent $ ffor (Reflex.select (_element_events e) (WrapArg Input)) $ \_ ->
218+ getMyValue
219+ valueChangedBySetValue <- performEvent $ ffor (cfg ^. inputElementConfig_setValue) $ \v' -> do
220+ Input.setValue domInputElement $ Just v'
221+ getMyValue -- We get the value after setting it in case the browser has mucked with it somehow
222+ v <- holdDyn v0 $ leftmost
223+ [ valueChangedBySetValue
224+ , valueChangedByUI
225+ ]
226+ Input.setChecked domInputElement $ _inputElementConfig_initialChecked cfg
227+ checkedChangedByUI <- wrapDomEvent domInputElement (`on` Element.click) $ do
228+ Input.getChecked domInputElement
229+ checkedChangedBySetChecked <- performEvent $ ffor (_inputElementConfig_setChecked cfg) $ \newChecked -> do
230+ oldChecked <- Input.getChecked domInputElement
231+ if newChecked /= oldChecked
232+ then do Input.setChecked domInputElement newChecked
233+ return $ Just newChecked
234+ else return Nothing
235+ c <- holdDyn (_inputElementConfig_initialChecked cfg) $ leftmost
236+ [ fmapMaybe id checkedChangedBySetChecked
237+ , checkedChangedByUI
238+ ]
239+ let initialFocus = False --TODO: Is this correct?
240+ hasFocus <- holdDyn initialFocus $ leftmost
241+ [ False <$ Reflex.select (_element_events e) (WrapArg Blur)
242+ , True <$ Reflex.select (_element_events e) (WrapArg Focus)
243+ ]
244+ inputValue <- wrapDomEvent domElement (`on` Element.input) $ fromMaybe "" <$> Input.getValue domInputElement
245+ return $ InputElement v (nubDyn c) inputValue hasFocus e
246+ {-# INLINABLE textAreaElement #-}
247+ textAreaElement cfg = do --TODO
248+ (i, _) <- element "textarea" (_textAreaElementConfig_elementConfig cfg) $ return ()
249+ return $ TextAreaElement (constDyn "") never (constDyn False) i
250+
251+{-# INLINABLE insertImmediateAbove #-}
252+insertImmediateAbove :: (Reflex t, IsNode placeholder, PerformEvent t m, MonadIO (Performable m)) => placeholder -> Event t (ImmediateDomBuilderT t (Performable m) a) -> ImmediateDomBuilderT t m (Event t a)
253+insertImmediateAbove n toInsertAbove = do
254+ events <- askEvents
255+ lift $ performEvent $ ffor toInsertAbove $ \new -> do
256+ Just doc <- getOwnerDocument n
257+ Just df <- createDocumentFragment doc
258+ result <- runImmediateDomBuilderT new $ ImmediateDomBuilderEnv
259+ { _immediateDomBuilderEnv_parent = toNode df
260+ , _immediateDomBuilderEnv_document = doc
261+ , _immediateDomBuilderEnv_events = events
262+ }
263+ mp <- getParentNode n
264+ case mp of
265+ Nothing -> trace "ImmediateDomBuilderT: placeholder: Warning: (getParentNode n) returned Nothing" $ return ()
266+ Just p -> void $ insertBefore p (Just df) (Just n) -- If there's no parent, that means we've been removed from the DOM; this should not happen if the we're removing ourselves from the performEvent properly
267+ return result
268+
269+instance SupportsImmediateDomBuilder t m => Deletable t (ImmediateDomBuilderT t m) where
270+ {-# INLINABLE deletable #-}
271+ deletable = deletableFragment
272+
273+instance PerformEvent t m => PerformEvent t (ImmediateDomBuilderT t m) where
274+ type Performable (ImmediateDomBuilderT t m) = Performable m
275+ {-# INLINABLE performEvent_ #-}
276+ performEvent_ e = lift $ performEvent_ e
277+ {-# INLINABLE performEvent #-}
278+ performEvent e = lift $ performEvent e
279+
280+instance PostBuild t m => PostBuild t (ImmediateDomBuilderT t m) where
281+ {-# INLINABLE getPostBuild #-}
282+ getPostBuild = lift getPostBuild
283+
284+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ImmediateDomBuilderT t m) where
285+ {-# INLINABLE newEventWithTrigger #-}
286+ newEventWithTrigger = lift . newEventWithTrigger
287+ {-# INLINABLE newFanEventWithTrigger #-}
288+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
289+
290+instance (Monad m, MonadRef m, Ref m ~ Ref IO, MonadReflexCreateTrigger t m) => TriggerEvent t (ImmediateDomBuilderT t m) where
291+ {-# INLINABLE newTriggerEvent #-}
292+ newTriggerEvent = do
293+ (e, t) <- newTriggerEventWithOnComplete
294+ return (e, \a -> t a $ return ())
295+ {-# INLINABLE newTriggerEventWithOnComplete #-}
296+ newTriggerEventWithOnComplete = do
297+ events <- askEvents
298+ (eResult, reResultTrigger) <- lift newEventWithTriggerRef
299+ return $ (,) eResult $ \a cb -> writeChan events [TriggerRef reResultTrigger :=> TriggerInvocation a cb]
300+ {-# INLINABLE newEventWithLazyTriggerWithOnComplete #-}
301+ newEventWithLazyTriggerWithOnComplete f = do
302+ events <- askEvents
303+ eResult <- lift $ newEventWithTrigger $ \t -> f $ \a cb -> do
304+ reResultTrigger <- newIORef $ Just t
305+ writeChan events [TriggerRef reResultTrigger :=> TriggerInvocation a cb]
306+ return eResult
307+
308+instance HasWebView m => HasWebView (ImmediateDomBuilderT t m) where
309+ type WebViewPhantom (ImmediateDomBuilderT t m) = WebViewPhantom m
310+ askWebView = lift askWebView
311+
312+instance MonadRef m => MonadRef (ImmediateDomBuilderT t m) where
313+ type Ref (ImmediateDomBuilderT t m) = Ref m
314+ {-# INLINABLE newRef #-}
315+ newRef = lift . newRef
316+ {-# INLINABLE readRef #-}
317+ readRef = lift . readRef
318+ {-# INLINABLE writeRef #-}
319+ writeRef r = lift . writeRef r
320+
321+instance MonadAtomicRef m => MonadAtomicRef (ImmediateDomBuilderT t m) where
322+ {-# INLINABLE atomicModifyRef #-}
323+ atomicModifyRef r = lift . atomicModifyRef r
324+
325+instance (HasJS x m, ReflexHost t) => HasJS x (ImmediateDomBuilderT t m) where
326+ type JSM (ImmediateDomBuilderT t m) = JSM m
327+ liftJS = lift . liftJS
328+
329+type family EventType en where
330+ EventType 'AbortTag = UIEvent
331+ EventType 'BlurTag = FocusEvent
332+ EventType 'ChangeTag = DOM.Event
333+ EventType 'ClickTag = MouseEvent
334+ EventType 'ContextmenuTag = MouseEvent
335+ EventType 'DblclickTag = MouseEvent
336+ EventType 'DragTag = MouseEvent
337+ EventType 'DragendTag = MouseEvent
338+ EventType 'DragenterTag = MouseEvent
339+ EventType 'DragleaveTag = MouseEvent
340+ EventType 'DragoverTag = MouseEvent
341+ EventType 'DragstartTag = MouseEvent
342+ EventType 'DropTag = MouseEvent
343+ EventType 'ErrorTag = UIEvent
344+ EventType 'FocusTag = FocusEvent
345+ EventType 'InputTag = DOM.Event
346+ EventType 'InvalidTag = DOM.Event
347+ EventType 'KeydownTag = KeyboardEvent
348+ EventType 'KeypressTag = KeyboardEvent
349+ EventType 'KeyupTag = KeyboardEvent
350+ EventType 'LoadTag = UIEvent
351+ EventType 'MousedownTag = MouseEvent
352+ EventType 'MouseenterTag = MouseEvent
353+ EventType 'MouseleaveTag = MouseEvent
354+ EventType 'MousemoveTag = MouseEvent
355+ EventType 'MouseoutTag = MouseEvent
356+ EventType 'MouseoverTag = MouseEvent
357+ EventType 'MouseupTag = MouseEvent
358+ EventType 'MousewheelTag = MouseEvent
359+ EventType 'ScrollTag = UIEvent
360+ EventType 'SelectTag = UIEvent
361+ EventType 'SubmitTag = DOM.Event
362+ EventType 'WheelTag = WheelEvent
363+ {-
364+ EventType 'BeforecutTag = DOM.Event
365+ EventType 'CutTag = DOM.Event
366+ EventType 'BeforecopyTag = DOM.Event
367+ EventType 'CopyTag = DOM.Event
368+ EventType 'BeforepasteTag = DOM.Event
369+ EventType 'PasteTag = DOM.Event
370+-}
371+ EventType 'ResetTag = DOM.Event
372+ EventType 'SearchTag = DOM.Event
373+-- EventType 'SelectstartTag = DOM.Event
374+ EventType 'TouchstartTag = TouchEvent
375+ EventType 'TouchmoveTag = TouchEvent
376+ EventType 'TouchendTag = TouchEvent
377+ EventType 'TouchcancelTag = TouchEvent
378+
379+{-# INLINABLE defaultDomEventHandler #-}
380+defaultDomEventHandler :: IsElement e => e -> EventName en -> EventM e (EventType en) (Maybe (EventResult en))
381+defaultDomEventHandler e evt = liftM (Just . EventResult) $ case evt of
382+ Click -> return ()
383+ Dblclick -> return ()
384+ Keypress -> getKeyEvent
385+ Scroll -> getScrollTop e
386+ Keydown -> getKeyEvent
387+ Keyup -> getKeyEvent
388+ Mousemove -> getMouseEventCoords
389+ Mouseup -> getMouseEventCoords
390+ Mousedown -> getMouseEventCoords
391+ Mouseenter -> return ()
392+ Mouseleave -> return ()
393+ Focus -> return ()
394+ Blur -> return ()
395+ Change -> return ()
396+ Drag -> return ()
397+ Dragend -> return ()
398+ Dragenter -> return ()
399+ Dragleave -> return ()
400+ Dragover -> return ()
401+ Dragstart -> return ()
402+ Drop -> return ()
403+ Abort -> return ()
404+ Contextmenu -> return ()
405+ Error -> return ()
406+ Input -> return ()
407+ Invalid -> return ()
408+ Load -> return ()
409+ Mouseout -> return ()
410+ Mouseover -> return ()
411+ Select -> return ()
412+ Submit -> return ()
413+ {-
414+ Beforecut -> return ()
415+ Cut -> return ()
416+ Beforecopy -> return ()
417+ Copy -> return ()
418+ Beforepaste -> return ()
419+ Paste -> return ()
420+-}
421+ Reset -> return ()
422+ Search -> return ()
423+-- Selectstart -> return ()
424+ Touchstart -> return ()
425+ Touchmove -> return ()
426+ Touchend -> return ()
427+ Touchcancel -> return ()
428+ Mousewheel -> return ()
429+ Wheel -> return ()
430+
431+{-# INLINABLE defaultDomWindowEventHandler #-}
432+defaultDomWindowEventHandler :: DOM.Window -> EventName en -> EventM DOM.Window (EventType en) (Maybe (EventResult en))
433+defaultDomWindowEventHandler w evt = liftM (Just . EventResult) $ case evt of
434+ Click -> return ()
435+ Dblclick -> return ()
436+ Keypress -> getKeyEvent
437+ Scroll -> Window.getScrollY w
438+ Keydown -> getKeyEvent
439+ Keyup -> getKeyEvent
440+ Mousemove -> getMouseEventCoords
441+ Mouseup -> getMouseEventCoords
442+ Mousedown -> getMouseEventCoords
443+ Mouseenter -> return ()
444+ Mouseleave -> return ()
445+ Focus -> return ()
446+ Blur -> return ()
447+ Change -> return ()
448+ Drag -> return ()
449+ Dragend -> return ()
450+ Dragenter -> return ()
451+ Dragleave -> return ()
452+ Dragover -> return ()
453+ Dragstart -> return ()
454+ Drop -> return ()
455+ Abort -> return ()
456+ Contextmenu -> return ()
457+ Error -> return ()
458+ Input -> return ()
459+ Invalid -> return ()
460+ Load -> return ()
461+ Mouseout -> return ()
462+ Mouseover -> return ()
463+ Select -> return ()
464+ Submit -> return ()
465+ {-
466+ Beforecut -> return ()
467+ Cut -> return ()
468+ Beforecopy -> return ()
469+ Copy -> return ()
470+ Beforepaste -> return ()
471+ Paste -> return ()
472+-}
473+ Reset -> return ()
474+ Search -> return ()
475+-- Selectstart -> return ()
476+ Touchstart -> return ()
477+ Touchmove -> return ()
478+ Touchend -> return ()
479+ Touchcancel -> return ()
480+ Mousewheel -> return ()
481+ Wheel -> return ()
482+
483+{-# INLINABLE withIsEvent #-}
484+withIsEvent :: EventName en -> (IsEvent (EventType en) => r) -> r
485+withIsEvent en r = case en of
486+ Click -> r
487+ Dblclick -> r
488+ Keypress -> r
489+ Scroll -> r
490+ Keydown -> r
491+ Keyup -> r
492+ Mousemove -> r
493+ Mouseup -> r
494+ Mousedown -> r
495+ Mouseenter -> r
496+ Mouseleave -> r
497+ Focus -> r
498+ Blur -> r
499+ Change -> r
500+ Drag -> r
501+ Dragend -> r
502+ Dragenter -> r
503+ Dragleave -> r
504+ Dragover -> r
505+ Dragstart -> r
506+ Drop -> r
507+ Abort -> r
508+ Contextmenu -> r
509+ Error -> r
510+ Input -> r
511+ Invalid -> r
512+ Load -> r
513+ Mouseout -> r
514+ Mouseover -> r
515+ Select -> r
516+ Submit -> r
517+ {-
518+ Beforecut -> r
519+ Cut -> r
520+ Beforecopy -> r
521+ Copy -> r
522+ Beforepaste -> r
523+ Paste -> r
524+-}
525+ Reset -> r
526+ Search -> r
527+-- Selectstart -> r
528+ Touchstart -> r
529+ Touchmove -> r
530+ Touchend -> r
531+ Touchcancel -> r
532+ Mousewheel -> r
533+ Wheel -> r
534+
535+{-# INLINABLE elementOnEventName #-}
536+elementOnEventName :: IsElement e => EventName en -> e -> EventM e (EventType en) () -> IO (IO ())
537+elementOnEventName en e = case en of
538+ Abort -> on e Element.abort
539+ Blur -> on e Element.blurEvent
540+ Change -> on e Element.change
541+ Click -> on e Element.click
542+ Contextmenu -> on e Element.contextMenu
543+ Dblclick -> on e Element.dblClick
544+ Drag -> on e Element.drag
545+ Dragend -> on e Element.dragEnd
546+ Dragenter -> on e Element.dragEnter
547+ Dragleave -> on e Element.dragLeave
548+ Dragover -> on e Element.dragOver
549+ Dragstart -> on e Element.dragStart
550+ Drop -> on e Element.drop
551+ Error -> on e Element.error
552+ Focus -> on e Element.focusEvent
553+ Input -> on e Element.input
554+ Invalid -> on e Element.invalid
555+ Keydown -> on e Element.keyDown
556+ Keypress -> on e Element.keyPress
557+ Keyup -> on e Element.keyUp
558+ Load -> on e Element.load
559+ Mousedown -> on e Element.mouseDown
560+ Mouseenter -> on e Element.mouseEnter
561+ Mouseleave -> on e Element.mouseLeave
562+ Mousemove -> on e Element.mouseMove
563+ Mouseout -> on e Element.mouseOut
564+ Mouseover -> on e Element.mouseOver
565+ Mouseup -> on e Element.mouseUp
566+ Mousewheel -> on e Element.mouseWheel
567+ Scroll -> on e Element.scroll
568+ Select -> on e Element.select
569+ Submit -> on e Element.submit
570+ Wheel -> on e Element.wheel
571+ {-
572+ Beforecut -> on e Element.beforeCut
573+ Cut -> on e Element.cut
574+ Beforecopy -> on e Element.beforeCopy
575+ Copy -> on e Element.copy
576+ Beforepaste -> on e Element.beforePaste
577+ Paste -> on e Element.paste
578+-}
579+ Reset -> on e Element.reset
580+ Search -> on e Element.search
581+-- Selectstart -> on e Element.selectStart
582+ Touchstart -> on e Element.touchStart
583+ Touchmove -> on e Element.touchMove
584+ Touchend -> on e Element.touchEnd
585+ Touchcancel -> on e Element.touchCancel
586+
587+{-# INLINABLE windowOnEventName #-}
588+windowOnEventName :: EventName en -> DOM.Window -> EventM DOM.Window (EventType en) () -> IO (IO ())
589+windowOnEventName en e = case en of
590+ Abort -> on e Window.abort
591+ Blur -> on e Window.blurEvent
592+ Change -> on e Window.change
593+ Click -> on e Window.click
594+ Contextmenu -> on e Window.contextMenu
595+ Dblclick -> on e Window.dblClick
596+ Drag -> on e Window.drag
597+ Dragend -> on e Window.dragEnd
598+ Dragenter -> on e Window.dragEnter
599+ Dragleave -> on e Window.dragLeave
600+ Dragover -> on e Window.dragOver
601+ Dragstart -> on e Window.dragStart
602+ Drop -> on e Window.drop
603+ Error -> on e Window.error
604+ Focus -> on e Window.focusEvent
605+ Input -> on e Window.input
606+ Invalid -> on e Window.invalid
607+ Keydown -> on e Window.keyDown
608+ Keypress -> on e Window.keyPress
609+ Keyup -> on e Window.keyUp
610+ Load -> on e Window.load
611+ Mousedown -> on e Window.mouseDown
612+ Mouseenter -> on e Window.mouseEnter
613+ Mouseleave -> on e Window.mouseLeave
614+ Mousemove -> on e Window.mouseMove
615+ Mouseout -> on e Window.mouseOut
616+ Mouseover -> on e Window.mouseOver
617+ Mouseup -> on e Window.mouseUp
618+ Mousewheel -> on e Window.mouseWheel
619+ Scroll -> on e Window.scrollEvent
620+ Select -> on e Window.select
621+ Submit -> on e Window.submit
622+ Wheel -> on e Window.wheel
623+ {-
624+ Beforecut -> on e Window.beforeCut
625+ Cut -> on e Window.cut
626+ Beforecopy -> on e Window.beforeCopy
627+ Copy -> on e Window.copy
628+ Beforepaste -> on e Window.beforePaste
629+ Paste -> on e Window.paste
630+-}
631+ Reset -> on e Window.reset
632+ Search -> on e Window.search
633+-- Selectstart -> on e Window.selectStart
634+ Touchstart -> on e Window.touchStart
635+ Touchmove -> on e Window.touchMove
636+ Touchend -> on e Window.touchEnd
637+ Touchcancel -> on e Window.touchCancel
638+
639+{-# INLINABLE wrapDomEvent #-}
640+wrapDomEvent :: (MonadIO m, TriggerEvent t m) => e -> (e -> EventM e event () -> IO (IO ())) -> EventM e event a -> m (Event t a)
641+wrapDomEvent el elementOnevent getValue = wrapDomEventMaybe el elementOnevent $ liftM Just getValue
642+
643+{-# INLINABLE subscribeDomEvent #-}
644+subscribeDomEvent :: (EventM e event () -> IO (IO ()))
645+ -> EventM e event (Maybe a)
646+ -> Chan [DSum (TriggerRef t) TriggerInvocation]
647+ -> EventTrigger t a
648+ -> IO (IO ())
649+subscribeDomEvent elementOnevent getValue eventChan et = elementOnevent $ do
650+ mv <- getValue
651+ forM_ mv $ \v -> liftIO $ do
652+ --TODO: I don't think this is quite right: if a new trigger is created between when this is enqueued and when it fires, this may not work quite right
653+ etr <- newIORef $ Just et
654+ writeChan eventChan [TriggerRef etr :=> TriggerInvocation v (return ())]
655+
656+{-# INLINABLE wrapDomEventMaybe #-}
657+wrapDomEventMaybe :: (MonadIO m, TriggerEvent t m)
658+ => e
659+ -> (e -> EventM e event () -> IO (IO ()))
660+ -> EventM e event (Maybe a)
661+ -> m (Event t a)
662+wrapDomEventMaybe el elementOnevent getValue = do
663+ e <- newEventWithLazyTriggerWithOnComplete $ \trigger -> elementOnevent el $ do
664+ mv <- getValue
665+ forM_ mv $ \v -> liftIO $ trigger v $ return ()
666+ return e
667+
668+{-# INLINABLE wrapDomEventsMaybe #-}
669+wrapDomEventsMaybe :: (MonadIO m, MonadReflexCreateTrigger t m)
670+ => e
671+ -> (forall en. IsEvent (EventType en) => EventName en -> EventM e (EventType en) (Maybe (f en)))
672+ -> (forall en. EventName en -> e -> EventM e (EventType en) () -> IO (IO ()))
673+ -> ImmediateDomBuilderT t m (EventSelector t (WrapArg f EventName))
674+wrapDomEventsMaybe target handlers onEventName = do
675+ eventChan <- askEvents
676+ e <- lift $ newFanEventWithTrigger $ \(WrapArg en) -> withIsEvent en $ subscribeDomEvent (onEventName en target) (handlers en) eventChan
677+ return $! e
678+
679+{-# INLINABLE getKeyEvent #-}
680+getKeyEvent :: EventM e KeyboardEvent Int
681+getKeyEvent = do
682+ e <- event
683+ which <- getWhich e
684+ if which /= 0 then return which else do
685+ charCode <- getCharCode e
686+ if charCode /= 0 then return charCode else
687+ getKeyCode e
688+
689+{-# INLINABLE getMouseEventCoords #-}
690+getMouseEventCoords :: EventM e MouseEvent (Int, Int)
691+getMouseEventCoords = do
692+ e <- event
693+ bisequence (getX e, getY e)
694+
695+instance MonadSample t m => MonadSample t (ImmediateDomBuilderT t m) where
696+ {-# INLINABLE sample #-}
697+ sample = lift . sample
698+
699+instance MonadHold t m => MonadHold t (ImmediateDomBuilderT t m) where
700+ {-# INLINABLE hold #-}
701+ hold v0 v' = lift $ hold v0 v'
702+ {-# INLINABLE holdDyn #-}
703+ holdDyn v0 v' = lift $ holdDyn v0 v'
704+ {-# INLINABLE holdIncremental #-}
705+ holdIncremental v0 v' = lift $ holdIncremental v0 v'
706+
707+data WindowConfig t = WindowConfig -- No config options yet
708+
709+instance Default (WindowConfig t) where
710+ def = WindowConfig
711+
712+data Window t = Window
713+ { _window_events :: EventSelector t (WrapArg EventResult EventName)
714+ , _window_raw :: DOM.Window
715+ }
716+
717+wrapWindow :: (MonadIO m, MonadReflexCreateTrigger t m) => DOM.Window -> WindowConfig t -> ImmediateDomBuilderT t m (Window t)
718+wrapWindow wv _ = do
719+ events <- wrapDomEventsMaybe wv (defaultDomWindowEventHandler wv) windowOnEventName
720+ return $ Window
721+ { _window_events = events
722+ , _window_raw = wv
723+ }
src/Reflex/Dom/Class.hs+8−136
@@ -1,25 +1,17 @@
1−{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TemplateHaskell, PolyKinds, TypeOperators, DeriveFunctor, LambdaCase, CPP, ForeignFunctionInterface, DeriveDataTypeable, ConstraintKinds, DefaultSignatures #-}
21module Reflex.Dom.Class ( module Reflex.Dom.Class
32 , module Foreign.JavaScript.TH
43 ) where
54
5+import Reflex.Class
6+import Reflex.Dom.PostBuild.Class
7+import Reflex.Dom.PerformEvent.Class
8+
69import Prelude hiding (mapM, mapM_, sequence, concat)
710
8−import Reflex
9−import Reflex.Host.Class
1011import Foreign.JavaScript.TH
1112
12−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence)
1313import Data.Map (Map)
1414import qualified Data.Map as Map
15−import Data.Foldable
16−import Control.Monad.Ref
17−import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence)
18−import Control.Monad.State hiding (mapM, mapM_, forM, forM_, sequence)
19−import qualified Control.Monad.State.Strict as Strict hiding (mapM, mapM_, forM, forM_, sequence)
20−import Data.Dependent.Sum (DSum (..))
21−import GHCJS.DOM.Types hiding (Event)
22−--import GHCJS.DOM (WebView)
2315
2416-- | Alias for Data.Map.singleton
2517(=:) :: k -> a -> Map k a
@@ -31,127 +23,7 @@ keycodeEnter = 13
3123keycodeEscape :: Int
3224keycodeEscape = 27
3325
34−class ( Reflex t, MonadHold t m, MonadIO m, Functor m, MonadReflexCreateTrigger t m
35− , HasDocument m, HasWebView m, HasWebView (WidgetHost m), HasWebView (GuiAction m)
36− , MonadIO (WidgetHost m), MonadIO (GuiAction m), Functor (WidgetHost m), MonadSample t (WidgetHost m)
37− , HasPostGui t (GuiAction m) (WidgetHost m), HasPostGui t (GuiAction m) m, HasPostGui t (GuiAction m) (GuiAction m)
38− , MonadRef m, MonadRef (WidgetHost m)
39− , Ref m ~ Ref IO, Ref (WidgetHost m) ~ Ref IO --TODO: Eliminate this reliance on IO
40− , MonadFix m
41− ) => MonadWidget t m | m -> t where
42− type WidgetHost m :: * -> *
43− type GuiAction m :: * -> *
44− type WidgetOutput t m :: *
45− type WidgetOutput t m = Event t (WidgetHost m ())
46− askParent :: m Node
47− subWidget :: Node -> m a -> m a
48− subWidgetWithVoidActions :: Node -> m a -> m (a, WidgetOutput t m)
49− liftWidgetHost :: WidgetHost m a -> m a --TODO: Is this a good idea?
50− schedulePostBuild :: WidgetHost m () -> m ()
51− addVoidAction :: Event t (WidgetHost m ()) -> m ()
52− getRunWidget :: IsNode n => m (n -> m a -> WidgetHost m (a, WidgetHost m (), WidgetOutput t m))
53− tellWidgetOutput :: Ord k => Dynamic t (Map k (WidgetOutput t m)) -> m ()
54− default tellWidgetOutput :: (WidgetOutput t m ~ Event t (WidgetHost m ()), Ord k) => Dynamic t (Map k (WidgetOutput t m)) -> m ()
55− tellWidgetOutput = addVoidAction . switch . fmap (mergeWith (>>) . toList) . current
56−
57−class Monad m => HasDocument m where
58− askDocument :: m HTMLDocument
59−
60−instance HasDocument m => HasDocument (ReaderT r m) where
61− askDocument = lift askDocument
62−
63−instance HasDocument m => HasDocument (StateT r m) where
64− askDocument = lift askDocument
65−
66−instance HasDocument m => HasDocument (Strict.StateT r m) where
67− askDocument = lift askDocument
68−
69−newtype Restore m = Restore { restore :: forall a. m a -> IO a }
70−
71−class Monad m => MonadIORestore m where
72− askRestore :: m (Restore m)
73−
74−instance MonadIORestore m => MonadIORestore (ReaderT r m) where
75− askRestore = do
76− r <- ask
77− parentRestore <- lift askRestore
78− return $ Restore $ \a -> restore parentRestore $ runReaderT a r
79−
80−class (MonadRef h, Ref h ~ Ref m, MonadRef m) => HasPostGui t h m | m -> t h where
81− askPostGui :: m (h () -> IO ())
82− askRunWithActions :: m ([DSum (EventTrigger t) Identity] -> h ())
83− scheduleFollowup :: Ref m (Maybe (EventTrigger t a)) -> a -> m ()
84−
85−runFrameWithTriggerRef :: (HasPostGui t h m, MonadRef m, MonadIO m) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
86−runFrameWithTriggerRef r a = do
87− postGui <- askPostGui
88− runWithActions <- askRunWithActions
89− liftIO . postGui $ mapM_ (\t -> runWithActions [t :=> Identity a]) =<< readRef r
90−
91−runFrameWithTriggerRefNow :: (HasPostGui t m m, MonadRef m, MonadIO m) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
92−runFrameWithTriggerRefNow r a = do
93− runWithActions <- askRunWithActions
94− mapM_ (\t -> runWithActions [t :=> Identity a]) =<< readRef r
95−
96−instance HasPostGui t h m => HasPostGui t h (ReaderT r m) where
97− askPostGui = lift askPostGui
98− askRunWithActions = lift askRunWithActions
99− scheduleFollowup r a = lift $ scheduleFollowup r a
100−
101−instance HasPostGui t h m => HasPostGui t h (StateT s m) where
102− askPostGui = lift askPostGui
103− askRunWithActions = lift askRunWithActions
104− scheduleFollowup r a = lift $ scheduleFollowup r a
105−
106−instance HasPostGui t h m => HasPostGui t h (Strict.StateT s m) where
107− askPostGui = lift askPostGui
108− askRunWithActions = lift askRunWithActions
109− scheduleFollowup r a = lift $ scheduleFollowup r a
110−
111−instance MonadWidget t m => MonadWidget t (ReaderT r m) where
112− type WidgetHost (ReaderT r m) = WidgetHost m
113− type GuiAction (ReaderT r m) = GuiAction m
114− type WidgetOutput t (ReaderT r m) = WidgetOutput t m
115− askParent = lift askParent
116− subWidget n w = do
117− r <- ask
118− lift $ subWidget n $ runReaderT w r
119− subWidgetWithVoidActions n w = do
120− r <- ask
121− lift $ subWidgetWithVoidActions n $ runReaderT w r
122− liftWidgetHost = lift . liftWidgetHost
123− schedulePostBuild = lift . schedulePostBuild
124− addVoidAction = lift . addVoidAction
125− getRunWidget = do
126− r <- ask
127− runWidget <- lift getRunWidget
128− return $ \rootElement w -> do
129− (a, postBuild, voidActions) <- runWidget rootElement $ runReaderT w r
130− return (a, postBuild, voidActions)
131− tellWidgetOutput = lift . tellWidgetOutput
132−
133−performEvent_ :: MonadWidget t m => Event t (WidgetHost m ()) -> m ()
134−performEvent_ = addVoidAction
135−
136−performEvent :: (MonadWidget t m, Ref m ~ Ref IO) => Event t (WidgetHost m a) -> m (Event t a)
137−performEvent e = do
138− (eResult, reResultTrigger) <- newEventWithTriggerRef
139− addVoidAction $ ffor e $ \o -> do
140− result <- o
141− runFrameWithTriggerRef reResultTrigger result
142− return eResult
143−
144−performEventAsync :: forall t m a. MonadWidget t m => Event t ((a -> IO ()) -> WidgetHost m ()) -> m (Event t a)
145−performEventAsync e = do
146− (eResult, reResultTrigger) <- newEventWithTriggerRef
147− addVoidAction $ ffor e $ \o -> do
148− postGui <- askPostGui
149− runWithActions <- askRunWithActions
150− o $ \a -> postGui $ mapM_ (\t -> runWithActions [t :=> Identity a]) =<< readRef reResultTrigger
151− return eResult
152−
153−getPostBuild :: MonadWidget t m => m (Event t ())
154−getPostBuild = do
155− (e, trigger) <- newEventWithTriggerRef
156− schedulePostBuild $ runFrameWithTriggerRef trigger ()
157− return e
26+{-# INLINABLE holdOnStartup #-}
27+holdOnStartup :: (PostBuild t m, PerformEvent t m, MonadHold t m) => a -> Performable m a -> m (Behavior t a)
28+holdOnStartup a0 ma = do
29+ hold a0 =<< performEvent . (ma <$) =<< getPostBuild
src/Reflex/Dom/Deletable/Class.hsnew file+14
@@ -0,0 +1,14 @@
1+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}
2+module Reflex.Dom.Deletable.Class where
3+
4+import Reflex
5+
6+import Control.Monad.Reader
7+import Control.Monad.Trans.Control
8+
9+class (Reflex t, Monad m) => Deletable t m | m -> t where
10+ deletable :: Event t () -> m a -> m a
11+
12+instance Deletable t m => Deletable t (ReaderT r m) where
13+ {-# INLINABLE deletable #-}
14+ deletable e = liftThrough $ deletable e
src/Reflex/Dom/DynamicWriter.hs+90−36
@@ -1,50 +1,24 @@
1−{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, GeneralizedNewtypeDeriving, UndecidableInstances, StandaloneDeriving, FunctionalDependencies #-}
1+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses, TypeFamilies, GeneralizedNewtypeDeriving, UndecidableInstances, StandaloneDeriving, FunctionalDependencies, RecursiveDo #-}
22module Reflex.Dom.DynamicWriter where
33
44import Reflex
55import Reflex.Host.Class
66import Reflex.Dom.Class
7+import Reflex.Dom.Builder.Class
8+import Reflex.Dom.PerformEvent.Class
9+import Reflex.Dom.PostBuild.Class
710
11+import Control.Lens hiding (element)
812import Control.Monad.IO.Class
913import Control.Monad.State.Strict
1014import Control.Monad.Reader
1115import Control.Monad.Ref
1216import Control.Monad.Exception
13−import qualified Data.Map as Map
1417
1518instance MonadTrans (DynamicWriterT t w) where
1619 lift = DynamicWriterT . lift
1720
18−instance (MonadWidget t m, Monoid w) => MonadWidget t (DynamicWriterT t w m) where
19− type WidgetHost (DynamicWriterT t w m) = WidgetHost m
20− type GuiAction (DynamicWriterT t w m) = GuiAction m
21− type WidgetOutput t (DynamicWriterT t w m) = (Dynamic t w, WidgetOutput t m)
22− askParent = lift askParent
23− subWidget n (DynamicWriterT child) = DynamicWriterT $ do
24− ws <- get
25− (result, ws') <- lift $ subWidget n $ runStateT child ws
26− put ws'
27− return result
28− subWidgetWithVoidActions n (DynamicWriterT child) = DynamicWriterT $ do
29− ((result, ws), v) <- lift $ subWidgetWithVoidActions n $ runStateT child []
30− w <- mconcatDyn ws
31− return (result, (w, v))
32− liftWidgetHost = lift . liftWidgetHost
33− schedulePostBuild = lift . schedulePostBuild
34− addVoidAction = lift . addVoidAction
35− getRunWidget = do
36− runWidget <- lift getRunWidget
37− return $ \n (DynamicWriterT child) -> do
38− ((result, w), postBuild, o) <- runWidget n $ do
39− (result, ws) <- runStateT child []
40− w <- mconcatDyn ws
41− return (result, w)
42− return (result, postBuild, (w, o))
43− tellWidgetOutput o = do
44− tellDyn =<< mapDyn (mconcat . Map.elems) . joinDynThroughMap =<< mapDyn (fmap fst) o
45− lift . tellWidgetOutput =<< mapDyn (fmap snd) o
46−
47−newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException, HasDocument) -- The list is kept in reverse order
21+newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT [Dynamic t w] m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
4822
4923instance HasWebView m => HasWebView (DynamicWriterT t w m) where
5024 type WebViewPhantom (DynamicWriterT t w m) = WebViewPhantom m
@@ -56,12 +30,13 @@ instance MonadRef m => MonadRef (DynamicWriterT t w m) where
5630 readRef = lift . readRef
5731 writeRef r = lift . writeRef r
5832
33+instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
34+ atomicModifyRef r = lift . atomicModifyRef r
35+
5936instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
6037 newEventWithTrigger = lift . newEventWithTrigger
6138 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
6239
63−deriving instance HasPostGui t h m => HasPostGui t h (DynamicWriterT t w m)
64−
6540runDynamicWriterT :: (Reflex t, MonadHold t m, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
6641runDynamicWriterT (DynamicWriterT a) = do
6742 (result, ws) <- runStateT a []
@@ -74,11 +49,90 @@ class Monad m => MonadDynamicWriter t w m | m -> t w where
7449instance Monad m => MonadDynamicWriter t w (DynamicWriterT t w m) where
7550 tellDyn w = DynamicWriterT $ modify (w:)
7651
52+instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
53+ ask = lift ask
54+ local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
55+ reader = lift . reader
56+
57+liftDynamicWriterTThroughSync :: Monad m' => (m (a, [Dynamic t w]) -> m' (b, (a, [Dynamic t w]))) -> DynamicWriterT t w m a -> DynamicWriterT t w m' (b, a)
58+liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
59+ s <- get
60+ (b, (a, newS)) <- lift $ f $ runStateT child s
61+ put newS
62+ return (b, a)
63+
64+{-# INLINABLE liftDynamicWriterTElementConfig #-}
65+liftDynamicWriterTElementConfig :: ElementConfig er t (DynamicWriterT t w m) -> ElementConfig er t m
66+liftDynamicWriterTElementConfig cfg = cfg
67+ { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
68+ , _elementConfig_eventHandler = _elementConfig_eventHandler cfg -- This requires PolyKinds, and will fail to unify types otherwise
69+ }
70+
71+instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
72+ type DomBuilderSpace (DynamicWriterT t w m) = DomBuilderSpace m
73+ textNode = liftTextNode
74+ element elementTag cfg (DynamicWriterT child) = DynamicWriterT $ do
75+ s <- get
76+ let cfg' = cfg
77+ { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
78+ , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
79+ }
80+ (el, (a, newS)) <- lift $ element elementTag cfg' $ runStateT child s
81+ put newS
82+ return (el, a)
83+ fragment cfg (DynamicWriterT child) = do --TODO: Delete these things
84+ s <- DynamicWriterT get
85+ let cfg' = cfg
86+ { _fragmentConfig_insertAbove = fmap runDynamicWriterT $ _fragmentConfig_insertAbove cfg
87+ }
88+ rec children <- foldDyn (:) [] childOutputs
89+ tellDyn $ mconcat =<< children -- Needs to go above the child to preserve order
90+ (f, (a, newS)) <- DynamicWriterT $ lift $ fragment cfg' $ runStateT child s
91+ DynamicWriterT $ put newS
92+ let result = fmap fst $ _fragment_insertedAbove f
93+ childOutputs = fmap snd $ _fragment_insertedAbove f
94+ let f' = f
95+ { _fragment_insertedAbove = result
96+ }
97+ return (f', a)
98+ placeholder cfg = do --TODO: Delete these things
99+ let cfg' = cfg
100+ { _placeholderConfig_insertAbove = fmap runDynamicWriterT $ _placeholderConfig_insertAbove cfg
101+ }
102+ rec children <- foldDyn (:) [] childOutputs
103+ tellDyn $ mconcat =<< children -- Needs to go above the child to preserve order
104+ p <- DynamicWriterT $ lift $ placeholder cfg'
105+ let result = fmap fst $ _placeholder_insertedAbove p
106+ childOutputs = fmap snd $ _placeholder_insertedAbove p
107+ return $ p
108+ { _placeholder_insertedAbove = result
109+ }
110+ inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
111+ textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
112+
113+instance (Deletable t m, Reflex t, Monoid w, MonadHold t m) => Deletable t (DynamicWriterT t w m) where
114+ deletable delete child = do
115+ (result, output) <- lift $ deletable delete $ runDynamicWriterT child
116+ output' <- holdDyn output $ constDyn mempty <$ delete
117+ tellDyn $ join output'
118+ return result
119+
120+instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
121+ type Performable (DynamicWriterT t w m) = Performable m
122+ performEvent_ = lift . performEvent_
123+ performEvent = lift . performEvent
124+
125+instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
126+ newTriggerEvent = lift newTriggerEvent
127+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
128+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
129+
130+instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
131+ getPostBuild = lift getPostBuild
132+
77133instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
78134 tellDyn = lift . tellDyn
79135
80−deriving instance MonadReader r m => MonadReader r (DynamicWriterT t w m)
81−
82136instance MonadState s m => MonadState s (DynamicWriterT t w m) where
83137 get = lift get
84138 put = lift . put
src/Reflex/Dom/Internal.hs+71−253
@@ -3,291 +3,109 @@ module Reflex.Dom.Internal where
33
44import Prelude hiding (mapM, mapM_, concat, sequence, sequence_)
55
6−import Reflex.Dom.Internal.Foreign
6+import Reflex.Dom.Builder.Immediate
77import Reflex.Dom.Class
8+import Reflex.Dom.Internal.Foreign
9+import Reflex.Dom.PerformEvent.Base
10+import Reflex.Dom.PostBuild.Class
11+import Reflex.Host.Class
12+import Reflex.Spider (Spider, SpiderHost, runSpiderHost, Global)
813
914import GHCJS.DOM hiding (runWebGUI)
10−import GHCJS.DOM.Types hiding (Widget, unWidget, Event)
15+import qualified GHCJS.DOM.Types as DOM
1116import GHCJS.DOM.Node
1217import GHCJS.DOM.Element
1318import GHCJS.DOM.Document
14−import Reflex.Class
15−import Reflex.Host.Class
16−import Reflex.Spider (Spider, SpiderHost (..))
1719import Control.Lens
1820import Control.Monad hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
1921import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
2022import Control.Monad.Ref
21−import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_, get)
22−import Control.Monad.Exception
2323import Control.Concurrent
24−import Control.Applicative
2524import Data.ByteString (ByteString)
2625import Data.Dependent.Sum (DSum (..))
2726import Data.Foldable
28−import Data.Traversable
2927import qualified Data.Text as T
3028import Data.Text.Encoding
3129import Data.Monoid ((<>))
3230import Data.IORef
31+import Control.Monad
3332import Data.Maybe
3433
35−newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
36−
37−data GuiEnv t h x
38− = GuiEnv { _guiEnvDocument :: !HTMLDocument
39− , _guiEnvPostGui :: !(h () -> IO ())
40− , _guiEnvRunWithActions :: !([DSum (EventTrigger t) Identity] -> h ())
41− , _guiEnvWebView :: !(WebViewSingleton x)
42− , _guiEnvFollowupEvents :: Ref h [DSum (EventTriggerRef t h) Identity]
43− }
44−
45−--TODO: Poorly named
46−newtype Gui t h x m a = Gui { unGui :: ReaderT (GuiEnv t h x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
47−
48−runGui :: Gui t h x m a -> GuiEnv t h x -> m a
49−runGui (Gui g) env = runReaderT g env
50−
51−instance MonadTrans (Gui t h x) where
52− lift = Gui . lift
53−
54−instance MonadRef m => MonadRef (Gui t h x m) where
55− type Ref (Gui t h x m) = Ref m
56− newRef = lift . newRef
57− readRef = lift . readRef
58− writeRef r = lift . writeRef r
59−
60−instance MonadAtomicRef m => MonadAtomicRef (Gui t h x m) where
61− atomicModifyRef r f = lift $ atomicModifyRef r f
62−
63−instance MonadSample t m => MonadSample t (Gui t h x m) where
64− sample b = lift $ sample b
65−
66−instance MonadHold t m => MonadHold t (Gui t h x m) where
67− hold a0 e = lift $ hold a0 e
68−
69−instance (Reflex t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (Gui t h x m) where
70− newEventWithTrigger = lift . newEventWithTrigger
71− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
72−
73−data WidgetEnv
74− = WidgetEnv { _widgetEnvParent :: !Node
75− }
76−
77−data WidgetState t m
78− = WidgetState { _widgetStatePostBuild :: !(m ())
79− , _widgetStateVoidActions :: ![Event t (m ())] --TODO: Would it help to make this a strict list?
80− }
81−
82−liftM concat $ mapM makeLenses
83− [ ''WidgetEnv
84− , ''WidgetState
85− , ''GuiEnv
86− ]
87−
88−instance Monad m => HasDocument (Gui t h x m) where
89− askDocument = Gui $ view guiEnvDocument
90−
91−instance HasDocument m => HasDocument (Widget t m) where
92− askDocument = lift askDocument
93−
94−instance Monad m => HasWebView (Gui t h x m) where
95− type WebViewPhantom (Gui t h x m) = x
96− askWebView = Gui $ view guiEnvWebView
97−
98−instance MonadIORestore m => MonadIORestore (Gui t h x m) where
99− askRestore = Gui $ do
100− r <- askRestore
101− return $ Restore $ restore r . unGui
102−
103−instance HasWebView m => HasWebView (Widget t m) where
104− type WebViewPhantom (Widget t m) = WebViewPhantom m
105− askWebView = lift askWebView
106−
107−instance (MonadRef h, Ref h ~ Ref m, MonadRef m) => HasPostGui t h (Gui t h x m) where
108− askPostGui = Gui $ view guiEnvPostGui
109− askRunWithActions = Gui $ view guiEnvRunWithActions
110− scheduleFollowup r a = Gui $ do
111− followupEventsRef <- view guiEnvFollowupEvents
112− modifyRef' followupEventsRef $ ((EventTriggerRef r :=> Identity a) :)
113−
114−instance HasPostGui t h m => HasPostGui t h (Widget t m) where
115− askPostGui = lift askPostGui
116− askRunWithActions = lift askRunWithActions
117− scheduleFollowup r a = lift $ scheduleFollowup r a
118−
119−type WidgetInternal t m a = ReaderT WidgetEnv (StateT (WidgetState t m) m) a
120−
121−instance MonadTrans (Widget t) where
122− lift = Widget . lift . lift
123−
124−newtype Widget t m a = Widget { unWidget :: WidgetInternal t m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
125−
126−instance MonadSample t m => MonadSample t (Widget t m) where
127− sample b = lift $ sample b
128−
129−instance MonadHold t m => MonadHold t (Widget t m) where
130− hold v0 e = lift $ hold v0 e
131−
132−-- Need to build FRP circuit first, then elements
133− -- Can't read from FRP until the whole thing is built
134−--TODO: Use JSString when in JS
135−
136−instance MonadRef m => MonadRef (Widget t m) where
137− type Ref (Widget t m) = Ref m
138− newRef = lift . newRef
139− readRef = lift . readRef
140− writeRef r = lift . writeRef r
141−
142−instance MonadAtomicRef m => MonadAtomicRef (Widget t m) where
143− atomicModifyRef r f = lift $ atomicModifyRef r f
144−
145−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Widget t m) where
146− newEventWithTrigger = lift . newEventWithTrigger
147− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
148−
149−instance ( MonadRef m, Ref m ~ Ref IO, MonadRef h, Ref h ~ Ref IO --TODO: Shouldn't need to be IO
150− , MonadIO m, MonadIO h, Functor m
151− , ReflexHost t, MonadReflexCreateTrigger t m, MonadSample t m, MonadHold t m
152− , MonadFix m, HasWebView h, HasPostGui t h h
153− ) => MonadWidget t (Widget t (Gui t h x m)) where
154− type WidgetHost (Widget t (Gui t h x m)) = Gui t h x m
155− type GuiAction (Widget t (Gui t h x m)) = h
156− askParent = Widget $ view widgetEnvParent
157− --TODO: Use types to separate cohorts of possibly-recursive events/behaviors
158− -- | Schedule an action to occur after the current cohort has been built; this is necessary because Behaviors built in the current cohort may not be read until after it is complete
159− --schedulePostBuild :: Monad m => m () -> WidgetInternal t m ()
160− liftWidgetHost = Widget . lift . lift
161− schedulePostBuild a = Widget $ widgetStatePostBuild %= (a>>) --TODO: Can this >> be made strict?
162−
163− --addVoidAction :: Monad m => Event t (m ()) -> WidgetInternal t m ()
164− addVoidAction a = Widget $ widgetStateVoidActions %= (a:)
165− subWidget n child = Widget $ local (widgetEnvParent .~ toNode n) $ unWidget child
166− subWidgetWithVoidActions n child = Widget $ do
167− oldActions <- use widgetStateVoidActions
168− widgetStateVoidActions .= []
169− result <- local (widgetEnvParent .~ toNode n) $ unWidget child
170− actions <- use widgetStateVoidActions
171− widgetStateVoidActions .= oldActions
172− return (result, mergeWith (>>) actions)
173−-- runWidget :: (Monad m, IsNode n, Reflex t) => n -> Widget t m a -> m (a, Event t (m ()))
174− getRunWidget = return runWidget
175−
176−getQuitWidget :: MonadWidget t m => m (WidgetHost m ())
177−getQuitWidget = return $ do WebViewSingleton wv <- askWebView
178− liftIO $ quitWebView wv
179−
180−runWidget :: (Monad m, Reflex t, IsNode n) => n -> Widget t (Gui t h x m) a -> WidgetHost (Widget t (Gui t h x m)) (a, WidgetHost (Widget t (Gui t h x m)) (), Event t (WidgetHost (Widget t (Gui t h x m)) ()))
181−runWidget rootElement w = do
182− (result, WidgetState postBuild voidActions) <- runStateT (runReaderT (unWidget w) (WidgetEnv $ toNode rootElement)) (WidgetState (return ()) [])
183− let voidAction = mergeWith (>>) voidActions
184− return (result, postBuild, voidAction)
185−
186−holdOnStartup :: MonadWidget t m => a -> WidgetHost m a -> m (Behavior t a)
187−holdOnStartup a0 ma = do
188− (startupDone, startupDoneTriggerRef) <- newEventWithTriggerRef
189− schedulePostBuild $ do
190− a <- ma
191− runFrameWithTriggerRef startupDoneTriggerRef a
192− hold a0 startupDone
193−
194−mainWidget :: (forall x. Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) ()) -> IO ()
34+{-# INLINABLE mainWidget #-}
35+mainWidget :: (forall x. Widget x ()) -> IO ()
19536mainWidget w = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
196− Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
37+ Just doc <- liftM (fmap DOM.castToHTMLDocument) $ webViewGetDomDocument webView
19738 Just body <- getBody doc
19839 attachWidget body webViewSing w
19940
20041--TODO: The x's should be unified here
201−mainWidgetWithHead :: (forall x. Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) ()) -> (forall x. Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) ()) -> IO ()
42+{-# INLINABLE mainWidgetWithHead #-}
43+mainWidgetWithHead :: (forall x. Widget x ()) -> (forall x. Widget x ()) -> IO ()
20244mainWidgetWithHead h b = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
203− Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
204− Just headElement <- liftM (fmap castToHTMLElement) $ getHead doc
45+ Just doc <- liftM (fmap DOM.castToHTMLDocument) $ webViewGetDomDocument webView
46+ Just headElement <- liftM (fmap DOM.castToHTMLElement) $ getHead doc
20547 attachWidget headElement webViewSing h
20648 Just body <- getBody doc
20749 attachWidget body webViewSing b
20850
209−mainWidgetWithCss :: ByteString -> (forall x. Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) ()) -> IO ()
51+{-# INLINABLE mainWidgetWithCss #-}
52+mainWidgetWithCss :: ByteString -> (forall x. Widget x ()) -> IO ()
21053mainWidgetWithCss css w = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
211− Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
212− Just headElement <- liftM (fmap castToHTMLElement) $ getHead doc
54+ Just doc <- liftM (fmap DOM.castToHTMLDocument) $ webViewGetDomDocument webView
55+ Just headElement <- liftM (fmap DOM.castToHTMLElement) $ getHead doc
21356 setInnerHTML headElement . Just $ "<style>" <> T.unpack (decodeUtf8 css) <> "</style>" --TODO: Fix this
21457 Just body <- getBody doc
21558 attachWidget body webViewSing w
21659
217−instance HasPostGui t h m => HasPostGui t (WithWebView x h) (WithWebView x m) where
218− askPostGui = do
219− postGui <- lift askPostGui
220− webView <- askWebView
221− return $ \h -> postGui $ runWithWebView h webView
222− askRunWithActions = do
223− runWithActions <- lift askRunWithActions
224− return $ lift . runWithActions
225− scheduleFollowup r a = lift $ scheduleFollowup r a
226−
227−instance HasPostGui Spider SpiderHost SpiderHost where
228− askPostGui = return $ \h -> liftIO $ runSpiderHost h
229− askRunWithActions = return fireEvents
230− scheduleFollowup _ _ = liftIO $ putStrLn "scheduleFollowup{Spider,SpiderHost,SpiderHost}: not implemented"
231−
232−instance MonadTrans (WithWebView x) where
233− lift = WithWebView . lift
234−
235−instance MonadRef m => MonadRef (WithWebView x m) where
236− type Ref (WithWebView x m) = Ref m
237− newRef = lift . newRef
238− readRef = lift . readRef
239− writeRef r = lift . writeRef r
240−
241−instance MonadAtomicRef m => MonadAtomicRef (WithWebView x m) where
242− atomicModifyRef r = lift . atomicModifyRef r
243−
244−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView x m) where
245− newEventWithTrigger = lift . newEventWithTrigger
246− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
247−
248−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (WithWebView x m) where
249− subscribeEvent = lift . subscribeEvent
250−
251−instance MonadReflexHost t m => MonadReflexHost t (WithWebView x m) where
252− type ReadPhase (WithWebView x m) = ReadPhase m
253− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
254− runHostFrame = lift . runHostFrame
255−
256−attachWidget :: forall e x a. (IsHTMLElement e) => e -> WebViewSingleton x -> Widget Spider (Gui Spider (WithWebView x SpiderHost) x (HostFrame Spider)) a -> IO a
257−attachWidget rootElement wv w = runSpiderHost $ flip runWithWebView wv $ do --TODO: It seems to re-run this handler if the URL changes, even if it's only the fragment
258− Just doc <- liftM (fmap castToHTMLDocument) $ getOwnerDocument rootElement
259− frames <- liftIO newChan
260− followupEvents <- liftIO $ newIORef []
261− rec let guiEnv = GuiEnv doc (writeChan frames . runSpiderHost . flip runWithWebView wv) runWithActions wv followupEvents :: GuiEnv Spider (WithWebView x SpiderHost) x
262− runWithActions dm = do
263− voidActionNeeded <- fireEventsAndRead dm $ do
264− sequence =<< readEvent voidActionHandle
265− case voidActionNeeded of
266− Nothing -> return ()
267− Just va -> do
268− runHostFrame $ runGui va guiEnv
269− doFollowup
270− doFollowup = do
271− fe <- liftIO $ atomicModifyIORef followupEvents $ \x -> ([], x)
272− fe' <- forM fe $ \(EventTriggerRef r :=> a) -> do
273− mt <- readRef r
274− case mt of
275− Nothing -> return Nothing
276− Just t -> return $ Just (t :=> a)
277− let fe'' = catMaybes fe'
278− when (not $ null fe'') $ runWithActions fe''
279− Just df <- liftIO $ createDocumentFragment doc
280− (result, voidAction) <- runHostFrame $ flip runGui guiEnv $ do
281− (r, postBuild, va) <- runWidget df w
282− postBuild -- This probably shouldn't be run inside the frame; we need to make sure we don't run a frame inside of a frame
283− return (r, va)
284− setInnerHTML rootElement $ Just ""
285− _ <- appendChild rootElement $ Just df
286− voidActionHandle <- subscribeEvent voidAction --TODO: Should be unnecessary
287− --postGUISync seems to leak memory on GHC (unknown on GHCJS)
288− doFollowup -- This must go after voidActionHandle is subscribed; otherwise, a loop results
289− _ <- liftIO $ forkIO $ forever $ postGUISync =<< readChan frames -- postGUISync is necessary to prevent segfaults in GTK, which is not thread-safe
290− return result
291−
292−--type MonadWidget t h m = (t ~ Spider, h ~ Gui Spider SpiderHost (HostFrame Spider), m ~ Widget t h, Monad h, MonadHold t h, HasDocument h, MonadSample t h, MonadRef h, MonadIO h, Functor (Event t), Functor h, Reflex t) -- Locking down these types seems to help a little in GHCJS, but not really in GHC
293−
60+type Widget x = PostBuildT Spider (ImmediateDomBuilderT Spider (WithWebView x (PerformEventT Spider (SpiderHost Global)))) --TODO: Make this more abstract --TODO: Put the WithWebView underneath PerformEventT - I think this would perform better
61+
62+{-# INLINABLE attachWidget #-}
63+attachWidget :: DOM.IsElement e => e -> WebViewSingleton x -> Widget x a -> IO a
64+attachWidget rootElement wv w = fst <$> attachWidget' rootElement wv w
65+
66+{-# INLINABLE attachWidget' #-}
67+attachWidget' :: DOM.IsElement e => e -> WebViewSingleton x -> Widget x a -> IO (a, FireCommand Spider (SpiderHost Global))
68+attachWidget' rootElement wv w = do
69+ Just doc <- getOwnerDocument rootElement
70+ events <- newChan
71+ (result, fc@(FireCommand fire)) <- runSpiderHost $ do
72+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
73+ let builderEnv = ImmediateDomBuilderEnv
74+ { _immediateDomBuilderEnv_document = doc
75+ , _immediateDomBuilderEnv_parent = toNode rootElement
76+ , _immediateDomBuilderEnv_events = events
77+ }
78+ results@(_, FireCommand fire) <- hostPerformEventT $ runWithWebView (runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv) wv
79+ mPostBuildTrigger <- readRef postBuildTriggerRef
80+ forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
81+ return results
82+ void $ forkIO $ forever $ do
83+ ers <- readChan events
84+ _ <- postGUISync $ runSpiderHost $ do
85+ mes <- liftIO $ forM ers $ \(TriggerRef er :=> TriggerInvocation a _) -> do
86+ me <- readIORef er
87+ return $ fmap (\e -> e :=> Identity a) me
88+ _ <- fire (catMaybes mes) $ return ()
89+ liftIO $ forM_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
90+ return ()
91+ return (result, fc)
92+
93+data AppInput t = AppInput
94+ { _appInput_window :: Window t
95+ }
96+
97+data AppOutput t = AppOutput --TODO: Add quit event
98+ { _appOutput_windowConfig :: WindowConfig t
99+ }
100+
101+runApp' :: (t ~ Spider) => (forall x. AppInput t -> Widget x (AppOutput t)) -> IO ()
102+runApp' app = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
103+ Just doc <- liftM (fmap DOM.castToHTMLDocument) $ webViewGetDomDocument webView
104+ Just body <- getBody doc
105+ Just win <- getDefaultView doc
106+ rec o <- attachWidget body webViewSing $ do
107+ w <- lift $ wrapWindow win $ _appOutput_windowConfig o
108+ app $ AppInput
109+ { _appInput_window = w
110+ }
111+ return ()
src/Reflex/Dom/Location.hs+3−2
@@ -2,11 +2,12 @@ module Reflex.Dom.Location (getLocationHost, getLocationProtocol) where
22
33import Reflex.Dom.Class
44import qualified Reflex.Dom.Internal.Foreign as F
5+import Data.Text (Text)
56
67import Control.Monad.IO.Class
78
8−getLocationHost :: (MonadIO m) => WebViewSingleton x -> m String
9+getLocationHost :: (MonadIO m) => WebViewSingleton x -> m Text
910getLocationHost (WebViewSingleton wv) = liftIO $ F.getLocationHost wv
1011
11−getLocationProtocol :: (MonadIO m) => WebViewSingleton x -> m String
12+getLocationProtocol :: (MonadIO m) => WebViewSingleton x -> m Text
1213getLocationProtocol (WebViewSingleton wv) = liftIO $ F.getLocationProtocol wv
src/Reflex/Dom/Old.hsnew file+44
@@ -0,0 +1,44 @@
1+{-# LANGUAGE ConstraintKinds, TypeFamilies, FlexibleContexts #-}
2+module Reflex.Dom.Old where
3+
4+import Control.Monad.Exception
5+import Control.Monad.Fix
6+import Control.Monad.IO.Class
7+import Control.Monad.Ref
8+import Foreign.JavaScript.TH
9+import Reflex
10+import Reflex.Dom.Builder.Class
11+import Reflex.Dom.Builder.Immediate
12+import Reflex.Dom.PerformEvent.Class
13+import Reflex.Dom.PostBuild.Class
14+import Reflex.Host.Class
15+
16+--TODO: HasDocument is still not accounted for
17+type MonadWidget t m =
18+ ( DomBuilder t m
19+ , DomBuilderSpace m ~ GhcjsDomSpace
20+ , MonadFix m
21+ , MonadHold t m
22+ , MonadSample t (Performable m)
23+ , MonadReflexCreateTrigger t m
24+ , PostBuild t m
25+ , PerformEvent t m
26+ , MonadIO m
27+ , MonadIO (Performable m)
28+ , TriggerEvent t m
29+ , HasWebView m
30+ , HasWebView (Performable m)
31+ , MonadAsyncException m
32+ , MonadAsyncException (Performable m)
33+ , MonadRef m
34+ , Ref m ~ Ref IO
35+ , MonadRef (Performable m)
36+ , Ref (Performable m) ~ Ref IO
37+ )
38+
39+type WidgetHost m = Performable m
40+
41+type El = Element EventResult GhcjsDomSpace
42+
43+_el_element :: El t -> RawElement GhcjsDomSpace
44+_el_element = _element_raw
src/Reflex/Dom/PerformEvent/Base.hsnew file+166
@@ -0,0 +1,166 @@
1+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, GeneralizedNewtypeDeriving, FlexibleInstances, TypeFamilies, TypeOperators, RankNTypes, ScopedTypeVariables, StandaloneDeriving, FlexibleContexts, RecursiveDo, UndecidableInstances #-}
2+module Reflex.Dom.PerformEvent.Base where
3+
4+import Reflex
5+import Reflex.Host.Class
6+import Reflex.Dom.PerformEvent.Class
7+import Reflex.Dom.Deletable.Class
8+import Foreign.JavaScript.TH
9+
10+import Control.Monad.Identity
11+import Control.Monad.Ref
12+import Control.Monad.State.Strict
13+import Control.Monad.Exception
14+import Data.Dependent.Sum
15+import Data.Maybe
16+import Data.Functor.Misc
17+import Data.Dependent.Map (DMap)
18+import qualified Data.Dependent.Map as DMap
19+import Data.Word
20+import Data.Functor.Compose
21+import Data.Semigroup
22+import Data.These
23+import Data.Align
24+import Control.Lens
25+
26+newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
27+
28+newtype FireCommand t m = FireCommand { runFireCommand :: forall a. [DSum (EventTrigger t) Identity] -> (ReadPhase m a) -> m [a] } --TODO: The handling of this ReadPhase seems wrong, or at least inelegant; how do we actually make the decision about what order frames run in?
29+
30+newtype PerformEventT t m a = PerformEventT { unPerformEventT :: StateT [Event t (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())] (HostFrame t) a }
31+
32+deriving instance ReflexHost t => Functor (PerformEventT t m)
33+deriving instance ReflexHost t => Applicative (PerformEventT t m)
34+deriving instance ReflexHost t => Monad (PerformEventT t m)
35+deriving instance ReflexHost t => MonadFix (PerformEventT t m)
36+deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
37+deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
38+
39+instance (ReflexHost t, MonadAsyncException (HostFrame t)) => MonadAsyncException (PerformEventT t m) where
40+ mask f = PerformEventT $ mask $ \unmask -> unPerformEventT $ f $ PerformEventT . unmask . unPerformEventT
41+
42+instance (ReflexHost t, HasWebView (HostFrame t)) => HasWebView (PerformEventT t m) where
43+ type WebViewPhantom (PerformEventT t m) = WebViewPhantom (HostFrame t)
44+ askWebView = PerformEventT $ lift askWebView
45+
46+instance (ReflexHost t, Ref m ~ Ref IO, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => PerformEvent t (PerformEventT t m) where
47+ type Performable (PerformEventT t m) = PerformEventT t m
48+ {-# INLINABLE performEvent_ #-}
49+ performEvent_ = PerformEventT . modify . (:) . fmap (This . (>> return []))
50+ {-# INLINABLE performEvent #-}
51+ performEvent e = PerformEventT $ do
52+ (eResult, reResultTrigger) <- newEventWithTriggerRef
53+ modify $ (:) $ ffor e $ \o -> This $ do
54+ result <- o
55+ return [EventTriggerRef reResultTrigger :=> Identity result]
56+ return eResult
57+
58+instance (ReflexHost t, Monad (HostFrame t), Ref (HostFrame t) ~ Ref IO, Ref m ~ Ref IO) => Deletable t (PerformEventT t m) where
59+ {-# INLINABLE deletable #-}
60+ deletable d a = PerformEventT $ do
61+ (result, reverseEventsToPerform) <- lift $ runStateT (unPerformEventT a) []
62+ let (numberedEvents, _) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
63+ eventToPerform <- lift $ mergePerformEventsAndAdd (DMap.fromList numberedEvents) never
64+ modify (align eventToPerform d :)
65+ return result
66+
67+instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
68+ {-# INLINABLE newEventWithTrigger #-}
69+ newEventWithTrigger = PerformEventT . lift . newEventWithTrigger
70+ {-# INLINABLE newFanEventWithTrigger #-}
71+ newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
72+
73+{-# INLINABLE numberWith #-}
74+numberWith :: (Enum n, Traversable t) => (n -> a -> b) -> t a -> n -> (t b, n)
75+numberWith f t n0 = flip runState n0 $ forM t $ \x -> state $ \n -> (f n x, succ n)
76+
77+{-# INLINABLE mergePerformEventsAndAdd #-}
78+mergePerformEventsAndAdd :: forall t m. ReflexHost t
79+ => DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)
80+ -> Event t (PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)))
81+ -> HostFrame t (Event t (PerformEventT t m [DSum (EventTriggerRef t m) Identity]))
82+mergePerformEventsAndAdd initial eAddNew = do
83+ rec events :: Incremental t PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t))
84+ <- holdIncremental initial $ eAddNew <> eDelete
85+ let event = mergeIncremental events
86+ eventToPerform :: Event t (PerformEventT t m [DSum (EventTriggerRef t m) Identity])
87+ eventToPerform = ffor event $
88+ fmap concat .
89+ sequence .
90+ fmapMaybe (\(Const2 _ :=> Identity x) -> x ^? here) .
91+ DMap.toList
92+ eDelete :: Event t (PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)))
93+ eDelete = fforMaybe event $
94+ fmap (PatchDMap . DMap.fromList) .
95+ (\l -> if null l then Nothing else Just l) .
96+ fmapMaybe (\(Const2 k :=> Identity x) -> (Const2 k :=> Compose Nothing) <$ x ^? there) .
97+ DMap.toList
98+ return eventToPerform
99+
100+{-# INLINABLE runPerformEventT #-}
101+runPerformEventT :: forall t m a. (ReflexHost t, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO, Ref m ~ Ref IO) => PerformEventT t m a -> HostFrame t (a, Event t (HostFrame t [DSum (EventTriggerRef t m) Identity]))
102+runPerformEventT (PerformEventT a) = do
103+ (result, reverseEventsToPerform) <- runStateT a []
104+ (eAddNew, reAddNewTrigger) <- newEventWithTriggerRef
105+ let (numberedEvents, nextNumber) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
106+ nextNumberRef <- newRef nextNumber
107+ let runResult :: PerformEventT t m [DSum (EventTriggerRef t m) Identity] -> HostFrame t [DSum (EventTriggerRef t m) Identity]
108+ runResult (PerformEventT r) = do
109+ (followups, reverseNewEventsToPerform) <- runStateT r []
110+ oldN <- readRef nextNumberRef
111+ let (numberedNewEvents, newN) = numberWith (\n e -> Const2 n :=> Compose (Just e)) (reverse reverseNewEventsToPerform) oldN
112+ writeRef nextNumberRef newN
113+ return $ (EventTriggerRef reAddNewTrigger :=> Identity (PatchDMap (DMap.fromList numberedNewEvents))) : followups --TODO: Don't fire eAddNew when the list is empty
114+ eventToPerform <- mergePerformEventsAndAdd (DMap.fromList numberedEvents) eAddNew
115+ return (result, runResult <$> eventToPerform)
116+
117+{-# INLINABLE hostPerformEventT #-}
118+hostPerformEventT :: forall t m a. (Monad m, MonadSubscribeEvent t m, MonadReflexHost t m, MonadRef m, Ref m ~ Ref IO, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => PerformEventT t m a -> m (a, FireCommand t m)
119+hostPerformEventT a = do
120+ (result, eventToPerform) <- runHostFrame $ runPerformEventT a
121+ eventToPerformHandle <- subscribeEvent eventToPerform
122+ return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
123+ let go :: [DSum (EventTrigger t) Identity] -> m [a']
124+ go ts = do
125+ (result', mToPerform) <- fireEventsAndRead ts $ do
126+ mToPerform <- sequence =<< readEvent eventToPerformHandle
127+ result' <- readPhase
128+ return (result', mToPerform)
129+ case mToPerform of
130+ Nothing -> return [result']
131+ Just toPerform -> do
132+ followupEventTriggerRefs <- runHostFrame toPerform
133+ followupEventTriggers <- forM followupEventTriggerRefs $ \(EventTriggerRef rt :=> x) -> do
134+ mt <- readRef rt
135+ return $ fmap (\t -> t :=> x) mt
136+ fmap (result':) $ go $ catMaybes followupEventTriggers
137+ go triggers
138+
139+instance ReflexHost t => MonadSample t (PerformEventT t m) where
140+ {-# INLINABLE sample #-}
141+ sample = PerformEventT . lift . sample
142+
143+instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
144+ {-# INLINABLE hold #-}
145+ hold v0 v' = PerformEventT $ lift $ hold v0 v'
146+ {-# INLINABLE holdDyn #-}
147+ holdDyn v0 v' = PerformEventT $ lift $ holdDyn v0 v'
148+ {-# INLINABLE holdIncremental #-}
149+ holdIncremental v0 v' = PerformEventT $ lift $ holdIncremental v0 v'
150+
151+instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
152+ type Ref (PerformEventT t m) = Ref (HostFrame t)
153+ {-# INLINABLE newRef #-}
154+ newRef = PerformEventT . lift . newRef
155+ {-# INLINABLE readRef #-}
156+ readRef = PerformEventT . lift . readRef
157+ {-# INLINABLE writeRef #-}
158+ writeRef r = PerformEventT . lift . writeRef r
159+
160+instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
161+ {-# INLINABLE atomicModifyRef #-}
162+ atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
163+
164+instance (HasJS x (HostFrame t), ReflexHost t) => HasJS x (PerformEventT t m) where
165+ type JSM (PerformEventT t m) = JSM (HostFrame t)
166+ liftJS = PerformEventT . lift . liftJS
src/Reflex/Dom/PerformEvent/Class.hsnew file+38
@@ -0,0 +1,38 @@
1+{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, GeneralizedNewtypeDeriving, FlexibleInstances, TypeFamilies, TypeOperators, RankNTypes, ScopedTypeVariables, StandaloneDeriving, FlexibleContexts, UndecidableInstances #-}
2+module Reflex.Dom.PerformEvent.Class where
3+
4+import Reflex
5+
6+import Control.Monad.Reader
7+
8+class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
9+ type Performable m :: * -> *
10+ performEvent :: Event t (Performable m a) -> m (Event t a)
11+ performEvent_ :: Event t (Performable m ()) -> m ()
12+
13+--TODO: Shouldn't have IO hard-coded
14+class Monad m => TriggerEvent t m | m -> t where
15+ newTriggerEvent :: m (Event t a, a -> IO ())
16+ newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
17+ newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
18+
19+{-# INLINABLE performEventAsync #-}
20+performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
21+performEventAsync e = do
22+ (eOut, triggerEOut) <- newTriggerEvent
23+ performEvent_ $ fmap ($ triggerEOut) e
24+ return eOut
25+
26+instance PerformEvent t m => PerformEvent t (ReaderT r m) where
27+ type Performable (ReaderT r m) = ReaderT r (Performable m)
28+ performEvent_ e = do
29+ r <- ask
30+ lift $ performEvent_ $ flip runReaderT r <$> e
31+ performEvent e = do
32+ r <- ask
33+ lift $ performEvent $ flip runReaderT r <$> e
34+
35+instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
36+ newTriggerEvent = lift newTriggerEvent
37+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
38+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
src/Reflex/Dom/PostBuild/Class.hsnew file+133
@@ -0,0 +1,133 @@
1+{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, GeneralizedNewtypeDeriving, TypeFamilies, UndecidableInstances, RecursiveDo, ScopedTypeVariables, DataKinds, TypeOperators, PolyKinds #-}
2+module Reflex.Dom.PostBuild.Class where
3+
4+import Reflex
5+import Reflex.Host.Class
6+import Reflex.Dom.Builder.Class
7+import Reflex.Dom.PerformEvent.Class
8+import Foreign.JavaScript.TH
9+
10+import Control.Lens hiding (element)
11+import Control.Monad.Reader
12+import Control.Monad.Trans.Control
13+import Control.Monad.Ref
14+import Control.Monad.Exception
15+
16+class (Reflex t, Monad m) => PostBuild t m where
17+ getPostBuild :: m (Event t ())
18+
19+newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
20+
21+instance MonadTransControl (PostBuildT t) where
22+ type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
23+ {-# INLINABLE liftWith #-}
24+ liftWith = defaultLiftWith PostBuildT unPostBuildT
25+ {-# INLINABLE restoreT #-}
26+ restoreT = defaultRestoreT PostBuildT
27+
28+instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
29+ {-# INLINABLE getPostBuild #-}
30+ getPostBuild = PostBuildT ask
31+
32+instance Deletable t m => Deletable t (PostBuildT t m) where
33+ {-# INLINABLE deletable #-}
34+ deletable d = liftThrough $ deletable d
35+
36+{-# INLINABLE liftPostBuildTElementConfig #-}
37+liftPostBuildTElementConfig :: ElementConfig er t (PostBuildT t m) -> ElementConfig er t m
38+liftPostBuildTElementConfig cfg = cfg
39+ { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
40+ , _elementConfig_eventHandler = _elementConfig_eventHandler cfg -- This requires PolyKinds, and will fail to unify types otherwise
41+ }
42+
43+instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBuilder t (PostBuildT t m) where
44+ type DomBuilderSpace (PostBuildT t m) = DomBuilderSpace m
45+ {-# INLINABLE textNode #-}
46+ textNode = lift . textNode
47+ {-# INLINABLE element #-}
48+ element t cfg child = liftWith $ \run -> element t (liftPostBuildTElementConfig cfg) $ run child
49+ {-# INLINABLE fragment #-}
50+ fragment cfg child = liftWith $ \run -> do
51+ rec (delayedDelete, childPostBuild) <- deletable delayedDelete $ do
52+ delayedDeleteInner <- performEvent $ return () <$ cfg ^. deleteSelf
53+ childPostBuildInner <- performEvent $ return () <$ _fragment_insertedAbove f
54+ return (delayedDeleteInner, childPostBuildInner)
55+ let cfg' = cfg
56+ { _fragmentConfig_insertAbove = fmap (\a -> runPostBuildT a =<< headE childPostBuild) $ _fragmentConfig_insertAbove cfg
57+ }
58+ (f, result) <- fragment cfg' $ run child
59+ return (f, result)
60+ {-# INLINABLE placeholder #-}
61+ placeholder cfg = lift $ do
62+ rec (delayedDelete, childPostBuild) <- deletable delayedDelete $ do
63+ delayedDeleteInner <- performEvent $ return () <$ cfg ^. deleteSelf
64+ childPostBuildInner <- performEvent $ return () <$ _placeholder_insertedAbove p
65+ return (delayedDeleteInner, childPostBuildInner)
66+ p <- placeholder $ cfg
67+ { _placeholderConfig_insertAbove = fmap (\a -> runPostBuildT a =<< headE childPostBuild) $ _placeholderConfig_insertAbove cfg
68+ }
69+ return p
70+ {-# INLINABLE inputElement #-}
71+ inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftPostBuildTElementConfig
72+ {-# INLINABLE textAreaElement #-}
73+ textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftPostBuildTElementConfig
74+
75+instance MonadSample t m => MonadSample t (PostBuildT t m) where
76+ {-# INLINABLE sample #-}
77+ sample = lift . sample
78+
79+instance MonadHold t m => MonadHold t (PostBuildT t m) where
80+ {-# INLINABLE hold #-}
81+ hold v0 = lift . hold v0
82+ {-# INLINABLE holdDyn #-}
83+ holdDyn v0 = lift . holdDyn v0
84+ {-# INLINABLE holdIncremental #-}
85+ holdIncremental v0 = lift . holdIncremental v0
86+
87+instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
88+ type Performable (PostBuildT t m) = PostBuildT t (Performable m)
89+ {-# INLINABLE performEvent_ #-}
90+ performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
91+ {-# INLINABLE performEvent #-}
92+ performEvent e = liftWith $ \run -> performEvent $ fmap run e
93+
94+instance (ReflexHost t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (PostBuildT t m) where
95+ {-# INLINABLE newEventWithTrigger #-}
96+ newEventWithTrigger = PostBuildT . lift . newEventWithTrigger
97+ {-# INLINABLE newFanEventWithTrigger #-}
98+ newFanEventWithTrigger f = PostBuildT $ lift $ newFanEventWithTrigger f
99+
100+instance TriggerEvent t m => TriggerEvent t (PostBuildT t m) where
101+ {-# INLINABLE newTriggerEvent #-}
102+ newTriggerEvent = lift newTriggerEvent
103+ {-# INLINABLE newTriggerEventWithOnComplete #-}
104+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
105+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
106+
107+instance MonadRef m => MonadRef (PostBuildT t m) where
108+ type Ref (PostBuildT t m) = Ref m
109+ {-# INLINABLE newRef #-}
110+ newRef = lift . newRef
111+ {-# INLINABLE readRef #-}
112+ readRef = lift . readRef
113+ {-# INLINABLE writeRef #-}
114+ writeRef r = lift . writeRef r
115+
116+instance MonadAtomicRef m => MonadAtomicRef (PostBuildT t m) where
117+ {-# INLINABLE atomicModifyRef #-}
118+ atomicModifyRef r = lift . atomicModifyRef r
119+
120+instance (HasJS x m, ReflexHost t) => HasJS x (PostBuildT t m) where
121+ type JSM (PostBuildT t m) = JSM m
122+ liftJS = lift . liftJS
123+
124+instance HasWebView m => HasWebView (PostBuildT t m) where
125+ type WebViewPhantom (PostBuildT t m) = WebViewPhantom m
126+ askWebView = lift askWebView
127+
128+{-# INLINABLE runPostBuildT #-}
129+runPostBuildT :: PostBuildT t m a -> Event t () -> m a
130+runPostBuildT (PostBuildT a) postBuild = runReaderT a postBuild
131+
132+instance PostBuild t m => PostBuild t (ReaderT r m) where
133+ getPostBuild = lift getPostBuild
src/Reflex/Dom/Storage.hsnew file+31
@@ -0,0 +1,31 @@
1+module Reflex.Dom.Storage where
2+
3+type Key = Text
4+
5+type Value = Text
6+
7+data StorageConfig t = StorageConfig
8+ { _storageConfig_getLength :: Event t () --TODO: Traversable functor on each of these
9+ , _storageConfig_getKey :: Event t Integer
10+ , _storageConfig_getItem :: Event t Key
11+ , _storageConfig_setItem :: Event t (Key, Value)
12+ , _storageConfig_removeItem :: Event t Key
13+ , _storageConfig_clear :: Event t ()
14+ }
15+
16+data Storage t = Storage
17+ { _storage_length :: Event t Integer
18+ , _storage_key :: Event t (Maybe Key)
19+ , _storage_item :: Event t (Maybe Value)
20+ , _storage_itemWasSet :: Event t ()
21+ , _storage_itemWasRemoved :: Event t ()
22+ , _storage_wasCleared :: Event t ()
23+ , _storage_changed :: Event t StorageEvent -- Only fires when an *external* event causes a change, not one of the ones in our StorageConfig
24+ }
25+
26+data StorageEvent = StorageEvent
27+ { _storageEvent_key :: Key
28+ , _storageEvent_oldValue :: Value
29+ , _storageEvent_newValue :: Value
30+ , _storageEvent_url :: Text --URL
31+ }
src/Reflex/Dom/Time.hs+13−12
@@ -1,11 +1,13 @@
1−{-# LANGUAGE DeriveDataTypeable, TypeFamilies, ScopedTypeVariables #-}
1+{-# LANGUAGE DeriveDataTypeable, TypeFamilies, ScopedTypeVariables, FlexibleContexts #-}
22module Reflex.Dom.Time where
33
44import Reflex
5−import Reflex.Dom.Class
5+import Reflex.Dom.PostBuild.Class
6+import Reflex.Dom.PerformEvent.Class
67
78import Control.Concurrent
89import Control.Monad
10+import Control.Monad.Fix
911import Control.Monad.IO.Class
1012import Data.Fixed
1113import Data.Time.Clock
@@ -24,14 +26,14 @@ data TickInfo
2426
2527-- | Special case of tickLossyFrom that uses the post-build event to start the
2628-- tick thread.
27−tickLossy :: MonadWidget t m => NominalDiffTime -> UTCTime -> m (Event t TickInfo)
29+tickLossy :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> UTCTime -> m (Event t TickInfo)
2830tickLossy dt t0 = tickLossyFrom dt t0 =<< getPostBuild
2931
3032-- | Send events over time with the given basis time and interval
3133-- If the system starts running behind, occurrences will be dropped rather than buffered
3234-- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the basis time
3335tickLossyFrom
34− :: MonadWidget t m
36+ :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m))
3537 => NominalDiffTime
3638 -> UTCTime
3739 -> Event t a
@@ -45,7 +47,7 @@ tickLossyFrom dt t0 e = performEventAsync $ fmap callAtNextInterval e
4547 threadDelay $ ceiling $ (dt - _tickInfo_alreadyElapsed tick) * 1000000
4648 cb tick
4749
48−clockLossy :: MonadWidget t m => NominalDiffTime -> UTCTime -> m (Dynamic t TickInfo)
50+clockLossy :: (MonadIO m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), PostBuild t m, MonadHold t m) => NominalDiffTime -> UTCTime -> m (Dynamic t TickInfo)
4951clockLossy dt t0 = do
5052 initial <- liftIO $ getCurrentTick dt t0
5153 e <- tickLossy dt t0
@@ -59,7 +61,7 @@ getCurrentTick dt t0 = do
5961 return $ TickInfo t n alreadyElapsed
6062
6163-- | Delay an Event's occurrences by a given amount in seconds.
62−delay :: MonadWidget t m => NominalDiffTime -> Event t a -> m (Event t a)
64+delay :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a)
6365delay dt e = performEventAsync $ ffor e $ \a cb -> liftIO $ void $ forkIO $ do
6466 threadDelay $ ceiling $ dt * 1000000
6567 cb a
@@ -68,7 +70,7 @@ delay dt e = performEventAsync $ ffor e $ \a cb -> liftIO $ void $ forkIO $ do
6870-- Each occurence of the resulting event will contain the index of
6971-- the current interval, with 0 representing the basis time
7072poissonLossyFrom
71− :: (RandomGen g, MonadWidget t m)
73+ :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m)
7274 => g
7375 -> Double
7476 -- ^ Poisson event rate (Hz)
@@ -89,7 +91,7 @@ poissonLossyFrom rnd rate t0 t =
8991-- the current interval, with 0 representing the basis time.
9092-- Automatically begin sending events when the DOM is built
9193poissonLossy
92− :: (RandomGen g, MonadWidget t m)
94+ :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, PostBuild t m)
9395 => g
9496 -> Double
9597 -- ^ Poisson event rate (Hz)
@@ -101,7 +103,7 @@ poissonLossy rnd rate t0 = poissonLossyFrom rnd rate t0 =<< getPostBuild
101103-- | Send events with inhomogeneous Poisson timing with the given basis
102104-- and variable rate. Provide a maxRate that you expect to support.
103105inhomogeneousPoissonFrom
104− :: (RandomGen g, MonadWidget t m)
106+ :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m)
105107 => g
106108 -> Behavior t Double
107109 -> Double
@@ -145,7 +147,6 @@ inhomogeneousPoissonFrom rnd rate maxRate t0 e = do
145147 -- Inter-event interval is drawn from exponential
146148 -- distribution accourding to u
147149 let dt = realToFrac $ -1 * log(u)/maxRate :: NominalDiffTime
148− offset = t `diffUTCTime` t0
149150 nEvents = lastN + 1
150151 alreadyElapsed = diffUTCTime t tTargetLast
151152 tTarget = addUTCTime dt tTargetLast
@@ -157,7 +158,7 @@ inhomogeneousPoissonFrom rnd rate maxRate t0 e = do
157158-- | Send events with inhomogeneous Poisson timing with the given basis
158159-- and variable rate. Provide a maxRate that you expect to support
159160inhomogeneousPoisson
160− :: (RandomGen g, MonadWidget t m)
161+ :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, PostBuild t m)
161162 => g
162163 -> Behavior t Double
163164 -> Double
@@ -168,7 +169,7 @@ inhomogeneousPoisson rnd rate maxRate t0 =
168169
169170-- | Block occurrences of an Event until the given number of seconds elapses without
170171-- the Event firing, at which point the last occurrence of the Event will fire.
171−debounce :: MonadWidget t m => NominalDiffTime -> Event t a -> m (Event t a)
172+debounce :: (MonadFix m, MonadHold t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a)
172173debounce dt e = do
173174 n :: Dynamic t Integer <- count e
174175 let tagged = attachDynWith (,) n e
src/Reflex/Dom/WebSocket.hs+13−20
@@ -5,7 +5,8 @@ module Reflex.Dom.WebSocket where
55import Prelude hiding (div, span, mapM, mapM_, concat, concatMap, all, sequence)
66
77import Reflex
8−import Reflex.Host.Class
8+import Reflex.Dom.PerformEvent.Class
9+import Reflex.Dom.PostBuild.Class
910import Reflex.Dom.Class
1011import Reflex.Dom.WebSocket.Foreign
1112
@@ -15,14 +16,12 @@ import Control.Exception (catch, SomeException)
1516import Control.Lens
1617import Control.Monad hiding (forM, forM_, mapM, mapM_, sequence)
1718import Control.Monad.IO.Class
18−import Control.Monad.Ref
1919import Control.Monad.State
20−import Control.Arrow (second)
2120import Data.Maybe (isJust)
2221import Data.ByteString (ByteString)
2322import Data.Default
24−import Data.Dependent.Map (DSum (..))
2523import Data.IORef
24+import Data.Text
2625
2726data WebSocketConfig t
2827 = WebSocketConfig { _webSocketConfig_send :: Event t [ByteString]
@@ -36,28 +35,22 @@ data WebSocket t
3635 , _webSocket_open :: Event t ()
3736 }
3837
39−webSocket :: forall t m. (HasWebView m, MonadWidget t m) => String -> WebSocketConfig t -> m (WebSocket t)
38+webSocket :: (MonadIO m, MonadIO (Performable m), HasWebView m, PerformEvent t m, TriggerEvent t m, PostBuild t m) => Text -> WebSocketConfig t -> m (WebSocket t)
4039webSocket url config = do
4140 wv <- fmap unWebViewSingleton askWebView
42− postGui <- askPostGui
43− runWithActions <- askRunWithActions
44− (eRecv, eRecvTriggerRef) <- newEventWithTriggerRef
41+ (eRecv, triggerERecv) <- newTriggerEvent
4542 currentSocketRef <- liftIO $ newIORef Nothing
4643 --TODO: Disconnect if value no longer needed
47− (eOpen, eOpenTriggerRef) <- newEventWithTriggerRef
44+ (eOpen, triggerEOpen) <- newTriggerEventWithOnComplete
4845 payloadQueue <- liftIO newTQueueIO
4946 isOpen <- liftIO newEmptyTMVarIO
5047 let onMessage :: ByteString -> IO ()
51− onMessage m = postGui $ do
52− mt <- readRef eRecvTriggerRef
53− forM_ mt $ \t -> runWithActions [t :=> Identity m]
54− onOpen = postGui $ do
55− mt <- readRef eOpenTriggerRef
56− forM_ mt $ \t -> runWithActions [t :=> Identity ()]
48+ onMessage m = triggerERecv m
49+ onOpen = triggerEOpen () $ do
5750 liftIO $ void $ atomically $ tryPutTMVar isOpen ()
5851 --TODO: Is the fork necessary, or do event handlers run in their own threads automatically?
5952 onClose = void $ forkIO $ do
60− liftIO $ atomically $ tryTakeTMVar isOpen
53+ _ <- liftIO $ atomically $ tryTakeTMVar isOpen
6154 liftIO $ writeIORef currentSocketRef Nothing
6255 liftIO $ threadDelay 1000000
6356 start
@@ -65,23 +58,23 @@ webSocket url config = do
6558 ws <- liftIO $ newWebSocket wv url onMessage onOpen onClose
6659 liftIO $ writeIORef currentSocketRef $ Just ws
6760 return ()
68− schedulePostBuild $ liftIO start
61+ performEvent_ . (liftIO start <$) =<< getPostBuild
6962 performEvent_ $ ffor (_webSocketConfig_send config) $ \payloads -> forM_ payloads $ \payload ->
7063 liftIO $ atomically $ writeTQueue payloadQueue payload
7164
72− liftIO $ forkIO $ forever $ do
65+ _ <- liftIO $ forkIO $ forever $ do
7366 payload <- atomically $ do
7467 pl <- readTQueue payloadQueue
7568 open <- tryReadTMVar isOpen
7669 if isJust open then return pl else retry
7770
7871 mws <- liftIO $ readIORef currentSocketRef
79− succ <- case mws of
72+ success <- case mws of
8073 Nothing -> return False
8174 Just ws -> liftIO ((webSocketSend ws payload >> return True)
8275 `catch`
8376 (\(_ :: SomeException) -> return False))
84− unless succ $ atomically $ unGetTQueue payloadQueue payload
77+ unless success $ atomically $ unGetTQueue payloadQueue payload
8578 return $ WebSocket eRecv eOpen
8679
8780makeLensesWith (lensRules & simpleLenses .~ True) ''WebSocketConfig
src/Reflex/Dom/Widget/Basic.hs+185−701
@@ -1,137 +1,102 @@
1−{-# LANGUAGE ScopedTypeVariables, LambdaCase, ConstraintKinds, TypeFamilies, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances, RecursiveDo, GADTs, DataKinds, RankNTypes, TemplateHaskell #-}
1+{-# LANGUAGE OverloadedStrings, ScopedTypeVariables, LambdaCase, ConstraintKinds, TypeFamilies, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances, RecursiveDo, GADTs, DataKinds, RankNTypes, TemplateHaskell #-}
22
33module Reflex.Dom.Widget.Basic where
44
55import Reflex.Dom.Class
66import Reflex.Dom.Internal.Foreign ()
7+import Reflex.Dom.PostBuild.Class
8+import Reflex.Dom.Builder.Class
79
810import Prelude hiding (mapM, mapM_, sequence, sequence_)
911import Reflex
10−import Reflex.Host.Class
1112import Data.Functor.Misc
1213import Data.Map (Map)
1314import qualified Data.Map as Map
1415import Data.Set (Set)
1516import qualified Data.Set as Set
16−import Data.Dependent.Sum (DSum (..))
1717import Data.Foldable
1818import Data.Traversable
19−import Control.Monad.Trans
2019import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
21−import Control.Monad.State hiding (state, mapM, mapM_, forM, forM_, sequence, sequence_)
22−import GHCJS.DOM.Node
23−import GHCJS.DOM.UIEvent
24−import GHCJS.DOM.EventM (on, event, EventM, stopPropagation)
25−import GHCJS.DOM.Document
26−import GHCJS.DOM.Element as E
27−import GHCJS.DOM.Types hiding (Event)
28−import qualified GHCJS.DOM.Types as DOM (Event)
29−import GHCJS.DOM.NamedNodeMap as NNM
3020import Control.Lens hiding (element, children)
3121import Data.These
3222import Data.Align
3323import Data.Maybe
34−import Data.GADT.Compare.TH
35−import Data.Bitraversable
36−import GHCJS.DOM.MouseEvent
37−import Data.IORef
3824import Data.Default
25+import Data.Either
26+import Data.Semigroup
27+import Data.Text (Text)
28+import qualified Data.Text as T
29+
30+widgetHoldInternal :: DomBuilder t m => m a -> Event t (m b) -> m (a, Event t b)
31+widgetHoldInternal child0 child' = do
32+ childResult0 <- deletable (void child') child0
33+ childResult' <- placeholder $ def & placeholderConfig_insertAbove .~ fmap (deletable (void child')) child'
34+ return (childResult0, _placeholder_insertedAbove childResult')
35+
36+-- | Breaks the given Map into pieces based on the given Set. Each piece will contain only keys that are less than the key of the piece, and greater than or equal to the key of the piece with the next-smaller key. There will be one additional piece containing all keys from the original Map that are larger or equal to the largest key in the Set.
37+-- Either k () is used instead of Maybe k so that the resulting map of pieces is sorted so that the additional piece has the largest key.
38+-- No empty pieces will be included in the output.
39+
40+--TODO: This can probably be done more efficiently by dividing and conquering, re-using the structure of the Set instead of going through the Set linearally
41+partitionMapBySetLT :: forall k v. Ord k => Set k -> Map k v -> Map (Either k ()) (Map k v)
42+partitionMapBySetLT s m0 = Map.fromDistinctAscList $ go (Set.toAscList s) m0
43+ where go :: [k] -> Map k v -> [(Either k (), Map k v)]
44+ go [] m = if Map.null m
45+ then []
46+ else [(Right (), m)]
47+ go (h:t) m = let (lt, eq, gt) = Map.splitLookup h m
48+ geq = maybe id (Map.insert h) eq gt
49+ in if Map.null lt
50+ then go t geq
51+ else (Left h, lt) : go t geq
52+
53+newtype ChildResult t k a = ChildResult { unChildResult :: (a, Event t (Map k (Maybe (ChildResult t k a)))) }
54+
55+listHoldWithKey :: forall t m k v a. (Ord k, DomBuilder t m, MonadHold t m, MonadFix m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
56+listHoldWithKey initialChildren modifyChildren buildChild = do
57+ let deleteChildSelector = fanMap $ fmap void modifyChildren
58+ liveChildren :: Dynamic t (Set k) <- foldDyn applyMapKeysSet (Map.keysSet initialChildren) modifyChildren
59+ let placeChildSelector = fanMap $ attachWith partitionMapBySetLT (current liveChildren) $ fmap (Map.mapMaybe id) modifyChildren
60+ buildAugmentedChild :: k -> v -> m (ChildResult t k a)
61+ buildAugmentedChild k v = do
62+ let delete = select deleteChildSelector $ Const2 k
63+ myCfg = def
64+ & insertAbove .~ fmap (imapM buildAugmentedChild) (select placeChildSelector $ Const2 $ Left k)
65+ & deleteSelf .~ delete
66+ (me, result) <- fragment myCfg $ buildChild k v
67+ return $ ChildResult (result, (fmap (fmap Just) $ _fragment_insertedAbove me) <> (Map.singleton k Nothing <$ _fragment_deleted me)) --Note: we could also use the "deleted" output on deletable, if it had one; we're using this so that everything changes all at once, instead of deletions being prompt and insertions being delayed
68+ rec initialAugmentedResults <- iforM initialChildren buildAugmentedChild
69+ augmentedResults <- foldDyn applyMap initialAugmentedResults $ newInsertedBelow <> newInsertedAbove
70+ let newInsertedAbove = switch $ fmap (mconcat . reverse . fmap (snd . unChildResult) . Map.elems) $ current augmentedResults
71+ belowAll <- placeholder $ def & placeholderConfig_insertAbove .~ fmap (imapM buildAugmentedChild) (select placeChildSelector $ Const2 $ Right ())
72+ let newInsertedBelow = fmap (fmap Just) $ _placeholder_insertedAbove belowAll
73+ mapDyn (fmap (fst . unChildResult)) augmentedResults
74+
75+text :: DomBuilder t m => Text -> m ()
76+text t = void $ textNode $ def & textNodeConfig_contents .~ t
77+
78+dynText :: (PostBuild t m, DomBuilder t m) => Dynamic t Text -> m ()
79+dynText t = do
80+ postBuild <- getPostBuild
81+ _ <- widgetHoldInternal (return ()) $ text <$> leftmost
82+ [ updated t
83+ , tag (current t) postBuild
84+ ]
85+ return ()
86+
87+display :: (PostBuild t m, DomBuilder t m, Show a) => Dynamic t a -> m ()
88+display = dynText <=< mapDyn (T.pack . show)
3989
40−type AttributeMap = Map String String
41−
42−data ElConfig attrs
43− = ElConfig { _elConfig_namespace :: Maybe String
44− , _elConfig_attributes :: attrs
45− }
46−
47−makeLenses ''ElConfig
48−
49−instance (attrs ~ Map String String) => Default (ElConfig attrs) where
50− def = ElConfig { _elConfig_namespace = Nothing
51− , _elConfig_attributes = Map.empty
52− }
53−
54−data El t
55− = El { _el_element :: Element
56− , _el_events :: EventSelector t (WrapArg EventResult EventName)
57− }
58−
59−class Attributes m a where
60− addAttributes :: IsElement e => a -> e -> m ()
61−
62−instance MonadIO m => Attributes m AttributeMap where
63− addAttributes curAttrs e = imapM_ (setAttribute e) curAttrs
64−
65−instance MonadWidget t m => Attributes m (Dynamic t AttributeMap) where
66− addAttributes attrs e = do
67− schedulePostBuild $ do
68− curAttrs <- sample $ current attrs
69− imapM_ (setAttribute e) curAttrs
70− addVoidAction $ flip fmap (updated attrs) $ \newAttrs -> liftIO $ do
71− oldAttrs <- maybe (return Set.empty) namedNodeMapGetNames =<< getAttributes e
72− forM_ (Set.toList $ oldAttrs `Set.difference` Map.keysSet newAttrs) $ removeAttribute e
73− imapM_ (setAttribute e) newAttrs --TODO: avoid re-setting unchanged attributes; possibly do the compare using Align in haskell
74−
75−buildEmptyElementNS :: (MonadWidget t m, Attributes m attrs) => Maybe String -> String -> attrs -> m Element
76−buildEmptyElementNS mns elementTag attrs = do
77− doc <- askDocument
78− p <- askParent
79− Just e <- liftIO $ case mns of
80− Nothing -> createElement doc (Just elementTag)
81− Just ns -> createElementNS doc (Just ns) (Just elementTag)
82− addAttributes attrs e
83− _ <- appendChild p $ Just e
84− return $ castToElement e
85−
86−buildEmptyElement :: (MonadWidget t m, Attributes m attrs) => String -> attrs -> m Element
87−buildEmptyElement = buildEmptyElementNS Nothing
88−
89−-- We need to decide what type of attrs we've got statically, because it will often be a recursively defined value, in which case inspecting it will lead to a cycle
90−buildElementNS :: (MonadWidget t m, Attributes m attrs) => Maybe String -> String -> attrs -> m a -> m (Element, a)
91−buildElementNS mns elementTag attrs child = do
92− e <- buildEmptyElementNS mns elementTag attrs
93− result <- subWidget (toNode e) child
94− return (e, result)
95−
96−buildElement :: (MonadWidget t m, Attributes m attrs) => String -> attrs -> m a -> m (Element, a)
97−buildElement = buildElementNS Nothing
98−
99−namedNodeMapGetNames :: NamedNodeMap -> IO (Set String)
100−namedNodeMapGetNames self = do
101− l <- NNM.getLength self
102− let locations = if l == 0 then [] else [0..l-1] -- Can't use 0..l-1 if l is 0 because l is unsigned and will wrap around
103− liftM (Set.fromList . catMaybes) $ forM locations $ \i -> do
104− Just n <- NNM.item self i
105− getNodeName n
106−
107−text :: MonadWidget t m => String -> m ()
108−text = void . text'
109−
110−--TODO: Wrap the result
111−text' :: MonadWidget t m => String -> m Text
112−text' s = do
113− doc <- askDocument
114− p <- askParent
115− Just n <- createTextNode doc s
116− _ <- appendChild p $ Just n
117− return n
118−
119−dynText :: MonadWidget t m => Dynamic t String -> m ()
120−dynText s = do
121− n <- text' ""
122− schedulePostBuild $ do
123− curS <- sample $ current s
124− setNodeValue n $ Just curS
125− addVoidAction $ fmap (setNodeValue n . Just) $ updated s
126−
127−display :: (MonadWidget t m, Show a) => Dynamic t a -> m ()
128−display a = dynText =<< mapDyn show a
90+button :: DomBuilder t m => Text -> m (Event t ())
91+button t = do
92+ (e, _) <- element "button" def $ text t
93+ return $ domEvent Click e
12994
13095--TODO: Should this be renamed to 'widgetView' for consistency with 'widgetHold'?
13196-- | Given a Dynamic of widget-creating actions, create a widget that is recreated whenever the Dynamic updates.
13297-- The returned Event of widget results occurs when the Dynamic does.
13398-- Note: Often, the type 'a' is an Event, in which case the return value is an Event-of-Events that would typically be flattened (via 'switchPromptly').
134−dyn :: MonadWidget t m => Dynamic t (m a) -> m (Event t a)
99+dyn :: (DomBuilder t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
135100dyn child = do
136101 postBuild <- getPostBuild
137102 let newChild = leftmost [updated child, tag (current child) postBuild]
@@ -140,158 +105,93 @@ dyn child = do
140105-- | Given an initial widget and an Event of widget-creating actions, create a widget that is recreated whenever the Event fires.
141106-- The returned Dynamic of widget results occurs when the Event does.
142107-- Note: Often, the type 'a' is an Event, in which case the return value is a Dynamic-of-Events that would typically be flattened.
143−widgetHold :: MonadWidget t m => m a -> Event t (m a) -> m (Dynamic t a)
108+widgetHold :: (DomBuilder t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
144109widgetHold child0 newChild = do
145110 (result0, newResult) <- widgetHoldInternal child0 newChild
146111 holdDyn result0 newResult
147112
148−widgetHoldInternal :: MonadWidget t m => m a -> Event t (m b) -> m (a, Event t b)
149−widgetHoldInternal child0 newChild = do
150− startPlaceholder <- text' ""
151− (result0, childVoidAction0) <- do
152− p <- askParent
153− subWidgetWithVoidActions p child0
154− endPlaceholder <- text' ""
155− (newChildBuilt, newChildBuiltTriggerRef) <- newEventWithTriggerRef
156− performEvent_ $ fmap (const $ return ()) newChildBuilt --TODO: Get rid of this hack
157− childOutput <- holdDyn (Map.singleton () childVoidAction0) $ fmap (Map.singleton () . snd) newChildBuilt
158− tellWidgetOutput childOutput
159− doc <- askDocument
160− runWidget <- getRunWidget
161− let build c = do
162− Just df <- createDocumentFragment doc
163− (result, postBuild, voidActions) <- runWidget df c
164− scheduleFollowup newChildBuiltTriggerRef (result, voidActions)
165− postBuild
166− mp' <- getParentNode endPlaceholder
167− forM_ mp' $ \p' -> insertBefore p' (Just df) (Just endPlaceholder)
168− return ()
169− addVoidAction $ ffor newChild $ \c -> do
170− Just df <- createDocumentFragment doc
171− (result, postBuild, voidActions) <- runWidget df c
172− runFrameWithTriggerRef newChildBuiltTriggerRef (result, voidActions)
173− postBuild
174− liftIO $ deleteBetweenExclusive startPlaceholder endPlaceholder
175− mp' <- getParentNode endPlaceholder
176− forM_ mp' $ \p' -> insertBefore p' (Just df) (Just endPlaceholder)
177− return ()
178− return (result0, fmap fst newChildBuilt)
179−
180113diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v)
181114diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case
182115 This _ -> Just Nothing
183116 These _ new -> Just $ Just new
184117 That new -> Just $ Just new
185118
186−applyMap :: Ord k => Map k v -> Map k (Maybe v) -> Map k v
187−applyMap olds diffs = flip Map.mapMaybe (align olds diffs) $ \case
188− This old -> Just old
189− These _ new -> new
190− That new -> new
119+diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v)
120+diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
121+ This _ -> Just Nothing
122+ These old new
123+ | old == new -> Nothing
124+ | otherwise -> Just $ Just new
125+ That new -> Just $ Just new
126+
127+applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
128+applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
129+ where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
130+ maybeToEither = \case
131+ Nothing -> Left ()
132+ Just r -> Right r
133+
134+mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
135+mapPartitionEithers m = (fromLeft <$> ls, fromRight <$> rs)
136+ where (ls, rs) = Map.partition isLeft m
137+ fromLeft (Left l) = l
138+ fromLeft _ = error "mapPartitionEithers: fromLeft received a Right value; this should be impossible"
139+ fromRight (Right r) = r
140+ fromRight _ = error "mapPartitionEithers: fromRight received a Left value; this should be impossible"
141+
142+-- | Apply a map patch to a set
143+-- > applyMapKeysSet patch (Map.keysSet m) == Map.keysSet (applyMap patch m)
144+applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k
145+applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions)
146+ where (insertions, deletions) = Map.partition isJust patch
191147
192148--HACK: A listWithKey that is inefficient but works around a subtle buggy interaction with DynamicWriterT
193−listWithKey :: forall t k v m a. (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
149+listWithKey :: forall t k v m a. (PostBuild t m, DomBuilder t m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
194150listWithKey eltsMapD body = do
195151 pb <- getPostBuild
196152 widgetHold (return Map.empty) $ ffor (leftmost [ tagDyn eltsMapD pb, updated eltsMapD ]) $ imapM $ \k v -> body k (constDyn v)
197153
198154{-
199155--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
200−listWithKey :: forall t k v m a. (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
156+listWithKey :: forall t k v m a. (Ord k, DomBuilder t m, PostBuild t m, MonadFix m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Dynamic t (Map k a))
201157listWithKey vals mkChild = do
202158 postBuild <- getPostBuild
203− rec sentVals :: Dynamic t (Map k v) <- foldDyn (flip applyMap) Map.empty changeVals
159+ rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals
204160 let changeVals :: Event t (Map k (Maybe v))
205161 changeVals = attachWith diffMapNoEq (current sentVals) $ leftmost
206162 [ updated vals
207− , tag (current vals) postBuild
163+ , tag (current vals) postBuild --TODO: This should probably be added to the attachWith, not to the updated; if we were using diffMap instead of diffMapNoEq, I think it might not work
208164 ]
209165 listWithKeyShallowDiff Map.empty changeVals $ \k v0 dv -> do
210166 mkChild k =<< holdDyn v0 dv
211167-}
212168
213169{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
214−listWithKey' :: (Ord k, MonadWidget t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
170+listWithKey' :: (Ord k, DomBuilder t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
215171listWithKey' = listWithKeyShallowDiff
216172
217173-- | Display the given map of items (in key order) using the builder function provided, and update it with the given event. 'Nothing' update entries will delete the corresponding children, and 'Just' entries will create them if they do not exist or send an update event to them if they do.
218−listWithKeyShallowDiff :: (Ord k, MonadWidget t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
174+listWithKeyShallowDiff :: (Ord k, DomBuilder t m, MonadFix m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> Event t v -> m a) -> m (Dynamic t (Map k a))
219175listWithKeyShallowDiff initialVals valsChanged mkChild = do
220176 let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
221− sentVals <- foldDyn (flip applyMap) Map.empty $ fmap (fmap (fmap (\_ -> ()))) valsChanged
222− let relevantDiff diff _ = case diff of
177+ sentVals <- foldDyn applyMap Map.empty $ fmap (fmap (fmap (\_ -> ()))) valsChanged
178+ let relevantPatch patch _ = case patch of
223179 Nothing -> Just Nothing -- Even if we let a Nothing through when the element doesn't already exist, this doesn't cause a problem because it is ignored
224180 Just _ -> Nothing -- We don't want to let spurious re-creations of items through
225− listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantDiff)) (current sentVals) valsChanged) $ \k v ->
181+ listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantPatch)) (current sentVals) valsChanged) $ \k v ->
226182 mkChild k v $ Reflex.select childValChangedSelector $ Const2 k
227183
228−-- | Display the given map of items using the builder function provided, and update it with the given event. 'Nothing' entries will delete the corresponding children, and 'Just' entries will create or replace them. Since child events do not take any signal arguments, they are always rebuilt. To update a child without rebuilding, either embed signals in the map's values, or refer to them directly in the builder function.
229−listHoldWithKey :: (Ord k, MonadWidget t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
230−listHoldWithKey initialVals valsChanged mkChild = do
231− doc <- askDocument
232− endPlaceholder <- text' ""
233− (newChildren, newChildrenTriggerRef) <- newEventWithTriggerRef
234− runWidget <- getRunWidget
235− let buildChild df k v = runWidget df $ wrapChild k v
236− wrapChild k v = do
237− childStart <- text' ""
238− result <- mkChild k v
239− childEnd <- text' ""
240− return (result, (childStart, childEnd))
241− Just dfOrig <- createDocumentFragment doc
242− initialState <- iforM initialVals $ \k v -> subWidgetWithVoidActions (toNode dfOrig) $ wrapChild k v --Note: we have to use subWidgetWithVoidActions rather than runWidget here, because running post-build actions during build can cause not-yet-constructed values to be read
243− stateRef <- liftIO $ newIORef initialState
244− children <- holdDyn initialState newChildren
245− tellWidgetOutput =<< mapDyn (fmap snd) children
246− mpOrig <- liftIO $ getParentNode endPlaceholder
247− forM_ mpOrig $ \pOrig -> liftIO $ insertBefore pOrig (Just dfOrig) (Just endPlaceholder)
248− addVoidAction $ flip fmap valsChanged $ \newVals -> do
249− curState <- liftIO $ readIORef stateRef
250− --TODO: Should we remove the parent from the DOM first to avoid reflows?
251− (newState, postBuild) <- flip runStateT (return ()) $ liftM (Map.mapMaybe id) $ iforM (align curState newVals) $ \k -> \case
252− These ((_, (start, end)), _) Nothing -> do -- Deleting child
253− liftIO $ deleteBetweenInclusive start end
254− return Nothing
255− These ((_, (start, end)), _) (Just v) -> do -- Replacing existing child
256− liftIO $ deleteBetweenExclusive start end
257− Just df <- createDocumentFragment doc
258− (childResult, childPostBuild, childVoidAction) <- lift $ buildChild df k v
259− let s = (childResult, childVoidAction)
260− modify (>>childPostBuild)
261− mp <- getParentNode end
262− forM_ mp $ \p -> insertBefore p (Just df) (Just end)
263− return $ Just s
264− That Nothing -> return Nothing -- Deleting non-existent child
265− That (Just v) -> do -- Creating new child
266− Just df <- createDocumentFragment doc
267− (childResult, childPostBuild, childVoidAction) <- lift $ buildChild df k v
268− let s = (childResult, childVoidAction)
269− modify (>>childPostBuild)
270− let placeholder = case Map.lookupGT k curState of
271− Nothing -> endPlaceholder
272− Just (_, ((_, (start, _)), _)) -> start
273− mp <- getParentNode placeholder
274− forM_ mp $ \p -> insertBefore p (Just df) (Just placeholder)
275− return $ Just s
276− This state -> do -- No change
277− return $ Just state
278− liftIO $ writeIORef stateRef newState
279− runFrameWithTriggerRef newChildrenTriggerRef newState
280− postBuild
281− mapDyn (fmap (fst . fst)) children
282−
283184--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
284185-- | Create a dynamically-changing set of Event-valued widgets.
285186-- This is like listWithKey, specialized for widgets returning (Event t a). listWithKey would return 'Dynamic t (Map k (Event t a))' in this scenario, but listViewWithKey flattens this to 'Event t (Map k a)' via 'switch'.
286−listViewWithKey :: (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
187+listViewWithKey :: (Ord k, DomBuilder t m, PostBuild t m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
287188listViewWithKey vals mkChild = liftM (switch . fmap mergeMap) $ listViewWithKey' vals mkChild
288189
289−
290−listViewWithKey' :: (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
190+listViewWithKey' :: (DomBuilder t m, PostBuild t m, MonadHold t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
291191listViewWithKey' vals mkChild = liftM current $ listWithKey vals mkChild
292192
293193-- | Create a dynamically-changing set of widgets, one of which is selected at any time.
294−selectViewListWithKey :: forall t m k v a. (MonadWidget t m, Ord k)
194+selectViewListWithKey :: forall t m k v a. (DomBuilder t m, Ord k, PostBuild t m, MonadHold t m)
295195 => Dynamic t k -- ^ Current selection key
296196 -> Dynamic t (Map k v) -- ^ Dynamic key/value map
297197 -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
@@ -304,460 +204,86 @@ selectViewListWithKey selection vals mkChild = do
304204 return $ fmap ((,) k) selectSelf
305205 liftM switchPromptlyDyn $ mapDyn (leftmost . Map.elems) selectChild
306206
307−selectViewListWithKey_ :: forall t m k v a. (MonadWidget t m, Ord k)
207+selectViewListWithKey_ :: forall t m k v a. (DomBuilder t m, Ord k, PostBuild t m, MonadHold t m)
308208 => Dynamic t k -- ^ Current selection key
309209 -> Dynamic t (Map k v) -- ^ Dynamic key/value map
310210 -> (k -> Dynamic t v -> Dynamic t Bool -> m (Event t a)) -- ^ Function to create a widget for a given key from Dynamic value and Dynamic Bool indicating if this widget is currently selected
311211 -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
312212selectViewListWithKey_ selection vals mkChild = liftM (fmap fst) $ selectViewListWithKey selection vals mkChild
313213
314−--------------------------------------------------------------------------------
315−-- Basic DOM manipulation helpers
316−--------------------------------------------------------------------------------
317−
318−-- | s and e must both be children of the same node and s must precede e
319−deleteBetweenExclusive :: (IsNode start, IsNode end) => start -> end -> IO ()
320−deleteBetweenExclusive s e = do
321− mCurrentParent <- getParentNode e -- May be different than it was at initial construction, e.g., because the parent may have dumped us in from a DocumentFragment
322− case mCurrentParent of
323− Nothing -> return () --TODO: Is this the right behavior?
324− Just currentParent -> do
325− let go = do
326− Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
327− when (toNode s /= toNode x) $ do
328− _ <- removeChild currentParent $ Just x
329− go
330− go
331−
332−-- | s and e must both be children of the same node and s must precede e
333−deleteBetweenInclusive :: (IsNode start, IsNode end) => start -> end -> IO ()
334−deleteBetweenInclusive s e = do
335− mCurrentParent <- getParentNode e -- May be different than it was at initial construction, e.g., because the parent may have dumped us in from a DocumentFragment
336− case mCurrentParent of
337− Nothing -> return () --TODO: Is this the right behavior?
338− Just currentParent -> do
339− let go = do
340− Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
341− _ <- removeChild currentParent $ Just x
342− when (toNode s /= toNode x) go
343− go
344− _ <- removeChild currentParent $ Just e
345− return ()
346−
347−nodeClear :: IsNode self => self -> IO ()
348−nodeClear n = do
349− mfc <- getFirstChild n
350− case mfc of
351− Nothing -> return ()
352− Just fc -> do
353− _ <- removeChild n $ Just fc
354− nodeClear n
355−
356−--------------------------------------------------------------------------------
357−-- Adapters
358−--------------------------------------------------------------------------------
359−
360−wrapDomEvent :: (Functor (Event t), MonadIO m, MonadSample t m, MonadReflexCreateTrigger t m, Reflex t, HasPostGui t h m) => e -> (e -> EventM e event () -> IO (IO ())) -> EventM e event a -> m (Event t a)
361−wrapDomEvent element elementOnevent getValue = wrapDomEventMaybe element elementOnevent $ liftM Just getValue
362−
363−wrapDomEventMaybe :: (Functor (Event t), MonadIO m, MonadSample t m, MonadReflexCreateTrigger t m, Reflex t, HasPostGui t h m) => e -> (e -> EventM e event () -> IO (IO ())) -> EventM e event (Maybe a) -> m (Event t a)
364−wrapDomEventMaybe element elementOnevent getValue = do
365− postGui <- askPostGui
366− runWithActions <- askRunWithActions
367− e <- newEventWithTrigger $ \et -> do
368− unsubscribe <- {-# SCC "a" #-} liftIO $ {-# SCC "b" #-} elementOnevent element $ {-# SCC "c" #-} do
369− mv <- {-# SCC "d" #-} getValue
370− forM_ mv $ \v -> liftIO $ postGui $ runWithActions [et :=> Identity v]
371− return $ liftIO $ do
372− {-# SCC "e" #-} unsubscribe
373− return $! {-# SCC "f" #-} e
374−
375−data EventTag
376− = AbortTag
377− | BlurTag
378− | ChangeTag
379− | ClickTag
380− | ContextmenuTag
381− | DblclickTag
382− | DragTag
383− | DragendTag
384− | DragenterTag
385− | DragleaveTag
386− | DragoverTag
387− | DragstartTag
388− | DropTag
389− | ErrorTag
390− | FocusTag
391− | InputTag
392− | InvalidTag
393− | KeydownTag
394− | KeypressTag
395− | KeyupTag
396− | LoadTag
397− | MousedownTag
398− | MouseenterTag
399− | MouseleaveTag
400− | MousemoveTag
401− | MouseoutTag
402− | MouseoverTag
403− | MouseupTag
404− | MousewheelTag
405− | ScrollTag
406− | SelectTag
407− | SubmitTag
408− | WheelTag
409− | BeforecutTag
410− | CutTag
411− | BeforecopyTag
412− | CopyTag
413− | BeforepasteTag
414− | PasteTag
415− | ResetTag
416− | SearchTag
417− | SelectstartTag
418− | TouchstartTag
419− | TouchmoveTag
420− | TouchendTag
421− | TouchcancelTag
422−
423−data EventName :: EventTag -> * where
424− Abort :: EventName 'AbortTag
425− Blur :: EventName 'BlurTag
426− Change :: EventName 'ChangeTag
427− Click :: EventName 'ClickTag
428− Contextmenu :: EventName 'ContextmenuTag
429− Dblclick :: EventName 'DblclickTag
430− Drag :: EventName 'DragTag
431− Dragend :: EventName 'DragendTag
432− Dragenter :: EventName 'DragenterTag
433− Dragleave :: EventName 'DragleaveTag
434− Dragover :: EventName 'DragoverTag
435− Dragstart :: EventName 'DragstartTag
436− Drop :: EventName 'DropTag
437− Error :: EventName 'ErrorTag
438− Focus :: EventName 'FocusTag
439− Input :: EventName 'InputTag
440− Invalid :: EventName 'InvalidTag
441− Keydown :: EventName 'KeydownTag
442− Keypress :: EventName 'KeypressTag
443− Keyup :: EventName 'KeyupTag
444− Load :: EventName 'LoadTag
445− Mousedown :: EventName 'MousedownTag
446− Mouseenter :: EventName 'MouseenterTag
447− Mouseleave :: EventName 'MouseleaveTag
448− Mousemove :: EventName 'MousemoveTag
449− Mouseout :: EventName 'MouseoutTag
450− Mouseover :: EventName 'MouseoverTag
451− Mouseup :: EventName 'MouseupTag
452− Mousewheel :: EventName 'MousewheelTag
453− Scroll :: EventName 'ScrollTag
454− Select :: EventName 'SelectTag
455− Submit :: EventName 'SubmitTag
456− Wheel :: EventName 'WheelTag
457− Beforecut :: EventName 'BeforecutTag
458− Cut :: EventName 'CutTag
459− Beforecopy :: EventName 'BeforecopyTag
460− Copy :: EventName 'CopyTag
461− Beforepaste :: EventName 'BeforepasteTag
462− Paste :: EventName 'PasteTag
463− Reset :: EventName 'ResetTag
464− Search :: EventName 'SearchTag
465− Selectstart :: EventName 'SelectstartTag
466− Touchstart :: EventName 'TouchstartTag
467− Touchmove :: EventName 'TouchmoveTag
468− Touchend :: EventName 'TouchendTag
469− Touchcancel :: EventName 'TouchcancelTag
470−
471−type family EventType en where
472− EventType 'AbortTag = UIEvent
473− EventType 'BlurTag = FocusEvent
474− EventType 'ChangeTag = DOM.Event
475− EventType 'ClickTag = MouseEvent
476− EventType 'ContextmenuTag = MouseEvent
477− EventType 'DblclickTag = MouseEvent
478− EventType 'DragTag = MouseEvent
479− EventType 'DragendTag = MouseEvent
480− EventType 'DragenterTag = MouseEvent
481− EventType 'DragleaveTag = MouseEvent
482− EventType 'DragoverTag = MouseEvent
483− EventType 'DragstartTag = MouseEvent
484− EventType 'DropTag = MouseEvent
485− EventType 'ErrorTag = UIEvent
486− EventType 'FocusTag = FocusEvent
487− EventType 'InputTag = DOM.Event
488− EventType 'InvalidTag = DOM.Event
489− EventType 'KeydownTag = KeyboardEvent
490− EventType 'KeypressTag = KeyboardEvent
491− EventType 'KeyupTag = KeyboardEvent
492− EventType 'LoadTag = UIEvent
493− EventType 'MousedownTag = MouseEvent
494− EventType 'MouseenterTag = MouseEvent
495− EventType 'MouseleaveTag = MouseEvent
496− EventType 'MousemoveTag = MouseEvent
497− EventType 'MouseoutTag = MouseEvent
498− EventType 'MouseoverTag = MouseEvent
499− EventType 'MouseupTag = MouseEvent
500− EventType 'MousewheelTag = MouseEvent
501− EventType 'ScrollTag = UIEvent
502− EventType 'SelectTag = UIEvent
503− EventType 'SubmitTag = DOM.Event
504− EventType 'WheelTag = WheelEvent
505− EventType 'BeforecutTag = DOM.Event
506− EventType 'CutTag = DOM.Event
507− EventType 'BeforecopyTag = DOM.Event
508− EventType 'CopyTag = DOM.Event
509− EventType 'BeforepasteTag = DOM.Event
510− EventType 'PasteTag = DOM.Event
511− EventType 'ResetTag = DOM.Event
512− EventType 'SearchTag = DOM.Event
513− EventType 'SelectstartTag = DOM.Event
514− EventType 'TouchstartTag = TouchEvent
515− EventType 'TouchmoveTag = TouchEvent
516− EventType 'TouchendTag = TouchEvent
517− EventType 'TouchcancelTag = TouchEvent
518−
519−onEventName :: IsElement e => EventName en -> e -> EventM e (EventType en) () -> IO (IO ())
520−onEventName en e = case en of
521− Abort -> on e E.abort
522− Blur -> on e E.blurEvent
523− Change -> on e E.change
524− Click -> on e E.click
525− Contextmenu -> on e E.contextMenu
526− Dblclick -> on e E.dblClick
527− Drag -> on e E.drag
528− Dragend -> on e E.dragEnd
529− Dragenter -> on e E.dragEnter
530− Dragleave -> on e E.dragLeave
531− Dragover -> on e E.dragOver
532− Dragstart -> on e E.dragStart
533− Drop -> on e E.drop
534− Error -> on e E.error
535− Focus -> on e E.focusEvent
536− Input -> on e E.input
537− Invalid -> on e E.invalid
538− Keydown -> on e E.keyDown
539− Keypress -> on e E.keyPress
540− Keyup -> on e E.keyUp
541− Load -> on e E.load
542− Mousedown -> on e E.mouseDown
543− Mouseenter -> on e E.mouseEnter
544− Mouseleave -> on e E.mouseLeave
545− Mousemove -> on e E.mouseMove
546− Mouseout -> on e E.mouseOut
547− Mouseover -> on e E.mouseOver
548− Mouseup -> on e E.mouseUp
549− Mousewheel -> on e E.mouseWheel
550− Scroll -> on e E.scroll
551− Select -> on e E.select
552− Submit -> on e E.submit
553− Wheel -> on e E.wheel
554− Beforecut -> on e E.beforeCut
555− Cut -> on e E.cut
556− Beforecopy -> on e E.beforeCopy
557− Copy -> on e E.copy
558− Beforepaste -> on e E.beforePaste
559− Paste -> on e E.paste
560− Reset -> on e E.reset
561− Search -> on e E.search
562− Selectstart -> on e E.selectStart
563− Touchstart -> on e E.touchStart
564− Touchmove -> on e E.touchMove
565− Touchend -> on e E.touchEnd
566− Touchcancel -> on e E.touchCancel
567−
568−newtype EventResult en = EventResult { unEventResult :: EventResultType en }
569−
570−type family EventResultType (en :: EventTag) :: * where
571− EventResultType 'ClickTag = ()
572− EventResultType 'DblclickTag = ()
573− EventResultType 'KeypressTag = Int
574− EventResultType 'KeydownTag = Int
575− EventResultType 'KeyupTag = Int
576− EventResultType 'ScrollTag = Int
577− EventResultType 'MousemoveTag = (Int, Int)
578− EventResultType 'MousedownTag = (Int, Int)
579− EventResultType 'MouseupTag = (Int, Int)
580− EventResultType 'MouseenterTag = ()
581− EventResultType 'MouseleaveTag = ()
582− EventResultType 'FocusTag = ()
583− EventResultType 'BlurTag = ()
584− EventResultType 'ChangeTag = ()
585− EventResultType 'DragTag = ()
586− EventResultType 'DragendTag = ()
587− EventResultType 'DragenterTag = ()
588− EventResultType 'DragleaveTag = ()
589− EventResultType 'DragoverTag = ()
590− EventResultType 'DragstartTag = ()
591− EventResultType 'DropTag = ()
592− EventResultType 'AbortTag = ()
593− EventResultType 'ContextmenuTag = ()
594− EventResultType 'ErrorTag = ()
595− EventResultType 'InputTag = ()
596− EventResultType 'InvalidTag = ()
597− EventResultType 'LoadTag = ()
598− EventResultType 'MouseoutTag = ()
599− EventResultType 'MouseoverTag = ()
600− EventResultType 'SelectTag = ()
601− EventResultType 'SubmitTag = ()
602− EventResultType 'BeforecutTag = ()
603− EventResultType 'CutTag = ()
604− EventResultType 'BeforecopyTag = ()
605− EventResultType 'CopyTag = ()
606− EventResultType 'BeforepasteTag = ()
607− EventResultType 'PasteTag = ()
608− EventResultType 'ResetTag = ()
609− EventResultType 'SearchTag = ()
610− EventResultType 'SelectstartTag = ()
611− EventResultType 'TouchstartTag = ()
612− EventResultType 'TouchmoveTag = ()
613− EventResultType 'TouchendTag = ()
614− EventResultType 'TouchcancelTag = ()
615− EventResultType 'MousewheelTag = ()
616− EventResultType 'WheelTag = ()
617−
618−wrapDomEventsMaybe :: (Functor (Event t), IsElement e, MonadIO m, MonadSample t m, MonadReflexCreateTrigger t m, Reflex t, HasPostGui t h m) => e -> (forall en. EventName en -> EventM e (EventType en) (Maybe (f en))) -> m (EventSelector t (WrapArg f EventName))
619−wrapDomEventsMaybe element handlers = do
620− postGui <- askPostGui
621− runWithActions <- askRunWithActions
622− e <- newFanEventWithTrigger $ \(WrapArg en) et -> do
623− unsubscribe <- onEventName en element $ do
624− mv <- handlers en
625− forM_ mv $ \v -> liftIO $ postGui $ runWithActions [et :=> Identity v]
626− return $ liftIO $ do
627− unsubscribe
628− return $! e
629−
630−getKeyEvent :: EventM e KeyboardEvent Int
631−getKeyEvent = do
632− e <- event
633− which <- getWhich e
634− if which /= 0 then return which else do
635− charCode <- getCharCode e
636− if charCode /= 0 then return charCode else
637− getKeyCode e
638−
639−getMouseEventCoords :: EventM e MouseEvent (Int, Int)
640−getMouseEventCoords = do
641− e <- event
642− bisequence (getX e, getY e)
643−
644−defaultDomEventHandler :: IsElement e => e -> EventName en -> EventM e (EventType en) (Maybe (EventResult en))
645−defaultDomEventHandler e evt = liftM (Just . EventResult) $ case evt of
646− Click -> return ()
647− Dblclick -> return ()
648− Keypress -> getKeyEvent
649− Scroll -> getScrollTop e
650− Keydown -> getKeyEvent
651− Keyup -> getKeyEvent
652− Mousemove -> getMouseEventCoords
653− Mouseup -> getMouseEventCoords
654− Mousedown -> getMouseEventCoords
655− Mouseenter -> return ()
656− Mouseleave -> return ()
657− Focus -> return ()
658− Blur -> return ()
659− Change -> return ()
660− Drag -> return ()
661− Dragend -> return ()
662− Dragenter -> return ()
663− Dragleave -> return ()
664− Dragover -> return ()
665− Dragstart -> return ()
666− Drop -> return ()
667− Abort -> return ()
668− Contextmenu -> return ()
669− Error -> return ()
670− Input -> return ()
671− Invalid -> return ()
672− Load -> return ()
673− Mouseout -> return ()
674− Mouseover -> return ()
675− Select -> return ()
676− Submit -> return ()
677− Beforecut -> return ()
678− Cut -> return ()
679− Beforecopy -> return ()
680− Copy -> return ()
681− Beforepaste -> return ()
682− Paste -> return ()
683− Reset -> return ()
684− Search -> return ()
685− Selectstart -> return ()
686− Touchstart -> return ()
687− Touchmove -> return ()
688− Touchend -> return ()
689− Touchcancel -> return ()
690− Mousewheel -> return ()
691− Wheel -> return ()
692−
693−wrapElement :: forall t h m. (Functor (Event t), MonadIO m, MonadSample t m, MonadReflexCreateTrigger t m, Reflex t, HasPostGui t h m) => (forall en. Element -> EventName en -> EventM Element (EventType en) (Maybe (EventResult en))) -> Element -> m (El t)
694−wrapElement eh e = do
695− es <- wrapDomEventsMaybe e $ eh e
696− return $ El e es
697−
698−{-# INLINABLE elStopPropagationNS #-}
699−elStopPropagationNS :: (MonadWidget t m, IsEvent (EventType en)) => Maybe String -> String -> EventName en -> m a -> m a
700−elStopPropagationNS mns elementTag evt child = do
701− (e, result) <- buildElementNS mns elementTag (Map.empty :: Map String String) child
702− _ <- liftIO $ onEventName evt e stopPropagation
703− return result
704−
214+{-
705215{-# INLINABLE elWith #-}
706−elWith :: (MonadWidget t m, Attributes m attrs) => String -> ElConfig attrs -> m a -> m a
216+elWith :: (DomBuilder t m, Attributes m attrs) => Text -> ElConfig attrs -> m a -> m a
707217elWith elementTag cfg child = do
708218 (_, result) <- buildElementNS (cfg ^. namespace) elementTag (cfg ^. attributes) child
709219 return result
710220
711221{-# INLINABLE elWith' #-}
712−elWith' :: (MonadWidget t m, Attributes m attrs) => String -> ElConfig attrs -> m a -> m (El t, a)
222+elWith' :: (DomBuilder t m, Attributes m attrs) => Text -> ElConfig attrs -> m a -> m (El t, a)
713223elWith' elementTag cfg child = do
714224 (e, result) <- buildElementNS (cfg ^. namespace) elementTag (cfg ^. attributes) child
715225 e' <- wrapElement defaultDomEventHandler e
716226 return (e', result)
717227
718228{-# INLINABLE emptyElWith #-}
719−emptyElWith :: (MonadWidget t m, Attributes m attrs) => String -> ElConfig attrs -> m ()
229+emptyElWith :: (DomBuilder t m, Attributes m attrs) => Text -> ElConfig attrs -> m ()
720230emptyElWith elementTag cfg = do
721231 _ <- buildEmptyElementNS (cfg ^. namespace) elementTag (cfg ^. attributes)
722232 return ()
723233
724234{-# INLINABLE emptyElWith' #-}
725−emptyElWith' :: (MonadWidget t m, Attributes m attrs) => String -> ElConfig attrs -> m (El t)
235+emptyElWith' :: (DomBuilder t m, Attributes m attrs) => Text -> ElConfig attrs -> m (El t)
726236emptyElWith' elementTag cfg = do
727237 wrapElement defaultDomEventHandler =<< buildEmptyElementNS (cfg ^. namespace) elementTag (cfg ^. attributes)
238+-}
239+
240+dynamicAttributesToModifyAttributes :: PostBuild t m => Dynamic t (Map Text Text) -> m (Event t (Map AttributeName (Maybe Text)))
241+dynamicAttributesToModifyAttributes d = do
242+ postBuild <- getPostBuild
243+ let modificationsNeeded = flip pushAlways (align postBuild $ updated d) $ \case
244+ This () -> fmap (fmap Just) $ sample $ current d
245+ These () new -> return $ fmap Just new
246+ That new -> do
247+ old <- sample $ current d
248+ return $ diffMap old new
249+ return $ Map.fromList . fmap (\(k, v) -> ((Nothing, k), v)) . Map.toList <$> modificationsNeeded
728250
729251{-# INLINABLE elDynAttrNS' #-}
730−elDynAttrNS' :: forall t m a. MonadWidget t m => Maybe String -> String -> Dynamic t (Map String String) -> m a -> m (El t, a)
731−elDynAttrNS' mns elementTag attrs = elWith' elementTag $
732− def & namespace .~ mns
733− & elConfig_attributes .~ attrs
252+elDynAttrNS' :: forall t m a. (DomBuilder t m, PostBuild t m) => Maybe Text -> Text -> Dynamic t (Map Text Text) -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
253+elDynAttrNS' mns elementTag attrs child = do
254+ modifyAttrs <- dynamicAttributesToModifyAttributes attrs
255+ let cfg = def
256+ & elementConfig_namespace .~ mns
257+ & modifyAttributes .~ modifyAttrs
258+ element elementTag cfg child
734259
735260{-# INLINABLE elDynAttr' #-}
736−elDynAttr' :: forall t m a. MonadWidget t m => String -> Dynamic t (Map String String) -> m a -> m (El t, a)
737−elDynAttr' elementTag attrs = elWith' elementTag $ def & elConfig_attributes .~ attrs
261+elDynAttr' :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t (Map Text Text) -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
262+elDynAttr' = elDynAttrNS' Nothing
738263
739264{-# INLINABLE elAttr #-}
740−elAttr :: forall t m a. MonadWidget t m => String -> Map String String -> m a -> m a
741−elAttr elementTag attrs = elWith elementTag $ def & attributes .~ attrs
265+elAttr :: forall t m a. DomBuilder t m => Text -> Map Text Text -> m a -> m a
266+elAttr elementTag attrs child = snd <$> elAttr' elementTag attrs child
742267
743268{-# INLINABLE el' #-}
744−el' :: forall t m a. MonadWidget t m => String -> m a -> m (El t, a)
745−el' elementTag = elWith' elementTag def
269+el' :: forall t m a. DomBuilder t m => Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
270+el' elementTag = element elementTag def
746271
747272{-# INLINABLE elAttr' #-}
748−elAttr' :: forall t m a. MonadWidget t m => String -> Map String String -> m a -> m (El t, a)
749−elAttr' elementTag attrs = elWith' elementTag $ def & attributes .~ attrs
273+elAttr' :: forall t m a. DomBuilder t m => Text -> Map Text Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
274+elAttr' elementTag attrs = element elementTag $ def
275+ & initialAttributes .~ (Map.fromList $ fmap (\(k, v) -> ((Nothing, k), v)) $ Map.toList $ attrs)
750276
751277{-# INLINABLE elDynAttr #-}
752−elDynAttr :: forall t m a. MonadWidget t m => String -> Dynamic t (Map String String) -> m a -> m a
753−elDynAttr elementTag attrs = elWith elementTag $ def & elConfig_attributes .~ attrs
278+elDynAttr :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t (Map Text Text) -> m a -> m a
279+elDynAttr elementTag attrs child = snd <$> elDynAttr' elementTag attrs child
754280
755281{-# INLINABLE el #-}
756−el :: forall t m a. MonadWidget t m => String -> m a -> m a
757−el elementTag = elWith elementTag def
282+el :: forall t m a. DomBuilder t m => Text -> m a -> m a
283+el elementTag child = snd <$> el' elementTag child
758284
759−elClass :: forall t m a. MonadWidget t m => String -> String -> m a -> m a
760−elClass elementTag c = elWith elementTag $ def & attributes .~ "class" =: c
285+elClass :: forall t m a. DomBuilder t m => Text -> Text -> m a -> m a
286+elClass elementTag c child = fmap snd $ element elementTag (def & initialAttributes .~ (Nothing, "class") =: c) child
761287
762288--------------------------------------------------------------------------------
763289-- Copied and pasted from Reflex.Widget.Class
@@ -765,98 +291,77 @@ elClass elementTag c = elWith elementTag $ def & attributes .~ "class" =: c
765291
766292-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
767293-- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with.
768−list :: (MonadWidget t m, Ord k) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
294+list :: (DomBuilder t m, MonadHold t m, PostBuild t m) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
769295list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
770296
771297-- | Create a dynamically-changing set of widgets from a Dynamic list.
772−simpleList :: MonadWidget t m => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
298+simpleList :: (DomBuilder t m, MonadHold t m, PostBuild t m) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
773299simpleList xs mkChild = mapDyn (map snd . Map.toList) =<< flip list mkChild =<< mapDyn (Map.fromList . zip [(1::Int)..]) xs
774300
775−elDynHtml' :: MonadWidget t m => String -> Dynamic t String -> m (El t)
301+{-
302+schedulePostBuild x = performEvent_ . (x <$) =<< getPostBuild
303+
304+elDynHtml' :: DomBuilder t m => Text -> Dynamic t Text -> m (El t)
776305elDynHtml' elementTag html = do
777− e <- buildEmptyElement elementTag (Map.empty :: Map String String)
306+ e <- buildEmptyElement elementTag (Map.empty :: Map Text Text)
778307 schedulePostBuild $ setInnerHTML e . Just =<< sample (current html)
779− addVoidAction $ fmap (setInnerHTML e . Just) $ updated html
308+ performEvent_ $ fmap (setInnerHTML e . Just) $ updated html
780309 wrapElement defaultDomEventHandler e
781310
782−elDynHtmlAttr' :: MonadWidget t m => String -> Map String String -> Dynamic t String -> m (El t)
311+elDynHtmlAttr' :: DomBuilder t m => Text -> Map Text Text -> Dynamic t Text -> m (El t)
783312elDynHtmlAttr' elementTag attrs html = do
784313 e <- buildEmptyElement elementTag attrs
785314 schedulePostBuild $ setInnerHTML e . Just =<< sample (current html)
786− addVoidAction $ fmap (setInnerHTML e . Just) $ updated html
315+ performEvent_ $ fmap (setInnerHTML e . Just) $ updated html
787316 wrapElement defaultDomEventHandler e
317+-}
788318
789319data Link t
790320 = Link { _link_clicked :: Event t ()
791321 }
792322
793−class HasAttributes a where
794− type Attrs a :: *
795− attributes :: Lens' a (Attrs a)
796−
797−instance HasAttributes (ElConfig attrs) where
798− type Attrs (ElConfig attrs) = attrs
799− attributes = elConfig_attributes
800−
801−class HasNamespace a where
802− namespace :: Lens' a (Maybe String)
803−
804−instance HasNamespace (ElConfig attrs) where
805− namespace = elConfig_namespace
806−
807−class HasDomEvent t a where
808− domEvent :: EventName en -> a -> Event t (EventResultType en)
809−
810−instance Reflex t => HasDomEvent t (El t) where
811− domEvent en e = fmap unEventResult $ Reflex.select (_el_events e) (WrapArg en)
812−
813−linkClass :: MonadWidget t m => String -> String -> m (Link t)
323+linkClass :: DomBuilder t m => Text -> Text -> m (Link t)
814324linkClass s c = do
815325 (l,_) <- elAttr' "a" ("class" =: c) $ text s
816326 return $ Link $ domEvent Click l
817327
818−link :: MonadWidget t m => String -> m (Link t)
328+link :: DomBuilder t m => Text -> m (Link t)
819329link s = linkClass s ""
820330
821−button :: MonadWidget t m => String -> m (Event t ())
822−button s = do
823− (e, _) <- elAttr' "button" (Map.singleton "type" "button") $ text s
824− return $ domEvent Click e
825−
826331newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
827332
828−workflow :: forall t m a. MonadWidget t m => Workflow t m a -> m (Dynamic t a)
333+workflow :: forall t m a. (DomBuilder t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a)
829334workflow w0 = do
830335 rec eResult <- widgetHold (unWorkflow w0) $ fmap unWorkflow $ switch $ fmap snd $ current eResult
831336 mapDyn fst eResult
832337
833−workflowView :: forall t m a. MonadWidget t m => Workflow t m a -> m (Event t a)
338+workflowView :: forall t m a. (DomBuilder t m, MonadFix m, MonadHold t m, PostBuild t m) => Workflow t m a -> m (Event t a)
834339workflowView w0 = do
835340 rec eResult <- dyn =<< mapDyn unWorkflow =<< holdDyn w0 eReplace
836341 eReplace <- liftM switch $ hold never $ fmap snd eResult
837342 return $ fmap fst eResult
838343
839−mapWorkflow :: (MonadWidget t m) => (a -> b) -> Workflow t m a -> Workflow t m b
344+mapWorkflow :: (DomBuilder t m) => (a -> b) -> Workflow t m a -> Workflow t m b
840345mapWorkflow f (Workflow x) = Workflow (fmap (\(v,e) -> (f v, fmap (mapWorkflow f) e)) x)
841346
842−divClass :: forall t m a. MonadWidget t m => String -> m a -> m a
347+divClass :: forall t m a. DomBuilder t m => Text -> m a -> m a
843348divClass = elClass "div"
844349
845−dtdd :: forall t m a. MonadWidget t m => String -> m a -> m a
350+dtdd :: forall t m a. DomBuilder t m => Text -> m a -> m a
846351dtdd h w = do
847352 el "dt" $ text h
848353 el "dd" $ w
849354
850−blank :: forall t m. MonadWidget t m => m ()
355+blank :: forall m. Monad m => m ()
851356blank = return ()
852357
853358-- | A widget to display a table with static columns and dynamic rows.
854−tableDynAttr :: forall t m r k v. (MonadWidget t m, Show k, Ord k)
855− => String -- ^ Class applied to <table> element
856− -> [(String, k -> Dynamic t r -> m v)] -- ^ Columns of (header, row key -> row value -> child widget)
359+tableDynAttr :: forall t m r k v. (DomBuilder t m, MonadHold t m, PostBuild t m)
360+ => Text -- ^ Class applied to <table> element
361+ -> [(Text, k -> Dynamic t r -> m v)] -- ^ Columns of (header, row key -> row value -> child widget)
857362 -> Dynamic t (Map k r) -- ^ Map from row key to row value
858− -> (k -> m (Dynamic t (Map String String))) -- ^ Function to compute <tr> element attributes from row key
859− -> m (Dynamic t (Map k (El t, [v]))) -- ^ Map from row key to (El, list of widget return values)
363+ -> (k -> m (Dynamic t (Map Text Text))) -- ^ Function to compute <tr> element attributes from row key
364+ -> m (Dynamic t (Map k (Element EventResult (DomBuilderSpace m) t, [v]))) -- ^ Map from row key to (El, list of widget return values)
860365tableDynAttr klass cols dRows rowAttrs = elAttr "div" (Map.singleton "style" "zoom: 1; overflow: auto; background: white;") $ do
861366 elAttr "table" (Map.singleton "class" klass) $ do
862367 el "thead" $ el "tr" $ do
@@ -870,10 +375,10 @@ tableDynAttr klass cols dRows rowAttrs = elAttr "div" (Map.singleton "style" "zo
870375-- | A widget to construct a tabbed view that shows only one of its child widgets at a time.
871376-- Creates a header bar containing a <ul> with one <li> per child; clicking a <li> displays
872377-- the corresponding child and hides all others.
873−tabDisplay :: forall t m k. (MonadFix m, MonadWidget t m, Show k, Ord k)
874− => String -- ^ Class applied to <ul> element
875− -> String -- ^ Class applied to currently active <li> element
876− -> Map k (String, m ()) -- ^ Map from (arbitrary) key to (tab label, child widget)
378+tabDisplay :: forall t m k. (MonadFix m, DomBuilder t m, MonadHold t m, PostBuild t m, Ord k)
379+ => Text -- ^ Class applied to <ul> element
380+ -> Text -- ^ Class applied to currently active <li> element
381+ -> Map k (Text, m ()) -- ^ Map from (arbitrary) key to (tab label, child widget)
877382 -> m ()
878383tabDisplay ulClass activeClass tabItems = do
879384 let t0 = listToMaybe $ Map.keys tabItems
@@ -888,34 +393,13 @@ tabDisplay ulClass activeClass tabItems = do
888393 elDynAttr "div" attrs w
889394 return ()
890395 where
891− headerBarLink :: (MonadWidget t m, Ord k) => String -> k -> Dynamic t Bool -> m (Event t k)
396+ headerBarLink :: Text -> k -> Dynamic t Bool -> m (Event t k)
892397 headerBarLink x k isSelected = do
893398 attrs <- mapDyn (\b -> if b then Map.singleton "class" activeClass else Map.empty) isSelected
894399 elDynAttr "li" attrs $ do
895400 a <- link x
896401 return $ fmap (const k) (_link_clicked a)
897402
898−-- | Place an element into the DOM and wrap it with Reflex event handlers. Note: undefined behavior may result if the element is placed multiple times, removed from the DOM after being placed, or in other situations. Don't use this unless you understand the internals of MonadWidget.
899−unsafePlaceElement :: MonadWidget t m => Element -> m (El t)
900−unsafePlaceElement e = do
901− p <- askParent
902− _ <- appendChild p $ Just e
903− wrapElement defaultDomEventHandler e
904−
905−deriveGEq ''EventName
906−deriveGCompare ''EventName
907−
908−--------------------------------------------------------------------------------
909−-- Deprecated functions
910−
911−{-# DEPRECATED _el_clicked "Use `domEvent Click` instead" #-}
912−_el_clicked :: Reflex t => El t -> Event t ()
913−_el_clicked = domEvent Click
914−
915−{-# DEPRECATED _el_keypress "Use `domEvent Keypress` instead" #-}
916−_el_keypress :: Reflex t => El t -> Event t Int
917−_el_keypress = domEvent Keypress
918−
919−{-# DEPRECATED _el_scrolled "Use `domEvent Scroll` instead" #-}
920−_el_scrolled :: Reflex t => El t -> Event t Int
921−_el_scrolled = domEvent Scroll
403+class HasAttributes a where
404+ type Attrs a :: *
405+ attributes :: Lens' a (Attrs a)
src/Reflex/Dom/Widget/Input.hs+283−58
@@ -1,35 +1,45 @@
1−{-# LANGUAGE ConstraintKinds, TypeFamilies, FlexibleContexts, DataKinds, GADTs, ScopedTypeVariables, FlexibleInstances, RecursiveDo, TemplateHaskell #-}
1+{-# LANGUAGE OverloadedStrings, ConstraintKinds, TypeFamilies, FlexibleContexts, DataKinds, GADTs, ScopedTypeVariables, FlexibleInstances, RecursiveDo, TemplateHaskell, UndecidableInstances, PolyKinds #-}
22module Reflex.Dom.Widget.Input (module Reflex.Dom.Widget.Input, def, (&), (.~)) where
33
44import Prelude
55
66import Reflex.Dom.Class
77import Reflex.Dom.Widget.Basic
8+import Reflex.Dom.PostBuild.Class
9+import Reflex.Dom.Builder.Class
10+import Reflex.Dom.Builder.Immediate
11+
12+-- For dropdown
13+import Reflex.Dom.PerformEvent.Class
14+import GHCJS.DOM.HTMLInputElement (HTMLInputElement, castToHTMLInputElement)
15+import GHCJS.DOM.HTMLTextAreaElement (HTMLTextAreaElement, castToHTMLTextAreaElement)
16+import GHCJS.DOM.HTMLSelectElement (castToHTMLSelectElement)
17+import qualified GHCJS.DOM.HTMLSelectElement as HTMLSelectElement
18+import Data.Maybe
19+import Data.Semigroup
20+import Control.Monad.Fix
21+import Control.Monad.IO.Class
822
923import Reflex
10−import Reflex.Host.Class
11−import GHCJS.DOM.HTMLInputElement as Input
12−import GHCJS.DOM.HTMLTextAreaElement as TextArea
13−import GHCJS.DOM.Element hiding (error)
14−import GHCJS.DOM.HTMLSelectElement as Select
15−import GHCJS.DOM.EventM
16−import GHCJS.DOM.File
17−import qualified GHCJS.DOM.FileList as FileList
18−import Data.Monoid
1924import qualified Data.Map as Map
20−import Control.Lens hiding (ix)
25+import Control.Lens hiding (ix, element)
2126import Control.Monad hiding (forM_)
22−import Control.Monad.IO.Class
23−import qualified Data.Bimap as B
2427import Data.Default
25−import Data.Dependent.Sum (DSum (..))
2628import Data.Map (Map)
27−import Data.Maybe
28−import Text.Read
29+import Data.Text (Text)
30+import qualified Data.Text as T
31+import Data.Dependent.Map (DMap)
32+import qualified Data.Dependent.Map as DMap
33+import qualified Data.Bimap as Bimap
34+import Data.Proxy
35+import Data.Functor.Misc
36+
37+import qualified GHCJS.DOM.HTMLInputElement as Input
38+import qualified GHCJS.DOM.Event as Event
2939
3040data TextInput t
31− = TextInput { _textInput_value :: Dynamic t String
32− , _textInput_input :: Event t String
41+ = TextInput { _textInput_value :: Dynamic t Text
42+ , _textInput_input :: Event t Text
3343 , _textInput_keypress :: Event t Int
3444 , _textInput_keydown :: Event t Int
3545 , _textInput_keyup :: Event t Int
@@ -38,13 +48,14 @@ data TextInput t
3848 }
3949
4050data TextInputConfig t
41− = TextInputConfig { _textInputConfig_inputType :: String
42− , _textInputConfig_initialValue :: String
43− , _textInputConfig_setValue :: Event t String
44− , _textInputConfig_attributes :: Dynamic t (Map String String)
51+ = TextInputConfig { _textInputConfig_inputType :: Text
52+ , _textInputConfig_initialValue :: Text
53+ , _textInputConfig_setValue :: Event t Text
54+ , _textInputConfig_attributes :: Dynamic t (Map Text Text)
4555 }
4656
4757instance Reflex t => Default (TextInputConfig t) where
58+ {-# INLINABLE def #-}
4859 def = TextInputConfig { _textInputConfig_inputType = "text"
4960 , _textInputConfig_initialValue = ""
5061 , _textInputConfig_setValue = never
@@ -52,8 +63,24 @@ instance Reflex t => Default (TextInputConfig t) where
5263 }
5364
5465-- | Create an input whose value is a string. By default, the "type" attribute is set to "text", but it can be changed using the _textInputConfig_inputType field. Note that only types for which the value is always a string will work - types whose value may be null will not work properly with this widget.
55−textInput :: MonadWidget t m => TextInputConfig t -> m (TextInput t)
66+{-# INLINABLE textInput #-}
67+textInput :: (DomBuilder t m, PostBuild t m, DomBuilderSpace m ~ GhcjsDomSpace) => TextInputConfig t -> m (TextInput t)
5668textInput (TextInputConfig inputType initial eSetValue dAttrs) = do
69+ modifyAttrs <- dynamicAttributesToModifyAttributes $ fmap (Map.insert "type" inputType) dAttrs
70+ i <- inputElement $ def
71+ & inputElementConfig_initialValue .~ initial
72+ & inputElementConfig_setValue .~ eSetValue
73+ & inputElementConfig_elementConfig . elementConfig_modifyAttributes .~ modifyAttrs
74+ return $ TextInput
75+ { _textInput_value = _inputElement_value i
76+ , _textInput_input = _inputElement_input i
77+ , _textInput_keypress = domEvent Keypress i
78+ , _textInput_keydown = domEvent Keydown i
79+ , _textInput_keyup = domEvent Keyup i
80+ , _textInput_hasFocus = _inputElement_hasFocus i
81+ , _textInput_element = castToHTMLInputElement $ _element_raw $ _inputElement_element i
82+ }
83+{-
5784 e <- liftM castToHTMLInputElement $ buildEmptyElement "input" =<< mapDyn (Map.insert "type" inputType) dAttrs
5885 Input.setValue e $ Just initial
5986 performEvent_ $ fmap (Input.setValue e . Just) eSetValue
@@ -72,32 +99,49 @@ textInput (TextInputConfig inputType initial eSetValue dAttrs) = do
7299 eKeyup <- wrapDomEvent e (`on` keyUp) getKeyEvent
73100 dValue <- holdDyn initial $ leftmost [eSetValue, eChange]
74101 return $ TextInput dValue eChange eKeypress eKeydown eKeyup dFocus e
102+-}
75103
104+{-# INLINABLE textInputGetEnter #-}
76105textInputGetEnter :: Reflex t => TextInput t -> Event t ()
77106textInputGetEnter i = fmapMaybe (\n -> if n == keycodeEnter then Just () else Nothing) $ _textInput_keypress i
78107
79108data TextAreaConfig t
80− = TextAreaConfig { _textAreaConfig_initialValue :: String
81− , _textAreaConfig_setValue :: Event t String
82− , _textAreaConfig_attributes :: Dynamic t (Map String String)
109+ = TextAreaConfig { _textAreaConfig_initialValue :: Text
110+ , _textAreaConfig_setValue :: Event t Text
111+ , _textAreaConfig_attributes :: Dynamic t (Map Text Text)
83112 }
84113
85114instance Reflex t => Default (TextAreaConfig t) where
115+ {-# INLINABLE def #-}
86116 def = TextAreaConfig { _textAreaConfig_initialValue = ""
87117 , _textAreaConfig_setValue = never
88118 , _textAreaConfig_attributes = constDyn mempty
89119 }
90120
91121data TextArea t
92− = TextArea { _textArea_value :: Dynamic t String
93− , _textArea_input :: Event t String
94− , _textArea_element :: HTMLTextAreaElement
122+ = TextArea { _textArea_value :: Dynamic t Text
123+ , _textArea_input :: Event t Text
95124 , _textArea_hasFocus :: Dynamic t Bool
96125 , _textArea_keypress :: Event t Int
126+ , _textArea_element :: HTMLTextAreaElement
97127 }
98128
99−textArea :: MonadWidget t m => TextAreaConfig t -> m (TextArea t)
129+{-# INLINABLE textArea #-}
130+textArea :: (DomBuilder t m, PostBuild t m, DomBuilderSpace m ~ GhcjsDomSpace) => TextAreaConfig t -> m (TextArea t)
100131textArea (TextAreaConfig initial eSet attrs) = do
132+ modifyAttrs <- dynamicAttributesToModifyAttributes attrs
133+ i <- textAreaElement $ def
134+ & textAreaElementConfig_initialValue .~ initial
135+ & textAreaElementConfig_setValue .~ eSet
136+ & textAreaElementConfig_elementConfig . elementConfig_modifyAttributes .~ modifyAttrs
137+ return $ TextArea
138+ { _textArea_value = _textAreaElement_value i
139+ , _textArea_input = _textAreaElement_input i
140+ , _textArea_keypress = domEvent Keypress i
141+ , _textArea_hasFocus = _textAreaElement_hasFocus i
142+ , _textArea_element = castToHTMLTextAreaElement $ _element_raw $ _textAreaElement_element i
143+ }
144+{-
101145 e <- liftM castToHTMLTextAreaElement $ buildEmptyElement "textarea" attrs
102146 TextArea.setValue e $ Just initial
103147 postGui <- askPostGui
@@ -114,36 +158,138 @@ textArea (TextAreaConfig initial eSet attrs) = do
114158 v <- holdDyn initial $ leftmost [eSet, ev]
115159 eKeypress <- wrapDomEvent e (`on` keyPress) getKeyEvent
116160 return $ TextArea v ev e f eKeypress
161+-}
117162
118163data CheckboxConfig t
119164 = CheckboxConfig { _checkboxConfig_setValue :: Event t Bool
120− , _checkboxConfig_attributes :: Dynamic t (Map String String)
165+ , _checkboxConfig_attributes :: Dynamic t (Map Text Text)
121166 }
122167
123168instance Reflex t => Default (CheckboxConfig t) where
169+ {-# INLINABLE def #-}
124170 def = CheckboxConfig { _checkboxConfig_setValue = never
125171 , _checkboxConfig_attributes = constDyn mempty
126172 }
127173
128174data Checkbox t
129175 = Checkbox { _checkbox_value :: Dynamic t Bool
130− , _checkbox_change :: Event t Bool
176+-- , _checkbox_change :: Event t Bool
131177 }
132178
133−--TODO: Make attributes possibly dynamic
134179-- | Create an editable checkbox
135180-- Note: if the "type" or "checked" attributes are provided as attributes, they will be ignored
136−checkbox :: MonadWidget t m => Bool -> CheckboxConfig t -> m (Checkbox t)
181+{-# INLINABLE checkbox #-}
182+checkbox :: (DomBuilder t m, PostBuild t m) => Bool -> CheckboxConfig t -> m (Checkbox t)
137183checkbox checked config = do
184+ let insertType = Map.insert "type" "checkbox"
185+ insertChecked = if checked
186+ then Map.insert "checked" "checked"
187+ else id
188+ dAttrs = fmap (insertChecked . insertType) $ _checkboxConfig_attributes config
189+ modifyAttrs <- dynamicAttributesToModifyAttributes dAttrs
190+ i <- inputElement $ def
191+ & inputElementConfig_setChecked .~ _checkboxConfig_setValue config
192+ & inputElementConfig_elementConfig . elementConfig_modifyAttributes .~ modifyAttrs
193+ return $ Checkbox
194+ { _checkbox_value = _inputElement_checked i
195+ }
196+{-
138197 attrs <- mapDyn (\c -> Map.insert "type" "checkbox" $ (if checked then Map.insert "checked" "checked" else Map.delete "checked") c) (_checkboxConfig_attributes config)
139198 e <- liftM castToHTMLInputElement $ buildEmptyElement "input" attrs
140199 eClick <- wrapDomEvent e (`on` click) $ Input.getChecked e
141200 performEvent_ $ fmap (\v -> Input.setChecked e $! v) $ _checkboxConfig_setValue config
142201 dValue <- holdDyn checked $ leftmost [_checkboxConfig_setValue config, eClick]
143202 return $ Checkbox dValue eClick
203+-}
144204
145−checkboxView :: MonadWidget t m => Dynamic t (Map String String) -> Dynamic t Bool -> m (Event t Bool)
205+type family CheckboxViewEventResultType (en :: EventTag) :: * where
206+ CheckboxViewEventResultType 'ClickTag = Bool
207+ CheckboxViewEventResultType t = EventResultType t
208+
209+regularToCheckboxViewEventType :: EventName t -> EventResultType t -> CheckboxViewEventResultType t
210+regularToCheckboxViewEventType en r = case en of
211+ Click -> error "regularToCheckboxViewEventType: EventName Click should never be encountered"
212+ Abort -> r
213+ Blur -> r
214+ Change -> r
215+ Contextmenu -> r
216+ Dblclick -> r
217+ Drag -> r
218+ Dragend -> r
219+ Dragenter -> r
220+ Dragleave -> r
221+ Dragover -> r
222+ Dragstart -> r
223+ Drop -> r
224+ Error -> r
225+ Focus -> r
226+ Input -> r
227+ Invalid -> r
228+ Keydown -> r
229+ Keypress -> r
230+ Keyup -> r
231+ Load -> r
232+ Mousedown -> r
233+ Mouseenter -> r
234+ Mouseleave -> r
235+ Mousemove -> r
236+ Mouseout -> r
237+ Mouseover -> r
238+ Mouseup -> r
239+ Mousewheel -> r
240+ Scroll -> r
241+ Select -> r
242+ Submit -> r
243+ Wheel -> r
244+ {-
245+ Beforecut -> r
246+ Cut -> r
247+ Beforecopy -> r
248+ Copy -> r
249+ Beforepaste -> r
250+ Paste -> r
251+-}
252+ Reset -> r
253+ Search -> r
254+-- Selectstart -> r
255+ Touchstart -> r
256+ Touchmove -> r
257+ Touchend -> r
258+ Touchcancel -> r
259+
260+newtype CheckboxViewEventResult en = CheckboxViewEventResult { unCheckboxViewEventResult :: CheckboxViewEventResultType en }
261+
262+--TODO
263+{-# INLINABLE checkboxView #-}
264+checkboxView :: forall t m. (DomBuilder t m, DomBuilderSpace m ~ GhcjsDomSpace, PostBuild t m, MonadHold t m) => Dynamic t (Map Text Text) -> Dynamic t Bool -> m (Event t Bool)
146265checkboxView dAttrs dValue = do
266+ let insertType = Map.insert "type" "checkbox"
267+ modifyAttrs <- dynamicAttributesToModifyAttributes $ fmap insertType dAttrs
268+ postBuild <- getPostBuild
269+ let filters :: DMap EventName (EventFilter (DomBuilderSpace m) CheckboxViewEventResult)
270+ filters = DMap.singleton Click $ EventFilter $ GhcjsDomHandler $ \(GhcjsDomEvent evt) -> do
271+ Just t <- Event.getTarget evt
272+ b <- Input.getChecked $ Input.castToHTMLInputElement t
273+ return $ (,) preventDefault $ GhcjsDomHandler $ \_ -> return $ Just $ CheckboxViewEventResult b
274+ elementConfig :: ElementConfig CheckboxViewEventResult t m
275+ elementConfig = (def :: ElementConfig EventResult t m)
276+ { _elementConfig_modifyAttributes = modifyAttrs
277+ , _elementConfig_eventFilters = filters
278+ , _elementConfig_eventHandler = GhcjsDomHandler1 $ \p@(Pair1 en _) -> case en of
279+ Click -> error "impossible"
280+ _ -> do
281+ mr <- unGhcjsDomHandler1 (defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))) p
282+ case mr of
283+ Nothing1 -> return Nothing1
284+ Just1 (EventResult r) -> return $ Just1 $ CheckboxViewEventResult $ regularToCheckboxViewEventType en r
285+ }
286+ inputElementConfig :: InputElementConfig CheckboxViewEventResult t m
287+ inputElementConfig = (def :: InputElementConfig EventResult t m)
288+ & inputElementConfig_setChecked .~ leftmost [updated dValue, tag (current dValue) postBuild]
289+ & inputElementConfig_elementConfig .~ elementConfig
290+ i <- inputElement inputElementConfig
291+ return $ fmap unCheckboxViewEventResult $ select (_element_events $ _inputElement_element i) (WrapArg Click)
292+{-
147293 e <- liftM castToHTMLInputElement $ buildEmptyElement "input" =<< mapDyn (Map.insert "type" "checkbox") dAttrs
148294 eClicked <- wrapDomEvent e (`on` click) $ do
149295 preventDefault
@@ -153,6 +299,8 @@ checkboxView dAttrs dValue = do
153299 when v $ Input.setChecked e True
154300 performEvent_ $ fmap (\v -> Input.setChecked e $! v) $ updated dValue
155301 return eClicked
302+-}
303+{-
156304
157305data FileInput t
158306 = FileInput { _fileInput_value :: Dynamic t [File]
@@ -160,14 +308,14 @@ data FileInput t
160308 }
161309
162310data FileInputConfig t
163− = FileInputConfig { _fileInputConfig_attributes :: Dynamic t (Map String String)
311+ = FileInputConfig { _fileInputConfig_attributes :: Dynamic t (Map Text Text)
164312 }
165313
166314instance Reflex t => Default (FileInputConfig t) where
167315 def = FileInputConfig { _fileInputConfig_attributes = constDyn mempty
168316 }
169317
170−fileInput :: MonadWidget t m => FileInputConfig t -> m (FileInput t)
318+fileInput :: DomBuilder t m => FileInputConfig t -> m (FileInput t)
171319fileInput (FileInputConfig dAttrs) = do
172320 e <- liftM castToHTMLInputElement $ buildEmptyElement "input" =<< mapDyn (Map.insert "type" "file") dAttrs
173321 eChange <- wrapDomEvent e (flip on change) $ do
@@ -176,6 +324,7 @@ fileInput (FileInputConfig dAttrs) = do
176324 mapM (liftM (fromMaybe (error "fileInput: fileListItem returned null")) . FileList.item files) $ init [0..len]
177325 dValue <- holdDyn [] eChange
178326 return $ FileInput dValue e
327+-}
179328
180329data Dropdown t k
181330 = Dropdown { _dropdown_value :: Dynamic t k
@@ -184,35 +333,107 @@ data Dropdown t k
184333
185334data DropdownConfig t k
186335 = DropdownConfig { _dropdownConfig_setValue :: Event t k
187− , _dropdownConfig_attributes :: Dynamic t (Map String String)
336+ , _dropdownConfig_attributes :: Dynamic t (Map Text Text)
188337 }
189338
190−instance (Reflex t, Ord k, Show k, Read k) => Default (DropdownConfig t k) where
339+instance Reflex t => Default (DropdownConfig t k) where
191340 def = DropdownConfig { _dropdownConfig_setValue = never
192341 , _dropdownConfig_attributes = constDyn mempty
193342 }
194343
344+type family DropdownViewEventResultType (en :: EventTag) :: * where
345+ DropdownViewEventResultType 'ChangeTag = Text
346+ DropdownViewEventResultType t = EventResultType t
347+
348+newtype DropdownViewEventResult en = DropdownViewEventResult { unDropdownViewEventResult :: DropdownViewEventResultType en }
349+
350+regularToDropdownViewEventType :: EventName t -> EventResultType t -> DropdownViewEventResultType t
351+regularToDropdownViewEventType en r = case en of
352+ Change -> error "regularToDropdownViewEventType: EventName Change should never be encountered"
353+ Abort -> r
354+ Blur -> r
355+ Click -> r
356+ Contextmenu -> r
357+ Dblclick -> r
358+ Drag -> r
359+ Dragend -> r
360+ Dragenter -> r
361+ Dragleave -> r
362+ Dragover -> r
363+ Dragstart -> r
364+ Drop -> r
365+ Error -> r
366+ Focus -> r
367+ Input -> r
368+ Invalid -> r
369+ Keydown -> r
370+ Keypress -> r
371+ Keyup -> r
372+ Load -> r
373+ Mousedown -> r
374+ Mouseenter -> r
375+ Mouseleave -> r
376+ Mousemove -> r
377+ Mouseout -> r
378+ Mouseover -> r
379+ Mouseup -> r
380+ Mousewheel -> r
381+ Scroll -> r
382+ Select -> r
383+ Submit -> r
384+ Wheel -> r
385+ {-
386+ Beforecut -> r
387+ Cut -> r
388+ Beforecopy -> r
389+ Copy -> r
390+ Beforepaste -> r
391+ Paste -> r
392+-}
393+ Reset -> r
394+ Search -> r
395+-- Selectstart -> r
396+ Touchstart -> r
397+ Touchmove -> r
398+ Touchend -> r
399+ Touchcancel -> r
400+
195401--TODO: We should allow the user to specify an ordering instead of relying on the ordering of the Map
196402-- | Create a dropdown box
197403-- The first argument gives the initial value of the dropdown; if it is not present in the map of options provided, it will be added with an empty string as its text
198−dropdown :: forall k t m. (MonadWidget t m, Ord k) => k -> Dynamic t (Map k String) -> DropdownConfig t k -> m (Dropdown t k)
404+dropdown :: forall k t m. (DomBuilder t m, MonadFix m, MonadHold t m, PerformEvent t m, MonadIO (Performable m), PostBuild t m, DomBuilderSpace m ~ GhcjsDomSpace, Ord k) => k -> Dynamic t (Map k Text) -> DropdownConfig t k -> m (Dropdown t k)
199405dropdown k0 options (DropdownConfig setK attrs) = do
200406 optionsWithAddedKeys <- combineDyn Map.union options <=< foldDyn Map.union (k0 =: "") $ fmap (=: "") setK
201407 defaultKey <- holdDyn k0 setK
202408 (indexedOptions, ixKeys) <- splitDyn <=< forDyn optionsWithAddedKeys $ \os ->
203409 let xs = map (\(ix, (k, v)) -> ((ix, k), ((ix, k), v))) $ zip [0::Int ..] $ Map.toList os
204410 ixVals = Map.fromList $ map snd xs
205− ixKeys = B.fromList $ map fst xs
411+ ixKeys = Bimap.fromList $ map fst xs
206412 in (ixVals, ixKeys)
207− (eRaw, _) <- elDynAttr' "select" attrs $ listWithKey indexedOptions $ \(ix, k) v -> do
208− optionAttrs <- mapDyn (\dk -> "value" =: show ix <> if dk == k then "selected" =: "selected" else mempty) defaultKey
413+ modifyAttrs <- dynamicAttributesToModifyAttributes attrs
414+ let cfg = (def :: ElementConfig EventResult t m)
415+ { _elementConfig_modifyAttributes = modifyAttrs
416+ , _elementConfig_eventFilters = DMap.singleton Change $ EventFilter $ GhcjsDomHandler $ \(GhcjsDomEvent evt) -> do
417+ Just t <- Event.getTarget evt
418+ Just v <- HTMLSelectElement.getValue $ HTMLSelectElement.castToHTMLSelectElement t
419+ return $ (,) mempty $ GhcjsDomHandler $ \_ -> return $ Just $ DropdownViewEventResult v
420+ , _elementConfig_eventHandler = GhcjsDomHandler1 $ \p@(Pair1 en _) -> case en of
421+ Click -> error "impossible"
422+ _ -> do
423+ mr <- unGhcjsDomHandler1 (defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))) p
424+ case mr of
425+ Nothing1 -> return Nothing1
426+ Just1 (EventResult r) -> return $ Just1 $ DropdownViewEventResult $ regularToDropdownViewEventType en r
427+ }
428+ (eRaw, _) <- element "select" cfg $ listWithKey indexedOptions $ \(ix, k) v -> do
429+ optionAttrs <- mapDyn (\dk -> "value" =: T.pack (show ix) <> if dk == k then "selected" =: "selected" else mempty) defaultKey
209430 elDynAttr "option" optionAttrs $ dynText v
210− let e = castToHTMLSelectElement $ _el_element eRaw
211− performEvent_ $ fmap (Select.setValue e . Just . show) $ attachDynWithMaybe (flip B.lookupR) ixKeys setK
212− eChange <- attachDynWith (\ks s -> join $ B.lookup <$> join (readMaybe <$> s) <*> pure ks) ixKeys <$> (wrapDomEvent e (`on` change) $ Select.getValue e)
431+ let e = castToHTMLSelectElement $ _element_raw eRaw
432+ performEvent_ $ fmap (HTMLSelectElement.setValue e . Just . show) $ attachDynWithMaybe (flip Bimap.lookupR) ixKeys setK
433+ eChange <- return never -- attachDynWith (\ks s -> join $ Bimap.lookup <$> join (readMaybe <$> s) <*> pure ks) ixKeys <$> (wrapDomEvent e (`on` Element.change) $ HTMLSelectElement.getValue e)
213434 let readKey keys mk = fromMaybe k0 $ do
214435 k <- mk
215− guard $ B.memberR k keys
436+ guard $ Bimap.memberR k keys
216437 return k
217438 dValue <- combineDyn readKey ixKeys <=< holdDyn (Just k0) $ leftmost [eChange, fmap Just setK]
218439 return $ Dropdown dValue (attachDynWith readKey ixKeys eChange)
@@ -222,8 +443,8 @@ liftM concat $ mapM makeLenses
222443 , ''TextArea
223444 , ''TextInputConfig
224445 , ''TextInput
225− , ''FileInputConfig
226− , ''FileInput
446+-- , ''FileInputConfig
447+-- , ''FileInput
227448 , ''DropdownConfig
228449 , ''Dropdown
229450 , ''CheckboxConfig
@@ -231,35 +452,37 @@ liftM concat $ mapM makeLenses
231452 ]
232453
233454instance HasAttributes (TextAreaConfig t) where
234− type Attrs (TextAreaConfig t) = Dynamic t (Map String String)
455+ type Attrs (TextAreaConfig t) = Dynamic t (Map Text Text)
235456 attributes = textAreaConfig_attributes
236457
237458instance HasAttributes (TextInputConfig t) where
238− type Attrs (TextInputConfig t) = Dynamic t (Map String String)
459+ type Attrs (TextInputConfig t) = Dynamic t (Map Text Text)
239460 attributes = textInputConfig_attributes
240461
241462instance HasAttributes (DropdownConfig t k) where
242− type Attrs (DropdownConfig t k) = Dynamic t (Map String String)
463+ type Attrs (DropdownConfig t k) = Dynamic t (Map Text Text)
243464 attributes = dropdownConfig_attributes
244465
245466instance HasAttributes (CheckboxConfig t) where
246− type Attrs (CheckboxConfig t) = Dynamic t (Map String String)
467+ type Attrs (CheckboxConfig t) = Dynamic t (Map Text Text)
247468 attributes = checkboxConfig_attributes
248469
470+{-
249471instance HasAttributes (FileInputConfig t) where
250− type Attrs (FileInputConfig t) = Dynamic t (Map String String)
472+ type Attrs (FileInputConfig t) = Dynamic t (Map Text Text)
251473 attributes = fileInputConfig_attributes
474+-}
252475
253476class HasSetValue a where
254477 type SetValue a :: *
255478 setValue :: Lens' a (SetValue a)
256479
257480instance HasSetValue (TextAreaConfig t) where
258− type SetValue (TextAreaConfig t) = Event t String
481+ type SetValue (TextAreaConfig t) = Event t Text
259482 setValue = textAreaConfig_setValue
260483
261484instance HasSetValue (TextInputConfig t) where
262− type SetValue (TextInputConfig t) = Event t String
485+ type SetValue (TextInputConfig t) = Event t Text
263486 setValue = textInputConfig_setValue
264487
265488instance HasSetValue (DropdownConfig t k) where
@@ -275,16 +498,18 @@ class HasValue a where
275498 value :: a -> Value a
276499
277500instance HasValue (TextArea t) where
278− type Value (TextArea t) = Dynamic t String
501+ type Value (TextArea t) = Dynamic t Text
279502 value = _textArea_value
280503
281504instance HasValue (TextInput t) where
282− type Value (TextInput t) = Dynamic t String
505+ type Value (TextInput t) = Dynamic t Text
283506 value = _textInput_value
284507
508+{-
285509instance HasValue (FileInput t) where
286510 type Value (FileInput t) = Dynamic t [File]
287511 value = _fileInput_value
512+-}
288513
289514instance HasValue (Dropdown t k) where
290515 type Value (Dropdown t k) = Dynamic t k
src/Reflex/Dom/Widget/Lazy.hs+28−25
@@ -1,32 +1,35 @@
1−{-# LANGUAGE RecursiveDo, ScopedTypeVariables #-}
2−
1+{-# LANGUAGE RecursiveDo, ScopedTypeVariables, OverloadedStrings, TypeFamilies, FlexibleContexts #-}
32module Reflex.Dom.Widget.Lazy where
43
54import Reflex
65import Reflex.Dom.Class
6+import Reflex.Dom.Builder.Class
7+import Reflex.Dom.Builder.Immediate
8+import Reflex.Dom.PostBuild.Class
9+import Reflex.Dom.PerformEvent.Class
710import Reflex.Dom.Widget.Basic
811
9−import Control.Monad
12+import Control.Monad.Fix
1013import Control.Monad.IO.Class
1114import Data.Fixed
1215import Data.Monoid
1316import qualified Data.Map as Map
1417import Data.Map (Map)
1518import GHCJS.DOM.Element
16−import Data.Maybe
17−import Data.Int
19+import Data.Text (Text)
20+import qualified Data.Text as T
1821
1922-- |A list view for long lists. Creates a scrollable element and only renders child row elements near the current scroll position.
20−virtualListWithSelection :: forall t m k v. (MonadWidget t m, Ord k)
23+virtualListWithSelection :: forall t m k v. (DomBuilder t m, PostBuild t m, MonadHold t m, PerformEvent t m, MonadIO (Performable m), DomBuilderSpace m ~ GhcjsDomSpace, MonadFix m, Ord k)
2124 => Dynamic t Int -- ^ The height of the visible region in pixels
2225 -> Int -- ^ The height of each row in pixels
2326 -> Dynamic t Int -- ^ The total number of items
2427 -> Int -- ^ The index of the row to scroll to on initialization
2528 -> Event t Int -- ^ An 'Event' containing a row index. Used to scroll to the given index.
26− -> String -- ^ The element tag for the list
27− -> Dynamic t (Map String String) -- ^ The attributes of the list
28− -> String -- ^ The element tag for a row
29− -> Dynamic t (Map String String) -- ^ The attributes of each row
29+ -> Text -- ^ The element tag for the list
30+ -> Dynamic t (Map Text Text) -- ^ The attributes of the list
31+ -> Text -- ^ The element tag for a row
32+ -> Dynamic t (Map Text Text) -- ^ The attributes of each row
3033 -> (k -> Dynamic t (Maybe v) -> Dynamic t Bool -> m ()) -- ^ The row child element builder
3134 -> Dynamic t (Map k v) -- ^ The 'Map' of items
3235 -> (Int -> k) -- ^ Index to Key function, used to determine position of Map elements
@@ -49,17 +52,17 @@ virtualListWithSelection heightPx rowPx maxIndex i0 setI listTag listAttrs rowTa
4952 window <- combineDyn (\h -> findWindow h rowPx) heightPx scrollPosition
5053 itemsInWindow <- combineDyn (\(_,(idx,num)) is -> Map.fromList $ map (\i -> let ix = indexToKey i in (ix, Map.lookup ix is)) [idx .. idx + num]) window items
5154 postBuild <- getPostBuild
52− performEvent_ $ fmap (\i -> liftIO $ setScrollTop (_el_element container) (i * rowPx)) $ leftmost [setI, fmap (const i0) postBuild]
55+ performEvent_ $ fmap (\i -> liftIO $ setScrollTop (_element_raw container) (i * rowPx)) $ leftmost [setI, fmap (const i0) postBuild]
5356 indexAndLength <- mapDyn snd window
5457 return (indexAndLength, sel)
5558 where
5659 toStyleAttr m = "style" =: (Map.foldWithKey (\k v s -> k <> ":" <> v <> ";" <> s) "" m)
5760 toViewport h = toStyleAttr $ "overflow" =: "auto" <> "position" =: "absolute" <>
58− "left" =: "0" <> "right" =: "0" <> "height" =: (show h <> "px")
59− toContainer h = toStyleAttr $ "position" =: "relative" <> "height" =: (show h <> "px")
61+ "left" =: "0" <> "right" =: "0" <> "height" =: (T.pack (show h) <> "px")
62+ toContainer h = toStyleAttr $ "position" =: "relative" <> "height" =: (T.pack (show h) <> "px")
6063 listWrapperStyle t = toStyleAttr $ "position" =: "relative" <>
61− "top" =: (show t <> "px")
62− toHeightStyle h = toStyleAttr ("height" =: (show h <> "px") <> "overflow" =: "hidden")
64+ "top" =: (T.pack (show t) <> "px")
65+ toHeightStyle h = toStyleAttr ("height" =: (T.pack (show h) <> "px") <> "overflow" =: "hidden")
6366 tagWrapper elTag attrs attrsOverride c = do
6467 attrs' <- combineDyn Map.union attrsOverride attrs
6568 elDynAttr' elTag attrs' c
@@ -70,7 +73,7 @@ virtualListWithSelection heightPx rowPx maxIndex i0 setI listTag listAttrs rowTa
7073 preItems = min startingIndex numItems
7174 in (topPx - preItems * sizeIncrement, (startingIndex - preItems, preItems + numItems * 2))
7275
73−virtualList :: forall t m k v a. (MonadWidget t m, Ord k)
76+virtualList :: forall t m k v a. (DomBuilder t m, PostBuild t m, MonadHold t m, PerformEvent t m, MonadIO (Performable m), DomBuilderSpace m ~ GhcjsDomSpace, MonadFix m, Ord k)
7477 => Dynamic t Int -- ^ A 'Dynamic' of the visible region's height in pixels
7578 -> Int -- ^ The fixed height of each row in pixels
7679 -> Dynamic t Int -- ^ A 'Dynamic' of the total number of items
@@ -92,28 +95,28 @@ virtualList heightPx rowPx maxIndex i0 setI keyToIndex items0 itemsUpdate itemBu
9295 , fmap (const (i0 * rowPx)) pb
9396 ]
9497 window <- combineDyn (\h -> findWindow h rowPx) heightPx scrollPosition
95− performEvent_ $ fmap (\i -> liftIO $ setScrollTop (_el_element viewport) (i * rowPx)) $ leftmost [setI, fmap (const i0) pb]
98+ performEvent_ $ fmap (\i -> liftIO $ setScrollTop (_element_raw viewport) (i * rowPx)) $ leftmost [setI, fmap (const i0) pb]
9699 return (nubDyn window, result)
97100 where
98101 toStyleAttr m = "style" =: (Map.foldWithKey (\k v s -> k <> ":" <> v <> ";" <> s) "" m)
99102 mkViewport h = toStyleAttr $ "overflow" =: "auto" <> "position" =: "absolute" <>
100− "left" =: "0" <> "right" =: "0" <> "height" =: (show h <> "px")
101− mkContainer h = toStyleAttr $ "position" =: "relative" <> "height" =: (show h <> "px")
102− mkVirtualHeight h = let h' = h * rowPx
103− in toStyleAttr $ "height" =: (show h <> "px") <>
103+ "left" =: "0" <> "right" =: "0" <> "height" =: (T.pack (show h) <> "px")
104+ mkContainer h = toStyleAttr $ "position" =: "relative" <> "height" =: (T.pack (show h) <> "px")
105+ mkVirtualHeight h = let h' = h * rowPx --TODO: test the use of this
106+ in toStyleAttr $ "height" =: (T.pack (show h') <> "px") <>
104107 "overflow" =: "hidden" <>
105108 "position" =: "relative"
106− mkRow k = toStyleAttr $ "height" =: (show rowPx <> "px") <>
107− "top" =: ((<>"px") $ show $ keyToIndex k * rowPx) <>
109+ mkRow k = toStyleAttr $ "height" =: (T.pack (show rowPx) <> "px") <>
110+ "top" =: ((<>"px") $ T.pack $ show $ keyToIndex k * rowPx) <>
108111 "position" =: "absolute" <>
109112 "width" =: "100%"
110113 findWindow windowSize sizeIncrement startingPosition =
111− let (startingIndex, topOffsetPx) = startingPosition `divMod'` sizeIncrement
114+ let (startingIndex, _) = startingPosition `divMod'` sizeIncrement
112115 numItems = (windowSize + sizeIncrement - 1) `div` sizeIncrement
113116 in (startingIndex, numItems)
114117
115118virtualListBuffered
116− :: (Ord k, MonadWidget t m)
119+ :: (DomBuilder t m, PostBuild t m, MonadHold t m, PerformEvent t m, MonadIO (Performable m), DomBuilderSpace m ~ GhcjsDomSpace, MonadFix m, Ord k)
117120 => Int
118121 -> Dynamic t Int
119122 -> Int
src/Reflex/Dom/Widget/Resize.hs+10−5
@@ -1,13 +1,19 @@
1−{-# LANGUAGE RecursiveDo #-}
1+{-# LANGUAGE RecursiveDo, TypeFamilies, FlexibleContexts, OverloadedStrings #-}
22module Reflex.Dom.Widget.Resize where
33
44import Reflex
5+import Reflex.Dom.PostBuild.Class
6+import Reflex.Dom.PerformEvent.Class
7+import Reflex.Dom.Builder.Immediate
58import Reflex.Dom.Class
9+import Reflex.Dom.Old
610import Reflex.Dom.Widget.Basic
711
812import Control.Monad
913import Control.Monad.IO.Class
1014import Data.Map (Map)
15+import Data.Text (Text)
16+import qualified Data.Text as T
1117import Data.Monoid
1218import GHCJS.DOM.Element hiding (reset)
1319import GHCJS.DOM.EventM (on)
@@ -19,13 +25,13 @@ resizeDetector :: MonadWidget t m => m a -> m (Event t (), a)
1925resizeDetector = resizeDetectorWithStyle ""
2026
2127resizeDetectorWithStyle :: MonadWidget t m
22− => String -- ^ A css style string. Warning: It should not contain the "position" style attribute.
28+ => Text -- ^ A css style string. Warning: It should not contain the "position" style attribute.
2329 -> m a -- ^ The embedded widget
2430 -> m (Event t (), a) -- ^ An 'Event' that fires on resize, and the result of the embedded widget
2531resizeDetectorWithStyle styleString w = resizeDetectorWithAttrs ("style" =: styleString) w
2632
2733resizeDetectorWithAttrs :: MonadWidget t m
28− => Map String String -- ^ A map of attributes. Warning: It should not modify the "position" style attribute.
34+ => Map Text Text -- ^ A map of attributes. Warning: It should not modify the "position" style attribute.
2935 -> m a -- ^ The embedded widget
3036 -> m (Event t (), a) -- ^ An 'Event' that fires on resize, and the result of the embedded widget
3137resizeDetectorWithAttrs attrs w = do
@@ -44,7 +50,7 @@ resizeDetectorWithAttrs attrs w = do
4450 eoh <- getOffsetHeight e
4551 let ecw = eow + 10
4652 ech = eoh + 10
47− setAttribute (_el_element expandChild) "style" (childStyle <> "width: " <> show ecw <> "px;" <> "height: " <> show ech <> "px;")
53+ setAttribute (_el_element expandChild) ("style" :: Text) (childStyle <> "width: " <> T.pack (show ecw) <> "px;" <> "height: " <> T.pack (show ech) <> "px;")
4854 esw <- getScrollWidth e
4955 setScrollLeft e esw
5056 esh <- getScrollHeight e
@@ -69,4 +75,3 @@ resizeDetectorWithAttrs attrs w = do
6975 rec resize <- performEventAsync $ fmap (\d cb -> liftIO $ cb =<< resetIfChanged d) $ tag (current dimensions) $ leftmost [expandScroll, shrinkScroll]
7076 dimensions <- holdDyn (Nothing, Nothing) $ leftmost [ size0, fmapMaybe id resize ]
7177 return (fmap (const ()) $ fmapMaybe id resize, w')
72−
src/Reflex/Dom/Xhr.hs+47−52
@@ -1,4 +1,4 @@
1−{-# LANGUAGE TemplateHaskell, GADTs, DeriveDataTypeable #-}
1+{-# LANGUAGE TemplateHaskell, GADTs, DeriveDataTypeable, FlexibleContexts, OverloadedStrings #-}
22module Reflex.Dom.Xhr
33 ( XMLHttpRequest
44 , XhrRequest(..)
@@ -8,23 +8,33 @@ module Reflex.Dom.Xhr
88 , XhrResponseType(..)
99 , XhrException(..)
1010 , _xhrResponse_body
11− , xhrResponse_body
12− , xhrRequest
11+ , decodeText
12+ , decodeXhrResponse
13+ , getAndDecode
14+ , getMay
1315 , newXMLHttpRequest
1416 , newXMLHttpRequestWithError
15− , performRequestAsync
1617 , performMkRequestAsync
17− , performRequestAsyncWithError
18− , performRequestsAsync
1918 , performMkRequestsAsync
19+ , performRequestAsync
2020 , performRequestAsyncWithError
2121 , performRequestsAsync
2222 , performRequestsAsyncWithError
23− , getAndDecode
2423 , postJson
25− , getMay
26− , decodeText
27− , decodeXhrResponse
24+ , xhrRequest
25+ , xhrRequestConfig_headers
26+ , xhrRequestConfig_password
27+ , xhrRequestConfig_responseType
28+ , xhrRequestConfig_sendData
29+ , xhrRequestConfig_user
30+ , xhrRequest_config
31+ , xhrRequest_method
32+ , xhrRequest_url
33+ , xhrResponse_body
34+ , xhrResponse_response
35+ , xhrResponse_responseText
36+ , xhrResponse_status
37+ , xhrResponse_statusText
2838 , xmlHttpRequestGetReadyState
2939 , xmlHttpRequestGetResponseText
3040 , xmlHttpRequestGetStatus
@@ -35,24 +45,15 @@ module Reflex.Dom.Xhr
3545 , xmlHttpRequestSend
3646 , xmlHttpRequestSetRequestHeader
3747 , xmlHttpRequestSetResponseType
38− , xhrRequest_config
39− , xhrRequest_method
40− , xhrRequest_url
41− , xhrRequestConfig_headers
42− , xhrRequestConfig_password
43− , xhrRequestConfig_responseType
44− , xhrRequestConfig_sendData
45− , xhrRequestConfig_user
46− , xhrResponse_response
47− , xhrResponse_responseText
48− , xhrResponse_status
49− , xhrResponse_statusText
5048 )
5149where
5250
5351import Reflex
5452import Reflex.Dom.Class
5553import Reflex.Dom.Xhr.Foreign
54+import Reflex.Dom.PerformEvent.Class
55+import Reflex.Dom.Xhr.Exception
56+import Reflex.Dom.Xhr.ResponseType
5657
5758import Control.Concurrent
5859import Control.Exception (catch)
@@ -71,24 +72,19 @@ import Data.Maybe
7172import Data.Text (Text)
7273import Data.Text.Encoding
7374import Data.Traversable
74−import Reflex
75−import Reflex.Dom.Class
76−import Reflex.Dom.Xhr.Exception
77−import Reflex.Dom.Xhr.Foreign
78−import Reflex.Dom.Xhr.ResponseType
7975import Data.Typeable
8076
8177data XhrRequest a
82− = XhrRequest { _xhrRequest_method :: String
83− , _xhrRequest_url :: String
78+ = XhrRequest { _xhrRequest_method :: Text
79+ , _xhrRequest_url :: Text
8480 , _xhrRequest_config :: XhrRequestConfig a
8581 }
8682 deriving (Show, Read, Eq, Ord, Typeable)
8783
8884data XhrRequestConfig a
89− = XhrRequestConfig { _xhrRequestConfig_headers :: Map String String
90− , _xhrRequestConfig_user :: Maybe String
91− , _xhrRequestConfig_password :: Maybe String
85+ = XhrRequestConfig { _xhrRequestConfig_headers :: Map Text Text
86+ , _xhrRequestConfig_user :: Maybe Text
87+ , _xhrRequestConfig_password :: Maybe Text
9288 , _xhrRequestConfig_responseType :: Maybe XhrResponseType
9389 , _xhrRequestConfig_sendData :: a
9490 }
@@ -119,7 +115,7 @@ instance a ~ () => Default (XhrRequestConfig a) where
119115 }
120116
121117-- | Construct a request object from method, URL, and config record.
122−xhrRequest :: String -> String -> XhrRequestConfig a -> XhrRequest a
118+xhrRequest :: Text -> Text -> XhrRequestConfig a -> XhrRequest a
123119xhrRequest = XhrRequest
124120
125121-- | Make a new asyncronous XHR request. This does not block (it forks),
@@ -127,19 +123,18 @@ xhrRequest = XhrRequest
127123-- the XHR connection), and will pass an exception ('XhrException') to the
128124-- continuation if the connection cannot be made (or is aborted).
129125newXMLHttpRequestWithError
130− :: (HasWebView m, MonadIO m, HasPostGui t h m, IsXhrPayload a)
126+ :: (HasWebView m, MonadIO m, IsXhrPayload a)
131127 => XhrRequest a
132128 -- ^ The request to make.
133− -> (Either XhrException XhrResponse -> h ())
129+ -> (Either XhrException XhrResponse -> IO ())
134130 -- ^ A continuation to be called once a response comes back, or in
135131 -- case of error.
136132 -> m XMLHttpRequest
137133 -- ^ The XHR request, which could for example be aborted.
138134newXMLHttpRequestWithError req cb = do
139135 wv <- askWebView
140− postGui <- askPostGui
141136 xhr <- liftIO $ xmlHttpRequestNew $ unWebViewSingleton wv
142− void $ liftIO $ forkIO $ flip catch (postGui . cb . Left) $ void $ do
137+ void $ liftIO $ forkIO $ flip catch (cb . Left) $ void $ do
143138 let c = _xhrRequest_config req
144139 rt = _xhrRequestConfig_responseType c
145140 xmlHttpRequestOpen
@@ -161,7 +156,7 @@ newXMLHttpRequestWithError req cb = do
161156 then liftIO $ xmlHttpRequestGetResponseText xhr
162157 else return Nothing
163158 r <- liftIO $ xmlHttpRequestGetResponse xhr
164− _ <- liftIO $ postGui $ cb $ Right $
159+ _ <- liftIO $ cb $ Right $
165160 XhrResponse { _xhrResponse_status = status
166161 , _xhrResponse_statusText = statusText
167162 , _xhrResponse_response = r
@@ -173,7 +168,7 @@ newXMLHttpRequestWithError req cb = do
173168 return ()
174169 return xhr
175170
176−newXMLHttpRequest :: (HasWebView m, MonadIO m, HasPostGui t h m, IsXhrPayload a) => XhrRequest a -> (XhrResponse -> h ()) -> m XMLHttpRequest
171+newXMLHttpRequest :: (HasWebView m, MonadIO m, IsXhrPayload a) => XhrRequest a -> (XhrResponse -> IO ()) -> m XMLHttpRequest
177172newXMLHttpRequest req cb = newXMLHttpRequestWithError req $ mapM_ cb
178173
179174-- | Given Event of requests, issue them when the Event fires.
@@ -182,23 +177,23 @@ newXMLHttpRequest req cb = newXMLHttpRequestWithError req $ mapM_ cb
182177-- The request is processed asynchronously, therefore handling does
183178-- not block or cause a delay while creating the connection.
184179performRequestAsyncWithError
185− :: (MonadWidget t m, IsXhrPayload a)
180+ :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, IsXhrPayload a)
186181 => Event t (XhrRequest a)
187182 -> m (Event t (Either XhrException XhrResponse))
188183performRequestAsyncWithError = performRequestAsync' newXMLHttpRequestWithError . fmap return
189184
190185-- | Given Event of request, issue them when the Event fires. Returns Event of corresponding response.
191−performRequestAsync :: (MonadWidget t m, IsXhrPayload a) => Event t (XhrRequest a) -> m (Event t XhrResponse)
186+performRequestAsync :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, IsXhrPayload a) => Event t (XhrRequest a) -> m (Event t XhrResponse)
192187performRequestAsync = performRequestAsync' newXMLHttpRequest . fmap return
193188
194189-- | Given Event with an action that creates a request, build and issue the request when the Event fires. Returns Event of corresponding response.
195−performMkRequestAsync :: (MonadWidget t m, IsXhrPayload a) => Event t (WidgetHost m (XhrRequest a)) -> m (Event t XhrResponse)
190+performMkRequestAsync :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, IsXhrPayload a) => Event t (Performable m (XhrRequest a)) -> m (Event t XhrResponse)
196191performMkRequestAsync = performRequestAsync' newXMLHttpRequest
197192
198−performRequestAsync' :: (MonadWidget t m, MonadIO h, IsXhrPayload p) => (XhrRequest p -> (a -> h ()) -> WidgetHost m XMLHttpRequest) -> Event t (WidgetHost m (XhrRequest p)) -> m (Event t a)
193+performRequestAsync' :: (MonadIO (Performable m), PerformEvent t m, TriggerEvent t m) => (XhrRequest p -> (a -> IO ()) -> Performable m XMLHttpRequest) -> Event t (Performable m (XhrRequest p)) -> m (Event t a)
199194performRequestAsync' newXhr req = performEventAsync $ ffor req $ \hr cb -> do
200195 r <- hr
201− newXhr r $ liftIO . cb
196+ _ <- newXhr r $ liftIO . cb
202197 return ()
203198
204199-- | Issues a collection of requests when the supplied Event fires.
@@ -211,19 +206,19 @@ performRequestAsync' newXhr req = performEventAsync $ ffor req $ \hr cb -> do
211206-- Order of request execution and completion is not guaranteed, but
212207-- order of creation and the collection result is preserved.
213208performRequestsAsyncWithError
214− :: (Traversable f, MonadWidget t m, IsXhrPayload a)
209+ :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f, IsXhrPayload a)
215210 => Event t (f (XhrRequest a)) -> m (Event t (f (Either XhrException XhrResponse)))
216211performRequestsAsyncWithError = performRequestsAsync' newXMLHttpRequestWithError . fmap return
217212
218213-- | Issues a collection of requests when the supplied Event fires. When ALL requests from a given firing complete, the results are collected and returned via the return Event.
219−performRequestsAsync :: (Traversable f, MonadWidget t m, IsXhrPayload a) => Event t (f (XhrRequest a)) -> m (Event t (f XhrResponse))
214+performRequestsAsync :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f, IsXhrPayload a) => Event t (f (XhrRequest a)) -> m (Event t (f XhrResponse))
220215performRequestsAsync = performRequestsAsync' newXMLHttpRequest . fmap return
221216
222217-- | Builds and issues a collection of requests when the supplied Event fires. When ALL requests from a given firing complete, the results are collected and returned via the return Event.
223−performMkRequestsAsync :: (Traversable f, MonadWidget t m, IsXhrPayload a) => Event t (WidgetHost m (f (XhrRequest a))) -> m (Event t (f XhrResponse))
218+performMkRequestsAsync :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f, IsXhrPayload a) => Event t (Performable m (f (XhrRequest a))) -> m (Event t (f XhrResponse))
224219performMkRequestsAsync = performRequestsAsync' newXMLHttpRequest
225220
226−performRequestsAsync' :: (MonadWidget t m, MonadIO h, Traversable f, IsXhrPayload b) => (XhrRequest b -> (a -> h ()) -> WidgetHost m XMLHttpRequest) -> Event t (WidgetHost m (f (XhrRequest b))) -> m (Event t (f a))
221+performRequestsAsync' :: (MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f) => (XhrRequest b -> (a -> IO ()) -> Performable m XMLHttpRequest) -> Event t (Performable m (f (XhrRequest b))) -> m (Event t (f a))
227222performRequestsAsync' newXhr req = performEventAsync $ ffor req $ \hrs cb -> do
228223 rs <- hrs
229224 resps <- forM rs $ \r -> do
@@ -234,21 +229,21 @@ performRequestsAsync' newXhr req = performEventAsync $ ffor req $ \hrs cb -> do
234229 return ()
235230
236231-- | Simplified interface to "GET" URLs and return decoded results.
237−getAndDecode :: (FromJSON a, MonadWidget t m) => Event t String -> m (Event t (Maybe a))
232+getAndDecode :: (MonadIO m, MonadIO (Performable m), PerformEvent t m, HasWebView (Performable m), TriggerEvent t m, FromJSON a) => Event t Text -> m (Event t (Maybe a))
238233getAndDecode url = do
239234 r <- performRequestAsync $ fmap (\x -> XhrRequest "GET" x def) url
240235 return $ fmap decodeXhrResponse r
241236
242237-- | Create a "POST" request from an URL and thing with a JSON representation
243−postJson :: (ToJSON a) => String -> a -> XhrRequest String
238+postJson :: (ToJSON a) => Text -> a -> XhrRequest Text
244239postJson url a =
245240 XhrRequest "POST" url $ def { _xhrRequestConfig_headers = headerUrlEnc
246241 , _xhrRequestConfig_sendData = body
247242 }
248243 where headerUrlEnc = "Content-type" =: "application/json"
249− body = LT.unpack $ B.toLazyText $ encodeToTextBuilder $ toJSON a
244+ body = LT.toStrict $ B.toLazyText $ encodeToTextBuilder $ toJSON a
250245
251−getMay :: MonadWidget t m => (Event t a -> m (Event t b)) -> Event t (Maybe a) -> m (Event t (Maybe b))
246+getMay :: (Monad m, Reflex t) => (Event t a -> m (Event t b)) -> Event t (Maybe a) -> m (Event t (Maybe b))
252247getMay f e = do
253248 e' <- f (fmapMaybe id e)
254249 return $ leftmost [fmap Just e', fmapMaybe (maybe (Just Nothing) (const Nothing)) e]