on develop help sign in
Merge remote-tracking branch 'origin/placeholder-refactor' into develop

# Conflicts: # .gitignore # reflex-dom.cabal # src-ghc/Reflex/Dom/WebSocket/Foreign.hs # src-ghcjs/Reflex/Dom/WebSocket/Foreign.hs # src/Reflex/Dom/WebSocket.hs

commit7b9f1015735792cdd15250f5e20c62f1b39babd7 authorRyan Trinkle <ryan.trinkle@gmail.com> dateFri 11 Nov 2016, 18:04 (10 years ago) parentf2d7c28d308470
14 files changed+335−204
.gitignore+2 reflex-dom.cabal+32−24 src-ghc/Reflex/Dom/WebSocket/Foreign.hs+7−2 src-ghcjs/Reflex/Dom/WebSocket/Foreign.hs+10−4 src/Foreign/JavaScript/TH.hs+17−5 src/Reflex/Dom/Builder/Class.hs+31−40 src/Reflex/Dom/Builder/Class/Events.hs+1−1 src/Reflex/Dom/Builder/Immediate.hs+108−23 src/Reflex/Dom/Builder/InputDisabled.hs+12−5 src/Reflex/Dom/Builder/Static.hs+49−28 src/Reflex/Dom/Internal.hs+38−13 src/Reflex/Dom/Old.hs+1−19 src/Reflex/Dom/WebSocket.hs+19−14 src/Reflex/Dom/Widget/Basic.hs+8−26
.gitignore+2
@@ -36,3 +36,5 @@ hsenv.log
3636cabal.sandbox.config
3737.stack-work
3838codex.tags
39+*.dump-*
40+*.verbose-core2core
reflex-dom.cabal+32−24
@@ -20,39 +20,45 @@ extra-source-files: src-ghc/Reflex/Dom/Internal/Foreign.hs
2020 src/Reflex/Dom/Xhr/ResponseType.hs
2121 src/Reflex/Dom/Xhr/Exception.hs
2222
23+flag use-reflex-optimizer
24+ description: Use the GHC plugin Reflex.Optimizer on some of the modules in the package. This is still experimental.
25+ default: False
26+ manual: True
27+
2328library
2429 hs-source-dirs: src
2530 build-depends:
31+ aeson >= 0.8 && < 1.1,
2632 base >= 4.7 && < 4.10,
27− template-haskell,
28− stm == 2.4.*,
29− reflex == 0.5.*,
30− dependent-sum == 0.3.*,
31− dependent-map == 0.2.*,
32− semigroups >= 0.16 && < 0.19,
33− mtl >= 2.1 && < 2.3,
34− containers == 0.5.*,
35− these >= 0.4 && < 0.8,
36− ref-tf == 0.4.*,
37− random == 1.1.*,
38− ghcjs-dom >= 0.2.1 && < 0.3,
39− transformers >= 0.3 && < 0.6,
40− lens >= 4.7 && < 5,
41− text == 1.2.*,
33+ bifunctors >= 4.2 && < 6,
34+ bimap >= 0.3 && < 0.4,
4235 blaze-builder,
4336 bytestring == 0.10.*,
37+ containers == 0.5.*,
4438 data-default >= 0.5 && < 0.8,
45− aeson >= 0.8 && < 1.1,
46− time >= 1.4 && < 1.7,
47− exception-transformers == 0.4.*,
48− directory == 1.2.*,
39+ dependent-map == 0.2.*,
40+ dependent-sum == 0.3.*,
4941 dependent-sum-template >= 0.0.0.4 && < 0.1,
50− zenc == 0.1.*,
51− bifunctors >= 4.2 && < 6,
52− bimap >= 0.3 && < 0.4,
53− monad-control >= 1.0.1 && < 1.1,
42+ directory == 1.2.*,
43+ exception-transformers == 0.4.*,
44+ ghcjs-dom >= 0.2.1 && < 0.3,
5445 keycode == 0.2.*,
55− unbounded-delays >= 0.1.0.9 && < 0.2
46+ lens >= 4.7 && < 5,
47+ monad-control >= 1.0.1 && < 1.1,
48+ mtl >= 2.1 && < 2.3,
49+ primitive >= 0.5 && < 0.7,
50+ random == 1.1.*,
51+ ref-tf == 0.4.*,
52+ reflex == 0.5.*,
53+ semigroups >= 0.16 && < 0.19,
54+ stm == 2.4.*,
55+ template-haskell,
56+ text == 1.2.*,
57+ these >= 0.4 && < 0.8,
58+ time >= 1.4 && < 1.7,
59+ transformers >= 0.3 && < 0.6,
60+ unbounded-delays >= 0.1.0.9 && < 0.2,
61+ zenc == 0.1.*
5662
5763 if impl(ghcjs)
5864 hs-source-dirs: src-ghcjs
@@ -101,6 +107,8 @@ library
101107
102108 other-extensions: TemplateHaskell
103109 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2 -ferror-spans
110+ if flag(use-reflex-optimizer)
111+ ghc-options: -fplugin=Reflex.Optimizer
104112
105113test-suite hlint
106114 build-depends: base, hlint == 1.9.*
src-ghc/Reflex/Dom/WebSocket/Foreign.hs+7−2
@@ -77,7 +77,7 @@ closeWebSocket (JSWebSocket ws c) code reason = do
7777newWebSocket
7878 :: WebView
7979 -> Text -- url
80− -> (ByteString -> IO ()) -- onmessage
80+ -> (Either ByteString a -> IO ()) -- onmessage
8181 -> IO () -- onopen
8282 -> IO () -- onerror
8383 -> ((Bool, Word, Text) -> IO ()) -- onclose
@@ -94,7 +94,7 @@ newWebSocket wv url onMessage onOpen onError onClose = withWebViewContext wv $ \
9494 msg' <- fromJSStringMaybe c msg
9595 case msg' of
9696 Nothing -> return ()
97− Just m -> onMessage $ encodeUtf8 m
97+ Just m -> onMessage $ Left $ encodeUtf8 m
9898 jsvaluemakeundefined c
9999 onMessageCb <- jsobjectmakefunctionwithcallback c nullPtr onMessage'
100100 onOpen' <- wrapper $ \_ _ _ _ _ _ -> do
@@ -123,3 +123,8 @@ newWebSocket wv url onMessage onOpen onError onClose = withWebViewContext wv $ \
123123 addCbs <- jsstringcreatewithutf8cstring "this[0]['onmessage'] = this[1]; this[0]['onopen'] = this[2]; this[0]['onerror'] = this[3]; this[0]['onclose'] = this[4];"
124124 _ <- jsevaluatescript c addCbs o nullPtr 1 nullPtr
125125 return $ JSWebSocket ws c
126+
127+onBSMessage :: Either ByteString b -> ByteString
128+onBSMessage = either id (error "onBSMessage: ghc env expects ByteString.")
129+
130+type JSVal = ()
src-ghcjs/Reflex/Dom/WebSocket/Foreign.hs+10−4
@@ -1,7 +1,10 @@
11{-# LANGUAGE ForeignFunctionInterface #-}
22{-# LANGUAGE JavaScriptFFI #-}
33
4−module Reflex.Dom.WebSocket.Foreign where
4+module Reflex.Dom.WebSocket.Foreign
5+ ( module Reflex.Dom.WebSocket.Foreign
6+ , JSVal
7+ ) where
58
69import Prelude hiding (all, concat, concatMap, div, mapM, mapM_, sequence, span)
710
@@ -54,7 +57,7 @@ closeEvent = unsafeEventName (toJSString "close")
5457newWebSocket
5558 :: a
5659 -> Text -- url
57− -> (ByteString -> IO ()) -- onmessage
60+ -> (Either ByteString JSVal -> IO ()) -- onmessage
5861 -> IO () -- onopen
5962 -> IO () -- onerror
6063 -> ((Bool, Word, Text) -> IO ()) -- onclose
@@ -74,10 +77,13 @@ newWebSocket _ url onMessage onOpen onError onClose = do
7477 e <- ask
7578 d <- getData e
7679 liftIO $ case jsTypeOf d of
77− String -> onMessage $ encodeUtf8 $ pFromJSVal d
80+ String -> onMessage $ Right d
7881 _ -> do
7982 ab <- unsafeFreeze $ pFromJSVal d
80− onMessage $ toByteString 0 Nothing $ createFromArrayBuffer ab
83+ onMessage $ Left $ toByteString 0 Nothing $ createFromArrayBuffer ab
8184 return $ JSWebSocket ws
8285
8386foreign import javascript safe "new DataView($3,$1,$2)" js_dataView :: Int -> Int -> JSVal -> JSVal
87+
88+onBSMessage :: Either ByteString JSVal -> ByteString
89+onBSMessage = either id (encodeUtf8 . pFromJSVal)
src/Foreign/JavaScript/TH.hs+17−5
@@ -21,7 +21,6 @@ module Foreign.JavaScript.TH ( module Foreign.JavaScript.TH
2121 ) where
2222
2323import Reflex.Class
24−import Reflex.Deletable.Class
2524import Reflex.DynamicWriter
2625import Reflex.PerformEvent.Base
2726import Reflex.PerformEvent.Class
@@ -69,6 +68,7 @@ import Control.Monad
6968import Control.Monad.Exception
7069import Control.Monad.Fix
7170import Control.Monad.IO.Class
71+import Control.Monad.Primitive
7272import Control.Monad.Reader
7373import Control.Monad.Ref
7474import Control.Monad.State
@@ -109,8 +109,20 @@ instance HasWebView m => HasWebView (DynamicWriterT t w m) where
109109 type WebViewPhantom (DynamicWriterT t w m) = WebViewPhantom m
110110 askWebView = lift askWebView
111111
112+instance HasWebView m => HasWebView (RequestT t request response m) where
113+ type WebViewPhantom (RequestT t request response m) = WebViewPhantom m
114+ askWebView = lift askWebView
115+
112116newtype WithWebView x m a = WithWebView { unWithWebView :: ReaderT (WebViewSingleton x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadTrans, MonadException, MonadAsyncException)
113117
118+instance PrimMonad m => PrimMonad (WithWebView x m) where
119+ type PrimState (WithWebView x m) = PrimState m
120+ primitive = lift . primitive
121+
122+instance MonadAdjust t m => MonadAdjust t (WithWebView x m) where
123+ runWithReplace a0 a' = WithWebView $ runWithReplace (coerce a0) (coerceEvent a')
124+ sequenceDMapWithAdjust dm0 dm' = WithWebView $ sequenceDMapWithAdjust (coerce dm0) (coerceEvent dm')
125+
114126instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView x m) where
115127 {-# INLINABLE newEventWithTrigger #-}
116128 newEventWithTrigger = lift . newEventWithTrigger
@@ -154,10 +166,6 @@ instance PerformEvent t m => PerformEvent t (WithWebView x m) where
154166 {-# INLINABLE performEvent #-}
155167 performEvent e = liftWith $ \run -> performEvent $ fmap run e
156168
157−instance Deletable t m => Deletable t (WithWebView x m) where
158− {-# INLINABLE deletable #-}
159− deletable = liftThrough . deletable
160−
161169runWithWebView :: WithWebView x m a -> WebViewSingleton x -> m a
162170runWithWebView = runReaderT . unWithWebView
163171
@@ -218,6 +226,10 @@ instance HasJS x m => HasJS x (DynamicWriterT t w m) where
218226 type JSM (DynamicWriterT t w m) = JSM m
219227 liftJS = lift . liftJS
220228
229+instance HasJS x m => HasJS x (RequestT t request response m) where
230+ type JSM (RequestT t request response m) = JSM m
231+ liftJS = lift . liftJS
232+
221233-- | A Monad that is capable of executing JavaScript
222234class Monad m => MonadJS x m | m -> x where
223235 runJS :: JSFFI -> [JSRef x] -> m (JSRef x)
src/Reflex/Dom/Builder/Class.hs+31−40
@@ -18,14 +18,13 @@
1818module Reflex.Dom.Builder.Class
1919 ( module Reflex.Dom.Builder.Class
2020 , module Reflex.Dom.Builder.Class.Events
21− , module Reflex.Deletable.Class
2221 ) where
2322
2423import Reflex.Class as Reflex
2524import Reflex.Dom.Builder.Class.Events
26−import Reflex.Deletable.Class
2725import Reflex.DynamicWriter
2826import Reflex.PerformEvent.Class
27+import Reflex.PerformEvent.Base
2928import Reflex.PostBuild.Class
3029
3130import qualified Control.Category
@@ -34,16 +33,13 @@ import Control.Monad.Reader
3433import Control.Monad.State.Strict
3534import Control.Monad.Trans.Control
3635import Data.Default
37−import Data.Foldable
3836import Data.Functor.Misc
39−import Data.List.NonEmpty (NonEmpty, nonEmpty)
4037import Data.Map (Map)
4138import qualified Data.Map as Map
4239import Data.Proxy
4340import Data.Semigroup
4441import Data.String
4542import Data.Text (Text)
46−import Data.Traversable
4743import Data.Type.Coercion
4844import GHC.Exts (Constraint)
4945
@@ -65,7 +61,7 @@ liftElementConfig cfg = cfg
6561
6662-- | @'DomBuilder' t m@ indicates that @m@ is a 'Monad' capable of building
6763-- dynamic DOM in the 'Reflex' timeline @t@
68−class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBuilder t m | m -> t where
64+class (Monad m, Reflex t, DomSpace (DomBuilderSpace m), MonadAdjust t m) => DomBuilder t m | m -> t where
6965 type DomBuilderSpace m :: *
7066 textNode :: TextNodeConfig t -> m (TextNode (DomBuilderSpace m) t)
7167 default textNode :: ( MonadTrans f
@@ -86,9 +82,11 @@ class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBui
8682 => Text -> ElementConfig er t m -> m a -> m (Element er (DomBuilderSpace m) t, a)
8783 element t cfg child = liftWith $ \run -> element t (liftElementConfig cfg) $ run child
8884 {-# INLINABLE element #-}
85+ {-
8986 -- | Create a placeholder in the DOM, with the ability to insert new DOM before it
9087 -- 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
9188 placeholder :: PlaceholderConfig above t m -> m (Placeholder above t)
89+-}
9290 inputElement :: InputElementConfig er t m -> m (InputElement er (DomBuilderSpace m) t)
9391 default inputElement :: ( MonadTransControl f
9492 , m ~ f m'
@@ -403,13 +401,6 @@ instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (ElementConfig
403401
404402instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBuilder t (PostBuildT t m) where
405403 type DomBuilderSpace (PostBuildT t m) = DomBuilderSpace m
406− {-# INLINABLE placeholder #-}
407− placeholder cfg = lift $ do
408− rec childPostBuild <- deletable (_placeholder_deletedSelf p) $ performEvent $ return () <$ _placeholder_insertedAbove p
409− p <- placeholder $ cfg
410− { _placeholderConfig_insertAbove = ffor (_placeholderConfig_insertAbove cfg) $ \a -> runPostBuildT a =<< headE childPostBuild
411− }
412− return p
413404 wrapRawElement e cfg = liftWith $ \run -> wrapRawElement e $ fmap1 run cfg
414405
415406instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
@@ -421,32 +412,6 @@ instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (
421412 (el, (a, newS)) <- lift $ element elementTag cfg' $ runStateT child s
422413 put newS
423414 return (el, a)
424− placeholder cfg = do
425− let cfg' = cfg
426− { _placeholderConfig_insertAbove = runDynamicWriterTInternal <$> _placeholderConfig_insertAbove cfg
427− }
428− let manageChildren :: Event t (NonEmpty (Replaceable t (Dynamic t w))) -- ^ Add nodes on the right; these are in reverse order
429− -> Event t () -- ^ No more nodes will be added after this event fires
430− -> m (Replaceable t (Dynamic t w))
431− manageChildren newChildren additionsCeased = do
432− rec nextId <- hold (0 :: Int) newNextId -- We assume this will never wrap around
433− let numberedNewChildren :: Event t (Int, PatchMap (Map Int (Replaceable t (Dynamic t w))))
434− numberedNewChildren = flip pushAlways newChildren $ \rcs -> do
435− let cs = reverse $ toList rcs
436− myFirstId <- sample nextId
437− let (myNextId, numbered) = mapAccumL (\n v -> (succ n, (n, Just v))) myFirstId cs
438− return (myNextId, PatchMap $ Map.fromList numbered)
439− newNextId = fst <$> numberedNewChildren
440− mconcatIncrementalReplaceableDynMap Map.empty (snd <$> numberedNewChildren) additionsCeased
441− rec children <- lift $ manageChildren childOutputs $ cfg ^. deleteSelf
442− p <- DynamicWriterT $ do
443− modify (children:)
444− lift $ placeholder cfg'
445− let result = fst <$> _placeholder_insertedAbove p
446− childOutputs = fmapMaybe (nonEmpty . snd) $ _placeholder_insertedAbove p
447− return $ p
448− { _placeholder_insertedAbove = result
449− }
450415 inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftElementConfig
451416 textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftElementConfig
452417 selectElement cfg (DynamicWriterT child) = DynamicWriterT $ do
@@ -460,6 +425,31 @@ instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (
460425 { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
461426 }
462427
428+instance (DomBuilder t m, MonadHold t m, MonadFix m) => DomBuilder t (RequestT t request response m) where
429+ type DomBuilderSpace (RequestT t request response m) = DomBuilderSpace m
430+ textNode = liftTextNode
431+ element elementTag cfg (RequestT child) = RequestT $ do
432+ r <- ask
433+ s <- get
434+ let cfg' = liftElementConfig cfg
435+ (el, (a, newS)) <- lift $ lift $ element elementTag cfg' $ runReaderT (runStateT child s) r
436+ put newS
437+ return (el, a)
438+ inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftElementConfig
439+ textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftElementConfig
440+ selectElement cfg (RequestT child) = RequestT $ do
441+ r <- ask
442+ s <- get
443+ let cfg' = cfg & selectElementConfig_elementConfig %~ liftElementConfig
444+ (el, (a, newS)) <- lift $ lift $ selectElement cfg' $ runReaderT (runStateT child s) r
445+ put newS
446+ return (el, a)
447+ placeRawElement = lift . placeRawElement
448+ wrapRawElement e cfg = lift $ wrapRawElement e $ cfg
449+ { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
450+ }
451+
452+
463453-- * Convenience functions
464454
465455--TODO: Move/replace
@@ -521,7 +511,6 @@ instance Reflex t => HasDomEvent t (TextAreaElement EventResult d t) en where
521511
522512instance DomBuilder t m => DomBuilder t (ReaderT r m) where
523513 type DomBuilderSpace (ReaderT r m) = DomBuilderSpace m
524− placeholder = liftPlaceholder
525514
526515type LiftDomBuilder t f m =
527516 ( Reflex t
@@ -555,8 +544,10 @@ liftTextNode = lift . textNode
555544liftElement :: LiftDomBuilder t f m => Text -> ElementConfig er t (f m) -> f m a -> f m (Element er (DomBuilderSpace m) t, a)
556545liftElement elementTag cfg child = liftWithStateless $ \run -> element elementTag (fmap1 run cfg) $ run child
557546
547+{-
558548liftPlaceholder :: LiftDomBuilder t f m => PlaceholderConfig above t (f m) -> f m (Placeholder above t)
559549liftPlaceholder cfg = liftWithStateless $ \run -> placeholder $ fmap1 run cfg
550+-}
560551
561552liftInputElement :: LiftDomBuilder t f m => InputElementConfig er t (f m) -> f m (InputElement er (DomBuilderSpace m) t)
562553liftInputElement cfg = liftWithStateless $ \run -> inputElement $ fmap1 run cfg
src/Reflex/Dom/Builder/Class/Events.hs+1−1
@@ -108,7 +108,7 @@ newtype EventResult en = EventResult { unEventResult :: EventResultType en }
108108
109109type family EventResultType (en :: EventTag) :: * where
110110 EventResultType 'ClickTag = ()
111− EventResultType 'DblclickTag = ()
111+ EventResultType 'DblclickTag = (Int, Int)
112112 EventResultType 'KeypressTag = Int
113113 EventResultType 'KeydownTag = Int
114114 EventResultType 'KeyupTag = Int
src/Reflex/Dom/Builder/Immediate.hs+108−23
@@ -9,6 +9,7 @@
99{-# LANGUAGE OverloadedStrings #-}
1010{-# LANGUAGE PolyKinds #-}
1111{-# LANGUAGE RankNTypes #-}
12+{-# LANGUAGE RecursiveDo #-}
1213{-# LANGUAGE ScopedTypeVariables #-}
1314{-# LANGUAGE TemplateHaskell #-}
1415{-# LANGUAGE TypeFamilies #-}
@@ -27,6 +28,7 @@ import Reflex.Host.Class
2728import Control.Concurrent.Chan
2829import Control.Lens hiding (element)
2930import Control.Monad.Exception
31+import Control.Monad.Primitive
3032import Control.Monad.Reader
3133import Control.Monad.Ref
3234import Control.Monad.Trans.Control
@@ -37,8 +39,12 @@ import qualified Data.Dependent.Map as DMap
3739import Data.Dependent.Sum
3840import Data.Functor.Misc
3941import Data.IORef
42+import Data.Map (Map)
43+import qualified Data.Map as Map
4044import Data.Maybe
4145import Data.Monoid
46+import Data.Some (Some)
47+import qualified Data.Some as Some
4248import Data.Text (Text)
4349import GHCJS.DOM.Document (Document, createDocumentFragment, createElement, createElementNS, createTextNode)
4450import GHCJS.DOM.Element (getScrollTop, removeAttribute, removeAttributeNS, setAttribute, setAttributeNS)
@@ -51,8 +57,9 @@ import qualified GHCJS.DOM.HTMLInputElement as Input
5157import qualified GHCJS.DOM.HTMLSelectElement as Select
5258import qualified GHCJS.DOM.HTMLTextAreaElement as TextArea
5359import GHCJS.DOM.MouseEvent
54−import GHCJS.DOM.Node (appendChild, getOwnerDocument, getParentNode, getPreviousSibling, insertBefore,
60+import GHCJS.DOM.Node (appendChild, getOwnerDocument, getParentNode, getPreviousSibling,
5561 removeChild, setNodeValue, toNode)
62+import qualified GHCJS.DOM.Node as DOM
5663import GHCJS.DOM.Types (FocusEvent, IsElement, IsEvent, IsNode, KeyboardEvent, Node, ToDOMString, TouchEvent,
5764 WheelEvent, castToHTMLInputElement, castToHTMLSelectElement,
5865 castToHTMLTextAreaElement)
@@ -74,6 +81,10 @@ data ImmediateDomBuilderEnv t
7481
7582newtype ImmediateDomBuilderT t m a = ImmediateDomBuilderT { unImmediateDomBuilderT :: ReaderT (ImmediateDomBuilderEnv t) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
7683
84+instance PrimMonad m => PrimMonad (ImmediateDomBuilderT x m) where
85+ type PrimState (ImmediateDomBuilderT x m) = PrimState m
86+ primitive = lift . primitive
87+
7788instance MonadTransControl (ImmediateDomBuilderT t) where
7889 type StT (ImmediateDomBuilderT t) a = StT (ReaderT (ImmediateDomBuilderEnv t)) a
7990 liftWith = defaultLiftWith ImmediateDomBuilderT unImmediateDomBuilderT
@@ -117,19 +128,46 @@ textNodeInternal t = do
117128{-# INLINABLE deleteBetweenInclusive #-}
118129deleteBetweenInclusive :: (MonadIO m, IsNode start, IsNode end) => start -> end -> m ()
119130deleteBetweenInclusive s e = do
131+ 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
132+ case mCurrentParent of
133+ Nothing -> return () --TODO: Is this the right behavior?
134+ Just currentParent -> do
135+ deleteUpToGivenParent currentParent s e
136+ _ <- removeChild currentParent $ Just e
137+ return ()
138+
139+-- | s and e must both be children of the same node and s must precede e
140+deleteBetweenExclusive :: (MonadIO m, IsNode start, IsNode end) => start -> end -> m ()
141+deleteBetweenExclusive s e = do
120142 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
121143 case mCurrentParent of
122144 Nothing -> return () --TODO: Is this the right behavior?
123145 Just currentParent -> do
124146 let go = do
125147 Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
126− _ <- removeChild currentParent $ Just x
127− when (toNode s /= toNode x) go
148+ when (toNode s /= toNode x) $ do
149+ _ <- removeChild currentParent $ Just x
150+ go
128151 go
129− _ <- removeChild currentParent $ Just e
130− return ()
131152
132−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)
153+-- | s and e must both be children of the same node and s must precede e; s and all nodes between s and e will be removed, but e will not be removed
154+{-# INLINABLE deleteUpTo #-}
155+deleteUpTo :: (MonadIO m, IsNode start, IsNode end) => start -> end -> m ()
156+deleteUpTo s e = do
157+ 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
158+ case mCurrentParent of
159+ Nothing -> return () --TODO: Is this the right behavior?
160+ Just currentParent -> deleteUpToGivenParent currentParent s e
161+
162+{-# INLINABLE deleteUpToGivenParent #-}
163+deleteUpToGivenParent :: (MonadIO m, IsNode parent, IsNode start, IsNode end) => parent -> start -> end -> m ()
164+deleteUpToGivenParent currentParent s e = do
165+ fix $ \loop -> do
166+ Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
167+ _ <- removeChild currentParent $ Just x
168+ when (toNode s /= toNode x) loop
169+
170+type SupportsImmediateDomBuilder t m = (Reflex t, MonadIO m, MonadIO (Performable m), MonadHold t m, MonadFix m, PerformEvent t m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO, MonadAdjust t m)
133171
134172newtype EventFilterTriggerRef t er (en :: EventTag) = EventFilterTriggerRef (IORef (Maybe (EventTrigger t (er en))))
135173
@@ -248,6 +286,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
248286 return $ TextNode n
249287 {-# INLINABLE element #-}
250288 element elementTag cfg child = fst <$> makeElement elementTag cfg child
289+ {-
251290 {-# INLINABLE placeholder #-}
252291 placeholder (PlaceholderConfig toInsertAbove delete) = liftThrough (deletable delete) $ do
253292 n <- textNodeInternal ("" :: Text)
@@ -257,6 +296,7 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
257296 mp <- getParentNode n
258297 forM_ mp $ \p -> removeChild p $ Just n
259298 return $ Placeholder insertedAbove deleted
299+-}
260300 {-# INLINABLE inputElement #-}
261301 inputElement cfg = do
262302 ((e, _), domElement) <- makeElement "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
@@ -354,6 +394,57 @@ instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t
354394 placeRawElement = append
355395 wrapRawElement = wrap
356396
397+instance (Reflex t, MonadAdjust t m, MonadIO m, MonadHold t m, PerformEvent t m, MonadIO (Performable m)) => MonadAdjust t (ImmediateDomBuilderT t m) where
398+ runWithReplace a0 a' = do
399+ initialEnv <- ImmediateDomBuilderT ask
400+ before <- textNodeInternal ("" :: Text)
401+ -- We draw 'after' in this roundabout way to avoid using MonadFix
402+ Just after <- createTextNode (_immediateDomBuilderEnv_document initialEnv) ("" :: Text)
403+ let drawInitialChild = do
404+ result <- a0
405+ append after
406+ return result
407+ (result0, result') <- lift $ runWithReplace (runImmediateDomBuilderT drawInitialChild initialEnv) $ ffor a' $ \child -> do
408+ Just df <- createDocumentFragment $ _immediateDomBuilderEnv_document initialEnv
409+ result <- runImmediateDomBuilderT child $ initialEnv
410+ { _immediateDomBuilderEnv_parent = toNode df
411+ }
412+ deleteBetweenExclusive before after
413+ insertBefore df after
414+ return result
415+ return (result0, result')
416+ sequenceDMapWithAdjust (dm0 :: DMap k (ImmediateDomBuilderT t m)) dm' = do
417+ initialEnv <- ImmediateDomBuilderT ask
418+ let drawChildInitial :: ImmediateDomBuilderT t m a -> m (DOM.DocumentFragment, DOM.Text, a)
419+ drawChildInitial child = runImmediateDomBuilderT ((,,) <$> pure (error "sequenceDMapWithAdjust{ImmediateDomBuilderT}: drawChildInitial: DocumentFragment evaluated") <*> textNodeInternal ("" :: Text) <*> child) initialEnv --TODO: Don't return a DocumentFragment at all here; this will require that sequenceDMapWithAdjust accept differently-typed values for the constant and Event arguments.
420+ drawChildUpdate :: ImmediateDomBuilderT t m a -> m (DOM.DocumentFragment, DOM.Text, a)
421+ drawChildUpdate child = do
422+ Just df <- createDocumentFragment $ _immediateDomBuilderEnv_document initialEnv
423+ runImmediateDomBuilderT ((,,) <$> pure df <*> textNodeInternal ("" :: Text) <*> child) $ initialEnv
424+ { _immediateDomBuilderEnv_parent = toNode df
425+ }
426+ updateChildren = ffor dm' $ \(PatchDMap p) -> PatchDMap $
427+ mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mv) -> WrapArg k :=> ComposeMaybe (fmap drawChildUpdate mv)) p
428+ (children0, children') <- lift $ sequenceDMapWithAdjust (mapKeyValuePairsMonotonic (\(k :=> v) -> WrapArg k :=> drawChildInitial v) dm0) (updateChildren :: Event t (PatchDMap (WrapArg ((,,) DOM.DocumentFragment DOM.Text) k) m)) --TODO: Update stuff
429+ let result0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, _, v)) -> k :=> Identity v) children0
430+ placeholders0 = dmapToMap $ mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, ph, _)) -> Const2 (Some.This k) :=> Identity ph) children0
431+ result' = ffor children' $ \(PatchDMap p) -> PatchDMap $
432+ mapKeyValuePairsMonotonic (\(WrapArg k :=> v) -> k :=> fmap (\(_, _, r) -> r) v) p
433+ placeholders' = ffor children' $ \(PatchDMap p) -> PatchMap $
434+ dmapToMap $ mapKeyValuePairsMonotonic (\(WrapArg k :=> v) -> Const2 (Some.This k) :=> Identity (fmap ((\(_, ph, _) -> ph) . runIdentity) (getComposeMaybe v))) p
435+ placeholders :: Behavior t (Map (Some k) DOM.Text) <- current . incrementalToDynamic <$> holdIncremental placeholders0 placeholders'
436+ lastPlaceholder <- textNodeInternal ("" :: Text)
437+ performEvent_ $ flip push children' $ \(PatchDMap p) -> do
438+ phs <- sample placeholders
439+ if DMap.null p then return Nothing else return $ Just $ do
440+ let f :: DSum (WrapArg ((,,) DOM.DocumentFragment DOM.Text) k) (ComposeMaybe Identity) -> Performable m ()
441+ f (WrapArg k :=> ComposeMaybe mv) = do
442+ let nextPlaceholder = maybe lastPlaceholder snd $ Map.lookupGT (Some.This k) phs
443+ forM_ (Map.lookup (Some.This k) phs) $ \thisPlaceholder -> thisPlaceholder `deleteUpTo` nextPlaceholder
444+ forM_ mv $ \(Identity (df, _, _)) -> df `insertBefore` nextPlaceholder
445+ mapM_ f $ DMap.toList p
446+ return (result0, result')
447+
357448mkHasFocus :: (MonadHold t m, Reflex t) => Element er d t -> m (Dynamic t Bool)
358449mkHasFocus e = do
359450 let initialFocus = False --TODO: Actually get the initial focus of the element
@@ -363,7 +454,7 @@ mkHasFocus e = do
363454 ]
364455
365456{-# INLINABLE insertImmediateAbove #-}
366−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)
457+insertImmediateAbove :: (IsNode placeholder, PerformEvent t m, MonadIO (Performable m)) => placeholder -> Event t (ImmediateDomBuilderT t (Performable m) a) -> ImmediateDomBuilderT t m (Event t a)
367458insertImmediateAbove n toInsertAbove = do
368459 events <- askEvents
369460 lift $ performEvent $ ffor toInsertAbove $ \new -> do
@@ -374,21 +465,15 @@ insertImmediateAbove n toInsertAbove = do
374465 , _immediateDomBuilderEnv_document = doc
375466 , _immediateDomBuilderEnv_events = events
376467 }
377− mp <- getParentNode n
378− case mp of
379− Nothing -> trace "ImmediateDomBuilderT: placeholder: Warning: (getParentNode n) returned Nothing" $ return ()
380− 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
468+ df `insertBefore` n
381469 return result
382470
383−instance SupportsImmediateDomBuilder t m => Deletable t (ImmediateDomBuilderT t m) where
384− {-# INLINABLE deletable #-}
385− deletable delete child = liftThrough (deletable delete) $ do
386− top <- textNodeInternal ("" :: Text)
387− result <- child
388− bottom <- textNodeInternal ("" :: Text)
389− lift $ performEvent_ $ ffor delete $ \_ -> do
390− deleteBetweenInclusive top bottom
391− return result
471+insertBefore :: (MonadIO m, IsNode new, IsNode existing) => new -> existing -> m ()
472+insertBefore new existing = do
473+ mp <- getParentNode existing
474+ case mp of
475+ Nothing -> trace "ImmediateDomBuilderT: placeholder: Warning: (getParentNode n) returned Nothing" $ return ()
476+ Just p -> void $ DOM.insertBefore p (Just new) (Just existing) -- 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
392477
393478instance PerformEvent t m => PerformEvent t (ImmediateDomBuilderT t m) where
394479 type Performable (ImmediateDomBuilderT t m) = Performable m
@@ -497,7 +582,7 @@ type family EventType en where
497582defaultDomEventHandler :: IsElement e => e -> EventName en -> EventM e (EventType en) (Maybe (EventResult en))
498583defaultDomEventHandler e evt = fmap (Just . EventResult) $ case evt of
499584 Click -> return ()
500− Dblclick -> return ()
585+ Dblclick -> getMouseEventCoords
501586 Keypress -> getKeyEvent
502587 Scroll -> getScrollTop e
503588 Keydown -> getKeyEvent
@@ -547,7 +632,7 @@ defaultDomEventHandler e evt = fmap (Just . EventResult) $ case evt of
547632defaultDomWindowEventHandler :: DOM.Window -> EventName en -> EventM DOM.Window (EventType en) (Maybe (EventResult en))
548633defaultDomWindowEventHandler w evt = fmap (Just . EventResult) $ case evt of
549634 Click -> return ()
550− Dblclick -> return ()
635+ Dblclick -> getMouseEventCoords
551636 Keypress -> getKeyEvent
552637 Scroll -> Window.getScrollY w
553638 Keydown -> getKeyEvent
@@ -796,7 +881,7 @@ getKeyEvent = do
796881getMouseEventCoords :: EventM e MouseEvent (Int, Int)
797882getMouseEventCoords = do
798883 e <- event
799− bisequence (getX e, getY e)
884+ bisequence (getClientX e, getClientY e)
800885
801886instance MonadSample t m => MonadSample t (ImmediateDomBuilderT t m) where
802887 {-# INLINABLE sample #-}
src/Reflex/Dom/Builder/InputDisabled.hs+12−5
@@ -7,15 +7,15 @@
77{-# LANGUAGE UndecidableInstances #-}
88module Reflex.Dom.Builder.InputDisabled where
99
10−import Control.Lens
1110import Control.Monad.Fix
11+import Control.Monad.Primitive
1212import Control.Monad.Ref
1313import Control.Monad.Trans
1414import Control.Monad.Trans.Control
15+import Data.Coerce
1516import qualified Data.Map as Map
1617import Foreign.JavaScript.TH
1718import Reflex
18−import Reflex.Deletable.Class
1919import Reflex.Dom.Builder.Class
2020import Reflex.Host.Class
2121
@@ -47,15 +47,16 @@ instance PerformEvent t m => PerformEvent t (InputDisabledT m) where
4747 performEvent_ = lift . performEvent_
4848 performEvent = lift . performEvent
4949
50+instance PrimMonad m => PrimMonad (InputDisabledT m) where
51+ type PrimState (InputDisabledT m) = PrimState m
52+ primitive = lift . primitive
53+
5054disableElementConfig :: Reflex t => ElementConfig er t m -> ElementConfig er t m
5155disableElementConfig cfg = cfg
5256 { _elementConfig_initialAttributes = Map.insert "disabled" "disabled" $ _elementConfig_initialAttributes cfg
5357 , _elementConfig_modifyAttributes = Map.delete "disabled" <$> _elementConfig_modifyAttributes cfg
5458 }
5559
56−instance Deletable t m => Deletable t (InputDisabledT m) where
57− deletable d = liftThrough $ deletable d
58−
5960instance PostBuild t m => PostBuild t (InputDisabledT m) where
6061 getPostBuild = lift getPostBuild
6162
@@ -65,10 +66,16 @@ instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (InputDisabl
6566 newEventWithTrigger = lift . newEventWithTrigger
6667 newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
6768
69+instance MonadAdjust t m => MonadAdjust t (InputDisabledT m) where
70+ runWithReplace a0 a' = InputDisabledT $ runWithReplace (coerce a0) (coerceEvent a')
71+ sequenceDMapWithAdjust dm0 dm' = InputDisabledT $ sequenceDMapWithAdjust (coerce dm0) (coerceEvent dm')
72+
6873instance DomBuilder t m => DomBuilder t (InputDisabledT m) where
6974 type DomBuilderSpace (InputDisabledT m) = DomBuilderSpace m
75+ {-
7076 placeholder cfg = lift $ placeholder $ cfg
7177 & placeholderConfig_insertAbove %~ fmap runInputDisabledT
78+ -}
7279 inputElement cfg = lift $ inputElement $ cfg
7380 { _inputElementConfig_elementConfig = liftElementConfig $ disableElementConfig $ _inputElementConfig_elementConfig cfg
7481 }
src/Reflex/Dom/Builder/Static.hs+49−28
@@ -20,18 +20,24 @@ import Blaze.ByteString.Builder.Html.Utf8
2020import Control.Lens hiding (element)
2121import Control.Monad.Exception
2222import Control.Monad.Identity
23+import Control.Monad.Primitive
2324import Control.Monad.Ref
2425import Control.Monad.State.Strict
2526import Control.Monad.Trans.Control
2627import Data.ByteString (ByteString)
27−import Data.ByteString.Builder (toLazyByteString)
28−import qualified Data.ByteString.Lazy as BL
28+import Data.ByteString.Builder (Builder, byteString, toLazyByteString)
29+import qualified Data.ByteString.Lazy as LBS
2930import Data.Default
31+import Data.Dependent.Map (DMap)
32+import qualified Data.Dependent.Map as DMap
3033import Data.Dependent.Sum (DSum (..))
34+import Data.Functor.Constant
35+import Data.Functor.Misc
3136import qualified Data.Map as Map
3237import Data.Monoid
3338import qualified Data.Set as Set
3439import Data.Text.Encoding
40+import Data.Tuple
3541import GHC.Generics
3642import Reflex.Class
3743import Reflex.Dom.Builder.Class
@@ -43,21 +49,24 @@ import Reflex.PerformEvent.Class
4349import Reflex.PostBuild.Class
4450import Reflex.Spider
4551
46−
4752newtype StaticDomBuilderT t m a = StaticDomBuilderT
48− { unStaticDomBuilderT :: StateT [Behavior t ByteString] m a -- Accumulated Html will be in revesed order
53+ { unStaticDomBuilderT :: StateT [Behavior t Builder] m a -- Accumulated Html will be in reversed order
4954 }
5055 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
5156
57+instance PrimMonad m => PrimMonad (StaticDomBuilderT x m) where
58+ type PrimState (StaticDomBuilderT x m) = PrimState m
59+ primitive = lift . primitive
60+
5261instance MonadTransControl (StaticDomBuilderT t) where
53− type StT (StaticDomBuilderT t) a = StT (StateT [Behavior t ByteString]) a
62+ type StT (StaticDomBuilderT t) a = StT (StateT [Behavior t Builder]) a
5463 liftWith = defaultLiftWith StaticDomBuilderT unStaticDomBuilderT
5564 restoreT = defaultRestoreT StaticDomBuilderT
5665
5766instance MonadTrans (StaticDomBuilderT t) where
5867 lift = StaticDomBuilderT . lift
5968
60−runStaticDomBuilderT :: (Monad m, Reflex t) => StaticDomBuilderT t m a -> m (a, Behavior t ByteString)
69+runStaticDomBuilderT :: (Monad m, Reflex t) => StaticDomBuilderT t m a -> m (a, Behavior t Builder)
6170runStaticDomBuilderT (StaticDomBuilderT a) = do
6271 (result, a') <- runStateT a []
6372 return (result, mconcat $ reverse a')
@@ -87,15 +96,6 @@ instance MonadHold t m => MonadHold t (StaticDomBuilderT t m) where
8796 {-# INLINABLE holdIncremental #-}
8897 holdIncremental v0 v' = lift $ holdIncremental v0 v'
8998
90−instance (Reflex t, Monad m, MonadHold t (StateT [Behavior t ByteString] m)) => Deletable t (StaticDomBuilderT t m) where
91− {-# INLINABLE deletable #-}
92− deletable delete (StaticDomBuilderT a) = StaticDomBuilderT $ do
93− (result, a') <- lift $ runStateT a []
94− let html = mconcat $ reverse a'
95− b <- hold html (mempty <$ delete)
96− modify (join b:)
97− return result
98−
9999instance (Monad m, Ref m ~ Ref IO, Reflex t) => TriggerEvent t (StaticDomBuilderT t m) where
100100 {-# INLINABLE newTriggerEvent #-}
101101 newTriggerEvent = return (never, const $ return ())
@@ -113,7 +113,7 @@ instance MonadRef m => MonadRef (StaticDomBuilderT t m) where
113113instance MonadAtomicRef m => MonadAtomicRef (StaticDomBuilderT t m) where
114114 atomicModifyRef r = lift . atomicModifyRef r
115115
116−type SupportsStaticDomBuilder 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)
116+type SupportsStaticDomBuilder t m = (Reflex t, MonadIO m, MonadHold t m, MonadFix m, PerformEvent t m, MonadReflexCreateTrigger t m, MonadRef m, Ref m ~ Ref IO, MonadAdjust t m)
117117
118118data StaticDomSpace
119119
@@ -137,19 +137,45 @@ instance DomSpace StaticDomSpace where
137137 type RawSelectElement StaticDomSpace = ()
138138 addEventSpecFlags _ _ _ _ = StaticEventSpec
139139
140+instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (StaticDomBuilderT t m) where
141+ runWithReplace a0 a' = do
142+ (result0, result') <- lift $ runWithReplace (runStaticDomBuilderT a0) (runStaticDomBuilderT <$> a')
143+ o <- hold (snd result0) $ snd <$> result'
144+ StaticDomBuilderT $ modify $ (:) $ join o
145+ return (fst result0, fst <$> result')
146+ sequenceDMapWithAdjust (dm0 :: DMap k (StaticDomBuilderT t m)) dm' = do
147+ let loweredDm0 = mapKeyValuePairsMonotonic (\(k :=> v) -> WrapArg k :=> fmap swap (runStaticDomBuilderT v)) dm0
148+ loweredDm' = ffor dm' $ \(PatchDMap p) -> PatchDMap $
149+ mapKeyValuePairsMonotonic (\(k :=> ComposeMaybe mv) -> WrapArg k :=> ComposeMaybe (fmap (fmap swap . runStaticDomBuilderT) mv)) p
150+ (children0, children') <- lift $ sequenceDMapWithAdjust loweredDm0 loweredDm'
151+ let result0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (_, v)) -> k :=> Identity v) children0
152+ result' = ffor children' $ \(PatchDMap p) -> PatchDMap $
153+ mapKeyValuePairsMonotonic (\(WrapArg k :=> mv) -> k :=> fmap snd mv) p
154+ outputs0 :: DMap k (Constant (Behavior t Builder))
155+ outputs0 = mapKeyValuePairsMonotonic (\(WrapArg k :=> Identity (o, _)) -> k :=> Constant o) children0
156+ outputs' :: Event t (PatchDMap k (Constant (Behavior t Builder)))
157+ outputs' = ffor children' $ \(PatchDMap p) -> PatchDMap $
158+ mapKeyValuePairsMonotonic (\(WrapArg k :=> ComposeMaybe mv) -> k :=> ComposeMaybe (fmap (Constant . fst . runIdentity) mv)) p
159+ outputs <- holdIncremental outputs0 outputs'
160+ StaticDomBuilderT $ modify $ (:) $ pull $ do
161+ os <- sample $ currentIncremental outputs
162+ fmap mconcat $ forM (DMap.toList os) $ \(_ :=> Constant o) -> do
163+ sample o
164+ return (result0, result')
165+
140166instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) where
141167 type DomBuilderSpace (StaticDomBuilderT t m) = StaticDomSpace
142168 {-# INLINABLE textNode #-}
143169 textNode (TextNodeConfig initialContents setContents) = StaticDomBuilderT $ do
144170 --TODO: Do not escape quotation marks; see https://stackoverflow.com/questions/25612166/what-characters-must-be-escaped-in-html-5
145− let escape = BL.toStrict . toLazyByteString . fromHtmlEscapedText
171+ let escape = fromHtmlEscapedText
146172 modify . (:) <=< hold (escape initialContents) $ fmap escape setContents
147173 return $ TextNode ()
148174 {-# INLINABLE element #-}
149175 element elementTag cfg child = do
150176 -- https://www.w3.org/TR/html-markup/syntax.html#syntax-elements
151177 let voidElements = Set.fromList ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]
152− let toAttr (AttributeName _mns k) v = encodeUtf8 k <> "=\"" <> BL.toStrict (toLazyByteString $ fromHtmlEscapedText v) <> "\""
178+ let toAttr (AttributeName _mns k) v = byteString (encodeUtf8 k) <> byteString "=\"" <> fromHtmlEscapedText v <> byteString "\""
153179 es <- newFanEventWithTrigger $ \_ _ -> return (return ())
154180 StaticDomBuilderT $ do
155181 (result, innerHtml) <- lift $ runStaticDomBuilderT child
@@ -157,18 +183,12 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
157183 let attrs1 = ffor (current attrs0) $ mconcat . fmap (\(k, v) -> " " <> toAttr k v) . Map.toList
158184 let tagBS = encodeUtf8 elementTag
159185 if Set.member elementTag voidElements
160− then modify $ (:) $ mconcat [constant ("<" <> tagBS), attrs1, constant " />"]
186+ then modify $ (:) $ mconcat [constant ("<" <> byteString tagBS), attrs1, constant (byteString " />")]
161187 else do
162− let open = mconcat [constant ("<" <> tagBS <> " "), attrs1, constant ">"]
163− let close = constant $ "</" <> tagBS <> ">"
188+ let open = mconcat [constant ("<" <> byteString tagBS <> " "), attrs1, constant (byteString ">")]
189+ let close = constant $ byteString $ "</" <> tagBS <> ">"
164190 modify $ (:) $ mconcat [open, innerHtml, close]
165191 return (Element es (), result)
166− {-# INLINABLE placeholder #-}
167− placeholder (PlaceholderConfig toInsertAbove _delete) = StaticDomBuilderT $ do
168− result <- lift $ performEvent (fmap runStaticDomBuilderT toInsertAbove)
169− acc <- foldDyn (:) [] (fmap snd result)
170− modify $ (:) $ join $ mconcat . reverse <$> current acc
171− return $ Placeholder (fmap fst result) never
172192 {-# INLINABLE inputElement #-}
173193 inputElement cfg = do
174194 (e, _result) <- element "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
@@ -215,6 +235,7 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
215235--TODO: Make this more abstract --TODO: Put the WithWebView underneath PerformEventT - I think this would perform better
216236type StaticWidget x = PostBuildT Spider (StaticDomBuilderT Spider (PerformEventT Spider (SpiderHost Global)))
217237
238+{-# INLINE renderStatic #-}
218239renderStatic :: StaticWidget x a -> IO (a, ByteString)
219240renderStatic w = do
220241 runSpiderHost $ do
@@ -223,4 +244,4 @@ renderStatic w = do
223244 mPostBuildTrigger <- readRef postBuildTriggerRef
224245 forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
225246 bs' <- sample bs
226− return (res, bs')
247+ return (res, LBS.toStrict $ toLazyByteString bs')
src/Reflex/Dom/Internal.hs+38−13
@@ -15,6 +15,7 @@ module Reflex.Dom.Internal where
1515
1616import Prelude hiding (concat, mapM, mapM_, sequence, sequence_)
1717
18+import qualified Reflex as R
1819import Reflex.Dom.Builder.Immediate
1920import Reflex.Dom.Class
2021import Reflex.Dom.Internal.Foreign
@@ -74,23 +75,49 @@ type Widget x = PostBuildT Spider (ImmediateDomBuilderT Spider (WithWebView x (P
7475attachWidget :: DOM.IsElement e => e -> WebViewSingleton x -> Widget x a -> IO a
7576attachWidget rootElement wv w = fst <$> attachWidget' rootElement wv w
7677
77−{-# INLINABLE attachWidget' #-}
78−attachWidget' :: DOM.IsElement e => e -> WebViewSingleton x -> Widget x a -> IO (a, FireCommand Spider (SpiderHost Global))
79−attachWidget' rootElement wv w = do
78+mainWidgetWithHead' :: (forall x. (a -> Widget x b, b -> Widget x a)) -> IO ()
79+mainWidgetWithHead' widgets = runWebGUI $ \webView -> withWebViewSingleton webView $ \wv -> fmap fst $ attachWidget'' $ \events -> do
80+ let (headWidget, bodyWidget) = widgets
81+ Just doc <- liftIO $ fmap DOM.castToHTMLDocument <$> webViewGetDomDocument webView
82+ Just headElement <- getHead doc
83+ Just bodyElement <- getBody doc
84+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
85+ rec b <- unsafeReplaceElementContentsWithWidget events postBuild headElement wv $ headWidget a
86+ a <- unsafeReplaceElementContentsWithWidget events postBuild bodyElement wv $ bodyWidget b
87+ return ((), postBuildTriggerRef)
88+
89+unsafeReplaceElementContentsWithWidget :: DOM.IsElement e => EventChannel -> R.Event Spider () -> e -> WebViewSingleton x -> Widget x a -> PerformEventT Spider (SpiderHost Global) a
90+unsafeReplaceElementContentsWithWidget events postBuild rootElement wv w = do
8091 Just doc <- getOwnerDocument rootElement
8192 Just df <- createDocumentFragment doc
93+ let builderEnv = ImmediateDomBuilderEnv
94+ { _immediateDomBuilderEnv_document = doc
95+ , _immediateDomBuilderEnv_parent = toNode df
96+ , _immediateDomBuilderEnv_events = events
97+ }
98+ result <- runWithWebView (runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv) wv
99+ setInnerHTML rootElement $ Just ("" :: String)
100+ _ <- appendChild rootElement $ Just df
101+ return result
102+
103+{-# INLINABLE attachWidget' #-}
104+attachWidget' :: DOM.IsElement e => e -> WebViewSingleton x -> Widget x a -> IO (a, FireCommand Spider (SpiderHost Global))
105+attachWidget' rootElement wv w = attachWidget'' $ \events -> do
106+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
107+ result <- unsafeReplaceElementContentsWithWidget events postBuild rootElement wv w
108+ return (result, postBuildTriggerRef)
109+
110+type EventChannel = Chan [DSum (TriggerRef Spider) TriggerInvocation]
111+
112+{-# INLINABLE attachWidget'' #-}
113+attachWidget'' :: (EventChannel -> PerformEventT Spider (SpiderHost Global) (a, IORef (Maybe (EventTrigger Spider ())))) -> IO (a, FireCommand Spider (SpiderHost Global))
114+attachWidget'' w = do
82115 events <- newChan
83116 (result, fc@(FireCommand fire)) <- runSpiderHost $ do
84− (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
85− let builderEnv = ImmediateDomBuilderEnv
86− { _immediateDomBuilderEnv_document = doc
87− , _immediateDomBuilderEnv_parent = toNode df
88− , _immediateDomBuilderEnv_events = events
89− }
90− results@(_, FireCommand fire) <- hostPerformEventT $ runWithWebView (runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv) wv
117+ ((result, postBuildTriggerRef), fc@(FireCommand fire)) <- hostPerformEventT $ w events
91118 mPostBuildTrigger <- readRef postBuildTriggerRef
92119 forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
93− return results
120+ return (result, fc)
94121 void $ forkIO $ forever $ do
95122 ers <- readChan events
96123 _ <- postGUISync $ runSpiderHost $ do
@@ -100,8 +127,6 @@ attachWidget' rootElement wv w = do
100127 _ <- fire (catMaybes mes) $ return ()
101128 liftIO $ forM_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
102129 return ()
103− setInnerHTML rootElement $ Just (""::String)
104− _ <- appendChild rootElement $ Just df
105130 return (result, fc)
106131
107132-- | Run a reflex-dom application inside of an existing DOM element with the given ID
src/Reflex/Dom/Old.hs+1−19
@@ -24,7 +24,6 @@ module Reflex.Dom.Old
2424 , buildElementNS
2525 , buildEmptyElement
2626 , buildEmptyElementNS
27− , deleteBetweenExclusive
2827 , elDynHtml'
2928 , elDynHtmlAttr'
3029 , elStopPropagationNS
@@ -46,7 +45,6 @@ module Reflex.Dom.Old
4645import Control.Arrow (first)
4746import Control.Lens (makeLenses, (%~), (&), (.~), (^.))
4847import Control.Monad
49−import Control.Monad.Exception
5048import Control.Monad.Fix
5149import Control.Monad.IO.Class
5250import Control.Monad.Reader
@@ -64,7 +62,7 @@ import Foreign.JavaScript.TH
6462import qualified GHCJS.DOM.Element as Element
6563import GHCJS.DOM.EventM (EventM)
6664import GHCJS.DOM.NamedNodeMap as NNM
67−import GHCJS.DOM.Node (getFirstChild, getNodeName, getParentNode, getPreviousSibling, removeChild, toNode)
65+import GHCJS.DOM.Node (getFirstChild, getNodeName, removeChild)
6866import GHCJS.DOM.Types (IsElement, IsNode)
6967import qualified GHCJS.DOM.Types as DOM
7068import Reflex.Class
@@ -104,8 +102,6 @@ type MonadWidgetConstraints t m =
104102 , TriggerEvent t m
105103 , HasWebView m
106104 , HasWebView (Performable m)
107− , MonadAsyncException m
108− , MonadAsyncException (Performable m)
109105 , MonadRef m
110106 , Ref m ~ Ref IO
111107 , MonadRef (Performable m)
@@ -162,20 +158,6 @@ addDynamicAttributes attrs cfg = do
162158buildElementCommon :: MonadWidget t m => Text -> m a -> ElementConfig er t m -> m (Element er (DomBuilderSpace m) t, a)
163159buildElementCommon elementTag child cfg = element elementTag cfg child
164160
165−-- | s and e must both be children of the same node and s must precede e
166−deleteBetweenExclusive :: (IsNode start, IsNode end) => start -> end -> IO ()
167−deleteBetweenExclusive s e = do
168− 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
169− case mCurrentParent of
170− Nothing -> return () --TODO: Is this the right behavior?
171− Just currentParent -> do
172− let go = do
173− Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
174− when (toNode s /= toNode x) $ do
175− _ <- removeChild currentParent $ Just x
176− go
177− go
178−
179161onEventName :: IsElement e => EventName en -> e -> EventM e (EventType en) () -> IO (IO ())
180162onEventName = elementOnEventName
181163
src/Reflex/Dom/WebSocket.hs+19−14
@@ -51,17 +51,19 @@ data WebSocketConfig t a
5151instance Reflex t => Default (WebSocketConfig t a) where
5252 def = WebSocketConfig never never True
5353
54−data WebSocket t
55− = WebSocket { _webSocket_recv :: Event t ByteString
56− , _webSocket_open :: Event t ()
57− , _webSocket_error :: Event t () -- eror event does not carry any data and is always
58− -- followed by termination of the connection
59− -- for details see the close event
60− , _webSocket_close :: Event t ( Bool -- wasClean
61− , Word -- code
62− , Text -- reason
63− )
64− }
54+type WebSocket t = RawWebSocket t ByteString
55+
56+data RawWebSocket t a
57+ = RawWebSocket { _webSocket_recv :: Event t a
58+ , _webSocket_open :: Event t ()
59+ , _webSocket_error :: Event t () -- eror event does not carry any data and is always
60+ -- followed by termination of the connection
61+ -- for details see the close event
62+ , _webSocket_close :: Event t ( Bool -- wasClean
63+ , Word -- code
64+ , Text -- reason
65+ )
66+ }
6567
6668-- This can be used to send either binary or text messages for the same websocket connection
6769instance (IsWebSocketMessage a, IsWebSocketMessage b) => IsWebSocketMessage (Either a b) where
@@ -70,7 +72,10 @@ instance (IsWebSocketMessage a, IsWebSocketMessage b) => IsWebSocketMessage (Eit
7072
7173
7274webSocket :: (MonadIO m, MonadIO (Performable m), HasWebView m, PerformEvent t m, TriggerEvent t m, PostBuild t m, IsWebSocketMessage a) => Text -> WebSocketConfig t a -> m (WebSocket t)
73−webSocket url config = do
75+webSocket url config = webSocket' url config onBSMessage
76+
77+webSocket' :: (MonadIO m, MonadIO (Performable m), HasWebView m, PerformEvent t m, TriggerEvent t m, PostBuild t m, IsWebSocketMessage a) => Text -> WebSocketConfig t a -> (Either ByteString JSVal -> b) -> m (RawWebSocket t b)
78+webSocket' url config onRawMessage = do
7479 wv <- fmap unWebViewSingleton askWebView
7580 (eRecv, onMessage) <- newTriggerEvent
7681 currentSocketRef <- liftIO $ newIORef Nothing
@@ -91,7 +96,7 @@ webSocket url config = do
9196 liftIO $ threadDelay 1000000
9297 start
9398 start = do
94− ws <- liftIO $ newWebSocket wv url onMessage onOpen onError onClose
99+ ws <- liftIO $ newWebSocket wv url (onMessage . onRawMessage) onOpen onError onClose
95100 liftIO $ writeIORef currentSocketRef $ Just ws
96101 return ()
97102 performEvent_ . (liftIO start <$) =<< getPostBuild
@@ -116,6 +121,6 @@ webSocket url config = do
116121 `catch`
117122 (\(_ :: SomeException) -> return False))
118123 unless success $ atomically $ unGetTQueue payloadQueue payload
119− return $ WebSocket eRecv eOpen eError eClose
124+ return $ RawWebSocket eRecv eOpen eError eClose
120125
121126makeLensesWith (lensRules & simpleLenses .~ True) ''WebSocketConfig
src/Reflex/Dom/Widget/Basic.hs+8−26
@@ -97,7 +97,6 @@ import Data.Functor.Misc
9797import Data.Map (Map)
9898import qualified Data.Map as Map
9999import Data.Maybe
100−import Data.Semigroup
101100import Data.Set (Set)
102101import qualified Data.Set as Set
103102import Data.Text (Text)
@@ -106,11 +105,8 @@ import Data.These
106105import Data.Traversable
107106import Prelude hiding (mapM, mapM_, sequence, sequence_)
108107
109−widgetHoldInternal :: DomBuilder t m => m a -> Event t (m b) -> m (a, Event t b)
110−widgetHoldInternal child0 child' = do
111− childResult0 <- deletable (void child') child0
112− childResult' <- placeholder $ def & placeholderConfig_insertAbove .~ fmap (deletable (void child')) child'
113− return (childResult0, _placeholder_insertedAbove childResult')
108+widgetHoldInternal :: forall t m a b. DomBuilder t m => m a -> Event t (m b) -> m (a, Event t b)
109+widgetHoldInternal = runWithReplace
114110
115111-- | 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.
116112-- 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.
@@ -131,26 +127,12 @@ partitionMapBySetLT s m0 = Map.fromDistinctAscList $ go (Set.toAscList s) m0
131127
132128newtype ChildResult t k a = ChildResult { unChildResult :: (a, Event t (Map k (Maybe (ChildResult t k a)))) }
133129
134−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))
135−listHoldWithKey initialChildren modifyChildren buildChild = do
136− let deleteChildSelector = fanMap $ fmap void modifyChildren
137− liveChildren :: Dynamic t (Set k) <- foldDyn applyMapKeysSet (Map.keysSet initialChildren) modifyChildren
138− let placeChildSelector = fanMap $ attachWith partitionMapBySetLT (current liveChildren) $ fmap (Map.mapMaybe id) modifyChildren
139− buildAugmentedChild :: k -> v -> m (ChildResult t k a)
140− buildAugmentedChild k v = do
141− let delete = select deleteChildSelector $ Const2 k
142− myCfg = def
143− & insertAbove .~ fmap (imapM buildAugmentedChild) (select placeChildSelector $ Const2 $ Left k)
144− & deleteSelf .~ delete
145− ph <- placeholder myCfg
146− result <- deletable delete $ buildChild k v
147− return $ ChildResult (result, (fmap Just <$> _placeholder_insertedAbove ph) <> (Map.singleton k Nothing <$ _placeholder_deletedSelf ph)) --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
148− rec initialAugmentedResults <- iforM initialChildren buildAugmentedChild
149− augmentedResults <- foldDyn applyMap initialAugmentedResults $ newInsertedBelow <> newInsertedAbove
150− let newInsertedAbove = switch $ mconcat . reverse . fmap (snd . unChildResult) . Map.elems <$> current augmentedResults
151− belowAll <- placeholder $ def & placeholderConfig_insertAbove .~ fmap (imapM buildAugmentedChild) (select placeChildSelector $ Const2 $ Right ())
152− let newInsertedBelow = fmap Just <$> _placeholder_insertedAbove belowAll
153− return $ fmap (fmap (fst . unChildResult)) augmentedResults
130+listHoldWithKey :: forall t m k v a. (Ord k, DomBuilder t m, MonadHold t m) => Map k v -> Event t (Map k (Maybe v)) -> (k -> v -> m a) -> m (Dynamic t (Map k a))
131+listHoldWithKey m0 m' f = do
132+ let dm0 = mapWithFunctorToDMap $ Map.mapWithKey f m0
133+ dm' = fmap (PatchDMap . mapWithFunctorToDMap . Map.mapWithKey (\k v -> ComposeMaybe $ fmap (f k) v)) m'
134+ (a0, a') <- sequenceDMapWithAdjust dm0 dm'
135+ fmap dmapToMap . incrementalToDynamic <$> holdIncremental a0 a' --TODO: Move the dmapToMap to the righthand side so it doesn't get fully redone every time
154136
155137text :: DomBuilder t m => Text -> m ()
156138text t = void $ textNode $ def & textNodeConfig_initialContents .~ t