on develop help sign in
Merge develop into jsaddle
commit402ad561d30fb1daa291e7284d4ee05d825883a2 authorHamish Mackenzie <Hamish.K.Mackenzie@googlemail.com> dateFri 27 Jan 2017, 20:51 (10 years ago) parentc25c20fef8a2f4
9 files changed+175−74
reflex-dom-core/reflex-dom-core.cabal+8 reflex-dom-core/src/Foreign/JavaScript/TH.hs+12−3 reflex-dom-core/src/Reflex/Dom/Builder/Class.hs+27 reflex-dom-core/src/Reflex/Dom/Builder/Class/Events.hs+4−4 reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs+25−8 reflex-dom-core/src/Reflex/Dom/Builder/Static.hs+30−12 reflex-dom-core/src/Reflex/Dom/Main.hs+62−45 reflex-dom-core/src/Reflex/Dom/Widget/Basic.hs+5−1 reflex-dom-core/src/Reflex/Dom/Widget/Resize.hs+2−1
reflex-dom-core/reflex-dom-core.cabal+8
@@ -26,6 +26,11 @@ flag use-reflex-optimizer
2626 default: False
2727 manual: True
2828
29+flag expose-all-unfoldings
30+ description: Build the library with -fexpose-all-unfoldings which can help client code specialize better
31+ default: False
32+ manual: True
33+
2934library
3035 hs-source-dirs: src
3136 build-depends:
@@ -97,6 +102,9 @@ library
97102
98103 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2 -ferror-spans
99104
105+ if flag(expose-all-unfoldings)
106+ ghc-options: -fexpose-all-unfoldings
107+
100108 if flag(use-reflex-optimizer)
101109 ghc-options: -fplugin=Reflex.Optimizer
102110
reflex-dom-core/src/Foreign/JavaScript/TH.hs+12−3
@@ -28,6 +28,7 @@ module Foreign.JavaScript.TH ( module Foreign.JavaScript.TH
2828import Prelude hiding((!!))
2929import Reflex.Class
3030import Reflex.DynamicWriter
31+import Reflex.EventWriter
3132import Reflex.Host.Class
3233import Reflex.PerformEvent.Base
3334import Reflex.PerformEvent.Class
@@ -116,8 +117,12 @@ instance (ReflexHost t, HasJSContext (HostFrame t)) => HasJSContext (PerformEven
116117 type JSContextPhantom (PerformEventT t m) = JSContextPhantom (HostFrame t)
117118 askJSContext = PerformEventT $ lift askJSContext
118119
119−instance HasJSContext m => HasJSContext (DynamicWriterT t w m) where
120− type JSContextPhantom (DynamicWriterT t w m) = JSContextPhantom m
120+instance HasJSContext m => HasJSContext (EventWriterT t w m) where
121+ type JSContextPhantom (EventWriterT t w m) = JSContextPhantom m
122+ askJSContext = lift askJSContext
123+
124+instance HasJSContext m => HasJSContext (RequesterT t request response m) where
125+ type JSContextPhantom (RequesterT t request response m) = JSContextPhantom m
121126 askJSContext = lift askJSContext
122127
123128newtype WithJSContextSingleton x m a = WithJSContextSingleton { unWithJSContextSingleton :: ReaderT (JSContextSingleton x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadTrans, MonadException, MonadAsyncException)
@@ -192,7 +197,11 @@ instance MonadAtomicRef m => MonadAtomicRef (WithJSContextSingleton x m) where
192197withJSContextSingleton :: MonadJSM m => (forall x. JSContextSingleton x -> m r) -> m r
193198withJSContextSingleton f = askJSM >>= f . JSContextSingleton
194199
195−-- | A singleton type for a given JS Context; we use this to statically guarantee that different JSContexts (and thus different javscript contexts) don't get mixed up
200+-- | Warning: `withJSContextSingletonMono` does not provide the same guarantees that `withJSContextSingleton` does.
201+withJSContextSingletonMono :: MonadJSM m => (JSContextSingleton () -> m r) -> m r
202+withJSContextSingletonMono f = askJSM >>= f . JSContextSingleton
203+
204+-- | A singleton type for a given JSContext; we use this to statically guarantee that different JSContexts don't get mixed up
196205newtype JSContextSingleton x = JSContextSingleton { unJSContextSingleton :: JSContextRef }
197206
198207#ifdef __GHCJS__
reflex-dom-core/src/Reflex/Dom/Builder/Class.hs+27
@@ -490,6 +490,10 @@ instance InitialAttributes (TextAreaElementConfig er t m) where
490490 {-# INLINABLE initialAttributes #-}
491491 initialAttributes = textAreaElementConfig_elementConfig . elementConfig_initialAttributes
492492
493+instance InitialAttributes (SelectElementConfig er t m) where
494+ {-# INLINABLE initialAttributes #-}
495+ initialAttributes = selectElementConfig_elementConfig . elementConfig_initialAttributes
496+
493497class ModifyAttributes t a | a -> t where
494498 modifyAttributes :: Lens' a (Event t (Map AttributeName (Maybe Text)))
495499
@@ -505,6 +509,10 @@ instance ModifyAttributes t (TextAreaElementConfig er t m) where
505509 {-# INLINABLE modifyAttributes #-}
506510 modifyAttributes = textAreaElementConfig_elementConfig . elementConfig_modifyAttributes
507511
512+instance ModifyAttributes t (SelectElementConfig er t m) where
513+ {-# INLINABLE modifyAttributes #-}
514+ modifyAttributes = selectElementConfig_elementConfig . elementConfig_modifyAttributes
515+
508516instance ModifyAttributes t (RawElementConfig er t m) where
509517 {-# INLINABLE modifyAttributes #-}
510518 modifyAttributes = rawElementConfig_modifyAttributes
@@ -573,6 +581,25 @@ instance (DomBuilder t m, MonadHold t m, MonadFix m) => DomBuilder t (RequesterT
573581 { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
574582 }
575583
584+instance (DomBuilder t m, MonadHold t m, MonadFix m, Semigroup w, Monoid w) => DomBuilder t (EventWriterT t w m) where
585+ type DomBuilderSpace (EventWriterT t w m) = DomBuilderSpace m
586+ textNode = liftTextNode
587+ element elementTag cfg child = do
588+ let cfg' = liftElementConfig cfg
589+ (el, (a, e)) <- lift $ element elementTag cfg' $ runEventWriterT child
590+ tellEvent e
591+ return (el, a)
592+ inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftElementConfig
593+ textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftElementConfig
594+ selectElement cfg child = do
595+ let cfg' = cfg & selectElementConfig_elementConfig %~ liftElementConfig
596+ (el, (a, e)) <- lift $ selectElement cfg' $ runEventWriterT child
597+ tellEvent e
598+ return (el, a)
599+ placeRawElement = lift . placeRawElement
600+ wrapRawElement e cfg = lift $ wrapRawElement e $ cfg
601+ { _rawElementConfig_eventSpec = _rawElementConfig_eventSpec cfg
602+ }
576603
577604-- * Convenience functions
578605
reflex-dom-core/src/Reflex/Dom/Builder/Class/Events.hs+4−4
@@ -157,10 +157,10 @@ type family EventResultType (en :: EventTag) :: * where
157157 EventResultType 'ResetTag = ()
158158 EventResultType 'SearchTag = ()
159159 EventResultType 'SelectstartTag = ()
160− EventResultType 'TouchstartTag = ()
161− EventResultType 'TouchmoveTag = ()
162− EventResultType 'TouchendTag = ()
163− EventResultType 'TouchcancelTag = ()
160+ EventResultType 'TouchstartTag = [(Int, Int)]
161+ EventResultType 'TouchmoveTag = [(Int, Int)]
162+ EventResultType 'TouchendTag = [(Int, Int)]
163+ EventResultType 'TouchcancelTag = [(Int, Int)]
164164 EventResultType 'WheelTag = ()
165165
166166#ifdef USE_TEMPLATE_HASKELL
reflex-dom-core/src/Reflex/Dom/Builder/Immediate.hs+25−8
@@ -65,6 +65,9 @@ import qualified GHCJS.DOM.HTMLInputElement as Input
6565import qualified GHCJS.DOM.HTMLSelectElement as Select
6666import qualified GHCJS.DOM.HTMLTextAreaElement as TextArea
6767import GHCJS.DOM.MouseEvent
68+import qualified GHCJS.DOM.Touch as Touch
69+import qualified GHCJS.DOM.TouchEvent as TouchEvent
70+import qualified GHCJS.DOM.TouchList as TouchList
6871import GHCJS.DOM.Node (appendChild_, getOwnerDocumentUnchecked, getParentNodeUnchecked,
6972 setNodeValue, toNode)
7073import qualified GHCJS.DOM.Node as DOM (insertBefore_)
@@ -665,10 +668,10 @@ defaultDomEventHandler e evt = fmap (Just . EventResult) $ case evt of
665668 Reset -> return ()
666669 Search -> return ()
667670 Selectstart -> return ()
668− Touchstart -> return ()
669− Touchmove -> return ()
670− Touchend -> return ()
671− Touchcancel -> return ()
671+ Touchstart -> getTouchEventCoords
672+ Touchmove -> getTouchEventCoords
673+ Touchend -> getTouchEventCoords
674+ Touchcancel -> getTouchEventCoords
672675 Mousewheel -> return ()
673676 Wheel -> return ()
674677
@@ -715,10 +718,10 @@ defaultDomWindowEventHandler w evt = fmap (Just . EventResult) $ case evt of
715718 Reset -> return ()
716719 Search -> return ()
717720 Selectstart -> return ()
718− Touchstart -> return ()
719− Touchmove -> return ()
720− Touchend -> return ()
721− Touchcancel -> return ()
721+ Touchstart -> getTouchEventCoords
722+ Touchmove -> getTouchEventCoords
723+ Touchend -> getTouchEventCoords
724+ Touchcancel -> getTouchEventCoords
722725 Mousewheel -> return ()
723726 Wheel -> return ()
724727
@@ -979,6 +982,20 @@ getMouseEventCoords = do
979982 e <- event
980983 bisequence (getClientX e, getClientY e)
981984
985+{-# INLINABLE getTouchEventCoords #-}
986+getTouchEventCoords :: EventM e TouchEvent [(Int, Int)]
987+getTouchEventCoords = do
988+ e <- event
989+ mTouchList <- TouchEvent.getTouches e
990+ case mTouchList of
991+ Just touchList -> do
992+ touchListLength <- TouchList.getLength touchList
993+ mTouchCoords <- forM [0..touchListLength - 1] $ \i -> do
994+ mTouch <- TouchList.item touchList i
995+ return $ fmap (\t -> bisequence (Touch.getClientX t, Touch.getClientY t)) mTouch
996+ sequence $ catMaybes mTouchCoords
997+ _ -> return []
998+
982999instance MonadSample t m => MonadSample t (ImmediateDomBuilderT t m) where
9831000 {-# INLINABLE sample #-}
9841001 sample = lift . sample
reflex-dom-core/src/Reflex/Dom/Builder/Static.hs+30−12
@@ -36,6 +36,7 @@ import Data.Functor.Misc
3636import qualified Data.Map as Map
3737import Data.Monoid
3838import qualified Data.Set as Set
39+import Data.Text (Text)
3940import Data.Text.Encoding
4041import Data.Tuple
4142import GHC.Generics
@@ -50,10 +51,17 @@ import Reflex.PostBuild.Base
5051import Reflex.Spider
5152import Reflex.TriggerEvent.Class
5253
53−data StaticDomBuilderEnv = StaticDomBuilderEnv { _staticDomBuilderEnv_shouldEscape :: Bool }
54+data StaticDomBuilderEnv t = StaticDomBuilderEnv
55+ { _staticDomBuilderEnv_shouldEscape :: Bool
56+ , _staticDomBuilderEnv_selectValue :: Maybe (Behavior t Text)
57+ -- ^ When the parent element is a "select" whose value has been set, this value tells us the current value.
58+ -- We use this to add a "selected" attribute to the appropriate "option" child element.
59+ -- This is not yet a perfect simulation of what the browser does, but it is much closer than doing nothing.
60+ -- TODO: Handle edge cases, e.g. setting to a value for which there is no option, then adding that option dynamically afterwards.
61+ }
5462
5563newtype StaticDomBuilderT t m a = StaticDomBuilderT
56− { unStaticDomBuilderT :: ReaderT StaticDomBuilderEnv (StateT [Behavior t Builder] m) a -- Accumulated Html will be in reversed order
64+ { unStaticDomBuilderT :: ReaderT (StaticDomBuilderEnv t) (StateT [Behavior t Builder] m) a -- Accumulated Html will be in reversed order
5765 }
5866 deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
5967
@@ -64,7 +72,7 @@ instance PrimMonad m => PrimMonad (StaticDomBuilderT x m) where
6472instance MonadTrans (StaticDomBuilderT t) where
6573 lift = StaticDomBuilderT . lift . lift
6674
67−runStaticDomBuilderT :: (Monad m, Reflex t) => StaticDomBuilderT t m a -> StaticDomBuilderEnv -> m (a, Behavior t Builder)
75+runStaticDomBuilderT :: (Monad m, Reflex t) => StaticDomBuilderT t m a -> StaticDomBuilderEnv t -> m (a, Behavior t Builder)
6876runStaticDomBuilderT (StaticDomBuilderT a) e = do
6977 (result, a') <- runStateT (runReaderT a e) []
7078 return (result, mconcat $ reverse a')
@@ -139,9 +147,9 @@ instance (Reflex t, MonadAdjust t m, MonadHold t m) => MonadAdjust t (StaticDomB
139147 runWithReplace a0 a' = do
140148 e <- StaticDomBuilderT ask
141149 (result0, result') <- lift $ runWithReplace (runStaticDomBuilderT a0 e) (flip runStaticDomBuilderT e <$> a')
142− o <- hold (snd result0) $ snd <$> result'
150+ o <- hold (snd result0) $ fmapCheap snd result'
143151 StaticDomBuilderT $ modify $ (:) $ join o
144− return (fst result0, fst <$> result')
152+ return (fst result0, fmapCheap fst result')
145153 sequenceDMapWithAdjust (dm0 :: DMap k (StaticDomBuilderT t m)) dm' = do
146154 e <- StaticDomBuilderT ask
147155 let loweredDm0 = mapKeyValuePairsMonotonic (\(k :=> v) -> WrapArg k :=> fmap swap (runStaticDomBuilderT v e)) dm0
@@ -170,7 +178,7 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
170178 --TODO: Do not escape quotation marks; see https://stackoverflow.com/questions/25612166/what-characters-must-be-escaped-in-html-5
171179 shouldEscape <- asks _staticDomBuilderEnv_shouldEscape
172180 let escape = if shouldEscape then fromHtmlEscapedText else byteString . encodeUtf8
173− modify . (:) <=< hold (escape initialContents) $ fmap escape setContents
181+ modify . (:) <=< hold (escape initialContents) $ fmapCheap escape setContents --Only because it doesn't get optimized when profiling is on
174182 return $ TextNode ()
175183 {-# INLINABLE element #-}
176184 element elementTag cfg child = do
@@ -181,14 +189,21 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
181189 es <- newFanEventWithTrigger $ \_ _ -> return (return ())
182190 StaticDomBuilderT $ do
183191 let shouldEscape = elementTag `Set.notMember` noEscapeElements
184− (result, innerHtml) <- lift $ lift $ runStaticDomBuilderT child $ StaticDomBuilderEnv shouldEscape
192+ (result, innerHtml) <- lift $ lift $ runStaticDomBuilderT child $ StaticDomBuilderEnv shouldEscape Nothing
185193 attrs0 <- foldDyn applyMap (cfg ^. initialAttributes) (cfg ^. modifyAttributes)
186− let attrs1 = ffor (current attrs0) $ mconcat . fmap (\(k, v) -> " " <> toAttr k v) . Map.toList
194+ selectValue <- asks _staticDomBuilderEnv_selectValue
195+ let addSelectedAttr attrs sel = case Map.lookup "value" attrs of
196+ Just v | v == sel -> attrs <> Map.singleton "selected" ""
197+ _ -> Map.delete "selected" attrs
198+ let attrs1 = case (elementTag, selectValue) of
199+ ("option", Just sv) -> pull $ addSelectedAttr <$> sample (current attrs0) <*> sample sv
200+ _ -> current attrs0
201+ let attrs2 = ffor attrs1 $ mconcat . fmap (\(k, v) -> " " <> toAttr k v) . Map.toList
187202 let tagBS = encodeUtf8 elementTag
188203 if Set.member elementTag voidElements
189− then modify $ (:) $ mconcat [constant ("<" <> byteString tagBS), attrs1, constant (byteString " />")]
204+ then modify $ (:) $ mconcat [constant ("<" <> byteString tagBS), attrs2, constant (byteString " />")]
190205 else do
191− let open = mconcat [constant ("<" <> byteString tagBS), attrs1, constant (byteString ">")]
206+ let open = mconcat [constant ("<" <> byteString tagBS), attrs2, constant (byteString ">")]
192207 let close = constant $ byteString $ "</" <> tagBS <> ">"
193208 modify $ (:) $ mconcat [open, innerHtml, close]
194209 return (Element es (), result)
@@ -222,8 +237,11 @@ instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) wh
222237 , _textAreaElement_raw = ()
223238 }
224239 selectElement cfg child = do
225− (e, result) <- element "select" (_selectElementConfig_elementConfig cfg) child
226240 v <- holdDyn (cfg ^. selectElementConfig_initialValue) (cfg ^. selectElementConfig_setValue)
241+ (e, result) <- element "select" (_selectElementConfig_elementConfig cfg) $ do
242+ (a, innerHtml) <- StaticDomBuilderT $ lift $ lift $ runStaticDomBuilderT child $ StaticDomBuilderEnv False $ Just (current v)
243+ StaticDomBuilderT $ lift $ modify $ (:) innerHtml
244+ return a
227245 let wrapped = SelectElement
228246 { _selectElement_value = v
229247 , _selectElement_change = never
@@ -243,7 +261,7 @@ renderStatic :: StaticWidget x a -> IO (a, ByteString)
243261renderStatic w = do
244262 runSpiderHost $ do
245263 (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
246− let env0 = StaticDomBuilderEnv True
264+ let env0 = StaticDomBuilderEnv True Nothing
247265 ((res, bs), FireCommand fire) <- hostPerformEventT $ runStaticDomBuilderT (runPostBuildT w postBuild) env0
248266 mPostBuildTrigger <- readRef postBuildTriggerRef
249267 forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
reflex-dom-core/src/Reflex/Dom/Main.hs+62−45
@@ -17,7 +17,6 @@ module Reflex.Dom.Main where
1717
1818import Prelude hiding (concat, mapM, mapM_, sequence, sequence_)
1919
20−import qualified Reflex as R
2120import Reflex.Dom.Builder.Immediate
2221import Reflex.Dom.Class
2322import Reflex.Host.Class
@@ -90,41 +89,57 @@ instance MonadJSM m => MonadJSM (PostBuildT t m) where
9089
9190{-# INLINABLE attachWidget #-}
9291attachWidget :: DOM.IsElement e => e -> JSContextSingleton x -> Widget x a -> JSM a
93−attachWidget rootElement wv w = liftIO $ fst <$> attachWidget' rootElement wv w
94−
95−mainWidgetWithHead' :: (forall x. (a -> Widget x b, b -> Widget x a)) -> JSM ()
96−mainWidgetWithHead' widgets = withJSContextSingleton $ \jsSing -> do
97− doc <- currentDocumentUnchecked
98− headElement <- getHeadUnchecked doc
99− bodyElement <- getBodyUnchecked doc
100−-- runWebGUI $ \webView -> withWebViewSingleton webView $ \wv ->
101− liftIO $ fmap fst $ attachWidget'' $ \events -> do
102− let (headWidget, bodyWidget) = widgets
103− (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
104− rec b <- unsafeReplaceElementContentsWithWidget events postBuild headElement jsSing $ headWidget a
105− a <- unsafeReplaceElementContentsWithWidget events postBuild bodyElement jsSing $ bodyWidget b
106− return ((), postBuildTriggerRef)
107−
108−unsafeReplaceElementContentsWithWidget :: DOM.IsElement e => EventChannel -> R.Event Spider () -> e -> JSContextSingleton x -> Widget x a -> PerformEventT Spider (SpiderHost Global) a
109−unsafeReplaceElementContentsWithWidget events postBuild rootElement jsSing w = (`runWithJSContextSingleton` jsSing) $ do
110− doc <- getOwnerDocumentUnchecked rootElement
111− df <- createDocumentFragmentUnchecked doc
112− let builderEnv = ImmediateDomBuilderEnv
113− { _immediateDomBuilderEnv_document = doc
114− , _immediateDomBuilderEnv_parent = toNode df
115− , _immediateDomBuilderEnv_events = events
116− }
117− result <- runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv
118− setInnerHTML rootElement $ Just ("" :: String)
119− _ <- appendChildUnchecked rootElement $ Just df
120− return result
92+attachWidget rootElement wv w = fst <$> attachWidget' rootElement wv w
93+
94+-- | Warning: `mainWidgetWithHead'` is provided only as performance tweak. It is expected to disappear in future releases.
95+mainWidgetWithHead' :: (a -> Widget () b, b -> Widget () a) -> JSM ()
96+mainWidgetWithHead' widgets = withJSContextSingletonMono $ \jsSing -> do
97+ doc <- currentDocumentUnchecked
98+ headElement <- getHeadUnchecked doc
99+ headFragment <- createDocumentFragmentUnchecked doc
100+ bodyElement <- getBodyUnchecked doc
101+ bodyFragment <- createDocumentFragmentUnchecked doc
102+ (events, fc) <- liftIO . attachWidget'' $ \events -> do
103+ let (headWidget, bodyWidget) = widgets
104+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
105+ let go :: forall c. Widget () c -> DOM.DocumentFragment -> PerformEventT Spider (SpiderHost Global) c
106+ go w df = do
107+ let builderEnv = ImmediateDomBuilderEnv
108+ { _immediateDomBuilderEnv_document = toDocument doc
109+ , _immediateDomBuilderEnv_parent = toNode df
110+ , _immediateDomBuilderEnv_events = events
111+ }
112+ runWithJSContextSingleton (runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv) jsSing
113+ rec b <- go (headWidget a) headFragment
114+ a <- go (bodyWidget b) bodyFragment
115+ return (events, postBuildTriggerRef)
116+ replaceElementContents headElement headFragment
117+ replaceElementContents bodyElement bodyFragment
118+ liftIO $ processAsyncEvents events fc
119+
120+replaceElementContents :: DOM.IsElement e => e -> DOM.DocumentFragment -> JSM ()
121+replaceElementContents e df = do
122+ setInnerHTML e $ Just ("" :: String)
123+ _ <- appendChildUnchecked e $ Just df
124+ return ()
121125
122126{-# INLINABLE attachWidget' #-}
123−attachWidget' :: DOM.IsElement e => e -> JSContextSingleton x -> Widget x a -> IO (a, FireCommand Spider (SpiderHost Global))
124−attachWidget' rootElement jsSing w = attachWidget'' $ \events -> do
125− (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
126− result <- unsafeReplaceElementContentsWithWidget events postBuild rootElement jsSing w
127− return (result, postBuildTriggerRef)
127+attachWidget' :: DOM.IsElement e => e -> JSContextSingleton x -> Widget x a -> JSM (a, FireCommand Spider (SpiderHost Global))
128+attachWidget' rootElement jsSing w = do
129+ doc <- getOwnerDocumentUnchecked rootElement
130+ df <- createDocumentFragmentUnchecked doc
131+ ((a, events), fc) <- liftIO . attachWidget'' $ \events -> do
132+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
133+ let builderEnv = ImmediateDomBuilderEnv
134+ { _immediateDomBuilderEnv_document = toDocument doc
135+ , _immediateDomBuilderEnv_parent = toNode df
136+ , _immediateDomBuilderEnv_events = events
137+ }
138+ a <- runWithJSContextSingleton (runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv) jsSing
139+ return ((a, events), postBuildTriggerRef)
140+ replaceElementContents rootElement df
141+ liftIO $ processAsyncEvents events fc
142+ return (a, fc)
128143
129144type EventChannel = Chan [DSum (TriggerRef Spider) TriggerInvocation]
130145
@@ -132,21 +147,23 @@ type EventChannel = Chan [DSum (TriggerRef Spider) TriggerInvocation]
132147attachWidget'' :: (EventChannel -> PerformEventT Spider (SpiderHost Global) (a, IORef (Maybe (EventTrigger Spider ())))) -> IO (a, FireCommand Spider (SpiderHost Global))
133148attachWidget'' w = do
134149 events <- newChan
135− (result, fc@(FireCommand fire)) <- runSpiderHost $ do
150+ runSpiderHost $ do
136151 ((result, postBuildTriggerRef), fc@(FireCommand fire)) <- hostPerformEventT $ w events
137152 mPostBuildTrigger <- readRef postBuildTriggerRef
138153 forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
139154 return (result, fc)
140− void $ forkIO $ forever $ do
141− ers <- readChan events
142− _ <- runSpiderHost $ do
143− mes <- liftIO $ forM ers $ \(TriggerRef er :=> TriggerInvocation a _) -> do
144− me <- readIORef er
145− return $ fmap (\e -> e :=> Identity a) me
146− _ <- fire (catMaybes mes) $ return ()
147− liftIO $ forM_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
148− return ()
149− return (result, fc)
155+
156+processAsyncEvents :: EventChannel -> FireCommand Spider (SpiderHost Global) -> IO ()
157+processAsyncEvents events (FireCommand fire) = void $ forkIO $ forever $ do
158+ ers <- readChan events
159+ _ <- runSpiderHost $ do
160+ mes <- liftIO $ forM ers $ \(TriggerRef er :=> TriggerInvocation a _) -> do
161+ me <- readIORef er
162+ return $ fmap (\e -> e :=> Identity a) me
163+ _ <- fire (catMaybes mes) $ return ()
164+ liftIO $ forM_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
165+ return ()
166+
150167
151168-- | Run a reflex-dom application inside of an existing DOM element with the given ID
152169mainWidgetInElementById :: Text -> (forall x. Widget x ()) -> JSM ()
reflex-dom-core/src/Reflex/Dom/Widget/Basic.hs+5−1
@@ -71,6 +71,7 @@ module Reflex.Dom.Widget.Basic
7171 , workflow
7272 , workflowView
7373 , mapWorkflow
74+ , mapWorkflowCheap
7475
7576 -- * Tables and Lists
7677 , tableDynAttr
@@ -159,7 +160,7 @@ button t = do
159160dyn :: (DomBuilder t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
160161dyn child = do
161162 postBuild <- getPostBuild
162− let newChild = leftmost [updated child, tag (current child) postBuild]
163+ let newChild = leftmost [updated child, tagCheap (current child) postBuild]
163164 snd <$> widgetHoldInternal (return ()) newChild
164165
165166-- | Given an initial widget and an Event of widget-creating actions, create a widget that is recreated whenever the Event fires.
@@ -422,6 +423,9 @@ workflowView w0 = do
422423mapWorkflow :: (DomBuilder t m) => (a -> b) -> Workflow t m a -> Workflow t m b
423424mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x)
424425
426+mapWorkflowCheap :: (DomBuilder t m) => (a -> b) -> Workflow t m a -> Workflow t m b
427+mapWorkflowCheap f (Workflow x) = Workflow (fmap (f *** fmapCheap (mapWorkflowCheap f)) x)
428+
425429-- | A widget to display a table with static columns and dynamic rows.
426430tableDynAttr :: forall t m r k v. (Ord k, DomBuilder t m, MonadHold t m, PostBuild t m, MonadFix m)
427431 => Text -- ^ Class applied to <table> element
reflex-dom-core/src/Reflex/Dom/Widget/Resize.hs+2−1
@@ -10,6 +10,7 @@ import Reflex.Class
1010import Reflex.Dom.Builder.Class
1111import Reflex.Dom.Builder.Immediate
1212import Reflex.Dom.Class
13+import Reflex.Dom.Time
1314import Reflex.Dom.Widget.Basic
1415import Reflex.PerformEvent.Class
1516import Reflex.PostBuild.Class
@@ -76,7 +77,7 @@ resizeDetectorWithAttrs attrs w = do
7677 if ds == (Just pow, Just poh)
7778 then return Nothing
7879 else fmap Just reset
79− pb <- getPostBuild
80+ pb <- delay 0 =<< getPostBuild
8081 expandScroll <- wrapDomEvent (_element_raw expand) (`on` scroll) $ return ()
8182 shrinkScroll <- wrapDomEvent (_element_raw shrink) (`on` scroll) $ return ()
8283 size0 <- performEvent $ fmap (const $ liftJSM reset) pb