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

# Conflicts: # reflex-dom.cabal # src/Reflex/Dom/Widget/Basic.hs # src/Reflex/Dom/Xhr.hs

commite326226fdad1f88289ba79768890a9dca6c42e9b authorRyan Trinkle <ryan.trinkle@gmail.com> dateSat 6 Aug 2016, 15:01 (10 years ago) parentad83d0bce5d78f
40 files changed+4209−1916
.gitignore+2 .stylish-haskell.yaml+154 Quickref.md+3 default.nix+35−16 reflex-dom.cabal+34−7 src-ghc/Reflex/Dom/Internal/Foreign.hs+32−27 src-ghc/Reflex/Dom/WebSocket/Foreign.hs+20−7 src-ghc/Reflex/Dom/Xhr/Foreign.hs+48−40 src-ghcjs/Reflex/Dom/Internal/Foreign.hs+8−8 src-ghcjs/Reflex/Dom/WebSocket/Foreign.hs+17−15 src-ghcjs/Reflex/Dom/Xhr/Foreign.hs+39−14 src/Foreign/JavaScript/TH.hs+519 src/Reflex/Dom.hs+16−17 src/Reflex/Dom/Builder/Class.hs+345 src/Reflex/Dom/Builder/Class/Events.hs+164 src/Reflex/Dom/Builder/Immediate.hs+751 src/Reflex/Dom/Builder/Static.hs+174 src/Reflex/Dom/Class.hs+20−445 src/Reflex/Dom/Deletable/Class.hs+17 src/Reflex/Dom/DynamicWriter.hs+242 src/Reflex/Dom/Internal.hs+107−255 src/Reflex/Dom/Location.hs+11−1 src/Reflex/Dom/Old.hs+46 src/Reflex/Dom/PerformEvent/Base.hs+177 src/Reflex/Dom/PerformEvent/Class.hs+46 src/Reflex/Dom/PostBuild/Class.hs+129 src/Reflex/Dom/Storage.hs+31 src/Reflex/Dom/Time.hs+124−13 src/Reflex/Dom/WebSocket.hs+36−26 src/Reflex/Dom/Widget.hs+5−9 src/Reflex/Dom/Widget/Basic.hs+248−734 src/Reflex/Dom/Widget/Input.hs+374−124 src/Reflex/Dom/Widget/Lazy.hs+55−45 src/Reflex/Dom/Widget/Resize.hs+29−9 src/Reflex/Dom/Xhr.hs+114−76 src/Reflex/Dom/Xhr/Exception.hs+1−1 src/Reflex/Dom/Xhr/ResponseType.hs+2−2 test/BrokenHaltSpec.hs+9−10 test/HaltSpec.hs+13−15 test/hlint.hs+12
.gitignore+2
@@ -1,4 +1,5 @@
11dist
2+dist-*
23cabal-dev
34*.o
45*.hi
@@ -8,6 +9,7 @@ cabal-dev
89*.dyn_o
910*.p_hi
1011*.p_o
12+*.prof
1113*.js_dyn_hi
1214*.js_dyn_o
1315*.js_p_hi
.stylish-haskell.yamlnew file+154
@@ -0,0 +1,154 @@
1+# stylish-haskell configuration file
2+# ==================================
3+
4+# The stylish-haskell tool is mainly configured by specifying steps. These steps
5+# are a list, so they have an order, and one specific step may appear more than
6+# once (if needed). Each file is processed by these steps in the given order.
7+steps:
8+ # Convert some ASCII sequences to their Unicode equivalents. This is disabled
9+ # by default.
10+ # - unicode_syntax:
11+ # # In order to make this work, we also need to insert the UnicodeSyntax
12+ # # language pragma. If this flag is set to true, we insert it when it's
13+ # # not already present. You may want to disable it if you configure
14+ # # language extensions using some other method than pragmas. Default:
15+ # # true.
16+ # add_language_pragma: true
17+
18+ # Import cleanup
19+ - imports:
20+ # There are different ways we can align names and lists.
21+ #
22+ # - global: Align the import names and import list throughout the entire
23+ # file.
24+ #
25+ # - file: Like global, but don't add padding when there are no qualified
26+ # imports in the file.
27+ #
28+ # - group: Only align the imports per group (a group is formed by adjacent
29+ # import lines).
30+ #
31+ # - none: Do not perform any alignment.
32+ #
33+ # Default: global.
34+ align: none
35+
36+ # Folowing options affect only import list alignment.
37+ #
38+ # List align has following options:
39+ #
40+ # - after_alias: Import list is aligned with end of import including
41+ # 'as' and 'hiding' keywords.
42+ #
43+ # > import qualified Data.List as List (concat, foldl, foldr, head,
44+ # > init, last, length)
45+ #
46+ # - with_alias: Import list is aligned with start of alias or hiding.
47+ #
48+ # > import qualified Data.List as List (concat, foldl, foldr, head,
49+ # > init, last, length)
50+ #
51+ # - new_line: Import list starts always on new line.
52+ #
53+ # > import qualified Data.List as List
54+ # > (concat, foldl, foldr, head, init, last, length)
55+ #
56+ # Default: after alias
57+ list_align: after_alias
58+
59+ # Long list align style takes effect when import is too long. This is
60+ # determined by 'columns' setting.
61+ #
62+ # - inline: This option will put as much specs on same line as possible.
63+ #
64+ # - new_line: Import list will start on new line.
65+ #
66+ # - new_line_multiline: Import list will start on new line when it's
67+ # short enough to fit to single line. Otherwise it'll be multiline.
68+ #
69+ # - multiline: One line per import list entry.
70+ # Type with contructor list acts like single import.
71+ #
72+ # > import qualified Data.Map as M
73+ # > ( empty
74+ # > , singleton
75+ # > , ...
76+ # > , delete
77+ # > )
78+ #
79+ # Default: inline
80+ long_list_align: inline
81+
82+ # List padding determines indentation of import list on lines after import.
83+ # This option affects 'list_align' and 'long_list_align'.
84+ list_padding: 2
85+
86+ # Separate lists option affects formating of import list for type
87+ # or class. The only difference is single space between type and list
88+ # of constructors, selectors and class functions.
89+ #
90+ # - true: There is single space between Foldable type and list of it's
91+ # functions.
92+ #
93+ # > import Data.Foldable (Foldable (fold, foldl, foldMap))
94+ #
95+ # - false: There is no space between Foldable type and list of it's
96+ # functions.
97+ #
98+ # > import Data.Foldable (Foldable(fold, foldl, foldMap))
99+ #
100+ # Default: true
101+ separate_lists: true
102+
103+ # Language pragmas
104+ - language_pragmas:
105+ # We can generate different styles of language pragma lists.
106+ #
107+ # - vertical: Vertical-spaced language pragmas, one per line.
108+ #
109+ # - compact: A more compact style.
110+ #
111+ # - compact_line: Similar to compact, but wrap each line with
112+ # `{-#LANGUAGE #-}'.
113+ #
114+ # Default: vertical.
115+ style: vertical
116+
117+ # Align affects alignment of closing pragma brackets.
118+ #
119+ # - true: Brackets are aligned in same collumn.
120+ #
121+ # - false: Brackets are not aligned together. There is only one space
122+ # between actual import and closing bracket.
123+ #
124+ # Default: true
125+ align: false
126+
127+ # stylish-haskell can detect redundancy of some language pragmas. If this
128+ # is set to true, it will remove those redundant pragmas. Default: true.
129+ remove_redundant: true
130+
131+ # Align the types in record declarations
132+ # - records: {}
133+
134+ # Replace tabs by spaces. This is disabled by default.
135+ # - tabs:
136+ # # Number of spaces to use for each tab. Default: 8, as specified by the
137+ # # Haskell report.
138+ # spaces: 8
139+
140+ # Remove trailing whitespace
141+ - trailing_whitespace: {}
142+
143+# A common setting is the number of columns (parts of) code will be wrapped
144+# to. Different steps take this into account. Default: 80.
145+columns: 110
146+
147+# Sometimes, language extensions are specified in a cabal file or from the
148+# command line instead of using language pragmas in the file. stylish-haskell
149+# needs to be aware of these, so it can parse the file correctly.
150+#
151+# No language extensions are enabled by default.
152+# language_extensions:
153+ # - TemplateHaskell
154+ # - QuasiQuotes
Quickref.md+3
@@ -113,6 +113,9 @@ Some of these widget builders take a configuration record and return a record co
113113[ ] textInputGetEnter :: TextInput -> Event ()
114114[W] textArea :: TextAreaConfig -> m TextArea
115115
116+-- Range input (slider with float values).
117+[W] rangeInput :: RangeInputConfig -> m RangeInput
118+
116119-- Checkbox. The Bool supplies the initial state.
117120[W] checkbox :: Bool -> CheckboxConfig -> m Checkbox
118121
default.nix+35−16
@@ -1,8 +1,10 @@
11{ mkDerivation, pkgs, dependent-map, ghcjs-dom, lens
22, mtl, ref-tf, reflex, text, these
3−, transformers, data-default, semigroups, aeson
3+, transformers, data-default, semigroups, blaze-builder, aeson
44, ghc, webkitgtk3-javascriptcore, exception-transformers
5−, dependent-sum-template, bifunctors, bimap, raw-strings-qq
5+, webkitgtk24x, dependent-sum-template, bifunctors, bimap
6+, raw-strings-qq, zenc, random, monad-control, keycode, hlint
7+, unbounded-delays
68}:
79
810mkDerivation {
@@ -10,22 +12,39 @@ mkDerivation {
1012 version = "0.3";
1113 src = builtins.filterSource (path: type: baseNameOf path != ".git") ./.;
1214 buildDepends = [
13− reflex
14− dependent-map
15− mtl
16− transformers
17− these
18− lens
19− ghcjs-dom
20− text
21− data-default
22− semigroups
23− ref-tf
2415 aeson
25− exception-transformers
26− dependent-sum-template
2716 bifunctors
2817 bimap
29− ] ++ (if (ghc.pname or null) == "ghcjs" then [ ] else [ webkitgtk3-javascriptcore raw-strings-qq ]);
18+ blaze-builder
19+ data-default
20+ dependent-map
21+ dependent-sum-template
22+ exception-transformers
23+ ghcjs-dom
24+ keycode
25+ lens
26+ monad-control
27+ mtl
28+ random
29+ ref-tf
30+ reflex
31+ semigroups
32+ text
33+ these
34+ transformers
35+ zenc
36+ unbounded-delays
37+ ] ++ (if ghc.isGhcjs or false then [] else [
38+ raw-strings-qq
39+ webkitgtk3-javascriptcore
40+ ]);
41+ testDepends = if ghc.isGhcjs or false then [] else [
42+ hlint
43+ ];
44+ pkgconfigDepends = if ghc.isGhcjs or false then [] else [
45+ raw-strings-qq
46+ webkitgtk24x
47+ webkitgtk3-javascriptcore
48+ ];
3049 license = null;
3150}
reflex-dom.cabal+34−7
@@ -24,6 +24,7 @@ library
2424 hs-source-dirs: src
2525 build-depends:
2626 base >= 4.7 && < 4.10,
27+ template-haskell,
2728 stm == 2.4.*,
2829 reflex == 0.4.*,
2930 dependent-sum == 0.3.*,
@@ -33,24 +34,31 @@ library
3334 containers == 0.5.*,
3435 these >= 0.4 && < 0.8,
3536 ref-tf == 0.4.*,
37+ random == 1.1.*,
3638 ghcjs-dom >= 0.2.1 && < 0.3,
37− transformers >= 0.3.0 && < 0.6,
39+ transformers >= 0.3 && < 0.6,
3840 lens >= 4.7 && < 5,
3941 text == 1.2.*,
42+ blaze-builder,
4043 bytestring == 0.10.*,
41− data-default < 0.7,
44+ data-default >= 0.5 && < 0.8,
4245 aeson >= 0.8 && < 0.12,
43− time >= 1.4.0 && < 1.7,
46+ time >= 1.4 && < 1.7,
4447 exception-transformers == 0.4.*,
4548 directory == 1.2.*,
4649 dependent-sum-template >= 0.0.0.4 && < 0.1,
50+ zenc == 0.1.*,
4751 bifunctors >= 4.2 && < 6,
48− bimap >= 0.3 && < 0.4
52+ bimap >= 0.3 && < 0.4,
53+ monad-control >= 1.0.1 && < 1.1,
54+ keycode == 0.2.*,
55+ unbounded-delays >= 0.1.0.9 && < 0.2
4956
5057 if impl(ghcjs)
5158 hs-source-dirs: src-ghcjs
5259 build-depends:
53− ghcjs-base
60+ ghcjs-base,
61+ hashable == 1.2.*
5462 else
5563 hs-source-dirs: src-ghc
5664 build-depends:
@@ -59,13 +67,28 @@ library
5967 webkitgtk3 == 0.14.*,
6068 webkitgtk3-javascriptcore == 0.13.*,
6169 raw-strings-qq
70+ pkgconfig-depends:
71+ webkitgtk-3.0 >= 1.8
6272 if !os(windows)
6373 build-depends: unix == 2.7.*
6474
6575 exposed-modules:
76+ Foreign.JavaScript.TH
6677 Reflex.Dom
78+ Reflex.Dom.Builder.Class
79+ Reflex.Dom.Builder.Class.Events
80+ Reflex.Dom.Builder.Immediate
81+ Reflex.Dom.Builder.Static
6782 Reflex.Dom.Class
83+ Reflex.Dom.Deletable.Class
84+ Reflex.Dom.DynamicWriter
6885 Reflex.Dom.Internal
86+ Reflex.Dom.Internal.Foreign
87+ Reflex.Dom.Location
88+ Reflex.Dom.PerformEvent.Base
89+ Reflex.Dom.PerformEvent.Class
90+ Reflex.Dom.PostBuild.Class
91+ Reflex.Dom.Old
6992 Reflex.Dom.Time
7093 Reflex.Dom.WebSocket
7194 Reflex.Dom.Widget
@@ -74,9 +97,7 @@ library
7497 Reflex.Dom.Widget.Lazy
7598 Reflex.Dom.Widget.Resize
7699 Reflex.Dom.Xhr
77− Reflex.Dom.Location
78100 other-modules:
79− Reflex.Dom.Internal.Foreign
80101 Reflex.Dom.WebSocket.Foreign
81102 Reflex.Dom.Xhr.Foreign
82103 Reflex.Dom.Xhr.ResponseType
@@ -85,6 +106,12 @@ library
85106 other-extensions: TemplateHaskell
86107 ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2 -ferror-spans
87108
109+test-suite hlint
110+ build-depends: base, hlint == 1.9.*
111+ hs-source-dirs: test
112+ main-is: hlint.hs
113+ type: exitcode-stdio-1.0
114+
88115source-repository head
89116 type: git
90117 location: https://github.com/reflex-frp/reflex-dom
src-ghc/Reflex/Dom/Internal/Foreign.hs+32−27
@@ -1,13 +1,21 @@
1−{-# LANGUAGE CPP, ForeignFunctionInterface, ScopedTypeVariables, LambdaCase #-}
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE ForeignFunctionInterface #-}
3+{-# LANGUAGE LambdaCase #-}
4+{-# LANGUAGE OverloadedStrings #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
26module Reflex.Dom.Internal.Foreign where
37
48import Control.Concurrent
59import Control.Exception (bracket)
610import Control.Lens hiding (set)
711import Control.Monad
8−import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_, get)
12+import Control.Monad.State.Strict hiding (forM, forM_, get, mapM, mapM_, sequence, sequence_)
913import Data.ByteString (ByteString)
10−import Data.List
14+import qualified Data.ByteString as BS
15+import Data.Maybe
16+import Data.Monoid
17+import Data.Text (Text)
18+import qualified Data.Text as T
1119import Foreign.Marshal hiding (void)
1220import Foreign.Ptr
1321import GHCJS.DOM hiding (runWebGUI)
@@ -19,31 +27,30 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
1927import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
2028import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
2129import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
22−import Graphics.UI.Gtk.WebKit.Types hiding (Event, Widget)
30+import Graphics.UI.Gtk.WebKit.Types hiding (Event, Text, Widget)
2331import Graphics.UI.Gtk.WebKit.WebFrame
2432import Graphics.UI.Gtk.WebKit.WebInspector
2533import Graphics.UI.Gtk.WebKit.WebSettings
2634import Graphics.UI.Gtk.WebKit.WebView
2735import System.Directory
2836import System.Glib.FFI hiding (void)
29−import qualified Data.ByteString as BS
3037
3138#ifndef mingw32_HOST_OS
3239import System.Posix.Signals
3340#endif
3441
3542quitWebView :: WebView -> IO ()
36−quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv
43+quitWebView wv = postGUIAsync $ do w <- widgetGetToplevel wv --TODO: Shouldn't this be postGUISync?
3744 widgetDestroy w
3845
3946installQuitHandler :: WebView -> IO ()
4047#ifdef mingw32_HOST_OS
4148installQuitHandler wv = return () -- TODO: Maybe figure something out here for Windows users.
4249#else
43−installQuitHandler wv = installHandler keyboardSignal (Catch (quitWebView wv)) Nothing >> return ()
50+installQuitHandler wv = void $ installHandler keyboardSignal (Catch (quitWebView wv)) Nothing
4451#endif
4552
46−makeDefaultWebView :: String -> (WebView -> IO ()) -> IO ()
53+makeDefaultWebView :: Text -> (WebView -> IO ()) -> IO ()
4754makeDefaultWebView userAgentKey main = do
4855 _ <- initGUI
4956 window <- windowNew
@@ -54,7 +61,7 @@ makeDefaultWebView userAgentKey main = do
5461 webView <- webViewNew
5562 settings <- webViewGetWebSettings webView
5663 userAgent <- settings `get` webSettingsUserAgent
57− settings `set` [ webSettingsUserAgent := userAgent ++ " " ++ userAgentKey
64+ settings `set` [ webSettingsUserAgent := userAgent <> " " <> userAgentKey
5865 , webSettingsEnableUniversalAccessFromFileUris := True
5966 , webSettingsEnableDeveloperExtras := True
6067 ]
@@ -77,14 +84,14 @@ makeDefaultWebView userAgentKey main = do
7784 return inspectorWebView
7885 wf <- webViewGetMainFrame webView
7986 pwd <- getCurrentDirectory
80− webFrameLoadString wf "" Nothing $ "file://" ++ pwd ++ "/"
87+ webFrameLoadString wf "" Nothing $ "file://" <> pwd <> "/"
8188 installQuitHandler webView
8289 mainGUI
8390
8491runWebGUI :: (WebView -> IO ()) -> IO ()
8592runWebGUI = runWebGUI' "GHCJS"
8693
87−runWebGUI' :: String -> (WebView -> IO ()) -> IO ()
94+runWebGUI' :: Text -> (WebView -> IO ()) -> IO ()
8895runWebGUI' userAgentKey main = do
8996 -- Are we in a java script inside some kind of browser
9097 mbWindow <- currentWindow
@@ -93,7 +100,7 @@ runWebGUI' userAgentKey main = do
93100 -- Check if we are running in javascript inside the the native version
94101 Just n <- getNavigator window
95102 agent <- getUserAgent n
96− unless ((" " ++ userAgentKey) `isSuffixOf` agent) $ main (castToWebView window)
103+ unless ((" " <> userAgentKey) `T.isSuffixOf` agent) $ main (castToWebView window)
97104 Nothing -> do
98105 makeDefaultWebView userAgentKey main
99106
@@ -108,32 +115,30 @@ toJSObject ctx args = do
108115 jsobjectsetproperty ctx o prop a 1 nullPtr
109116 return o
110117
111−fromJSStringMaybe :: JSContextRef -> JSValueRef -> IO (Maybe String)
118+fromJSStringMaybe :: JSContextRef -> JSValueRef -> IO (Maybe Text)
112119fromJSStringMaybe c t = do
113120 isNull <- jsvalueisnull c t
114− case isNull of
115− True -> return Nothing
116− False -> do
117− j <- jsvaluetostringcopy c t nullPtr
118− l <- jsstringgetmaximumutf8cstringsize j
119− s <- allocaBytes (fromIntegral l) $ \ps -> do
120− _ <- jsstringgetutf8cstring'_ j ps (fromIntegral l)
121− peekCString ps
122− return $ Just s
121+ if isNull then return Nothing else do
122+ j <- jsvaluetostringcopy c t nullPtr
123+ l <- jsstringgetmaximumutf8cstringsize j
124+ s <- allocaBytes (fromIntegral l) $ \ps -> do
125+ _ <- jsstringgetutf8cstring'_ j ps (fromIntegral l)
126+ peekCString ps
127+ return $ Just $ T.pack s
123128
124−getLocationHost :: WebView -> IO String
129+getLocationHost :: WebView -> IO Text
125130getLocationHost wv = withWebViewContext wv $ \c -> do
126131 script <- jsstringcreatewithutf8cstring "location.host"
127132 lh <- jsevaluatescript c script nullPtr nullPtr 1 nullPtr
128133 lh' <- fromJSStringMaybe c lh
129− return $ maybe "" id lh'
134+ return $ fromMaybe "" lh'
130135
131−getLocationProtocol :: WebView -> IO String
136+getLocationProtocol :: WebView -> IO Text
132137getLocationProtocol wv = withWebViewContext wv $ \c -> do
133138 script <- jsstringcreatewithutf8cstring "location.protocol"
134139 lp <- jsevaluatescript c script nullPtr nullPtr 1 nullPtr
135140 lp' <- fromJSStringMaybe c lp
136− return $ maybe "" id lp'
141+ return $ fromMaybe "" lp'
137142
138143bsToArrayBuffer :: JSContextRef -> ByteString -> IO JSValueRef
139144bsToArrayBuffer c bs = do
@@ -157,7 +162,7 @@ bsFromArrayBuffer c a = do
157162 i' <- jsvaluemakenumber c (fromIntegral i)
158163 args <- toJSObject c [uint8Array, i']
159164 getIntegral =<< jsevaluatescript c getIx args nullPtr 1 nullPtr
160− fmap BS.pack $ forM [0..byteLength-1] arrayLookup
165+ BS.pack <$> forM [0..byteLength-1] arrayLookup
161166
162167withWebViewContext :: WebView -> (JSContextRef -> IO a) -> IO a
163168withWebViewContext wv f = f =<< webFrameGetGlobalContext =<< webViewGetMainFrame wv
src-ghc/Reflex/Dom/WebSocket/Foreign.hs+20−7
@@ -1,12 +1,27 @@
1−{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI, CPP, TemplateHaskell, NoMonomorphismRestriction, EmptyDataDecls, RankNTypes, GADTs, RecursiveDo, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, TypeFamilies, FlexibleContexts, DeriveDataTypeable, GeneralizedNewtypeDeriving, StandaloneDeriving, ConstraintKinds, UndecidableInstances, PolyKinds, AllowAmbiguousTypes #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE JavaScriptFFI #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE NoMonomorphismRestriction #-}
9+{-# LANGUAGE PolyKinds #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE UndecidableInstances #-}
214
315module Reflex.Dom.WebSocket.Foreign where
416
5−import Prelude hiding (div, span, mapM, mapM_, concat, concatMap, all, sequence)
17+import Prelude hiding (all, concat, concatMap, div, mapM, mapM_, sequence, span)
618
719import Control.Exception
820import Control.Monad.State
921import Data.ByteString (ByteString)
22+import qualified Data.ByteString as BS
23+import Data.Text (Text)
24+import qualified Data.Text as T
1025import Data.Text.Encoding
1126import Foreign.Marshal hiding (void)
1227import Foreign.Ptr
@@ -16,8 +31,6 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
1631import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
1732import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
1833import Graphics.UI.Gtk.WebKit.WebView
19−import qualified Data.ByteString as BS
20−import qualified Data.Text as T
2134
2235import Reflex.Dom.Internal.Foreign
2336
@@ -25,9 +38,9 @@ data JSWebSocket = JSWebSocket { wsValue :: JSValueRef
2538 , wsContext :: JSContextRef
2639 }
2740
28−newWebSocket :: WebView -> String -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
41+newWebSocket :: WebView -> Text -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
2942newWebSocket wv url onMessage onOpen onClose = withWebViewContext wv $ \c -> do
30− url' <- jsvaluemakestring c =<< jsstringcreatewithutf8cstring url
43+ url' <- jsvaluemakestring c =<< jsstringcreatewithutf8cstring (T.unpack url)
3144 newWSArgs <- toJSObject c [url']
3245 newWS <- jsstringcreatewithutf8cstring "(function(that) { var ws = new WebSocket(that[0]); ws['binaryType'] = 'arraybuffer'; return ws; })(this)"
3346 ws <- jsevaluatescript c newWS newWSArgs nullPtr 1 nullPtr
@@ -38,7 +51,7 @@ newWebSocket wv url onMessage onOpen onClose = withWebViewContext wv $ \c -> do
3851 msg' <- fromJSStringMaybe c msg
3952 case msg' of
4053 Nothing -> return ()
41− Just m -> onMessage $ encodeUtf8 $ T.pack m
54+ Just m -> onMessage $ encodeUtf8 m
4255 jsvaluemakeundefined c
4356 onMessageCb <- jsobjectmakefunctionwithcallback c nullPtr onMessage'
4457 onOpen' <- wrapper $ \_ _ _ _ _ _ -> do
src-ghc/Reflex/Dom/Xhr/Foreign.hs+48−40
@@ -1,21 +1,25 @@
1−{-# LANGUAGE QuasiQuotes, ForeignFunctionInterface #-}
2−
3−module Reflex.Dom.Xhr.Foreign where
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE OverloadedStrings #-}
3+{-# LANGUAGE QuasiQuotes #-}
4+{-# LANGUAGE TypeSynonymInstances #-}
5+module Reflex.Dom.Xhr.Foreign ( module Reflex.Dom.Xhr.Foreign
6+ , XhrResponseType
7+ ) where
48
9+import Control.Concurrent.MVar
10+import Control.Exception.Base
511import Control.Monad
6−import qualified Data.Text as T
712import Data.Text (Text)
8−import System.Glib.FFI
9−import Graphics.UI.Gtk.WebKit.WebView
13+import qualified Data.Text as T
14+import GHCJS.DOM.File
1015import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
1116import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
1217import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
1318import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
14−import Reflex.Dom.Xhr.ResponseType
15−import Reflex.Dom.Xhr.Exception
16−import Control.Concurrent.MVar
17−import Control.Exception.Base
1819import Graphics.UI.Gtk.WebKit.Types hiding (Text)
20+import Reflex.Dom.Xhr.Exception
21+import Reflex.Dom.Xhr.ResponseType
22+import System.Glib.FFI
1923
2024import Reflex.Dom.Internal.Foreign
2125
@@ -23,20 +27,32 @@ import Text.RawString.QQ
2327
2428data XMLHttpRequest
2529 = XMLHttpRequest { xhrValue :: JSValueRef
26− , xhrContext :: JSContextRef
30+ , xhrContext :: JSContextRef --TODO: Get rid of this
2731 }
2832 deriving (Eq, Ord)
2933
30−stringToJSValue :: JSContextRef -> String -> IO JSValueRef
31−stringToJSValue ctx s = jsvaluemakestring ctx =<< jsstringcreatewithutf8cstring s
34+class IsXhrPayload a where
35+ xmlHttpRequestSend :: XMLHttpRequest -> a -> IO ()
36+
37+instance IsXhrPayload () where
38+ xmlHttpRequestSend xhr _ = xmlHttpRequestSendPayload xhr Nothing
39+
40+instance IsXhrPayload Text where
41+ xmlHttpRequestSend xhr = xmlHttpRequestSendPayload xhr . Just
42+
43+instance IsXhrPayload File where
44+ xmlHttpRequestSend = error "xmlHttpRequestSend{File}: not implemented"
45+
46+stringToJSValue :: JSContextRef -> Text -> IO JSValueRef
47+stringToJSValue ctx s = jsvaluemakestring ctx =<< jsstringcreatewithutf8cstring (T.unpack s)
3248
33−fromResponseType :: XhrResponseType -> String
49+fromResponseType :: XhrResponseType -> Text
3450fromResponseType XhrResponseType_Default = ""
3551fromResponseType XhrResponseType_ArrayBuffer = "arraybuffer"
3652fromResponseType XhrResponseType_Blob = "blob"
3753fromResponseType XhrResponseType_Text = "text"
3854
39−toResponseType :: String -> Maybe XhrResponseType
55+toResponseType :: Text -> Maybe XhrResponseType
4056toResponseType "" = Just XhrResponseType_Default
4157toResponseType "arraybuffer" = Just XhrResponseType_ArrayBuffer
4258toResponseType "blob" = Just XhrResponseType_Blob
@@ -50,7 +66,7 @@ xmlHttpRequestNew wv = withWebViewContext wv $ \jsContext -> do
5066 jsvalueprotect jsContext xhr'
5167 return $ XMLHttpRequest xhr' jsContext
5268
53−xmlHttpRequestOpen :: XMLHttpRequest -> String -> String -> Bool -> String -> String -> IO ()
69+xmlHttpRequestOpen :: XMLHttpRequest -> Text -> Text -> Bool -> Text -> Text -> IO ()
5470xmlHttpRequestOpen xhr method url async user password = do
5571 let c = xhrContext xhr
5672 method' <- stringToJSValue c method
@@ -97,24 +113,22 @@ xmlHttpRequestGetResponse xhr = do
97113 script <- jsstringcreatewithutf8cstring "this.response"
98114 t <- jsevaluatescript c script (xhrValue xhr) nullPtr 1 nullPtr
99115 isNull <- jsvalueisnull c t
100− case isNull of
101− True -> return Nothing
102− False -> case mrt of
103− Just XhrResponseType_ArrayBuffer -> Just . XhrResponseBody_ArrayBuffer <$> bsFromArrayBuffer c t
104− Just XhrResponseType_Blob -> Just . XhrResponseBody_Blob . Blob . castForeignPtr <$> newForeignPtr_ t
105− Just XhrResponseType_Default -> fmap (XhrResponseBody_Default . T.pack) <$> fromJSStringMaybe c t
106− Just XhrResponseType_Text -> fmap (XhrResponseBody_Text . T.pack) <$> fromJSStringMaybe c t
107− _ -> return Nothing
116+ if isNull then return Nothing else case mrt of
117+ Just XhrResponseType_ArrayBuffer -> Just . XhrResponseBody_ArrayBuffer <$> bsFromArrayBuffer c t
118+ Just XhrResponseType_Blob -> Just . XhrResponseBody_Blob . Blob . castForeignPtr <$> newForeignPtr_ t
119+ Just XhrResponseType_Default -> fmap XhrResponseBody_Default <$> fromJSStringMaybe c t
120+ Just XhrResponseType_Text -> fmap XhrResponseBody_Text <$> fromJSStringMaybe c t
121+ _ -> return Nothing
108122
109123xmlHttpRequestGetResponseText :: XMLHttpRequest -> IO (Maybe Text)
110124xmlHttpRequestGetResponseText xhr = do
111125 let c = xhrContext xhr
112126 script <- jsstringcreatewithutf8cstring "this.responseText"
113127 t <- jsevaluatescript c script (xhrValue xhr) nullPtr 1 nullPtr
114− fmap (fmap T.pack) $ fromJSStringMaybe c t
128+ fromJSStringMaybe c t
115129
116−xmlHttpRequestSend :: XMLHttpRequest -> Maybe String -> IO ()
117−xmlHttpRequestSend xhr payload = do
130+xmlHttpRequestSendPayload :: XMLHttpRequest -> Maybe Text -> IO ()
131+xmlHttpRequestSendPayload xhr payload = do
118132 let c = xhrContext xhr
119133 result <- newEmptyMVar
120134 let wrapper' x = wrapper $ \_ _ _ _ _ _ -> putMVar result x >> jsvaluemakeundefined c
@@ -124,17 +138,11 @@ xmlHttpRequestSend xhr payload = do
124138 onError <- jsobjectmakefunctionwithcallback c nullPtr e
125139 bracket (wrapper' Nothing) freeHaskellFunPtr $ \l -> do
126140 onLoad <- jsobjectmakefunctionwithcallback c nullPtr l
127− (o,s) <- case payload of
128− Nothing -> do
129− d <- jsvaluemakeundefined c
130− o <- toJSObject c [xhrValue xhr, d, onError, onAbort, onLoad]
131− s <- jsstringcreatewithutf8cstring send
132− return (o,s)
133− Just payload' -> do
134− d <- stringToJSValue c payload'
135− o <- toJSObject c [xhrValue xhr, d, onError, onAbort, onLoad]
136− s <- jsstringcreatewithutf8cstring send
137− return (o,s)
141+ d <- case payload of
142+ Nothing -> jsvaluemakeundefined c
143+ Just payload' -> stringToJSValue c payload'
144+ o <- toJSObject c [xhrValue xhr, d, onError, onAbort, onLoad]
145+ s <- jsstringcreatewithutf8cstring send
138146 _ <- jsevaluatescript c s o nullPtr 1 nullPtr
139147 takeMVar result >>= mapM_ throwIO
140148 where
@@ -166,7 +174,7 @@ xmlHttpRequestSend xhr payload = do
166174 })(this[0], this[1], this[2], this[3], this[4])
167175 |]
168176
169−xmlHttpRequestSetRequestHeader :: XMLHttpRequest -> String -> String -> IO ()
177+xmlHttpRequestSetRequestHeader :: XMLHttpRequest -> Text -> Text -> IO ()
170178xmlHttpRequestSetRequestHeader xhr header value = do
171179 let c = xhrContext xhr
172180 header' <- stringToJSValue c header
@@ -176,7 +184,7 @@ xmlHttpRequestSetRequestHeader xhr header value = do
176184 _ <- jsevaluatescript c script o nullPtr 1 nullPtr
177185 return ()
178186
179−xmlHttpRequestSetResponseType :: XMLHttpRequest -> String -> IO ()
187+xmlHttpRequestSetResponseType :: XMLHttpRequest -> Text -> IO ()
180188xmlHttpRequestSetResponseType xhr t = do
181189 let c = xhrContext xhr
182190 t' <- stringToJSValue c t
src-ghcjs/Reflex/Dom/Internal/Foreign.hs+8−8
@@ -1,19 +1,19 @@
1−{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI #-}
1+{-# LANGUAGE ForeignFunctionInterface #-}
2+{-# LANGUAGE JavaScriptFFI #-}
23module Reflex.Dom.Internal.Foreign ( module Reflex.Dom.Internal.Foreign
34 , runWebGUI
45 ) where
56
6−import Control.Monad
77import Data.ByteString (ByteString)
8+import qualified Data.ByteString as BS
89import Foreign
910import Foreign.C
11+import qualified GHCJS.Buffer as JS
1012import GHCJS.DOM
1113import GHCJS.DOM.Types
14+import qualified GHCJS.Marshal.Pure as JS
1215import GHCJS.Types
1316import JavaScript.TypedArray.ArrayBuffer as JS
14−import qualified Data.ByteString as BS
15−import qualified GHCJS.Buffer as JS
16−import qualified GHCJS.Marshal.Pure as JS
1717
1818quitWebView :: WebView -> IO ()
1919quitWebView = error "quitWebView: unimplemented in GHCJS"
@@ -21,12 +21,12 @@ quitWebView = error "quitWebView: unimplemented in GHCJS"
2121foreign import javascript unsafe "location['host']" getLocationHost_ :: IO JSString
2222
2323getLocationHost :: FromJSString r => a -> IO r
24−getLocationHost _ = liftM fromJSString getLocationHost_
24+getLocationHost _ = fmap fromJSString getLocationHost_
2525
2626foreign import javascript unsafe "location['protocol']" getLocationProtocol_ :: IO JSString
2727
2828getLocationProtocol :: FromJSString r => a -> IO r
29−getLocationProtocol _ = liftM fromJSString getLocationProtocol_
29+getLocationProtocol _ = fmap fromJSString getLocationProtocol_
3030
3131withWebViewContext :: a -> (a -> IO b) -> IO b
3232withWebViewContext a f = f a
@@ -40,4 +40,4 @@ bsToArrayBuffer _ bs = BS.useAsCString bs $ \cStr -> do
4040 extractByteArray cStr $ BS.length bs
4141
4242bsFromArrayBuffer :: a -> JSVal -> IO ByteString
43−bsFromArrayBuffer _ ab = liftM (JS.toByteString 0 Nothing . JS.createFromArrayBuffer) $ JS.unsafeFreeze $ JS.pFromJSVal ab
43+bsFromArrayBuffer _ ab = fmap (JS.toByteString 0 Nothing . JS.createFromArrayBuffer) $ JS.unsafeFreeze $ JS.pFromJSVal ab
src-ghcjs/Reflex/Dom/WebSocket/Foreign.hs+17−15
@@ -1,30 +1,32 @@
1−{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI, CPP #-}
1+{-# LANGUAGE ForeignFunctionInterface #-}
2+{-# LANGUAGE JavaScriptFFI #-}
23
34module Reflex.Dom.WebSocket.Foreign where
45
5−import Prelude hiding (div, span, mapM, mapM_, concat, concatMap, all, sequence)
6+import Prelude hiding (all, concat, concatMap, div, mapM, mapM_, sequence, span)
67
7−import Data.ByteString (ByteString)
8−import qualified Data.ByteString as BS
9−import GHCJS.Types
10−import GHCJS.DOM.WebSocket (message, open, closeEvent)
11−import qualified GHCJS.DOM.WebSocket as GD
12−import GHCJS.DOM.MessageEvent
13−import GHCJS.DOM.EventM (on)
14−import GHCJS.DOM.Types
158import Control.Monad.IO.Class
169import Control.Monad.Reader
10+import Data.ByteString (ByteString)
11+import qualified Data.ByteString as BS
12+import Data.Text (Text)
13+import Data.Text.Encoding
1714import GHCJS.Buffer
18−import JavaScript.TypedArray.ArrayBuffer as JS
19−import GHCJS.Marshal.Pure
15+import GHCJS.DOM.EventM (on)
16+import GHCJS.DOM.MessageEvent
17+import GHCJS.DOM.Types hiding (Text)
18+import GHCJS.DOM.WebSocket (closeEvent, message, open)
19+import qualified GHCJS.DOM.WebSocket as GD
2020import GHCJS.Foreign.Internal
21−import Data.Text.Encoding
21+import GHCJS.Marshal.Pure
22+import GHCJS.Types
23+import JavaScript.TypedArray.ArrayBuffer as JS
2224
2325data JSWebSocket = JSWebSocket { unWebSocket :: WebSocket }
2426
25−newWebSocket :: a -> String -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
27+newWebSocket :: a -> Text -> (ByteString -> IO ()) -> IO () -> IO () -> IO JSWebSocket
2628newWebSocket _ url onMessage onOpen onClose = do
27− ws <- GD.newWebSocket url (Just [] :: Maybe [String])
29+ ws <- GD.newWebSocket url (Just [] :: Maybe [Text])
2830 _ <- on ws open $ liftIO onOpen
2931 GD.setBinaryType ws "arraybuffer"
3032 _ <- on ws message $ do
src-ghcjs/Reflex/Dom/Xhr/Foreign.hs+39−14
@@ -1,4 +1,7 @@
1−{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI, OverloadedStrings #-}
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE JavaScriptFFI #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE TypeSynonymInstances #-}
25
36module Reflex.Dom.Xhr.Foreign (
47 XMLHttpRequest
@@ -6,19 +9,20 @@ module Reflex.Dom.Xhr.Foreign (
69 , module Reflex.Dom.Xhr.Foreign
710) where
811
9−import Prelude hiding (error)
12+import Control.Exception (catch, throwIO)
13+import Data.Maybe (fromMaybe)
1014import Data.Text (Text)
11−import GHCJS.DOM.Types hiding (Text)
1215import GHCJS.DOM
1316import GHCJS.DOM.Enums
14−import GHCJS.DOM.XMLHttpRequest
15−import Data.Maybe (fromMaybe)
16−import GHCJS.DOM.EventTarget (dispatchEvent)
1717import GHCJS.DOM.EventM (EventM, on)
18+import GHCJS.DOM.EventTarget (dispatchEvent)
19+import GHCJS.DOM.Types hiding (Text)
20+import GHCJS.DOM.XMLHttpRequest
21+import GHCJS.Types
22+import Prelude hiding (error)
1823import Reflex.Dom.Internal.Foreign
1924import Reflex.Dom.Xhr.Exception
2025import Reflex.Dom.Xhr.ResponseType
21−import Control.Exception (catch, throwIO)
2226
2327prepareWebView :: WebView -> IO ()
2428prepareWebView _ = return ()
@@ -36,9 +40,30 @@ convertException e = case e of
3640 XHRError -> XhrException_Error
3741 XHRAborted -> XhrException_Aborted
3842
43+class IsXhrPayload a where
44+ sendXhrPayload :: XMLHttpRequest -> a -> IO ()
45+
46+instance IsXhrPayload () where
47+ sendXhrPayload xhr _ = send xhr
48+
49+instance IsXhrPayload String where
50+ sendXhrPayload = sendString
51+
52+instance IsXhrPayload FormData where
53+ sendXhrPayload = sendFormData
54+
55+instance IsXhrPayload Document where
56+ sendXhrPayload = sendDocument
57+
58+instance IsXhrPayload Blob where
59+ sendXhrPayload = sendBlob
60+
61+newtype XhrPayload = XhrPayload { unXhrPayload :: JSVal }
62+
3963-- This used to be a non blocking call, but now it uses an interruptible ffi
40−xmlHttpRequestSend :: ToJSString payload => XMLHttpRequest -> Maybe payload -> IO ()
41−xmlHttpRequestSend self p = (maybe (send self) (sendString self) p) `catch` (throwIO . convertException)
64+xmlHttpRequestSend :: IsXhrPayload payload => XMLHttpRequest -> payload -> IO ()
65+xmlHttpRequestSend self p = sendXhrPayload self p `catch` (throwIO . convertException)
66+
4267
4368xmlHttpRequestSetRequestHeader :: (ToJSString header, ToJSString value)
4469 => XMLHttpRequest -> header -> value -> IO ()
@@ -141,11 +166,11 @@ xmlHttpRequestGetResponse xhr = do
141166 mr <- getResponse xhr
142167 rt <- xmlHttpRequestGetResponseType xhr
143168 case rt of
144− Just XhrResponseType_Blob -> return $ fmap (XhrResponseBody_Blob . castToBlob) mr
145− Just XhrResponseType_Text -> fmap (Just . XhrResponseBody_Text) $ xmlHttpRequestGetStatusText xhr
146− Just XhrResponseType_Default -> fmap (Just . XhrResponseBody_Text) $ xmlHttpRequestGetStatusText xhr
147− Just XhrResponseType_ArrayBuffer -> case (fmap unGObject mr) of
169+ Just XhrResponseType_Blob -> return $ XhrResponseBody_Blob . castToBlob <$> mr
170+ Just XhrResponseType_Text -> Just . XhrResponseBody_Text <$> xmlHttpRequestGetStatusText xhr
171+ Just XhrResponseType_Default -> Just . XhrResponseBody_Text <$> xmlHttpRequestGetStatusText xhr
172+ Just XhrResponseType_ArrayBuffer -> case fmap unGObject mr of
148173 Nothing -> return Nothing
149− Just ptr -> fmap (Just . XhrResponseBody_ArrayBuffer) $ bsFromArrayBuffer ptr ptr
174+ Just ptr -> Just . XhrResponseBody_ArrayBuffer <$> bsFromArrayBuffer ptr ptr
150175 _ -> return Nothing
151176
src/Foreign/JavaScript/TH.hsnew file+519
@@ -0,0 +1,519 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE EmptyDataDecls #-}
3+{-# LANGUAGE ExistentialQuantification #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE ForeignFunctionInterface #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE RankNTypes #-}
11+{-# LANGUAGE StandaloneDeriving #-}
12+{-# LANGUAGE TemplateHaskell #-}
13+{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE TypeSynonymInstances #-}
15+{-# LANGUAGE UndecidableInstances #-}
16+#ifdef __GHCJS__
17+{-# LANGUAGE JavaScriptFFI #-}
18+#endif
19+module Foreign.JavaScript.TH ( module Foreign.JavaScript.TH
20+ , Safety (..)
21+ ) where
22+
23+import Reflex.Class
24+import Reflex.Dom.Deletable.Class
25+import Reflex.Dom.PerformEvent.Class
26+import Reflex.Host.Class
27+
28+import Language.Haskell.TH
29+
30+#ifdef __GHCJS__
31+import qualified GHCJS.Buffer as JS
32+import GHCJS.DOM
33+import GHCJS.DOM.Types hiding (Text, fromJSString)
34+import qualified GHCJS.DOM.Types as JS
35+import qualified GHCJS.Foreign as JS
36+import qualified GHCJS.Foreign.Callback as JS
37+import qualified GHCJS.Foreign.Callback.Internal (Callback (..))
38+import qualified GHCJS.Marshal as JS
39+import qualified GHCJS.Marshal.Pure as JS
40+import qualified GHCJS.Types as JS
41+import qualified JavaScript.Array as JS
42+import qualified JavaScript.Array.Internal (SomeJSArray (..))
43+import qualified JavaScript.Object as JS
44+import qualified JavaScript.Object.Internal (Object (..))
45+import qualified JavaScript.TypedArray.ArrayBuffer as JSArrayBuffer
46+
47+import Data.Hashable
48+import Data.Word
49+import Foreign.C.Types
50+import Foreign.Ptr
51+import Text.Encoding.Z
52+#else
53+import Foreign.Marshal
54+import Graphics.UI.Gtk.WebKit.DOM.Node
55+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
56+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef
57+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef
58+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
59+import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame
60+import Graphics.UI.Gtk.WebKit.WebView
61+import System.Glib.FFI
62+#endif
63+
64+import Control.Concurrent
65+import Control.Monad
66+import Control.Monad.Exception
67+import Control.Monad.Fix
68+import Control.Monad.IO.Class
69+import Control.Monad.Reader
70+import Control.Monad.Ref
71+import Control.Monad.State
72+import qualified Control.Monad.State.Strict as Strict
73+import Control.Monad.Trans.Control
74+import Data.ByteString (ByteString)
75+import qualified Data.ByteString as BS
76+import Data.Coerce
77+import Data.Monoid
78+import Data.Text (Text)
79+import qualified Data.Text as T
80+
81+class Monad m => HasWebView m where
82+ type WebViewPhantom m :: *
83+ askWebView :: m (WebViewSingleton (WebViewPhantom m))
84+
85+instance HasWebView m => HasWebView (ReaderT r m) where
86+ type WebViewPhantom (ReaderT r m) = WebViewPhantom m
87+ askWebView = lift askWebView
88+
89+instance HasWebView m => HasWebView (StateT r m) where
90+ type WebViewPhantom (StateT r m) = WebViewPhantom m
91+ askWebView = lift askWebView
92+
93+instance HasWebView m => HasWebView (Strict.StateT r m) where
94+ type WebViewPhantom (Strict.StateT r m) = WebViewPhantom m
95+ askWebView = lift askWebView
96+
97+newtype WithWebView x m a = WithWebView { unWithWebView :: ReaderT (WebViewSingleton x) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadTrans, MonadException, MonadAsyncException)
98+
99+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView x m) where
100+ {-# INLINABLE newEventWithTrigger #-}
101+ newEventWithTrigger = lift . newEventWithTrigger
102+ {-# INLINABLE newFanEventWithTrigger #-}
103+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
104+
105+instance MonadSubscribeEvent t m => MonadSubscribeEvent t (WithWebView x m) where
106+ {-# INLINABLE subscribeEvent #-}
107+ subscribeEvent = lift . subscribeEvent
108+
109+instance MonadReflexHost t m => MonadReflexHost t (WithWebView x m) where
110+ type ReadPhase (WithWebView x m) = ReadPhase m
111+ {-# INLINABLE fireEventsAndRead #-}
112+ fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
113+ {-# INLINABLE runHostFrame #-}
114+ runHostFrame = lift . runHostFrame
115+
116+instance MonadSample t m => MonadSample t (WithWebView x m) where
117+ {-# INLINABLE sample #-}
118+ sample = lift . sample
119+
120+instance MonadHold t m => MonadHold t (WithWebView x m) where
121+ {-# INLINABLE hold #-}
122+ hold v0 = lift . hold v0
123+ {-# INLINABLE holdDyn #-}
124+ holdDyn v0 = lift . holdDyn v0
125+ {-# INLINABLE holdIncremental #-}
126+ holdIncremental v0 = lift . holdIncremental v0
127+
128+instance MonadTransControl (WithWebView x) where
129+ type StT (WithWebView x) a = StT (ReaderT (WebViewSingleton x)) a
130+ {-# INLINABLE liftWith #-}
131+ liftWith = defaultLiftWith WithWebView unWithWebView
132+ {-# INLINABLE restoreT #-}
133+ restoreT = defaultRestoreT WithWebView
134+
135+instance PerformEvent t m => PerformEvent t (WithWebView x m) where
136+ type Performable (WithWebView x m) = WithWebView x (Performable m)
137+ {-# INLINABLE performEvent_ #-}
138+ performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
139+ {-# INLINABLE performEvent #-}
140+ performEvent e = liftWith $ \run -> performEvent $ fmap run e
141+
142+instance Deletable t m => Deletable t (WithWebView x m) where
143+ {-# INLINABLE deletable #-}
144+ deletable = liftThrough . deletable
145+
146+runWithWebView :: WithWebView x m a -> WebViewSingleton x -> m a
147+runWithWebView = runReaderT . unWithWebView
148+
149+instance (Monad m) => HasWebView (WithWebView x m) where
150+ type WebViewPhantom (WithWebView x m) = x
151+ askWebView = WithWebView ask
152+
153+instance MonadRef m => MonadRef (WithWebView x m) where
154+ type Ref (WithWebView x m) = Ref m
155+ newRef = lift . newRef
156+ readRef = lift . readRef
157+ writeRef r = lift . writeRef r
158+
159+instance MonadAtomicRef m => MonadAtomicRef (WithWebView x m) where
160+ atomicModifyRef r = lift . atomicModifyRef r
161+
162+withWebViewSingleton :: WebView -> (forall x. WebViewSingleton x -> r) -> r
163+withWebViewSingleton wv f = f $ WebViewSingleton wv
164+
165+-- | A singleton type for a given WebView; we use this to statically guarantee that different WebViews (and thus different javscript contexts) don't get mixed up
166+newtype WebViewSingleton x = WebViewSingleton { unWebViewSingleton :: WebView }
167+
168+#ifdef __GHCJS__
169+type JSFFI_Internal = JS.MutableJSArray -> IO JS.JSVal
170+newtype JSFFI = JSFFI JSFFI_Internal
171+#else
172+newtype JSFFI = JSFFI String
173+#endif
174+
175+newtype JSFun x = JSFun { unJSFun :: JSRef x }
176+
177+instance ToJS x (JSFun x) where
178+ withJS (JSFun r) f = f r
179+
180+instance FromJS x (JSFun x) where
181+ fromJS = return . JSFun
182+
183+class IsJSContext x where
184+ data JSRef x
185+
186+class (Monad m, MonadIO (JSM m), MonadFix (JSM m), MonadJS x (JSM m)) => HasJS x m | m -> x where
187+ type JSM m :: * -> *
188+ liftJS :: JSM m a -> m a
189+
190+instance HasJS x m => HasJS x (ReaderT r m) where
191+ type JSM (ReaderT r m) = JSM m
192+ liftJS = lift . liftJS
193+
194+-- | A Monad that is capable of executing JavaScript
195+class Monad m => MonadJS x m | m -> x where
196+ runJS :: JSFFI -> [JSRef x] -> m (JSRef x)
197+ forkJS :: m () -> m ThreadId
198+ mkJSUndefined :: m (JSRef x)
199+ isJSNull :: JSRef x -> m Bool
200+ isJSUndefined :: JSRef x -> m Bool
201+ fromJSBool :: JSRef x -> m Bool
202+ fromJSString :: JSRef x -> m String
203+ fromJSArray :: JSRef x -> m [JSRef x]
204+ fromJSUint8Array :: JSRef x -> m ByteString
205+ fromJSNumber :: JSRef x -> m Double
206+ withJSBool :: Bool -> (JSRef x -> m r) -> m r
207+ withJSString :: String -> (JSRef x -> m r) -> m r
208+ withJSNumber :: Double -> (JSRef x -> m r) -> m r
209+ withJSArray :: [JSRef x] -> (JSRef x -> m r) -> m r
210+ withJSUint8Array :: ByteString -> (JSUint8Array x -> m r) -> m r
211+ -- | Create a JSFun with zero arguments; should be equilvant to `syncCallback AlwaysRetain True` in GHCJS
212+ mkJSFun :: ([JSRef x] -> m (JSRef x)) -> m (JSFun x) --TODO: Support 'this', exceptions
213+ freeJSFun :: JSFun x -> m ()
214+ setJSProp :: String -> JSRef x -> JSRef x -> m ()
215+ getJSProp :: String -> JSRef x -> m (JSRef x)
216+ withJSNode :: Node -> (JSRef x -> m r) -> m r
217+
218+#ifdef __GHCJS__
219+
220+data JSCtx_IO
221+
222+instance MonadIO m => HasJS JSCtx_IO (WithWebView x m) where
223+ type JSM (WithWebView x m) = IO
224+ liftJS = liftIO
225+
226+instance IsJSContext JSCtx_IO where
227+ newtype JSRef JSCtx_IO = JSRef_IO { unJSRef_IO :: JS.JSVal }
228+
229+instance MonadJS JSCtx_IO IO where
230+ runJS (JSFFI f) l = fmap JSRef_IO . f =<< JS.fromListIO (coerce l)
231+ forkJS = forkIO
232+ mkJSUndefined = return $ JSRef_IO JS.jsUndefined
233+ isJSNull (JSRef_IO r) = return $ JS.isNull r
234+ isJSUndefined (JSRef_IO r) = return $ JS.isUndefined r
235+ fromJSBool (JSRef_IO r) = return $ JS.fromJSBool r
236+ fromJSString (JSRef_IO r) = return $ JS.fromJSString $ JS.pFromJSVal r
237+ fromJSArray (JSRef_IO r) = fmap coerce $ JS.toListIO $ coerce r
238+ fromJSUint8Array (JSRef_IO r) = fmap (JS.toByteString 0 Nothing . JS.createFromArrayBuffer) $ JSArrayBuffer.unsafeFreeze $ JS.pFromJSVal r --TODO: Assert that this is immutable
239+ fromJSNumber (JSRef_IO r) = do
240+ Just n <- JS.fromJSVal r
241+ return n
242+ withJSBool b f = f $ JSRef_IO $ JS.toJSBool b
243+ withJSString s f = f $ JSRef_IO $ JS.pToJSVal $ JS.toJSString s
244+ withJSNumber n f = do
245+ r <- JS.toJSVal n
246+ f $ JSRef_IO r
247+ withJSArray l f = do
248+ r <- JS.fromListIO $ coerce l
249+ f $ JSRef_IO $ coerce r
250+ withJSUint8Array payload f = BS.useAsCString payload $ \cStr -> do
251+ ba <- extractByteArray cStr $ BS.length payload
252+ f $ JSUint8Array $ JSRef_IO ba
253+ mkJSFun f = do
254+ cb <- JS.syncCallback1' $ \args -> do
255+ l <- JS.toListIO $ coerce args
256+ JSRef_IO result <- f $ coerce l
257+ return result
258+ fmap (JSFun . JSRef_IO) $ funWithArguments $ coerce cb
259+ freeJSFun (JSFun (JSRef_IO r)) = JS.releaseCallback $ coerce r
260+ setJSProp s (JSRef_IO v) (JSRef_IO o) = JS.setProp (JS.toJSString s) v $ coerce o
261+ getJSProp s (JSRef_IO o) = do
262+ r <- JS.getProp (JS.toJSString s) $ coerce o
263+ return $ JSRef_IO r
264+ withJSNode n f = f $ JSRef_IO $ unNode n
265+
266+foreign import javascript unsafe "new Uint8Array($1_1.buf, $1_2, $2)" extractByteArray :: Ptr CChar -> Int -> IO JS.JSVal
267+
268+foreign import javascript unsafe "function(){ return $1(arguments); }" funWithArguments :: JS.Callback (JS.MutableJSArray -> IO a) -> IO JS.JSVal
269+
270+#else
271+
272+askJSContext :: MonadIO m => WithWebView x m JSContextRef
273+askJSContext = do
274+ wv <- askWebView
275+ liftIO $ webFrameGetGlobalContext =<< webViewGetMainFrame (unWebViewSingleton wv)
276+
277+-- | Make a JS string available that is not wrapped up as a JSValueRef
278+withJSStringRaw :: MonadAsyncException m => String -> (JSStringRef -> m r) -> m r
279+withJSStringRaw str a = do
280+ bracket (liftIO $ jsstringcreatewithutf8cstring str) (liftIO . jsstringrelease) $ \strRef -> do
281+ a strRef
282+
283+fromJSNumberRaw :: MonadIO m => JSValueRef -> WithWebView x m Double
284+fromJSNumberRaw val = do
285+ jsContext <- askJSContext
286+ liftIO $ jsvaluetonumber jsContext val nullPtr --TODO: Exceptions
287+
288+
289+data JSCtx_JavaScriptCore x
290+
291+instance IsJSContext (JSCtx_JavaScriptCore x) where
292+ newtype JSRef (JSCtx_JavaScriptCore x) = JSRef_JavaScriptCore { unJSRef_JavaScriptCore :: JSValueRef }
293+
294+instance MonadIO m => HasJS (JSCtx_JavaScriptCore x) (WithWebView x m) where
295+ type JSM (WithWebView x m) = WithWebView x IO
296+ liftJS a = do
297+ wv <- askWebView
298+ liftIO $ runWithWebView a wv
299+
300+instance MonadJS (JSCtx_JavaScriptCore x) (WithWebView x IO) where
301+ runJS (JSFFI body) args = do
302+ jsContext <- askJSContext
303+ withJSArray args $ \(JSRef_JavaScriptCore this) -> liftIO $ do
304+ result <- bracket (jsstringcreatewithutf8cstring body) jsstringrelease $ \script -> jsevaluatescript jsContext script this nullPtr 1 nullPtr
305+ return $ JSRef_JavaScriptCore result --TODO: Protect and unprotect result
306+ forkJS a = do
307+ wv <- askWebView
308+ liftIO $ forkIO $ runWithWebView a wv
309+ mkJSUndefined = do
310+ jsContext <- askJSContext
311+ fmap JSRef_JavaScriptCore $ liftIO $ jsvaluemakeundefined jsContext
312+ isJSNull (JSRef_JavaScriptCore r) = do
313+ jsContext <- askJSContext
314+ liftIO $ jsvalueisnull jsContext r
315+ isJSUndefined (JSRef_JavaScriptCore r) = do
316+ jsContext <- askJSContext
317+ liftIO $ jsvalueisundefined jsContext r
318+ fromJSBool (JSRef_JavaScriptCore r) = do
319+ jsContext <- askJSContext
320+ liftIO $ jsvaluetoboolean jsContext r
321+ fromJSString (JSRef_JavaScriptCore r) = do
322+ jsContext <- askJSContext
323+ liftIO $ do
324+ s <- jsvaluetostringcopy jsContext r nullPtr --TODO: Deal with exceptions
325+ l <- jsstringgetmaximumutf8cstringsize s
326+ allocaBytes (fromIntegral l) $ \ps -> do
327+ _ <- jsstringgetutf8cstring'_ s ps (fromIntegral l)
328+ peekCString ps
329+ withJSBool b a = do
330+ jsContext <- askJSContext
331+ valRef <- liftIO $ jsvaluemakeboolean jsContext b
332+ a $ JSRef_JavaScriptCore valRef
333+ withJSString str a = do
334+ jsContext <- askJSContext
335+ withJSStringRaw str $ \strRef -> do
336+ valRef <- liftIO $ jsvaluemakestring jsContext strRef
337+ a $ JSRef_JavaScriptCore valRef --TODO: Protect/unprotect valRef
338+ withJSNumber n a = do
339+ jsContext <- askJSContext
340+ valRef <- liftIO $ jsvaluemakenumber jsContext n
341+ a $ JSRef_JavaScriptCore valRef --TODO: Protect/unprotect valRef
342+ withJSArray elems a = do
343+ jsContext <- askJSContext
344+ let numElems = length elems
345+ bracket (liftIO $ mallocArray numElems) (liftIO . free) $ \elemsArr -> do
346+ liftIO $ pokeArray elemsArr $ coerce elems
347+ bracket (liftIO $ jsobjectmakearray jsContext (fromIntegral numElems) elemsArr nullPtr) (const $ return ()) $ \arrRef -> do --TODO: Do we need to protect/unprotect the array object?
348+ a $ JSRef_JavaScriptCore arrRef --TODO: Protect/unprotect valRef
349+ --TODO: When supported by webkitgtk3-javascriptcore, go directly from C string to Uint8Array without creating each item individually
350+ withJSUint8Array payload f = withJSArrayFromList (BS.unpack payload) $ \x -> do
351+ payloadRef <- runJS (JSFFI "new Uint8Array(this[0])") [x]
352+ f $ JSUint8Array payloadRef
353+ fromJSArray (JSRef_JavaScriptCore a) = do
354+ jsContext <- askJSContext
355+ lenRef <- withJSStringRaw "length" $ \lengthStr -> do
356+ liftIO $ jsobjectgetproperty jsContext a lengthStr nullPtr --TODO: Exceptions
357+ lenDouble <- fromJSNumberRaw lenRef
358+ let len = round lenDouble
359+ liftIO $ forM [0..len-1] $ \i -> do
360+ JSRef_JavaScriptCore <$> jsobjectgetpropertyatindex jsContext a i nullPtr --TODO: Exceptions
361+ fromJSUint8Array a = do
362+ vals <- fromJSArray a
363+ doubles <- mapM fromJSNumber vals
364+ return $ BS.pack $ map round doubles
365+ fromJSNumber (JSRef_JavaScriptCore val) = do
366+ jsContext <- askJSContext
367+ liftIO $ jsvaluetonumber jsContext val nullPtr --TODO: Exceptions
368+ mkJSFun a = do
369+ wv <- askWebView
370+ jsContext <- askJSContext
371+ cb <- liftIO $ mkJSObjectCallAsFunctionCallback $ \_ _ _ argc argv _ -> do --TODO: Use the exception pointer to handle haskell exceptions
372+ args <- forM [0..fromIntegral (argc-1)] $ \n -> do
373+ x <- peekElemOff argv n
374+ jsvalueprotect jsContext x
375+ return $ JSRef_JavaScriptCore x --TODO: Unprotect eventually
376+ unJSRef_JavaScriptCore <$> runWithWebView (a args) wv
377+ cbRef <- liftIO $ jsobjectmakefunctionwithcallback jsContext nullPtr cb
378+ liftIO $ jsvalueprotect jsContext cbRef
379+ return $ JSFun $ JSRef_JavaScriptCore cbRef
380+ freeJSFun (JSFun (JSRef_JavaScriptCore f)) = do
381+ jsContext <- askJSContext
382+ liftIO $ jsvalueunprotect jsContext f
383+ setJSProp propName (JSRef_JavaScriptCore valRef) (JSRef_JavaScriptCore objRef) = do
384+ withJSStringRaw propName $ \propNameRaw -> do
385+ jsContext <- askJSContext
386+ liftIO $ jsobjectsetproperty jsContext objRef propNameRaw valRef 0 nullPtr --TODO: property attribute, exceptions
387+ getJSProp propName (JSRef_JavaScriptCore objRef) = do
388+ withJSStringRaw propName $ \propNameRaw -> do
389+ jsContext <- askJSContext
390+ liftIO $ JSRef_JavaScriptCore <$> jsobjectgetproperty jsContext objRef propNameRaw nullPtr --TODO: property attribute, exceptions
391+ withJSNode = error "withJSNode is only supported in ghcjs"
392+
393+#endif
394+
395+class FromJS x a where
396+ fromJS :: MonadJS x m => JSRef x -> m a
397+
398+instance FromJS x () where
399+ fromJS _ = return () --TODO: Should this do some kind of checking for the js value?
400+
401+instance FromJS x Bool where
402+ fromJS = fromJSBool
403+
404+instance ToJS x Bool where
405+ withJS = withJSBool
406+
407+instance FromJS x String where
408+ fromJS = fromJSString
409+
410+instance FromJS x Text where
411+ fromJS s = T.pack <$> fromJSString s
412+
413+instance FromJS x a => FromJS x (Maybe a) where
414+ fromJS x = do
415+ n <- isJSNull x
416+ if n then return Nothing else Just <$> fromJS x
417+
418+class ToJS x a where
419+ withJS :: MonadJS x m => a -> (JSRef x -> m r) -> m r
420+
421+instance ToJS x (JSRef x) where
422+ withJS r = ($ r)
423+
424+instance FromJS x (JSRef x) where
425+ fromJS = return
426+
427+instance ToJS x String where
428+ withJS = withJSString
429+
430+instance ToJS x Text where
431+ withJS = withJSString . T.unpack
432+
433+newtype JSArray a = JSArray { unJSArray :: [a] }
434+
435+instance ToJS x a => ToJS x (JSArray a) where
436+ withJS = withJSArrayFromList . unJSArray
437+
438+instance FromJS x a => FromJS x (JSArray a) where
439+ fromJS = fmap JSArray . mapM fromJS <=< fromJSArray
440+
441+withJSArrayFromList :: (ToJS x a, MonadJS x m) => [a] -> (JSRef x -> m r) -> m r
442+withJSArrayFromList as f = go as []
443+ where go [] jsRefs = withJSArray (reverse jsRefs) f
444+ go (h:t) jsRefs = withJS h $ \hRef -> go t (hRef : jsRefs)
445+
446+newtype JSUint8Array x = JSUint8Array { unJSUint8Array :: JSRef x }
447+
448+instance ToJS x (JSUint8Array x) where
449+ withJS (JSUint8Array r) = ($ r)
450+
451+instance FromJS x (JSUint8Array x) where
452+ fromJS = return . JSUint8Array
453+
454+instance ToJS x Word8 where
455+ withJS n = withJSNumber $ fromIntegral n --TODO: Check things; throw exceptions
456+
457+instance ToJS x Int where
458+ withJS n = withJSNumber $ fromIntegral n --TODO: Check things; throw exceptions
459+
460+instance FromJS x Int where
461+ fromJS = fmap round . fromJSNumber --TODO: Check things; throw exceptions
462+
463+instance ToJS x Double where
464+ withJS = withJSNumber
465+
466+instance FromJS x Double where
467+ fromJS = fromJSNumber
468+
469+instance ToJS x Node where
470+ withJS = withJSNode
471+
472+
473+importJS :: Safety -> String -> String -> Q Type -> Q [Dec]
474+importJS safety body name qt = do
475+ t <- qt
476+ let (argTypes, _) = parseType t
477+ argNames <- forM argTypes $ \_ -> do
478+ arg <- newName "arg"
479+ argRef <- newName "argRef"
480+ return (arg, argRef)
481+ (jsffiDecs, jsffiExp) <- mkJSFFI safety body
482+ let go [] = [| runJS $(return jsffiExp) $(listE $ map (varE . snd) argNames) >>= fromJS
483+ |]
484+ go ((arg, argRef) : args) = [| withJS $(varE arg) $ $(lamE [varP argRef] $ go args) |]
485+ e <- lamE (map (varP. fst) argNames) $ go argNames
486+ let n = mkName name
487+ return $ jsffiDecs ++
488+ [ SigD n t
489+ , ValD (VarP n) (NormalB e) []
490+ ]
491+
492+mkJSFFI :: Safety -> String -> Q ([Dec], Exp)
493+#ifdef __GHCJS__
494+mkJSFFI safety body = do
495+ -- n <- newName "jsffi" --TODO: Should use newName, but that doesn't seem to work with ghcjs
496+ l <- location
497+ n <- newName $ "jsffi_" <> zEncodeString (loc_package l <> ":" <> loc_module l) <> "_" <> show (abs (hash (show safety, body)))
498+ t <- [t| JSFFI_Internal |]
499+ let wrappedBody = "(function(){ return (" <> body <> "); }).apply($1)"
500+ let decs = [ForeignD $ ImportF JavaScript safety wrappedBody n t]
501+ e <- [| JSFFI $(varE n) |]
502+ return (decs, e)
503+#else
504+mkJSFFI _ body = do
505+ e <- [| JSFFI body |]
506+ return ([], e)
507+#endif
508+
509+parseType :: Type -> ([Type], Type)
510+parseType (ForallT _ [AppT (AppT (ConT monadJs) (VarT _)) (VarT m)] funType)
511+ | monadJs == ''MonadJS = go funType
512+ where go t = case t of
513+ AppT (AppT ArrowT arg) t' ->
514+ let (args, result) = go t'
515+ in (arg : args, result)
516+ AppT (VarT m') result
517+ | m' == m -> ([], result)
518+ _ -> error $ "parseType: can't parse type " <> show t
519+parseType t = error $ "parseType: can't parse type " <> show t
src/Reflex/Dom.hs+16−17
@@ -1,18 +1,17 @@
1−module Reflex.Dom ( module Reflex
2− , module Reflex.Dom.Class
3− , module Reflex.Dom.Internal
4− , module Reflex.Dom.Widget
5− , module Reflex.Dom.Xhr
6− , module Reflex.Dom.Time
7− , module Reflex.Dom.WebSocket
8− , module Reflex.Dom.Location
9− ) where
1+module Reflex.Dom (module X) where
102
11−import Reflex
12−import Reflex.Dom.Class
13−import Reflex.Dom.Internal
14−import Reflex.Dom.Widget
15−import Reflex.Dom.Xhr
16−import Reflex.Dom.Time
17−import Reflex.Dom.WebSocket
18−import Reflex.Dom.Location
3+import Reflex as X
4+import Reflex.Dom.Builder.Class as X
5+import Reflex.Dom.Builder.Immediate as X
6+import Reflex.Dom.Builder.Static as X
7+import Reflex.Dom.Class as X
8+import Reflex.Dom.DynamicWriter as X
9+import Reflex.Dom.Internal as X
10+import Reflex.Dom.Location as X
11+import Reflex.Dom.Old as X
12+import Reflex.Dom.PerformEvent.Class as X
13+import Reflex.Dom.PostBuild.Class as X
14+import Reflex.Dom.Time as X
15+import Reflex.Dom.WebSocket as X
16+import Reflex.Dom.Widget as X
17+import Reflex.Dom.Xhr as X
src/Reflex/Dom/Builder/Class.hsnew file+345
@@ -0,0 +1,345 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE DefaultSignatures #-}
4+{-# LANGUAGE DeriveFunctor #-}
5+{-# LANGUAGE FlexibleContexts #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE FunctionalDependencies #-}
8+{-# LANGUAGE LambdaCase #-}
9+{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE OverloadedStrings #-}
11+{-# LANGUAGE PolyKinds #-}
12+{-# LANGUAGE RankNTypes #-}
13+{-# LANGUAGE ScopedTypeVariables #-}
14+{-# LANGUAGE TemplateHaskell #-}
15+{-# LANGUAGE TypeFamilies #-}
16+{-# LANGUAGE UndecidableInstances #-}
17+module Reflex.Dom.Builder.Class
18+ ( module Reflex.Dom.Builder.Class
19+ , module Reflex.Dom.Builder.Class.Events
20+ , module Reflex.Dom.Deletable.Class
21+ ) where
22+
23+import Reflex
24+import Reflex.Dom.Builder.Class.Events
25+import Reflex.Dom.Deletable.Class
26+
27+import qualified Control.Category
28+import Control.Lens hiding (element)
29+import Control.Monad.Reader
30+import Control.Monad.Trans.Control
31+import Data.Default
32+import Data.Dependent.Map (DMap)
33+import qualified Data.Dependent.Map as DMap
34+import Data.Functor.Misc
35+import Data.Map (Map)
36+import Data.Proxy
37+import Data.Semigroup
38+import Data.Text (Text)
39+import Data.Type.Coercion
40+import GHC.Exts (Constraint)
41+
42+data Pair1 (f :: k -> *) (g :: k -> *) (a :: k) = Pair1 (f a) (g a)
43+
44+data Maybe1 f a = Nothing1 | Just1 (f a)
45+
46+class DomSpace d where
47+ type DomHandler d :: * -> * -> *
48+ type DomHandler1 d :: (EventTag -> *) -> (EventTag -> *) -> * --TODO: Why can't this be k -> * ?
49+ type RawEvent d :: EventTag -> *
50+ type RawElement d :: *
51+ defaultEventHandler :: proxy d -> DomHandler1 d (Pair1 EventName (RawEvent d)) (Maybe1 EventResult)
52+
53+-- | @'DomBuilder' t m@ indicates that @m@ is a 'Monad' capable of building dynamic DOM in the 'Reflex' timeline @t@
54+class (Monad m, Reflex t, Deletable t m, DomSpace (DomBuilderSpace m)) => DomBuilder t m | m -> t where
55+ type DomBuilderSpace m :: *
56+ textNode :: TextNodeConfig t -> m (TextNode t)
57+ element :: Text -> ElementConfig er t m -> m a -> m (Element er (DomBuilderSpace m) t, a)
58+ -- | Create a placeholder in the DOM, with the ability to insert new DOM before it
59+ -- 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
60+ placeholder :: PlaceholderConfig above t m -> m (Placeholder above t)
61+ inputElement :: InputElementConfig er t m -> m (InputElement er (DomBuilderSpace m) t)
62+ textAreaElement :: TextAreaElementConfig er t m -> m (TextAreaElement er (DomBuilderSpace m) t)
63+
64+type Namespace = Text
65+
66+data TextNodeConfig t
67+ = TextNodeConfig { _textNodeConfig_initialContents :: Text
68+ , _textNodeConfig_setContents :: Event t Text
69+ }
70+
71+instance (Reflex t) => Default (TextNodeConfig t) where
72+ {-# INLINABLE def #-}
73+ def = TextNodeConfig
74+ { _textNodeConfig_initialContents = mempty
75+ , _textNodeConfig_setContents = never
76+ }
77+
78+data TextNode t = TextNode
79+
80+type AttributeName = (Maybe Namespace, Text)
81+
82+data Propagation
83+ = Propagation_Continue
84+ | Propagation_Stop
85+ | Propagation_StopImmediate
86+ deriving (Show, Read, Eq, Ord)
87+
88+instance Semigroup Propagation where
89+ {-# INLINABLE (<>) #-}
90+ (<>) = max
91+
92+instance Monoid Propagation where
93+ {-# INLINABLE mempty #-}
94+ mempty = Propagation_Continue
95+ {-# INLINABLE mappend #-}
96+ mappend = (<>)
97+
98+data EventFlags = EventFlags --TODO: Monoid; ways of building each flag
99+ { _eventFlags_propagation :: Propagation
100+ , _eventFlags_preventDefault :: Bool
101+ }
102+
103+instance Semigroup EventFlags where
104+ {-# INLINABLE (<>) #-}
105+ EventFlags p pd <> EventFlags p' pd' = EventFlags (p <> p') (pd || pd')
106+
107+instance Monoid EventFlags where
108+ {-# INLINABLE mempty #-}
109+ mempty = EventFlags Propagation_Continue False
110+ {-# INLINABLE mappend #-}
111+ mappend = (<>)
112+
113+preventDefault :: EventFlags
114+preventDefault = mempty { _eventFlags_preventDefault = True }
115+
116+stopPropagation :: EventFlags
117+stopPropagation = mempty { _eventFlags_propagation = Propagation_Stop }
118+
119+newtype EventFilter d er en = EventFilter (DomHandler d (RawEvent d en) (EventFlags, DomHandler d () (Maybe (er en))))
120+
121+data ElementConfig er t m
122+ = ElementConfig { _elementConfig_namespace :: Maybe Namespace
123+ , _elementConfig_initialAttributes :: Map AttributeName Text
124+ , _elementConfig_modifyAttributes :: Event t (Map AttributeName (Maybe Text))
125+ , _elementConfig_eventFilters :: DMap EventName (EventFilter (DomBuilderSpace m) er)
126+ , _elementConfig_eventHandler :: DomHandler1 (DomBuilderSpace m) (Pair1 EventName (RawEvent (DomBuilderSpace m))) (Maybe1 er)
127+ }
128+
129+instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (ElementConfig er t m) where
130+ {-# INLINABLE def #-}
131+ def = ElementConfig
132+ { _elementConfig_namespace = Nothing
133+ , _elementConfig_initialAttributes = mempty
134+ , _elementConfig_modifyAttributes = never
135+ , _elementConfig_eventFilters = DMap.empty
136+ , _elementConfig_eventHandler = defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))
137+ }
138+
139+data Element er d t
140+ = Element { _element_events :: EventSelector t (WrapArg er EventName) --TODO: EventSelector should have two arguments
141+ , _element_raw :: RawElement d
142+ }
143+
144+data PlaceholderConfig above t m
145+ = PlaceholderConfig { _placeholderConfig_insertAbove :: Event t (m above)
146+ , _placeholderConfig_deleteSelf :: Event t ()
147+ }
148+
149+instance Reflex t => Default (PlaceholderConfig above t m) where
150+ {-# INLINABLE def #-}
151+ def = PlaceholderConfig
152+ { _placeholderConfig_insertAbove = never
153+ , _placeholderConfig_deleteSelf = never
154+ }
155+
156+data Placeholder above t
157+ = Placeholder { _placeholder_insertedAbove :: Event t above
158+ , _placeholder_deletedSelf :: Event t ()
159+ }
160+
161+data InputElementConfig er t m
162+ = InputElementConfig { _inputElementConfig_initialValue :: Text
163+ , _inputElementConfig_setValue :: Event t Text
164+ , _inputElementConfig_initialChecked :: Bool
165+ , _inputElementConfig_setChecked :: Event t Bool
166+ , _inputElementConfig_elementConfig :: ElementConfig er t m
167+ }
168+
169+instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (InputElementConfig er t m) where
170+ {-# INLINABLE def #-}
171+ def = InputElementConfig
172+ { _inputElementConfig_initialValue = ""
173+ , _inputElementConfig_setValue = never
174+ , _inputElementConfig_initialChecked = False
175+ , _inputElementConfig_setChecked = never
176+ , _inputElementConfig_elementConfig = def
177+ }
178+
179+data InputElement er d t
180+ = InputElement { _inputElement_value :: Dynamic t Text
181+ , _inputElement_checked :: Dynamic t Bool
182+ , _inputElement_input :: Event t Text
183+ , _inputElement_hasFocus :: Dynamic t Bool
184+ , _inputElement_element :: Element er d t
185+ }
186+
187+data TextAreaElementConfig er t m
188+ = TextAreaElementConfig { _textAreaElementConfig_initialValue :: Text
189+ , _textAreaElementConfig_setValue :: Event t Text
190+ , _textAreaElementConfig_elementConfig :: ElementConfig er t m
191+ }
192+
193+instance (Reflex t, er ~ EventResult, DomBuilder t m) => Default (TextAreaElementConfig er t m) where
194+ {-# INLINABLE def #-}
195+ def = TextAreaElementConfig
196+ { _textAreaElementConfig_initialValue = ""
197+ , _textAreaElementConfig_setValue = never
198+ , _textAreaElementConfig_elementConfig = def
199+ }
200+
201+data TextAreaElement er d t
202+ = TextAreaElement { _textAreaElement_value :: Dynamic t Text
203+ , _textAreaElement_input :: Event t Text
204+ , _textAreaElement_hasFocus :: Dynamic t Bool
205+ , _textAreaElement_element :: Element er d t
206+ }
207+
208+makeLenses ''TextNodeConfig
209+makeLenses ''ElementConfig
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 (PlaceholderConfig above t m) where
218+ {-# INLINABLE deleteSelf #-}
219+ deleteSelf = placeholderConfig_deleteSelf
220+
221+class InsertAbove t m above above' a a' | a -> t m above, a' -> t m above', a above' -> a', a' above -> a where
222+ insertAbove :: Lens a a' (Event t (m above)) (Event t (m above'))
223+
224+instance InsertAbove t m above above' (PlaceholderConfig above t m) (PlaceholderConfig above' t m) where
225+ insertAbove = placeholderConfig_insertAbove
226+
227+class InitialAttributes a where
228+ initialAttributes :: Lens' a (Map AttributeName Text)
229+
230+instance InitialAttributes (ElementConfig er t m) where
231+ {-# INLINABLE initialAttributes #-}
232+ initialAttributes = elementConfig_initialAttributes
233+
234+class InitialAttributes a => ModifyAttributes t a | a -> t where
235+ modifyAttributes :: Lens' a (Event t (Map AttributeName (Maybe Text)))
236+
237+instance ModifyAttributes t (ElementConfig er t m) where
238+ {-# INLINABLE modifyAttributes #-}
239+ modifyAttributes = elementConfig_modifyAttributes
240+
241+class HasNamespace a where
242+ namespace :: Lens' a (Maybe Namespace)
243+
244+instance HasNamespace (ElementConfig er t m) where
245+ {-# INLINABLE namespace #-}
246+ namespace = elementConfig_namespace
247+
248+-- * Convenience functions
249+
250+--TODO: Move/replace
251+class Functor1 (f :: (k -> *) -> *) where
252+ type Functor1Constraint f (a :: k -> *) (b :: k -> *) :: Constraint
253+ type Functor1Constraint f a b = ()
254+ fmap1 :: Functor1Constraint f a b => (forall x. a x -> b x) -> f a -> f b
255+
256+instance Functor1 (ElementConfig er t) where
257+ type Functor1Constraint (ElementConfig er t) a b = DomBuilderSpace a ~ DomBuilderSpace b
258+ {-# INLINABLE fmap1 #-}
259+ fmap1 _ cfg = cfg
260+ { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
261+ , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
262+ }
263+
264+instance Reflex t => Functor1 (PlaceholderConfig above t) where
265+ {-# INLINABLE fmap1 #-}
266+ fmap1 f cfg = cfg
267+ { _placeholderConfig_insertAbove = f <$> _placeholderConfig_insertAbove cfg
268+ }
269+
270+instance Functor1 (InputElementConfig er t) where
271+ type Functor1Constraint (InputElementConfig er t) a b = Functor1Constraint (ElementConfig er t) a b
272+ fmap1 f cfg = cfg & inputElementConfig_elementConfig %~ fmap1 f
273+
274+instance Functor1 (TextAreaElementConfig er t) where
275+ type Functor1Constraint (TextAreaElementConfig er t) a b = Functor1Constraint (ElementConfig er t) a b
276+ fmap1 f cfg = cfg & textAreaElementConfig_elementConfig %~ fmap1 f
277+
278+class HasDomEvent t target eventName where
279+ type DomEventType target eventName :: *
280+ domEvent :: EventName eventName -> target -> Event t (DomEventType target eventName)
281+
282+instance Reflex t => HasDomEvent t (Element EventResult d t) en where
283+ type DomEventType (Element EventResult d t) en = EventResultType en
284+ {-# INLINABLE domEvent #-}
285+ domEvent en e = unEventResult <$> Reflex.select (_element_events e) (WrapArg en)
286+
287+instance Reflex t => HasDomEvent t (InputElement EventResult d t) en where
288+ type DomEventType (InputElement EventResult d t) en = EventResultType en
289+ {-# INLINABLE domEvent #-}
290+ domEvent en = domEvent en . _inputElement_element
291+
292+instance Reflex t => HasDomEvent t (TextAreaElement EventResult d t) en where
293+ type DomEventType (TextAreaElement EventResult d t) en = EventResultType en
294+ {-# INLINABLE domEvent #-}
295+ domEvent en = domEvent en . _textAreaElement_element
296+
297+instance DomBuilder t m => DomBuilder t (ReaderT r m) where
298+ type DomBuilderSpace (ReaderT r m) = DomBuilderSpace m
299+ {-# INLINABLE textNode #-}
300+ textNode = liftTextNode
301+ element = liftElement
302+ placeholder = liftPlaceholder
303+ inputElement = liftInputElement
304+ textAreaElement = liftTextAreaElement
305+
306+type LiftDomBuilder t f m =
307+ ( Reflex t
308+ , MonadTransControlStateless f
309+ , Monad m
310+ , DomBuilder t m
311+ , DomBuilderSpace (f m) ~ DomBuilderSpace m
312+ )
313+
314+class MonadTransControl t => MonadTransControlStateless t where
315+ stTCoercion :: proxy t -> Coercion (StT t a) a
316+ default stTCoercion :: proxy t -> Coercion a a
317+ stTCoercion _ = Control.Category.id
318+
319+toStT :: MonadTransControlStateless t => proxy t -> a -> StT t a
320+toStT = coerceWith . sym . stTCoercion
321+
322+fromStT :: MonadTransControlStateless t => proxy t -> StT t a -> a
323+fromStT = coerceWith . stTCoercion
324+
325+instance MonadTransControlStateless (ReaderT r)
326+
327+type RunStateless t = forall n b. Monad n => t n b -> n b
328+
329+liftWithStateless :: forall m t a. (Monad m, MonadTransControlStateless t) => (RunStateless t -> m a) -> t m a
330+liftWithStateless a = liftWith $ \run -> a $ \x -> fromStT (Proxy :: Proxy t) <$> run x
331+
332+liftTextNode :: (MonadTrans f, DomBuilder t m) => TextNodeConfig t -> f m (TextNode t)
333+liftTextNode = lift . textNode
334+
335+liftElement :: LiftDomBuilder t f m => Text -> ElementConfig er t (f m) -> f m a -> f m (Element er (DomBuilderSpace m) t, a)
336+liftElement elementTag cfg child = liftWithStateless $ \run -> element elementTag (fmap1 run cfg) $ run child
337+
338+liftPlaceholder :: LiftDomBuilder t f m => PlaceholderConfig above t (f m) -> f m (Placeholder above t)
339+liftPlaceholder cfg = liftWithStateless $ \run -> placeholder $ fmap1 run cfg
340+
341+liftInputElement :: LiftDomBuilder t f m => InputElementConfig er t (f m) -> f m (InputElement er (DomBuilderSpace m) t)
342+liftInputElement cfg = liftWithStateless $ \run -> inputElement $ fmap1 run cfg
343+
344+liftTextAreaElement :: LiftDomBuilder t f m => TextAreaElementConfig er t (f m) -> f m (TextAreaElement er (DomBuilderSpace m) t)
345+liftTextAreaElement cfg = liftWithStateless $ \run -> textAreaElement $ fmap1 run cfg
src/Reflex/Dom/Builder/Class/Events.hsnew file+164
@@ -0,0 +1,164 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE GADTs #-}
3+{-# LANGUAGE KindSignatures #-}
4+{-# LANGUAGE PolyKinds #-}
5+{-# LANGUAGE TemplateHaskell #-}
6+{-# LANGUAGE TypeFamilies #-}
7+module Reflex.Dom.Builder.Class.Events where
8+
9+import Data.GADT.Compare.TH
10+
11+data EventTag
12+ = AbortTag
13+ | BlurTag
14+ | ChangeTag
15+ | ClickTag
16+ | ContextmenuTag
17+ | DblclickTag
18+ | DragTag
19+ | DragendTag
20+ | DragenterTag
21+ | DragleaveTag
22+ | DragoverTag
23+ | DragstartTag
24+ | DropTag
25+ | ErrorTag
26+ | FocusTag
27+ | InputTag
28+ | InvalidTag
29+ | KeydownTag
30+ | KeypressTag
31+ | KeyupTag
32+ | LoadTag
33+ | MousedownTag
34+ | MouseenterTag
35+ | MouseleaveTag
36+ | MousemoveTag
37+ | MouseoutTag
38+ | MouseoverTag
39+ | MouseupTag
40+ | MousewheelTag
41+ | ScrollTag
42+ | SelectTag
43+ | SubmitTag
44+ | WheelTag
45+ {-
46+ | BeforecutTag
47+ | CutTag
48+ | BeforecopyTag
49+ | CopyTag
50+ | BeforepasteTag
51+ | PasteTag
52+-}
53+ | ResetTag
54+ | SearchTag
55+-- | SelectstartTag
56+ | TouchstartTag
57+ | TouchmoveTag
58+ | TouchendTag
59+ | TouchcancelTag
60+
61+data EventName :: EventTag -> * where
62+ Abort :: EventName 'AbortTag
63+ Blur :: EventName 'BlurTag
64+ Change :: EventName 'ChangeTag
65+ Click :: EventName 'ClickTag
66+ Contextmenu :: EventName 'ContextmenuTag
67+ Dblclick :: EventName 'DblclickTag
68+ Drag :: EventName 'DragTag
69+ Dragend :: EventName 'DragendTag
70+ Dragenter :: EventName 'DragenterTag
71+ Dragleave :: EventName 'DragleaveTag
72+ Dragover :: EventName 'DragoverTag
73+ Dragstart :: EventName 'DragstartTag
74+ Drop :: EventName 'DropTag
75+ Error :: EventName 'ErrorTag
76+ Focus :: EventName 'FocusTag
77+ Input :: EventName 'InputTag
78+ Invalid :: EventName 'InvalidTag
79+ Keydown :: EventName 'KeydownTag
80+ Keypress :: EventName 'KeypressTag
81+ Keyup :: EventName 'KeyupTag
82+ Load :: EventName 'LoadTag
83+ Mousedown :: EventName 'MousedownTag
84+ Mouseenter :: EventName 'MouseenterTag
85+ Mouseleave :: EventName 'MouseleaveTag
86+ Mousemove :: EventName 'MousemoveTag
87+ Mouseout :: EventName 'MouseoutTag
88+ Mouseover :: EventName 'MouseoverTag
89+ Mouseup :: EventName 'MouseupTag
90+ Mousewheel :: EventName 'MousewheelTag
91+ Scroll :: EventName 'ScrollTag
92+ Select :: EventName 'SelectTag
93+ Submit :: EventName 'SubmitTag
94+ Wheel :: EventName 'WheelTag
95+ {-
96+ Beforecut :: EventName 'BeforecutTag
97+ Cut :: EventName 'CutTag
98+ Beforecopy :: EventName 'BeforecopyTag
99+ Copy :: EventName 'CopyTag
100+ Beforepaste :: EventName 'BeforepasteTag
101+ Paste :: EventName 'PasteTag
102+-}
103+ Reset :: EventName 'ResetTag
104+ Search :: EventName 'SearchTag
105+-- Selectstart :: EventName 'SelectstartTag
106+ Touchstart :: EventName 'TouchstartTag
107+ Touchmove :: EventName 'TouchmoveTag
108+ Touchend :: EventName 'TouchendTag
109+ Touchcancel :: EventName 'TouchcancelTag
110+
111+newtype EventResult en = EventResult { unEventResult :: EventResultType en }
112+
113+type family EventResultType (en :: EventTag) :: * where
114+ EventResultType 'ClickTag = ()
115+ EventResultType 'DblclickTag = ()
116+ EventResultType 'KeypressTag = Int
117+ EventResultType 'KeydownTag = Int
118+ EventResultType 'KeyupTag = Int
119+ EventResultType 'ScrollTag = Int
120+ EventResultType 'MousemoveTag = (Int, Int)
121+ EventResultType 'MousedownTag = (Int, Int)
122+ EventResultType 'MouseupTag = (Int, Int)
123+ EventResultType 'MouseenterTag = ()
124+ EventResultType 'MouseleaveTag = ()
125+ EventResultType 'FocusTag = ()
126+ EventResultType 'BlurTag = ()
127+ EventResultType 'ChangeTag = ()
128+ EventResultType 'DragTag = ()
129+ EventResultType 'DragendTag = ()
130+ EventResultType 'DragenterTag = ()
131+ EventResultType 'DragleaveTag = ()
132+ EventResultType 'DragoverTag = ()
133+ EventResultType 'DragstartTag = ()
134+ EventResultType 'DropTag = ()
135+ EventResultType 'AbortTag = ()
136+ EventResultType 'ContextmenuTag = ()
137+ EventResultType 'ErrorTag = ()
138+ EventResultType 'InputTag = ()
139+ EventResultType 'InvalidTag = ()
140+ EventResultType 'LoadTag = ()
141+ EventResultType 'MouseoutTag = ()
142+ EventResultType 'MouseoverTag = ()
143+ EventResultType 'MousewheelTag = ()
144+ EventResultType 'SelectTag = ()
145+ EventResultType 'SubmitTag = ()
146+ {-
147+ EventResultType 'BeforecutTag = ()
148+ EventResultType 'CutTag = ()
149+ EventResultType 'BeforecopyTag = ()
150+ EventResultType 'CopyTag = ()
151+ EventResultType 'BeforepasteTag = ()
152+ EventResultType 'PasteTag = ()
153+-}
154+ EventResultType 'ResetTag = ()
155+ EventResultType 'SearchTag = ()
156+-- EventResultType 'SelectstartTag = ()
157+ EventResultType 'TouchstartTag = ()
158+ EventResultType 'TouchmoveTag = ()
159+ EventResultType 'TouchendTag = ()
160+ EventResultType 'TouchcancelTag = ()
161+ EventResultType 'WheelTag = ()
162+
163+deriveGEq ''EventName
164+deriveGCompare ''EventName
src/Reflex/Dom/Builder/Immediate.hsnew file+751
@@ -0,0 +1,751 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE EmptyDataDecls #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+{-# LANGUAGE LambdaCase #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE OverloadedStrings #-}
10+{-# LANGUAGE PolyKinds #-}
11+{-# LANGUAGE RankNTypes #-}
12+{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE TypeOperators #-}
15+{-# LANGUAGE UndecidableInstances #-}
16+module Reflex.Dom.Builder.Immediate where
17+
18+import Foreign.JavaScript.TH
19+import Reflex
20+import Reflex.Dom.Builder.Class
21+import Reflex.Dom.PerformEvent.Class
22+import Reflex.Dom.PostBuild.Class
23+import Reflex.Host.Class
24+
25+import Control.Concurrent.Chan
26+import Control.Lens hiding (element)
27+import Control.Monad.Exception
28+import Control.Monad.Reader
29+import Control.Monad.Ref
30+import Control.Monad.Trans.Control
31+import Data.Bitraversable
32+import Data.Default
33+import Data.Dependent.Map (DMap)
34+import qualified Data.Dependent.Map as DMap
35+import Data.Dependent.Sum
36+import Data.Functor.Misc
37+import Data.IORef
38+import Data.Maybe
39+import Data.Text (Text)
40+import GHCJS.DOM.Document (Document, createDocumentFragment, createElement, createElementNS, createTextNode)
41+import GHCJS.DOM.Element (getScrollTop, removeAttribute, removeAttributeNS, setAttribute, setAttributeNS)
42+import qualified GHCJS.DOM.Element as Element
43+import qualified GHCJS.DOM.Event as Event
44+import GHCJS.DOM.EventM (EventM, event, on)
45+import qualified GHCJS.DOM.EventM as DOM
46+import qualified GHCJS.DOM.HTMLInputElement as Input
47+import qualified GHCJS.DOM.HTMLTextAreaElement as TextArea
48+import GHCJS.DOM.MouseEvent
49+import GHCJS.DOM.Node (appendChild, getOwnerDocument, getParentNode, getPreviousSibling, insertBefore,
50+ removeChild, toNode, setNodeValue)
51+import GHCJS.DOM.Types (FocusEvent, IsElement, IsEvent, IsNode, KeyboardEvent, Node, ToDOMString, TouchEvent,
52+ WheelEvent, castToHTMLInputElement, castToHTMLTextAreaElement)
53+import qualified GHCJS.DOM.Types as DOM
54+import GHCJS.DOM.UIEvent
55+import qualified GHCJS.DOM.Window as Window
56+
57+import Debug.Trace hiding (traceEvent)
58+
59+data TriggerRef t a = TriggerRef { unTriggerRef :: IORef (Maybe (EventTrigger t a)) }
60+
61+data TriggerInvocation a = TriggerInvocation a (IO ())
62+
63+data ImmediateDomBuilderEnv t
64+ = ImmediateDomBuilderEnv { _immediateDomBuilderEnv_document :: Document
65+ , _immediateDomBuilderEnv_parent :: Node
66+ , _immediateDomBuilderEnv_events :: Chan [DSum (TriggerRef t) TriggerInvocation]
67+ }
68+
69+newtype ImmediateDomBuilderT t m a = ImmediateDomBuilderT { unImmediateDomBuilderT :: ReaderT (ImmediateDomBuilderEnv t) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
70+
71+instance MonadTransControl (ImmediateDomBuilderT t) where
72+ type StT (ImmediateDomBuilderT t) a = StT (ReaderT (ImmediateDomBuilderEnv t)) a
73+ liftWith = defaultLiftWith ImmediateDomBuilderT unImmediateDomBuilderT
74+ restoreT = defaultRestoreT ImmediateDomBuilderT
75+
76+instance MonadTrans (ImmediateDomBuilderT t) where
77+ lift = ImmediateDomBuilderT . lift
78+
79+{-# INLINABLE runImmediateDomBuilderT #-}
80+runImmediateDomBuilderT :: ImmediateDomBuilderT t m a -> ImmediateDomBuilderEnv t -> m a
81+runImmediateDomBuilderT (ImmediateDomBuilderT a) = runReaderT a
82+
83+{-# INLINABLE askDocument #-}
84+askDocument :: Monad m => ImmediateDomBuilderT t m Document
85+askDocument = ImmediateDomBuilderT $ asks _immediateDomBuilderEnv_document
86+
87+{-# INLINABLE askParent #-}
88+askParent :: Monad m => ImmediateDomBuilderT t m Node
89+askParent = ImmediateDomBuilderT $ asks _immediateDomBuilderEnv_parent
90+
91+{-# INLINABLE askEvents #-}
92+askEvents :: Monad m => ImmediateDomBuilderT t m (Chan [DSum (TriggerRef t) TriggerInvocation])
93+askEvents = ImmediateDomBuilderT $ asks _immediateDomBuilderEnv_events
94+
95+{-# INLINABLE append #-}
96+append :: (IsNode n, MonadIO m) => n -> ImmediateDomBuilderT t m ()
97+append n = do
98+ p <- askParent
99+ _ <- liftIO $ appendChild p $ Just n
100+ return ()
101+
102+{-# INLINABLE textNodeInternal #-}
103+textNodeInternal :: (MonadIO m, ToDOMString contents) => contents -> ImmediateDomBuilderT t m DOM.Text
104+textNodeInternal t = do
105+ doc <- askDocument
106+ Just n <- liftIO $ createTextNode doc t
107+ append n
108+ return n
109+
110+-- | s and e must both be children of the same node and s must precede e
111+{-# INLINABLE deleteBetweenInclusive #-}
112+deleteBetweenInclusive :: (MonadIO m, IsNode start, IsNode end) => start -> end -> m ()
113+deleteBetweenInclusive s e = do
114+ 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
115+ case mCurrentParent of
116+ Nothing -> return () --TODO: Is this the right behavior?
117+ Just currentParent -> do
118+ let go = do
119+ Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
120+ _ <- removeChild currentParent $ Just x
121+ when (toNode s /= toNode x) go
122+ go
123+ _ <- removeChild currentParent $ Just e
124+ return ()
125+
126+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)
127+
128+newtype EventFilterTriggerRef t er (en :: EventTag) = EventFilterTriggerRef (IORef (Maybe (EventTrigger t (er en))))
129+
130+{-# INLINABLE makeElement #-}
131+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)
132+makeElement elementTag cfg child = do
133+ doc <- askDocument
134+ Just e <- ImmediateDomBuilderT $ case cfg ^. namespace of
135+ Nothing -> createElement doc (Just elementTag)
136+ Just ens -> createElementNS doc (Just ens) (Just elementTag)
137+ ImmediateDomBuilderT $ iforM_ (cfg ^. initialAttributes) $ \(mAttrNamespace, n) v -> case mAttrNamespace of
138+ Nothing -> setAttribute e n v
139+ Just ans -> setAttributeNS e (Just ans) n v
140+ events <- askEvents
141+ result <- lift $ runImmediateDomBuilderT child $ ImmediateDomBuilderEnv
142+ { _immediateDomBuilderEnv_parent = toNode e
143+ , _immediateDomBuilderEnv_document = doc
144+ , _immediateDomBuilderEnv_events = events
145+ }
146+ append e
147+ lift $ performEvent_ $ ffor (cfg ^. modifyAttributes) $ imapM_ $ \(mAttrNamespace, n) mv -> case mAttrNamespace of
148+ Nothing -> maybe (removeAttribute e n) (setAttribute e n) mv
149+ Just ns -> maybe (removeAttributeNS e (Just ns) n) (setAttributeNS e (Just ns) n) mv
150+ eventTriggerRefs :: DMap EventName (EventFilterTriggerRef t er) <- liftIO $ fmap DMap.fromList $ forM (DMap.toList $ _elementConfig_eventFilters cfg) $ \(en :=> EventFilter (GhcjsDomHandler f)) -> do
151+ triggerRef <- newIORef Nothing
152+ _ <- elementOnEventName en e $ do
153+ evt <- DOM.event
154+ (flags, GhcjsDomHandler k) <- liftIO $ f $ GhcjsDomEvent evt
155+ when (_eventFlags_preventDefault flags) $ withIsEvent en DOM.preventDefault
156+ case _eventFlags_propagation flags of
157+ Propagation_Continue -> return ()
158+ Propagation_Stop -> withIsEvent en DOM.stopPropagation
159+ Propagation_StopImmediate -> withIsEvent en DOM.stopImmediatePropagation
160+ mv <- liftIO $ k ()
161+ liftIO $ forM_ mv $ \v -> writeChan events [TriggerRef triggerRef :=> TriggerInvocation v (return ())]
162+ return $ en :=> EventFilterTriggerRef triggerRef
163+ es <- newFanEventWithTrigger $ \(WrapArg en) t -> do
164+ case DMap.lookup en eventTriggerRefs of
165+ Just (EventFilterTriggerRef r) -> do
166+ writeIORef r $ Just t
167+ return $ do
168+ writeIORef r Nothing
169+ Nothing -> elementOnEventName en e $ do
170+ evt <- DOM.event
171+ let GhcjsDomHandler1 h = _elementConfig_eventHandler cfg
172+ mv <- liftIO $ h $ Pair1 en (GhcjsDomEvent evt)
173+ case mv of
174+ Nothing1 -> return ()
175+ Just1 v -> liftIO $ do
176+ --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
177+ ref <- newIORef $ Just t
178+ writeChan events [TriggerRef ref :=> TriggerInvocation v (return ())]
179+ return ((Element es e, result), e)
180+
181+newtype GhcjsDomHandler a b = GhcjsDomHandler { unGhcjsDomHandler :: a -> IO b }
182+
183+newtype GhcjsDomHandler1 a b = GhcjsDomHandler1 { unGhcjsDomHandler1 :: forall (x :: EventTag). a x -> IO (b x) }
184+
185+newtype GhcjsDomEvent en = GhcjsDomEvent { unGhcjsDomEvent :: EventType en }
186+
187+data GhcjsDomSpace
188+
189+instance DomSpace GhcjsDomSpace where
190+ type DomHandler GhcjsDomSpace = GhcjsDomHandler
191+ type DomHandler1 GhcjsDomSpace = GhcjsDomHandler1
192+ type RawEvent GhcjsDomSpace = GhcjsDomEvent
193+ type RawElement GhcjsDomSpace = DOM.Element
194+ {-# INLINABLE defaultEventHandler #-}
195+ defaultEventHandler _ = GhcjsDomHandler1 $ \(Pair1 en (GhcjsDomEvent evt)) -> do
196+ Just t <- withIsEvent en $ Event.getTarget evt
197+ mr <- runReaderT (defaultDomEventHandler (Element.castToElement t) en) evt
198+ return $ case mr of
199+ Nothing -> Nothing1
200+ Just r -> Just1 r
201+
202+instance SupportsImmediateDomBuilder t m => DomBuilder t (ImmediateDomBuilderT t m) where
203+ type DomBuilderSpace (ImmediateDomBuilderT t m) = GhcjsDomSpace
204+ {-# INLINABLE textNode #-}
205+ textNode (TextNodeConfig initialContents eSetContents) = do
206+ n <- textNodeInternal initialContents
207+ lift $ performEvent_ $ ffor eSetContents $ \t -> setNodeValue n (Just t)
208+ return TextNode
209+ {-# INLINABLE element #-}
210+ element elementTag cfg child = fst <$> makeElement elementTag cfg child
211+ {-# INLINABLE placeholder #-}
212+ placeholder (PlaceholderConfig toInsertAbove delete) = liftThrough (deletable delete) $ do
213+ n <- textNodeInternal ("" :: Text)
214+ insertedAbove <- insertImmediateAbove n toInsertAbove
215+ --Note: "deleteSelf" must come after "insertAbove", because we need to be able to insert above in the same frame that we delete
216+ deleted <- lift $ performEvent $ ffor delete $ \_ -> do
217+ mp <- getParentNode n
218+ forM_ mp $ \p -> removeChild p $ Just n
219+ return $ Placeholder insertedAbove deleted
220+ {-# INLINABLE inputElement #-}
221+ inputElement cfg = do
222+ ((e, _), domElement) <- makeElement "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
223+ let domInputElement = castToHTMLInputElement domElement
224+ Input.setValue domInputElement $ Just (cfg ^. inputElementConfig_initialValue)
225+ Just v0 <- Input.getValue domInputElement
226+ let getMyValue = fromMaybe "" <$> Input.getValue domInputElement
227+ valueChangedByUI <- performEvent $ getMyValue <$ Reflex.select (_element_events e) (WrapArg Input)
228+ valueChangedBySetValue <- performEvent $ ffor (cfg ^. inputElementConfig_setValue) $ \v' -> do
229+ Input.setValue domInputElement $ Just v'
230+ getMyValue -- We get the value after setting it in case the browser has mucked with it somehow
231+ v <- holdDyn v0 $ leftmost
232+ [ valueChangedBySetValue
233+ , valueChangedByUI
234+ ]
235+ Input.setChecked domInputElement $ _inputElementConfig_initialChecked cfg
236+ checkedChangedByUI <- wrapDomEvent domInputElement (`on` Element.click) $ do
237+ Input.getChecked domInputElement
238+ checkedChangedBySetChecked <- performEvent $ ffor (_inputElementConfig_setChecked cfg) $ \newChecked -> do
239+ oldChecked <- Input.getChecked domInputElement
240+ if newChecked /= oldChecked
241+ then do Input.setChecked domInputElement newChecked
242+ return $ Just newChecked
243+ else return Nothing
244+ c <- holdDyn (_inputElementConfig_initialChecked cfg) $ leftmost
245+ [ fmapMaybe id checkedChangedBySetChecked
246+ , checkedChangedByUI
247+ ]
248+ let initialFocus = False --TODO: Is this correct?
249+ hasFocus <- holdDyn initialFocus $ leftmost
250+ [ False <$ Reflex.select (_element_events e) (WrapArg Blur)
251+ , True <$ Reflex.select (_element_events e) (WrapArg Focus)
252+ ]
253+ return $ InputElement v (uniqDyn c) valueChangedByUI hasFocus e
254+ {-# INLINABLE textAreaElement #-}
255+ textAreaElement cfg = do --TODO
256+ ((e, _), domElement) <- makeElement "textarea" (cfg ^. textAreaElementConfig_elementConfig) $ return ()
257+ let domTextAreaElement = castToHTMLTextAreaElement domElement
258+ Just v0 <- TextArea.getValue domTextAreaElement
259+ let getMyValue = fromMaybe "" <$> TextArea.getValue domTextAreaElement
260+ valueChangedByUI <- performEvent $ getMyValue <$ Reflex.select (_element_events e) (WrapArg Input)
261+ valueChangedBySetValue <- performEvent $ ffor (cfg ^. textAreaElementConfig_setValue) $ \v' -> do
262+ TextArea.setValue domTextAreaElement $ Just v'
263+ getMyValue -- We get the value after setting it in case the browser has mucked with it somehow
264+ v <- holdDyn v0 $ leftmost
265+ [ valueChangedBySetValue
266+ , valueChangedByUI
267+ ]
268+ let initialFocus = False --TODO: Is this correct?
269+ hasFocus <- holdDyn initialFocus $ leftmost
270+ [ False <$ Reflex.select (_element_events e) (WrapArg Blur)
271+ , True <$ Reflex.select (_element_events e) (WrapArg Focus)
272+ ]
273+ return $ TextAreaElement v valueChangedByUI hasFocus e
274+
275+{-# INLINABLE insertImmediateAbove #-}
276+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)
277+insertImmediateAbove n toInsertAbove = do
278+ events <- askEvents
279+ lift $ performEvent $ ffor toInsertAbove $ \new -> do
280+ Just doc <- getOwnerDocument n
281+ Just df <- createDocumentFragment doc
282+ result <- runImmediateDomBuilderT new $ ImmediateDomBuilderEnv
283+ { _immediateDomBuilderEnv_parent = toNode df
284+ , _immediateDomBuilderEnv_document = doc
285+ , _immediateDomBuilderEnv_events = events
286+ }
287+ mp <- getParentNode n
288+ case mp of
289+ Nothing -> trace "ImmediateDomBuilderT: placeholder: Warning: (getParentNode n) returned Nothing" $ return ()
290+ 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
291+ return result
292+
293+instance SupportsImmediateDomBuilder t m => Deletable t (ImmediateDomBuilderT t m) where
294+ {-# INLINABLE deletable #-}
295+ deletable delete child = liftThrough (deletable delete) $ do
296+ top <- textNodeInternal ("" :: Text)
297+ result <- child
298+ bottom <- textNodeInternal ("" :: Text)
299+ lift $ performEvent_ $ ffor delete $ \_ -> do
300+ deleteBetweenInclusive top bottom
301+ return result
302+
303+instance PerformEvent t m => PerformEvent t (ImmediateDomBuilderT t m) where
304+ type Performable (ImmediateDomBuilderT t m) = Performable m
305+ {-# INLINABLE performEvent_ #-}
306+ performEvent_ e = lift $ performEvent_ e
307+ {-# INLINABLE performEvent #-}
308+ performEvent e = lift $ performEvent e
309+
310+instance PostBuild t m => PostBuild t (ImmediateDomBuilderT t m) where
311+ {-# INLINABLE getPostBuild #-}
312+ getPostBuild = lift getPostBuild
313+
314+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (ImmediateDomBuilderT t m) where
315+ {-# INLINABLE newEventWithTrigger #-}
316+ newEventWithTrigger = lift . newEventWithTrigger
317+ {-# INLINABLE newFanEventWithTrigger #-}
318+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
319+
320+instance (Monad m, MonadRef m, Ref m ~ Ref IO, MonadReflexCreateTrigger t m) => TriggerEvent t (ImmediateDomBuilderT t m) where
321+ {-# INLINABLE newTriggerEvent #-}
322+ newTriggerEvent = do
323+ (e, t) <- newTriggerEventWithOnComplete
324+ return (e, \a -> t a $ return ())
325+ {-# INLINABLE newTriggerEventWithOnComplete #-}
326+ newTriggerEventWithOnComplete = do
327+ events <- askEvents
328+ (eResult, reResultTrigger) <- lift newEventWithTriggerRef
329+ return $ (,) eResult $ \a cb -> writeChan events [TriggerRef reResultTrigger :=> TriggerInvocation a cb]
330+ {-# INLINABLE newEventWithLazyTriggerWithOnComplete #-}
331+ newEventWithLazyTriggerWithOnComplete f = do
332+ events <- askEvents
333+ lift $ newEventWithTrigger $ \t -> f $ \a cb -> do
334+ reResultTrigger <- newIORef $ Just t
335+ writeChan events [TriggerRef reResultTrigger :=> TriggerInvocation a cb]
336+
337+instance HasWebView m => HasWebView (ImmediateDomBuilderT t m) where
338+ type WebViewPhantom (ImmediateDomBuilderT t m) = WebViewPhantom m
339+ askWebView = lift askWebView
340+
341+instance MonadRef m => MonadRef (ImmediateDomBuilderT t m) where
342+ type Ref (ImmediateDomBuilderT t m) = Ref m
343+ {-# INLINABLE newRef #-}
344+ newRef = lift . newRef
345+ {-# INLINABLE readRef #-}
346+ readRef = lift . readRef
347+ {-# INLINABLE writeRef #-}
348+ writeRef r = lift . writeRef r
349+
350+instance MonadAtomicRef m => MonadAtomicRef (ImmediateDomBuilderT t m) where
351+ {-# INLINABLE atomicModifyRef #-}
352+ atomicModifyRef r = lift . atomicModifyRef r
353+
354+instance (HasJS x m, ReflexHost t) => HasJS x (ImmediateDomBuilderT t m) where
355+ type JSM (ImmediateDomBuilderT t m) = JSM m
356+ liftJS = lift . liftJS
357+
358+type family EventType en where
359+ EventType 'AbortTag = UIEvent
360+ EventType 'BlurTag = FocusEvent
361+ EventType 'ChangeTag = DOM.Event
362+ EventType 'ClickTag = MouseEvent
363+ EventType 'ContextmenuTag = MouseEvent
364+ EventType 'DblclickTag = MouseEvent
365+ EventType 'DragTag = MouseEvent
366+ EventType 'DragendTag = MouseEvent
367+ EventType 'DragenterTag = MouseEvent
368+ EventType 'DragleaveTag = MouseEvent
369+ EventType 'DragoverTag = MouseEvent
370+ EventType 'DragstartTag = MouseEvent
371+ EventType 'DropTag = MouseEvent
372+ EventType 'ErrorTag = UIEvent
373+ EventType 'FocusTag = FocusEvent
374+ EventType 'InputTag = DOM.Event
375+ EventType 'InvalidTag = DOM.Event
376+ EventType 'KeydownTag = KeyboardEvent
377+ EventType 'KeypressTag = KeyboardEvent
378+ EventType 'KeyupTag = KeyboardEvent
379+ EventType 'LoadTag = UIEvent
380+ EventType 'MousedownTag = MouseEvent
381+ EventType 'MouseenterTag = MouseEvent
382+ EventType 'MouseleaveTag = MouseEvent
383+ EventType 'MousemoveTag = MouseEvent
384+ EventType 'MouseoutTag = MouseEvent
385+ EventType 'MouseoverTag = MouseEvent
386+ EventType 'MouseupTag = MouseEvent
387+ EventType 'MousewheelTag = MouseEvent
388+ EventType 'ScrollTag = UIEvent
389+ EventType 'SelectTag = UIEvent
390+ EventType 'SubmitTag = DOM.Event
391+ EventType 'WheelTag = WheelEvent
392+ {-
393+ EventType 'BeforecutTag = DOM.Event
394+ EventType 'CutTag = DOM.Event
395+ EventType 'BeforecopyTag = DOM.Event
396+ EventType 'CopyTag = DOM.Event
397+ EventType 'BeforepasteTag = DOM.Event
398+ EventType 'PasteTag = DOM.Event
399+-}
400+ EventType 'ResetTag = DOM.Event
401+ EventType 'SearchTag = DOM.Event
402+-- EventType 'SelectstartTag = DOM.Event
403+ EventType 'TouchstartTag = TouchEvent
404+ EventType 'TouchmoveTag = TouchEvent
405+ EventType 'TouchendTag = TouchEvent
406+ EventType 'TouchcancelTag = TouchEvent
407+
408+{-# INLINABLE defaultDomEventHandler #-}
409+defaultDomEventHandler :: IsElement e => e -> EventName en -> EventM e (EventType en) (Maybe (EventResult en))
410+defaultDomEventHandler e evt = fmap (Just . EventResult) $ case evt of
411+ Click -> return ()
412+ Dblclick -> return ()
413+ Keypress -> getKeyEvent
414+ Scroll -> getScrollTop e
415+ Keydown -> getKeyEvent
416+ Keyup -> getKeyEvent
417+ Mousemove -> getMouseEventCoords
418+ Mouseup -> getMouseEventCoords
419+ Mousedown -> getMouseEventCoords
420+ Mouseenter -> return ()
421+ Mouseleave -> return ()
422+ Focus -> return ()
423+ Blur -> return ()
424+ Change -> return ()
425+ Drag -> return ()
426+ Dragend -> return ()
427+ Dragenter -> return ()
428+ Dragleave -> return ()
429+ Dragover -> return ()
430+ Dragstart -> return ()
431+ Drop -> return ()
432+ Abort -> return ()
433+ Contextmenu -> return ()
434+ Error -> return ()
435+ Input -> return ()
436+ Invalid -> return ()
437+ Load -> return ()
438+ Mouseout -> return ()
439+ Mouseover -> return ()
440+ Select -> return ()
441+ Submit -> return ()
442+ {-
443+ Beforecut -> return ()
444+ Cut -> return ()
445+ Beforecopy -> return ()
446+ Copy -> return ()
447+ Beforepaste -> return ()
448+ Paste -> return ()
449+-}
450+ Reset -> return ()
451+ Search -> return ()
452+-- Selectstart -> return ()
453+ Touchstart -> return ()
454+ Touchmove -> return ()
455+ Touchend -> return ()
456+ Touchcancel -> return ()
457+ Mousewheel -> return ()
458+ Wheel -> return ()
459+
460+{-# INLINABLE defaultDomWindowEventHandler #-}
461+defaultDomWindowEventHandler :: DOM.Window -> EventName en -> EventM DOM.Window (EventType en) (Maybe (EventResult en))
462+defaultDomWindowEventHandler w evt = fmap (Just . EventResult) $ case evt of
463+ Click -> return ()
464+ Dblclick -> return ()
465+ Keypress -> getKeyEvent
466+ Scroll -> Window.getScrollY w
467+ Keydown -> getKeyEvent
468+ Keyup -> getKeyEvent
469+ Mousemove -> getMouseEventCoords
470+ Mouseup -> getMouseEventCoords
471+ Mousedown -> getMouseEventCoords
472+ Mouseenter -> return ()
473+ Mouseleave -> return ()
474+ Focus -> return ()
475+ Blur -> return ()
476+ Change -> return ()
477+ Drag -> return ()
478+ Dragend -> return ()
479+ Dragenter -> return ()
480+ Dragleave -> return ()
481+ Dragover -> return ()
482+ Dragstart -> return ()
483+ Drop -> return ()
484+ Abort -> return ()
485+ Contextmenu -> return ()
486+ Error -> return ()
487+ Input -> return ()
488+ Invalid -> return ()
489+ Load -> return ()
490+ Mouseout -> return ()
491+ Mouseover -> return ()
492+ Select -> return ()
493+ Submit -> return ()
494+ {-
495+ Beforecut -> return ()
496+ Cut -> return ()
497+ Beforecopy -> return ()
498+ Copy -> return ()
499+ Beforepaste -> return ()
500+ Paste -> return ()
501+-}
502+ Reset -> return ()
503+ Search -> return ()
504+-- Selectstart -> return ()
505+ Touchstart -> return ()
506+ Touchmove -> return ()
507+ Touchend -> return ()
508+ Touchcancel -> return ()
509+ Mousewheel -> return ()
510+ Wheel -> return ()
511+
512+{-# INLINABLE withIsEvent #-}
513+withIsEvent :: EventName en -> (IsEvent (EventType en) => r) -> r
514+withIsEvent en r = case en of
515+ Click -> r
516+ Dblclick -> r
517+ Keypress -> r
518+ Scroll -> r
519+ Keydown -> r
520+ Keyup -> r
521+ Mousemove -> r
522+ Mouseup -> r
523+ Mousedown -> r
524+ Mouseenter -> r
525+ Mouseleave -> r
526+ Focus -> r
527+ Blur -> r
528+ Change -> r
529+ Drag -> r
530+ Dragend -> r
531+ Dragenter -> r
532+ Dragleave -> r
533+ Dragover -> r
534+ Dragstart -> r
535+ Drop -> r
536+ Abort -> r
537+ Contextmenu -> r
538+ Error -> r
539+ Input -> r
540+ Invalid -> r
541+ Load -> r
542+ Mouseout -> r
543+ Mouseover -> r
544+ Select -> r
545+ Submit -> r
546+ {-
547+ Beforecut -> r
548+ Cut -> r
549+ Beforecopy -> r
550+ Copy -> r
551+ Beforepaste -> r
552+ Paste -> r
553+-}
554+ Reset -> r
555+ Search -> r
556+-- Selectstart -> r
557+ Touchstart -> r
558+ Touchmove -> r
559+ Touchend -> r
560+ Touchcancel -> r
561+ Mousewheel -> r
562+ Wheel -> r
563+
564+{-# INLINABLE elementOnEventName #-}
565+elementOnEventName :: IsElement e => EventName en -> e -> EventM e (EventType en) () -> IO (IO ())
566+elementOnEventName en e = case en of
567+ Abort -> on e Element.abort
568+ Blur -> on e Element.blurEvent
569+ Change -> on e Element.change
570+ Click -> on e Element.click
571+ Contextmenu -> on e Element.contextMenu
572+ Dblclick -> on e Element.dblClick
573+ Drag -> on e Element.drag
574+ Dragend -> on e Element.dragEnd
575+ Dragenter -> on e Element.dragEnter
576+ Dragleave -> on e Element.dragLeave
577+ Dragover -> on e Element.dragOver
578+ Dragstart -> on e Element.dragStart
579+ Drop -> on e Element.drop
580+ Error -> on e Element.error
581+ Focus -> on e Element.focusEvent
582+ Input -> on e Element.input
583+ Invalid -> on e Element.invalid
584+ Keydown -> on e Element.keyDown
585+ Keypress -> on e Element.keyPress
586+ Keyup -> on e Element.keyUp
587+ Load -> on e Element.load
588+ Mousedown -> on e Element.mouseDown
589+ Mouseenter -> on e Element.mouseEnter
590+ Mouseleave -> on e Element.mouseLeave
591+ Mousemove -> on e Element.mouseMove
592+ Mouseout -> on e Element.mouseOut
593+ Mouseover -> on e Element.mouseOver
594+ Mouseup -> on e Element.mouseUp
595+ Mousewheel -> on e Element.mouseWheel
596+ Scroll -> on e Element.scroll
597+ Select -> on e Element.select
598+ Submit -> on e Element.submit
599+ Wheel -> on e Element.wheel
600+ {-
601+ Beforecut -> on e Element.beforeCut
602+ Cut -> on e Element.cut
603+ Beforecopy -> on e Element.beforeCopy
604+ Copy -> on e Element.copy
605+ Beforepaste -> on e Element.beforePaste
606+ Paste -> on e Element.paste
607+-}
608+ Reset -> on e Element.reset
609+ Search -> on e Element.search
610+-- Selectstart -> on e Element.selectStart
611+ Touchstart -> on e Element.touchStart
612+ Touchmove -> on e Element.touchMove
613+ Touchend -> on e Element.touchEnd
614+ Touchcancel -> on e Element.touchCancel
615+
616+{-# INLINABLE windowOnEventName #-}
617+windowOnEventName :: EventName en -> DOM.Window -> EventM DOM.Window (EventType en) () -> IO (IO ())
618+windowOnEventName en e = case en of
619+ Abort -> on e Window.abort
620+ Blur -> on e Window.blurEvent
621+ Change -> on e Window.change
622+ Click -> on e Window.click
623+ Contextmenu -> on e Window.contextMenu
624+ Dblclick -> on e Window.dblClick
625+ Drag -> on e Window.drag
626+ Dragend -> on e Window.dragEnd
627+ Dragenter -> on e Window.dragEnter
628+ Dragleave -> on e Window.dragLeave
629+ Dragover -> on e Window.dragOver
630+ Dragstart -> on e Window.dragStart
631+ Drop -> on e Window.drop
632+ Error -> on e Window.error
633+ Focus -> on e Window.focusEvent
634+ Input -> on e Window.input
635+ Invalid -> on e Window.invalid
636+ Keydown -> on e Window.keyDown
637+ Keypress -> on e Window.keyPress
638+ Keyup -> on e Window.keyUp
639+ Load -> on e Window.load
640+ Mousedown -> on e Window.mouseDown
641+ Mouseenter -> on e Window.mouseEnter
642+ Mouseleave -> on e Window.mouseLeave
643+ Mousemove -> on e Window.mouseMove
644+ Mouseout -> on e Window.mouseOut
645+ Mouseover -> on e Window.mouseOver
646+ Mouseup -> on e Window.mouseUp
647+ Mousewheel -> on e Window.mouseWheel
648+ Scroll -> on e Window.scrollEvent
649+ Select -> on e Window.select
650+ Submit -> on e Window.submit
651+ Wheel -> on e Window.wheel
652+ {-
653+ Beforecut -> on e Window.beforeCut
654+ Cut -> on e Window.cut
655+ Beforecopy -> on e Window.beforeCopy
656+ Copy -> on e Window.copy
657+ Beforepaste -> on e Window.beforePaste
658+ Paste -> on e Window.paste
659+-}
660+ Reset -> on e Window.reset
661+ Search -> on e Window.search
662+-- Selectstart -> on e Window.selectStart
663+ Touchstart -> on e Window.touchStart
664+ Touchmove -> on e Window.touchMove
665+ Touchend -> on e Window.touchEnd
666+ Touchcancel -> on e Window.touchCancel
667+
668+{-# INLINABLE wrapDomEvent #-}
669+wrapDomEvent :: TriggerEvent t m => e -> (e -> EventM e event () -> IO (IO ())) -> EventM e event a -> m (Event t a)
670+wrapDomEvent el elementOnevent getValue = wrapDomEventMaybe el elementOnevent $ fmap Just getValue
671+
672+{-# INLINABLE subscribeDomEvent #-}
673+subscribeDomEvent :: (EventM e event () -> IO (IO ()))
674+ -> EventM e event (Maybe a)
675+ -> Chan [DSum (TriggerRef t) TriggerInvocation]
676+ -> EventTrigger t a
677+ -> IO (IO ())
678+subscribeDomEvent elementOnevent getValue eventChan et = elementOnevent $ do
679+ mv <- getValue
680+ forM_ mv $ \v -> liftIO $ do
681+ --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
682+ etr <- newIORef $ Just et
683+ writeChan eventChan [TriggerRef etr :=> TriggerInvocation v (return ())]
684+
685+{-# INLINABLE wrapDomEventMaybe #-}
686+wrapDomEventMaybe :: TriggerEvent t m
687+ => e
688+ -> (e -> EventM e event () -> IO (IO ()))
689+ -> EventM e event (Maybe a)
690+ -> m (Event t a)
691+wrapDomEventMaybe el elementOnevent getValue = do
692+ newEventWithLazyTriggerWithOnComplete $ \trigger -> elementOnevent el $ do
693+ mv <- getValue
694+ forM_ mv $ \v -> liftIO $ trigger v $ return ()
695+
696+{-# INLINABLE wrapDomEventsMaybe #-}
697+wrapDomEventsMaybe :: (MonadIO m, MonadReflexCreateTrigger t m)
698+ => e
699+ -> (forall en. IsEvent (EventType en) => EventName en -> EventM e (EventType en) (Maybe (f en)))
700+ -> (forall en. EventName en -> e -> EventM e (EventType en) () -> IO (IO ()))
701+ -> ImmediateDomBuilderT t m (EventSelector t (WrapArg f EventName))
702+wrapDomEventsMaybe target handlers onEventName = do
703+ eventChan <- askEvents
704+ e <- lift $ newFanEventWithTrigger $ \(WrapArg en) -> withIsEvent en $ subscribeDomEvent (onEventName en target) (handlers en) eventChan
705+ return $! e
706+
707+{-# INLINABLE getKeyEvent #-}
708+getKeyEvent :: EventM e KeyboardEvent Int
709+getKeyEvent = do
710+ e <- event
711+ which <- getWhich e
712+ if which /= 0 then return which else do
713+ charCode <- getCharCode e
714+ if charCode /= 0 then return charCode else
715+ getKeyCode e
716+
717+{-# INLINABLE getMouseEventCoords #-}
718+getMouseEventCoords :: EventM e MouseEvent (Int, Int)
719+getMouseEventCoords = do
720+ e <- event
721+ bisequence (getX e, getY e)
722+
723+instance MonadSample t m => MonadSample t (ImmediateDomBuilderT t m) where
724+ {-# INLINABLE sample #-}
725+ sample = lift . sample
726+
727+instance MonadHold t m => MonadHold t (ImmediateDomBuilderT t m) where
728+ {-# INLINABLE hold #-}
729+ hold v0 v' = lift $ hold v0 v'
730+ {-# INLINABLE holdDyn #-}
731+ holdDyn v0 v' = lift $ holdDyn v0 v'
732+ {-# INLINABLE holdIncremental #-}
733+ holdIncremental v0 v' = lift $ holdIncremental v0 v'
734+
735+data WindowConfig t = WindowConfig -- No config options yet
736+
737+instance Default (WindowConfig t) where
738+ def = WindowConfig
739+
740+data Window t = Window
741+ { _window_events :: EventSelector t (WrapArg EventResult EventName)
742+ , _window_raw :: DOM.Window
743+ }
744+
745+wrapWindow :: (MonadIO m, MonadReflexCreateTrigger t m) => DOM.Window -> WindowConfig t -> ImmediateDomBuilderT t m (Window t)
746+wrapWindow wv _ = do
747+ events <- wrapDomEventsMaybe wv (defaultDomWindowEventHandler wv) windowOnEventName
748+ return $ Window
749+ { _window_events = events
750+ , _window_raw = wv
751+ }
src/Reflex/Dom/Builder/Static.hsnew file+174
@@ -0,0 +1,174 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE EmptyDataDecls #-}
4+{-# LANGUAGE FlexibleContexts #-}
5+{-# LANGUAGE FlexibleInstances #-}
6+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
7+{-# LANGUAGE LambdaCase #-}
8+{-# LANGUAGE MultiParamTypeClasses #-}
9+{-# LANGUAGE OverloadedStrings #-}
10+{-# LANGUAGE PolyKinds #-}
11+{-# LANGUAGE RankNTypes #-}
12+{-# LANGUAGE ScopedTypeVariables #-}
13+{-# LANGUAGE TypeFamilies #-}
14+{-# LANGUAGE TypeOperators #-}
15+{-# LANGUAGE UndecidableInstances #-}
16+module Reflex.Dom.Builder.Static where
17+
18+import Control.Monad.Identity
19+import Control.Monad.Ref
20+import Data.Dependent.Sum (DSum (..))
21+import Reflex
22+import Reflex.Dom.Builder.Class
23+import Reflex.Dom.PerformEvent.Base
24+import Reflex.Dom.PerformEvent.Class
25+import Reflex.Dom.PostBuild.Class
26+import Reflex.Dom.Widget.Basic (applyMap)
27+import Reflex.Host.Class
28+
29+import Blaze.ByteString.Builder.Html.Utf8
30+import Control.Lens hiding (element)
31+import Control.Monad.Exception
32+import Control.Monad.State.Strict
33+import Control.Monad.Trans.Control
34+import Data.ByteString (ByteString)
35+import Data.ByteString.Builder (toLazyByteString)
36+import qualified Data.ByteString.Lazy as BL
37+import qualified Data.Map as Map
38+import Data.Monoid
39+import Data.Text.Encoding
40+
41+
42+newtype StaticDomBuilderT t m a = StaticDomBuilderT
43+ { unStaticDomBuilderT :: StateT [Behavior t ByteString] m a -- Accumulated Html will be in revesed order
44+ }
45+ deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
46+
47+instance MonadTransControl (StaticDomBuilderT t) where
48+ type StT (StaticDomBuilderT t) a = StT (StateT [Behavior t ByteString]) a
49+ liftWith = defaultLiftWith StaticDomBuilderT unStaticDomBuilderT
50+ restoreT = defaultRestoreT StaticDomBuilderT
51+
52+instance MonadTrans (StaticDomBuilderT t) where
53+ lift = StaticDomBuilderT . lift
54+
55+runStaticDomBuilderT :: (Monad m, Reflex t) => StaticDomBuilderT t m a -> m (a, Behavior t ByteString)
56+runStaticDomBuilderT (StaticDomBuilderT a) = do
57+ (result, a') <- runStateT a []
58+ return (result, mconcat $ reverse a')
59+
60+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (StaticDomBuilderT t m) where
61+ {-# INLINABLE newEventWithTrigger #-}
62+ newEventWithTrigger = lift . newEventWithTrigger
63+ {-# INLINABLE newFanEventWithTrigger #-}
64+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
65+
66+instance PerformEvent t m => PerformEvent t (StaticDomBuilderT t m) where
67+ type Performable (StaticDomBuilderT t m) = Performable m
68+ {-# INLINABLE performEvent_ #-}
69+ performEvent_ e = lift $ performEvent_ e
70+ {-# INLINABLE performEvent #-}
71+ performEvent e = lift $ performEvent e
72+
73+instance MonadSample t m => MonadSample t (StaticDomBuilderT t m) where
74+ {-# INLINABLE sample #-}
75+ sample = lift . sample
76+
77+instance MonadHold t m => MonadHold t (StaticDomBuilderT t m) where
78+ {-# INLINABLE hold #-}
79+ hold v0 v' = lift $ hold v0 v'
80+ {-# INLINABLE holdDyn #-}
81+ holdDyn v0 v' = lift $ holdDyn v0 v'
82+ {-# INLINABLE holdIncremental #-}
83+ holdIncremental v0 v' = lift $ holdIncremental v0 v'
84+
85+instance (Reflex t, Monad m, MonadHold t (StateT [Behavior t ByteString] m)) => Deletable t (StaticDomBuilderT t m) where
86+ {-# INLINABLE deletable #-}
87+ deletable delete (StaticDomBuilderT a) = StaticDomBuilderT $ do
88+ (result, a') <- lift $ runStateT a []
89+ let html = mconcat $ reverse a'
90+ b <- hold html (mempty <$ delete)
91+ modify (join b:)
92+ return result
93+
94+instance (Monad m, Ref m ~ Ref IO, Reflex t) => TriggerEvent t (StaticDomBuilderT t m) where
95+ {-# INLINABLE newTriggerEvent #-}
96+ newTriggerEvent = return (never, const $ return ())
97+ {-# INLINABLE newTriggerEventWithOnComplete #-}
98+ newTriggerEventWithOnComplete = return (never, \_ _ -> return ())
99+ {-# INLINABLE newEventWithLazyTriggerWithOnComplete #-}
100+ newEventWithLazyTriggerWithOnComplete _ = return never
101+
102+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)
103+
104+data StaticDomSpace
105+
106+-- | Static documents never produce any events, so this type has no inhabitants
107+data StaticDomEvent (a :: k)
108+
109+-- | Static documents don't process events, so all handlers are equivalent
110+data StaticDomHandler (a :: k) (b :: k) = StaticDomHandler
111+
112+instance DomSpace StaticDomSpace where
113+ type RawElement StaticDomSpace = ByteString
114+ type RawEvent StaticDomSpace = StaticDomEvent
115+ type DomHandler StaticDomSpace = StaticDomHandler
116+ type DomHandler1 StaticDomSpace = StaticDomHandler
117+ defaultEventHandler _ = StaticDomHandler
118+
119+instance SupportsStaticDomBuilder t m => DomBuilder t (StaticDomBuilderT t m) where
120+ type DomBuilderSpace (StaticDomBuilderT t m) = StaticDomSpace
121+ {-# INLINABLE textNode #-}
122+ textNode (TextNodeConfig initialContents setContents) = StaticDomBuilderT $ do
123+ let escape = BL.toStrict . toLazyByteString . fromHtmlEscapedText
124+ modify . (:) <=< hold (escape initialContents) $ fmap escape setContents
125+ return TextNode
126+
127+ {-# INLINABLE element #-}
128+ element elementTag cfg child = do
129+ let toAttr (_mns, k) v = encodeUtf8 k <> "=\"" <> BL.toStrict (toLazyByteString $ fromHtmlEscapedText v) <> "\""
130+ es <- newFanEventWithTrigger $ \_ _ -> return (return ())
131+ StaticDomBuilderT $ do
132+ (result, innerHtml) <- lift $ runStaticDomBuilderT child
133+ attrs0 <- foldDyn applyMap (cfg ^. initialAttributes) (cfg ^. modifyAttributes)
134+ let attrs1 = ffor (current attrs0) $ mconcat . fmap (\(k, v) -> " " <> toAttr k v) . Map.toList
135+ let tagBS = encodeUtf8 elementTag
136+ let open = mconcat [constant ("<" <> tagBS <> " "), attrs1, constant ">"]
137+ let close = constant $ "</" <> tagBS <> ">" -- TODO handle elements without closing tags
138+ modify $ (:) $ mconcat [open, innerHtml, close]
139+ return (Element es (error "Static.element RawElement was used"), result)
140+
141+ {-# INLINABLE placeholder #-}
142+ placeholder (PlaceholderConfig toInsertAbove _delete) = StaticDomBuilderT $ do
143+ result <- lift $ performEvent (fmap runStaticDomBuilderT toInsertAbove)
144+ acc <- foldDyn (:) [] (fmap snd result)
145+ modify $ (:) $ join $ mconcat . reverse <$> current acc
146+ return $ Placeholder (fmap fst result) never
147+
148+ {-# INLINABLE inputElement #-}
149+ inputElement cfg = do
150+ (e, _result) <- element "input" (cfg ^. inputElementConfig_elementConfig) $ return ()
151+ let v0 = constDyn $ cfg ^. inputElementConfig_initialValue
152+ let c0 = constDyn $ cfg ^. inputElementConfig_initialChecked
153+ let hasFocus = constDyn False -- TODO should this be coming from initialAtttributes
154+ return $ InputElement v0 c0 never hasFocus e
155+
156+ {-# INLINABLE textAreaElement #-}
157+ textAreaElement cfg = do
158+ (e, _domElement) <- element "textarea" (cfg ^. textAreaElementConfig_elementConfig) $ return ()
159+ let v0 = constDyn $ cfg ^. textAreaElementConfig_initialValue
160+ let hasFocus = constDyn False -- TODO should this be coming from initialAtttributes
161+ return $ TextAreaElement v0 never hasFocus e
162+
163+--TODO: Make this more abstract --TODO: Put the WithWebView underneath PerformEventT - I think this would perform better
164+type StaticWidget x = PostBuildT Spider (StaticDomBuilderT Spider (PerformEventT Spider (SpiderHost Global)))
165+
166+renderStatic :: StaticWidget x a -> IO (a, ByteString)
167+renderStatic w = do
168+ runSpiderHost $ do
169+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
170+ ((res, bs), FireCommand fire) <- hostPerformEventT $ runStaticDomBuilderT (runPostBuildT w postBuild)
171+ mPostBuildTrigger <- readRef postBuildTriggerRef
172+ forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
173+ bs' <- sample bs
174+ return (res, bs')
src/Reflex/Dom/Class.hs+20−445
@@ -1,459 +1,34 @@
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 #-}
2−module Reflex.Dom.Class where
1+{-# LANGUAGE MultiParamTypeClasses #-}
2+module Reflex.Dom.Class ( module Reflex.Dom.Class
3+ , module Foreign.JavaScript.TH
4+ , module Web.KeyCode
5+ ) where
36
4−import Prelude hiding (mapM, mapM_, sequence, concat)
5−
6−import Reflex
7−import Reflex.Host.Class
8−
9−import Control.Monad.Identity hiding (mapM, mapM_, forM, forM_, sequence)
107import Data.Map (Map)
118import qualified Data.Map as Map
12−import Data.Foldable
13−import Control.Monad.Ref
14−import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence)
15−import Control.Monad.State hiding (mapM, mapM_, forM, forM_, sequence)
16−import Data.Dependent.Sum (DSum (..))
17−import GHCJS.DOM.Types hiding (Event)
18−import GHCJS.DOM (WebView)
19−import Control.Monad.Exception
9+import Reflex.Class
10+import Web.KeyCode
11+
12+import Foreign.JavaScript.TH
13+import Reflex.Dom.PerformEvent.Class
14+import Reflex.Dom.PostBuild.Class
15+
16+import Prelude hiding (concat, mapM, mapM_, sequence)
2017
2118-- | Alias for Data.Map.singleton
2219(=:) :: k -> a -> Map k a
2320(=:) = Map.singleton
21+infixr 7 =: -- Ought to bind tighter than <>, which is infixr 6
2422
23+{-# DEPRECATED keycodeEnter "Instead of `x == keycodeEnter`, use `keyCodeLookup x == Enter`" #-}
2524keycodeEnter :: Int
2625keycodeEnter = 13
2726
27+{-# DEPRECATED keycodeEscape "Instead of `x == keycodeEscape`, use `keyCodeLookup x == Escape`" #-}
2828keycodeEscape :: Int
2929keycodeEscape = 27
3030
31−class ( Reflex t, MonadHold t m, MonadIO m, MonadAsyncException m, Functor m, MonadReflexCreateTrigger t m
32− , HasDocument m, HasWebView m, HasWebView (WidgetHost m), HasWebView (GuiAction m)
33− , MonadIO (WidgetHost m), MonadAsyncException (WidgetHost m), MonadIO (GuiAction m), MonadAsyncException (GuiAction m), Functor (WidgetHost m), MonadSample t (WidgetHost m)
34− , HasPostGui t (GuiAction m) (WidgetHost m), HasPostGui t (GuiAction m) m, HasPostGui t (GuiAction m) (GuiAction m)
35− , MonadRef m, MonadRef (WidgetHost m)
36− , Ref m ~ Ref IO, Ref (WidgetHost m) ~ Ref IO --TODO: Eliminate this reliance on IO
37− , MonadFix m
38− ) => MonadWidget t m | m -> t where
39− type WidgetHost m :: * -> *
40− type GuiAction m :: * -> *
41− askParent :: m Node
42− subWidget :: Node -> m a -> m a
43− subWidgetWithVoidActions :: Node -> m a -> m (a, Event t (WidgetHost m ()))
44− liftWidgetHost :: WidgetHost m a -> m a --TODO: Is this a good idea?
45− schedulePostBuild :: WidgetHost m () -> m ()
46− addVoidAction :: Event t (WidgetHost m ()) -> m ()
47− getRunWidget :: IsNode n => m (n -> m a -> WidgetHost m (a, WidgetHost m (), Event t (WidgetHost m ())))
48−
49−class Monad m => HasDocument m where
50− askDocument :: m HTMLDocument
51−
52−instance HasDocument m => HasDocument (ReaderT r m) where
53− askDocument = lift askDocument
54−
55−instance HasDocument m => HasDocument (StateT r m) where
56− askDocument = lift askDocument
57−
58−class Monad m => HasWebView m where
59− askWebView :: m WebView
60−
61−instance HasWebView m => HasWebView (ReaderT r m) where
62− askWebView = lift askWebView
63−
64−instance HasWebView m => HasWebView (StateT r m) where
65− askWebView = lift askWebView
66−
67−newtype Restore m = Restore { restore :: forall a. m a -> IO a }
68−
69−class Monad m => MonadIORestore m where
70− askRestore :: m (Restore m)
71−
72−instance MonadIORestore m => MonadIORestore (ReaderT r m) where
73− askRestore = do
74− r <- ask
75− parentRestore <- lift askRestore
76− return $ Restore $ \a -> restore parentRestore $ runReaderT a r
77−
78−class (MonadRef h, Ref h ~ Ref m, MonadRef m) => HasPostGui t h m | m -> t h where
79− askPostGui :: m (h () -> IO ())
80− askRunWithActions :: m ([DSum (EventTrigger t) Identity] -> h ())
81−
82−runFrameWithTriggerRef :: (HasPostGui t h m, MonadRef m, MonadIO m) => Ref m (Maybe (EventTrigger t a)) -> a -> m ()
83−runFrameWithTriggerRef r a = do
84− postGui <- askPostGui
85− runWithActions <- askRunWithActions
86− liftIO . postGui $ mapM_ (\t -> runWithActions [t :=> Identity a]) =<< readRef r
87−
88−instance HasPostGui t h m => HasPostGui t h (ReaderT r m) where
89− askPostGui = lift askPostGui
90− askRunWithActions = lift askRunWithActions
91−
92−instance MonadWidget t m => MonadWidget t (ReaderT r m) where
93− type WidgetHost (ReaderT r m) = WidgetHost m
94− type GuiAction (ReaderT r m) = GuiAction m
95− askParent = lift askParent
96− subWidget n w = do
97− r <- ask
98− lift $ subWidget n $ runReaderT w r
99− subWidgetWithVoidActions n w = do
100− r <- ask
101− lift $ subWidgetWithVoidActions n $ runReaderT w r
102− liftWidgetHost = lift . liftWidgetHost
103− schedulePostBuild = lift . schedulePostBuild
104− addVoidAction = lift . addVoidAction
105− getRunWidget = do
106− r <- ask
107− runWidget <- lift getRunWidget
108− return $ \rootElement w -> do
109− (a, postBuild, voidActions) <- runWidget rootElement $ runReaderT w r
110− return (a, postBuild, voidActions)
111−
112−performEvent_ :: MonadWidget t m => Event t (WidgetHost m ()) -> m ()
113−performEvent_ = addVoidAction
114−
115−performEvent :: (MonadWidget t m, Ref m ~ Ref IO) => Event t (WidgetHost m a) -> m (Event t a)
116−performEvent e = do
117− (eResult, reResultTrigger) <- newEventWithTriggerRef
118− addVoidAction $ ffor e $ \o -> do
119− result <- o
120− runFrameWithTriggerRef reResultTrigger result
121− return eResult
122−
123−performEventAsync :: forall t m a. MonadWidget t m => Event t ((a -> IO ()) -> WidgetHost m ()) -> m (Event t a)
124−performEventAsync e = do
125− (eResult, reResultTrigger) <- newEventWithTriggerRef
126− addVoidAction $ ffor e $ \o -> do
127− postGui <- askPostGui
128− runWithActions <- askRunWithActions
129− o $ \a -> postGui $ mapM_ (\t -> runWithActions [t :=> Identity a]) =<< readRef reResultTrigger
130− return eResult
131−
132−getPostBuild :: MonadWidget t m => m (Event t ())
133−getPostBuild = do
134− (e, trigger) <- newEventWithTriggerRef
135− schedulePostBuild $ runFrameWithTriggerRef trigger ()
136− return e
137−
138−--------------------------------------------------------------------------------
139−--------------------------------------------------------------------------------
140−{-
141−
142−class HasRunFrame t m | m -> t where
143− askRunFrame :: m (DMap (EventTrigger t) -> IO ())
144−
145−type MonadWidget t h m = (MonadWidget' t h m, MonadWidget' t h (WidgetEventM m))
146−
147−--TODO: Remove Spider hardcoding
148−class (t ~ Spider, Reflex t, MonadHold t m, ReflexHost t, HasRunFrame t h, MonadReflexHost t h, MonadIO h, MonadRef h, Ref h ~ IORef, MonadFix h, HasDocument h, MonadSample t (WidgetEventM m), WidgetEventM m ~ WidgetEventM (WidgetEventM m), MonadFix m) => MonadWidget' t h m | m -> t h where
149−
150− type WidgetEventM m :: * -> *
151− -- | Warning: dAttributes should not contain "id" attributes
152− elDynAttr' :: String -> Dynamic t (Map String String) -> m a -> m (El t, a)
153− performEvent :: Event t (h a) -> m (Event t a)
154− performEvent_ :: Event t (h ()) -> m ()
155− getEInit :: m (Event t ())
156− putEChildren :: Event t [Node] -> m ()
157− dyn :: Dynamic t (WidgetEventM m a) -> m (Event t a) --TODO: Should probably return Dynamic t a
158− listWithKey :: (Ord k, Show k) => Dynamic t (Map k v) -> (k -> Dynamic t v -> WidgetEventM m v') -> m (Dynamic t (Map k v'))
159− text :: String -> m ()
160− dynText :: Dynamic t String -> m ()
161−
162−{-# INLINABLE performEventAsync #-}
163−performEventAsync :: forall t h m a. (MonadWidget t h m) => Event t ((a -> IO ()) -> h ()) -> m (Event t a)
164−performEventAsync eAction = do
165− let setup mAction = do
166− (eResult, reResultTrigger) <- newEventWithTriggerRef
167− runFrame <- askRunFrame
168− let runAction a = a $ \x -> readRef reResultTrigger >>= mapM_ (\eResultTrigger -> runFrame $ DMap.singleton eResultTrigger x)
169− maybe (return ()) runAction mAction
170− return (eResult, runAction)
171− eSetup <- performEvent . fmap (setup . (^?here)) . align eAction =<< getEInit
172− dRunAction <- holdDyn Nothing $ fmap (Just . snd) eSetup
173− performEvent_ $ attachDynWith (\mRunAction a -> maybe (return ()) ($ a) mRunAction) dRunAction eAction -- Note: this will never fire while eInit is firing, but that situation should be handled in setup; this relies on all the results from the first performEvent being fired simultaneously; otherwise, an eAction could potentially sneak in between setup being sent to performEvent and the result coming back
174− switchPromptly never (fmap fst eSetup)
175−
176−data TextInput t
177− = TextInput { _textInput_value :: Dynamic t String
178− , _textInput_keypress :: Event t Int
179− , _textInput_keydown :: Event t Int
180− , _textInput_keyup :: Event t Int
181− , _textInput_hasFocus :: Dynamic t Bool
182− , _textInput_element :: Dynamic t (Maybe HTMLInputElement)
183− }
184−
185−textInputGetEnter :: Reflex t => TextInput t -> Event t ()
186−textInputGetEnter i = fmapMaybe (\n -> if n == keycodeEnter then Just () else Nothing) $ _textInput_keypress i
187−
188−setElementAttributes :: IsElement self => self -> Map String String -> IO ()
189−setElementAttributes e attrs = do
190− oldAttrs <- maybe (return Set.empty) namedNodeMapGetNames =<< elementGetAttributes e
191− forM_ (Set.toList $ oldAttrs `Set.difference` Map.keysSet attrs) $ elementRemoveAttribute e
192− iforM_ attrs $ elementSetAttribute e
193−
194−wrapDomEvent :: (HasRunFrame t h, MonadIO m, MonadReflexHost t h, MonadIO h) => e -> (e -> m () -> IO (IO ())) -> m a -> h (Event t a)
195−wrapDomEvent self elementOnevent getValue = do
196− runFrame <- askRunFrame
197− e <- newEventWithTrigger $ \et -> do
198− unsubscribe <- {-# SCC "a" #-} liftIO $ {-# SCC "b" #-} elementOnevent self $ {-# SCC "c" #-} do
199− v <- {-# SCC "d" #-} getValue
200− liftIO $ runFrame $ DMap.singleton et v
201− return $ liftIO $ do
202− {-# SCC "e" #-} unsubscribe
203− return $! {-# SCC "f" #-} e
204−
205−{-# INLINABLE input #-}
206−input' :: MonadWidget t h m => String -> Event t String -> Dynamic t (Map String String) -> m (TextInput t)
207−input' inputType eSetValue dAttrs = do
208− dEffectiveAttrs <- mapDyn (Map.insert "type" inputType) dAttrs
209− let mkSelf (initialValue, initialAttrs) = do
210− doc <- askDocument
211− Just e <- liftIO $ liftM (fmap castToHTMLInputElement) $ documentCreateElement doc "input"
212− liftIO $ htmlInputElementSetValue e initialValue
213− iforM_ initialAttrs $ \attr value -> liftIO $ elementSetAttribute e attr value
214− eChange <- wrapDomEvent e elementOninput $ liftIO $ htmlInputElementGetValue e
215− runFrame <- askRunFrame
216− eChangeFocus <- newEventWithTrigger $ \eChangeFocusTrigger -> do
217− unsubscribeOnblur <- liftIO $ elementOnblur e $ liftIO $ do
218− runFrame $ DMap.singleton eChangeFocusTrigger False
219− unsubscribeOnfocus <- liftIO $ elementOnfocus e $ liftIO $ do
220− runFrame $ DMap.singleton eChangeFocusTrigger True
221− return $ liftIO $ unsubscribeOnblur >> unsubscribeOnfocus
222− eKeypress <- wrapDomEvent e elementOnkeypress $ liftIO . uiEventGetKeyCode =<< event
223− eKeydown <- wrapDomEvent e elementOnkeydown $ liftIO . uiEventGetKeyCode =<< event
224− eKeyup <- wrapDomEvent e elementOnkeyup $ liftIO . uiEventGetKeyCode =<< event
225− return (e, eChange, eKeypress, eKeydown, eKeyup, eChangeFocus)
226− dInitialValue <- holdDyn "" eSetValue
227− dInitial <- combineDyn (,) dInitialValue dEffectiveAttrs
228− eCreated <- performEvent . fmap mkSelf . tagDyn dInitial =<< getEInit
229− dMyElement <- holdDyn Nothing $ fmap (Just . (^. _1)) eCreated
230− performEvent_ . updated =<< combineDyn (maybe (const $ return ()) $ \e attrs -> liftIO $ setElementAttributes e attrs) dMyElement dEffectiveAttrs
231− putEChildren $ fmap ((:[]) . toNode . (^. _1)) eCreated
232− performEvent_ $ fmapMaybe (fmap $ \(e,v) -> liftIO $ htmlInputElementSetValue e v) $ attachWith (\e v -> case e of
233− Nothing -> Nothing
234− Just e' -> Just (e',v)) (current dMyElement) eSetValue
235− eChange <- liftM switch $ hold never $ fmap (^. _2) eCreated
236− dFocus <- holdDyn False . switch =<< hold never (fmap (^. _6) eCreated) --TODO: Check that 'False' is always the correct starting value - i.e.: an element will always receive an 'onfocus' event, even if it gets focus immediately
237− dValue <- holdDyn "" $ leftmost [eSetValue, eChange]
238− eKeypress <- liftM switch $ hold never $ fmap (^. _3) eCreated
239− eKeydown <- liftM switch $ hold never $ fmap (^. _4) eCreated
240− eKeyup <- liftM switch $ hold never $ fmap (^. _5) eCreated
241− return $ TextInput dValue eKeypress eKeydown eKeyup dFocus dMyElement
242−
243−input :: MonadWidget t h m => String -> Map String String -> m (TextInput t)
244−input t as = input' t never (constDyn as)
245−
246−data TextArea t
247− = TextArea { _textArea_value :: Dynamic t String
248− , _textArea_element :: Dynamic t (Maybe HTMLTextAreaElement)
249− }
250−
251−textArea' :: MonadWidget t h m => Event t String -> Dynamic t (Map String String) -> m (TextArea t)
252−textArea' eSetValue dAttrs = do
253− let mkSelf (initialValue, initialAttrs) = do
254− doc <- askDocument
255− Just e <- liftIO $ liftM (fmap castToHTMLTextAreaElement) $ documentCreateElement doc "textarea"
256− liftIO $ htmlTextAreaElementSetValue e initialValue
257− iforM_ initialAttrs $ \attr value -> liftIO $ elementSetAttribute e attr value
258− eChange <- wrapDomEvent e elementOninput $ liftIO $ htmlTextAreaElementGetValue e
259− return (e, eChange)
260− dInitialValue <- holdDyn "" eSetValue
261− dInitial <- combineDyn (,) dInitialValue dAttrs
262− eCreated <- performEvent . fmap mkSelf . tagDyn dInitial =<< getEInit
263− dMyElement <- holdDyn Nothing $ fmap (Just . fst) eCreated
264− --performEvent . updated =<< combineDyn (maybe (const $ return ()) $ \e attrs -> liftIO $ setElementAttributes e attrs) dMyElement dAttrs
265− putEChildren $ fmap ((:[]) . toNode . fst) eCreated
266− performEvent_ $ fmapMaybe (fmap $ \(e,v) -> liftIO $ htmlTextAreaElementSetValue e v) $ attachWith (\e v -> case e of
267− Nothing -> Nothing
268− Just e' -> Just (e',v)) (current dMyElement) eSetValue
269− eChange <- liftM switch $ hold never $ fmap snd eCreated
270− dValue <- holdDyn "" $ leftmost [eSetValue, eChange]
271− return $ TextArea dValue dMyElement
272−
273−
274−data Checkbox t
275− = Checkbox { _checkbox_checked :: Dynamic t Bool
276− }
277−
278−checkbox :: MonadWidget t h m => Bool -> Map String String -> m (Checkbox t)
279−checkbox checked attrs = do
280− let mkSelf = do
281− doc <- askDocument
282− Just e <- liftIO $ liftM (fmap castToHTMLInputElement) $ documentCreateElement doc "input"
283− liftIO $ elementSetAttribute e "type" "checkbox"
284− iforM_ attrs $ \attr value -> liftIO $ elementSetAttribute e attr value
285− if checked == True then liftIO $ elementSetAttribute e "checked" "true" else return ()
286− eChange <- wrapDomEvent e elementOnclick $ liftIO $ htmlInputElementGetChecked e
287− return ([toNode e], eChange)
288− eCreated <- performEvent . fmap (const mkSelf) =<< getEInit
289− putEChildren $ fmap (^. _1) eCreated
290− eChange <- liftM switch $ hold never $ fmap (^. _2) eCreated
291− dValue <- holdDyn checked eChange
292− return $ Checkbox dValue
293−
294−checkboxView :: MonadWidget t h m => Dynamic t (Map String String) -> Dynamic t Bool -> m (Event t ())
295−checkboxView dAttrs dValue = do
296− dEffectiveAttrs <- mapDyn (Map.insert "type" "checkbox") dAttrs
297− let mkSelf (initialValue, initialAttrs) = do
298− doc <- askDocument
299− Just e <- liftIO $ liftM (fmap castToHTMLInputElement) $ documentCreateElement doc "input"
300− iforM_ initialAttrs $ \attr value -> liftIO $ elementSetAttribute e attr value
301− liftIO $ htmlInputElementSetChecked e initialValue
302− eClicked <- wrapDomEvent e elementOnclick preventDefault
303− return (e, eClicked)
304− dInitial <- combineDyn (,) dValue dEffectiveAttrs
305− eCreated <- performEvent . fmap mkSelf . tagDyn dInitial =<< getEInit
306− putEChildren $ fmap ((:[]) . toNode . (^. _1)) eCreated
307− dElement <- holdDyn Nothing $ fmap (Just . (^. _1)) eCreated
308− performEvent_ . updated =<< combineDyn (\me a -> forM_ me $ \e -> liftIO $ setElementAttributes e a) dElement dEffectiveAttrs
309− performEvent_ . updated =<< combineDyn (\v me -> maybe (return ()) (\e -> liftIO $ htmlInputElementSetChecked e v) me) dValue dElement
310− liftM switch $ hold never $ fmap (^. _2) eCreated
311−
312−{-# INLINABLE textInput #-}
313−textInput :: MonadWidget t h m => m (TextInput t)
314−textInput = input "text" $ Map.singleton "class" "form-control"
315−
316−data Dropdown t k
317− = Dropdown { _dropdown_value :: Dynamic t k
318− }
319−
320−setInnerText :: (HasDocument m, IsHTMLElement self, MonadIO m) => self -> String -> m (Maybe Node)
321−setInnerText e s = do
322− liftIO $ htmlElementSetInnerHTML e ""
323− doc <- askDocument
324− t <- liftIO $ documentCreateTextNode doc s
325− liftIO $ nodeAppendChild e t
326−
327−--TODO: Retain set value when allowed values changes
328−{-# INLINABLE dropdown #-}
329−dropdown :: forall t h m k. (MonadWidget t h m, Show k, Read k) => Dynamic t (Map k String) -> m (Dropdown t (Maybe k))
330−dropdown dOptions = do
331− let triggerChange (e, reChangeTrigger) runFrame = do
332− v <- htmlSelectElementGetValue e
333− readRef reChangeTrigger >>= mapM_ (\eChangeTrigger -> runFrame $ DMap.singleton eChangeTrigger $ readMay v)
334− let mkSelf = do
335− doc <- askDocument
336− Just e <- liftIO $ liftM (fmap castToHTMLSelectElement) $ documentCreateElement doc "select"
337− liftIO $ elementSetAttribute e "class" "form-control"
338− runFrame <- askRunFrame
339− reChangeTrigger <- newRef Nothing
340− eChange <- newEventWithTrigger $ \eChangeTrigger -> do
341− writeRef reChangeTrigger $ Just eChangeTrigger
342− unsubscribe <- liftIO $ elementOnchange e $ liftIO $ triggerChange (e, reChangeTrigger) runFrame
343− return $ do
344− writeRef reChangeTrigger Nothing
345− liftIO unsubscribe
346− return ((e, reChangeTrigger), eChange)
347− eCreated <- performEvent . fmap (const mkSelf) =<< getEInit
348− dState <- holdDyn Nothing $ fmap (Just . (^. _1)) eCreated
349− putEChildren $ fmap ((:[]) . toNode . fst . (^. _1)) eCreated
350− let updateOptions :: Map k String -> Maybe (HTMLSelectElement, Ref h (Maybe (EventTrigger t (Maybe k)))) -> h ()
351− updateOptions options = maybe (return ()) $ \myState@(selectElement, _) -> do
352− doc <- askDocument
353− liftIO $ htmlElementSetInnerHTML selectElement ""
354− iforM_ options $ \k optionText -> do
355− Just optionElement <- liftIO $ liftM (fmap castToHTMLOptionElement) $ documentCreateElement doc "option"
356− liftIO $ elementSetAttribute optionElement "value" $ show k
357− _ <- setInnerText optionElement optionText
358− liftIO $ nodeAppendChild selectElement $ Just optionElement
359− runFrame <- askRunFrame
360− liftIO $ triggerChange myState runFrame
361− performEvent_ . updated =<< combineDyn updateOptions dOptions dState
362− eChange <- liftM switch $ hold never $ fmap (^. _2) eCreated
363− dValue <- holdDyn Nothing eChange
364− return $ Dropdown dValue
365−
366−data Link t
367− = Link { _link_clicked :: Event t ()
368− }
369−
370−linkClassWithExtraSetup :: MonadWidget t h m => (HTMLElement -> h ()) -> String -> String -> m (Link t)
371−linkClassWithExtraSetup extraSetup s c = do
372− let mkSelf = do
373− doc <- askDocument
374− Just e <- liftIO $ liftM (fmap castToHTMLElement) $ documentCreateElement doc "a"
375− _ <- setInnerText e s
376− liftIO $ elementSetAttribute e "class" c
377− eClicked <- wrapDomEvent e elementOnclick $ return ()
378− extraSetup e
379− return ([toNode e], eClicked)
380− eCreated <- performEvent . fmap (const mkSelf) =<< getEInit
381− putEChildren $ fmap (^. _1) eCreated
382− eClicked <- liftM switch $ hold never $ fmap (^. _2) eCreated
383− return $ Link eClicked
384−
385−{-# INLINABLE linkClass #-}
386−linkClass :: MonadWidget t h m => String -> String -> m (Link t)
387−linkClass = linkClassWithExtraSetup $ const $ return ()
388−
389−{-# INLINABLE link #-}
390−link :: MonadWidget t h m => String -> m (Link t)
391−link s = linkClass s ""
392−
393−{-# INLINABLE button #-}
394−button :: MonadWidget t h m => String -> m (Link t)
395−button s = linkClass s "btn btn-primary"
396−
397−{-# INLINABLE elAttr #-}
398−elAttr :: forall t h m a. MonadWidget t h m => String -> Map String String -> m a -> m a
399−elAttr elementTag attrs child = liftM snd $ elAttr' elementTag attrs child
400−
401−{-# INLINABLE el' #-}
402−el' :: forall t h m a. MonadWidget t h m => String -> m a -> m (El t, a)
403−el' elementTag child = elAttr' elementTag Map.empty child
404−
405−{-# INLINABLE elAttr' #-}
406−elAttr' :: forall t h m a. MonadWidget t h m => String -> Map String String -> m a -> m (El t, a)
407−elAttr' elementTag attrs child = do
408− dAttributes <- holdDyn attrs never
409− elDynAttr' elementTag dAttributes child
410−
411−{-# INLINABLE elDynAttr #-}
412−elDynAttr :: forall t h m a. MonadWidget t h m => String -> Dynamic t (Map String String) -> m a -> m a
413−elDynAttr elementTag dAttributes child = liftM snd $ elDynAttr' elementTag dAttributes child
414−
415−{-# INLINABLE el #-}
416−el :: forall t h m a. MonadWidget t h m => String -> m a -> m a
417−el elementTag child = elAttr elementTag Map.empty child
418−
419−newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
420−
421−workflowView :: forall t h m a. MonadWidget t h m => Workflow t (WidgetEventM m) a -> m (Event t a)
422−workflowView w0 = do
423− rec eResult <- dyn =<< mapDyn unWorkflow =<< holdDyn w0 eReplace
424− eReplace <- liftM switch $ hold never $ fmap snd eResult
425− eResult `seq` eReplace `seq` return ()
426− return $ fmap fst eResult
427−
428−insertNext :: (Ord k, Enum k) => v -> Map k v -> Map k v
429−insertNext v m =
430− let k = case Map.maxViewWithKey m of
431− Nothing -> toEnum 0
432− Just ((k0, _), _) -> succ k0
433− in Map.insert k v m
434−
435−{-# INLINABLE oldList #-}
436−oldList :: (MonadWidget t h m, MonadWidget t h (WidgetEventM m), Ord k, Show k) => Dynamic t (Map k (WidgetEventM m v')) -> m (Dynamic t (Map k ()))
437−oldList dm = el "ul" $ list dm (\dv -> el "li" (dyn dv) >> return ())
438−
439−{-# INLINABLE list #-}
440−list :: forall t m h k v v'. (MonadWidget t h m, Ord k, Show k) => Dynamic t (Map k v) -> (Dynamic t v -> WidgetEventM m v') -> m (Dynamic t (Map k v'))
441−list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
442−
443−instance MonadWidget' t h m => MonadWidget' t h (ReaderT r m) where
444− type WidgetEventM (ReaderT r m) = ReaderT r (WidgetEventM m)
445− elDynAttr' tag dAttrs = mapReaderT (elDynAttr' tag dAttrs)
446− performEvent = lift . performEvent
447− performEvent_ = lift . performEvent_
448− getEInit = lift getEInit
449− putEChildren = lift . putEChildren
450− dyn d = do
451− r <- ask
452− lift $ dyn =<< mapDyn (flip runReaderT r) d
453− listWithKey d w = do
454− r <- ask
455− lift $ listWithKey d $ \k dv -> runReaderT (w k dv) r
456− text = lift . text
457− dynText = lift . dynText
458−
459−-}
31+{-# INLINABLE holdOnStartup #-}
32+holdOnStartup :: (PostBuild t m, PerformEvent t m, MonadHold t m) => a -> Performable m a -> m (Behavior t a)
33+holdOnStartup a0 ma = do
34+ hold a0 =<< performEvent . (ma <$) =<< getPostBuild
src/Reflex/Dom/Deletable/Class.hsnew file+17
@@ -0,0 +1,17 @@
1+{-# LANGUAGE FlexibleInstances #-}
2+{-# LANGUAGE FunctionalDependencies #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE UndecidableInstances #-}
5+module Reflex.Dom.Deletable.Class where
6+
7+import Reflex
8+
9+import Control.Monad.Reader
10+import Control.Monad.Trans.Control
11+
12+class (Reflex t, Monad m) => Deletable t m | m -> t where
13+ deletable :: Event t () -> m a -> m a
14+
15+instance Deletable t m => Deletable t (ReaderT r m) where
16+ {-# INLINABLE deletable #-}
17+ deletable = liftThrough . deletable
src/Reflex/Dom/DynamicWriter.hsnew file+242
@@ -0,0 +1,242 @@
1+{-# LANGUAGE CPP #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE FunctionalDependencies #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
6+{-# LANGUAGE LambdaCase #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE RecursiveDo #-}
9+{-# LANGUAGE ScopedTypeVariables #-}
10+{-# LANGUAGE TypeFamilies #-}
11+{-# LANGUAGE UndecidableInstances #-}
12+module Reflex.Dom.DynamicWriter where
13+
14+import Reflex
15+import Reflex.Dom.Builder.Class
16+import Reflex.Dom.Class
17+import Reflex.Dom.PerformEvent.Class
18+import Reflex.Dom.PostBuild.Class
19+import Reflex.Host.Class
20+
21+import Control.Lens hiding (element)
22+import Control.Monad.Exception
23+import Control.Monad.IO.Class
24+import Control.Monad.Reader
25+import Control.Monad.Ref
26+import Control.Monad.State.Strict
27+import Data.Dependent.Map (DMap)
28+import Data.Foldable
29+import Data.Functor.Compose
30+import Data.Functor.Misc
31+import Data.List.NonEmpty (NonEmpty, nonEmpty)
32+import Data.Map (Map)
33+import qualified Data.Map as Map
34+import Data.Semigroup
35+import Data.Traversable
36+
37+instance MonadTrans (DynamicWriterT t w) where
38+ lift = DynamicWriterT . lift
39+
40+data PatchMap a where
41+ PatchMap :: Ord k => Map k (Maybe v) -> PatchMap (Map k v)
42+
43+instance Patch PatchMap where
44+ apply (PatchMap p) old = Just $! insertions `Map.union` (old `Map.difference` deletions) --TODO: return Nothing sometimes --Note: the strict application here is critical to ensuring that incremental merges don't hold onto all their prerequisite events forever; can we make this more robust?
45+ where insertions = Map.mapMaybeWithKey (const id) p
46+ deletions = Map.mapMaybeWithKey (const nothingToJust) p
47+ nothingToJust = \case
48+ Nothing -> Just ()
49+ Just _ -> Nothing
50+
51+instance Ord k => Semigroup (PatchMap (Map k v)) where
52+ PatchMap a <> PatchMap b = PatchMap $ a `mappend` b --TODO: Add a semigroup instance for Map
53+ -- PatchMap is idempotent, so stimes n is id for every n
54+#if MIN_VERSION_semigroups(0,17,0)
55+ stimes = stimesIdempotentMonoid
56+#else
57+ times1p n x = case compare n 0 of
58+ LT -> error "stimesIdempotentMonoid: negative multiplier"
59+ EQ -> mempty
60+ GT -> x
61+#endif
62+
63+instance Ord k => Monoid (PatchMap (Map k v)) where
64+ mempty = PatchMap mempty
65+ mappend = (<>)
66+
67+mconcatIncremental :: (Reflex t, MonadHold t m, MonadFix m, Monoid v) => Map k v -> Event t (PatchMap (Map k v)) -> m (Dynamic t v)
68+mconcatIncremental m0 e = do
69+ d <- foldDynMaybe apply m0 e
70+ return $ fmap (mconcat . Map.elems) d
71+
72+mapIncrementalMapValuesWithKey :: Reflex t => (k -> v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
73+mapIncrementalMapValuesWithKey f = unsafeMapIncremental (Map.mapWithKey f) $ \(PatchMap m) -> PatchMap $ Map.mapWithKey (\k mv -> fmap (f k) mv) m
74+
75+mapIncrementalMapValues :: Reflex t => (v -> v') -> Incremental t PatchMap (Map k v) -> Incremental t PatchMap (Map k v')
76+mapIncrementalMapValues f = mapIncrementalMapValuesWithKey $ const f
77+
78+unsafeMapIncremental :: (Reflex t, Patch p, Patch p') => (a -> a') -> (p a -> p' a') -> Incremental t p a -> Incremental t p' a'
79+unsafeMapIncremental f g a = unsafeBuildIncremental (fmap f $ sample $ currentIncremental a) $ g <$> updatedIncremental a
80+
81+incrementalExtractFunctorDMap :: Reflex t => Incremental t PatchMap (Map k (f v)) -> Incremental t PatchDMap (DMap (Const2 k v) f)
82+incrementalExtractFunctorDMap = unsafeMapIncremental mapWithFunctorToDMap $ \(PatchMap m) -> PatchDMap $ mapWithFunctorToDMap $ fmap Compose m
83+
84+mergeIncrementalMap :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Event t v)) -> Event t (Map k v)
85+mergeIncrementalMap = fmap dmapToMap . mergeIncremental . incrementalExtractFunctorDMap
86+
87+holdIncrementalReplaceableMap :: forall t k v m. (MonadFix m, MonadHold t m, Reflex t, Ord k) => Map k (Replaceable t v) -> Event t (PatchMap (Map k (Replaceable t v))) -> m (Incremental t PatchMap (Map k v))
88+holdIncrementalReplaceableMap m0 m' = do
89+ rec vals <- holdIncremental m0 $ m' <> valChanges
90+ let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
91+ return $ mapIncrementalMapValues _replaceable_value vals
92+
93+mergeDynIncremental :: (Reflex t, Ord k) => Incremental t PatchMap (Map k (Dynamic t v)) -> Incremental t PatchMap (Map k v)
94+mergeDynIncremental a = unsafeBuildIncremental (mapM (sample . current) =<< sample (currentIncremental a)) $ addedAndRemovedValues <> changedValues
95+ where changedValues = fmap (PatchMap . fmap Just) $ mergeIncrementalMap $ mapIncrementalMapValues updated a
96+ addedAndRemovedValues = flip pushAlways (updatedIncremental a) $ \(PatchMap m) -> PatchMap <$> mapM (mapM (sample . current)) m
97+
98+data Replaceable t w = Replaceable
99+ { _replaceable_value :: w
100+ , _replaceable_modify :: Event t (Maybe (Replaceable t w)) -- "Please delete" is represented with an event of Nothing; all subsequent events will be ignored
101+ }
102+
103+type DynamicWriterAccumulator t w = [Replaceable t (Dynamic t w)]
104+
105+newtype DynamicWriterT t w m a = DynamicWriterT { unDynamicWriterT :: StateT (DynamicWriterAccumulator t w) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadHold t, MonadSample t, MonadAsyncException, MonadException) -- The list is kept in reverse order
106+
107+instance HasWebView m => HasWebView (DynamicWriterT t w m) where
108+ type WebViewPhantom (DynamicWriterT t w m) = WebViewPhantom m
109+ askWebView = lift askWebView
110+
111+instance MonadRef m => MonadRef (DynamicWriterT t w m) where
112+ type Ref (DynamicWriterT t w m) = Ref m
113+ newRef = lift . newRef
114+ readRef = lift . readRef
115+ writeRef r = lift . writeRef r
116+
117+instance MonadAtomicRef m => MonadAtomicRef (DynamicWriterT t w m) where
118+ atomicModifyRef r = lift . atomicModifyRef r
119+
120+instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (DynamicWriterT t w m) where
121+ newEventWithTrigger = lift . newEventWithTrigger
122+ newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
123+
124+runDynamicWriterTInternal :: DynamicWriterT t w m a -> m (a, DynamicWriterAccumulator t w)
125+runDynamicWriterTInternal (DynamicWriterT a) = runStateT a []
126+
127+mconcatIncrementalReplaceableDynMap :: forall m t k v. (MonadFix m, MonadHold t m, Reflex t, Monoid v, Ord k) => Map k (Replaceable t (Dynamic t v)) -> Event t (PatchMap (Map k (Replaceable t (Dynamic t v)))) -> Event t () -> m (Replaceable t (Dynamic t v))
128+mconcatIncrementalReplaceableDynMap m0 m' additionsCeased = do
129+ rec vals <- holdIncremental m0 $ m' <> valChanges
130+ let valChanges = fmap PatchMap $ mergeIncrementalMap $ mapIncrementalMapValues _replaceable_modify vals
131+ let i = mapIncrementalMapValues _replaceable_value vals
132+ noMoreAdditions <- holdDyn False $ True <$ additionsCeased
133+ let replaceSelf nma x = do
134+ guard nma
135+ case Map.toList x of
136+ [] -> return Nothing -- Delete self
137+ [(_, n)] -> return $ Just n --TODO: What if this one is also replacing itself simultaneously?
138+ _ -> mzero
139+ result = fmap (mconcat . Map.elems) $ incrementalToDynamic $ mergeDynIncremental i
140+ return $ Replaceable result $ fmapMaybe id $ updated $ zipDynWith replaceSelf noMoreAdditions $ incrementalToDynamic vals
141+
142+runDynamicWriterT :: (MonadFix m, Reflex t, MonadHold t m, Monoid w) => DynamicWriterT t w m a -> m (a, Dynamic t w)
143+runDynamicWriterT (DynamicWriterT a) = do
144+ (result, ws) <- runStateT a []
145+ Replaceable w _ <- mconcatIncrementalReplaceableDynMap (Map.fromList $ zip [1 :: Int ..] $ reverse ws) never never
146+ return (result, w)
147+
148+class Monad m => MonadDynamicWriter t w m | m -> t w where
149+ tellDyn :: Dynamic t w -> m ()
150+
151+instance (Monad m, Reflex t) => MonadDynamicWriter t w (DynamicWriterT t w m) where
152+ tellDyn w = DynamicWriterT $ modify (Replaceable w never :)
153+
154+instance MonadReader r m => MonadReader r (DynamicWriterT t w m) where
155+ ask = lift ask
156+ local f (DynamicWriterT a) = DynamicWriterT $ mapStateT (local f) a
157+ reader = lift . reader
158+
159+liftDynamicWriterTThroughSync :: Monad m' => (m (a, DynamicWriterAccumulator t w) -> m' (b, (a, DynamicWriterAccumulator t w))) -> DynamicWriterT t w m a -> DynamicWriterT t w m' (b, a)
160+liftDynamicWriterTThroughSync f (DynamicWriterT child) = DynamicWriterT $ do
161+ s <- get
162+ (b, (a, newS)) <- lift $ f $ runStateT child s
163+ put newS
164+ return (b, a)
165+
166+{-# INLINABLE liftDynamicWriterTElementConfig #-}
167+liftDynamicWriterTElementConfig :: ElementConfig er t (DynamicWriterT t w m) -> ElementConfig er t m
168+liftDynamicWriterTElementConfig cfg = cfg
169+ { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
170+ , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
171+ }
172+
173+instance (DomBuilder t m, Monoid w, MonadHold t m, MonadFix m) => DomBuilder t (DynamicWriterT t w m) where
174+ type DomBuilderSpace (DynamicWriterT t w m) = DomBuilderSpace m
175+ textNode = liftTextNode
176+ element elementTag cfg (DynamicWriterT child) = DynamicWriterT $ do
177+ s <- get
178+ let cfg' = cfg
179+ { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
180+ , _elementConfig_eventHandler = _elementConfig_eventHandler cfg
181+ }
182+ (el, (a, newS)) <- lift $ element elementTag cfg' $ runStateT child s
183+ put newS
184+ return (el, a)
185+ placeholder cfg = do
186+ let cfg' = cfg
187+ { _placeholderConfig_insertAbove = runDynamicWriterTInternal <$> _placeholderConfig_insertAbove cfg
188+ }
189+ let manageChildren :: Event t (NonEmpty (Replaceable t (Dynamic t w))) -- ^ Add nodes on the right; these are in reverse order
190+ -> Event t () -- ^ No more nodes will be added after this event fires
191+ -> m (Replaceable t (Dynamic t w))
192+ manageChildren newChildren additionsCeased = do
193+ rec nextId <- hold (0 :: Int) newNextId -- We assume this will never wrap around
194+ let numberedNewChildren :: Event t (Int, PatchMap (Map Int (Replaceable t (Dynamic t w))))
195+ numberedNewChildren = flip pushAlways newChildren $ \rcs -> do
196+ let cs = reverse $ toList rcs
197+ myFirstId <- sample nextId
198+ let (myNextId, numbered) = mapAccumL (\n v -> (succ n, (n, Just v))) myFirstId cs
199+ return (myNextId, PatchMap $ Map.fromList numbered)
200+ newNextId = fst <$> numberedNewChildren
201+ mconcatIncrementalReplaceableDynMap Map.empty (snd <$> numberedNewChildren) additionsCeased
202+ rec children <- lift $ manageChildren childOutputs $ cfg ^. deleteSelf
203+ p <- DynamicWriterT $ do
204+ modify (children:)
205+ lift $ placeholder cfg'
206+ let result = fst <$> _placeholder_insertedAbove p
207+ childOutputs = fmapMaybe (nonEmpty . snd) $ _placeholder_insertedAbove p
208+ return $ p
209+ { _placeholder_insertedAbove = result
210+ }
211+ inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
212+ textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftDynamicWriterTElementConfig
213+
214+instance (Deletable t m, Reflex t, Monoid w, MonadHold t m, MonadFix m) => Deletable t (DynamicWriterT t w m) where
215+ deletable delete child = do
216+ (result, output) <- lift $ deletable delete $ runDynamicWriterT child
217+ DynamicWriterT $ modify (Replaceable output (Nothing <$ delete) :)
218+ return result
219+
220+instance PerformEvent t m => PerformEvent t (DynamicWriterT t w m) where
221+ type Performable (DynamicWriterT t w m) = Performable m
222+ performEvent_ = lift . performEvent_
223+ performEvent = lift . performEvent
224+
225+instance TriggerEvent t m => TriggerEvent t (DynamicWriterT t w m) where
226+ newTriggerEvent = lift newTriggerEvent
227+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
228+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
229+
230+instance PostBuild t m => PostBuild t (DynamicWriterT t w m) where
231+ getPostBuild = lift getPostBuild
232+
233+instance MonadDynamicWriter t w m => MonadDynamicWriter t w (ReaderT r m) where
234+ tellDyn = lift . tellDyn
235+
236+instance MonadState s m => MonadState s (DynamicWriterT t w m) where
237+ get = lift get
238+ put = lift . put
239+
240+instance HasJS x m => HasJS x (DynamicWriterT t w m) where
241+ type JSM (DynamicWriterT t w m) = JSM m
242+ liftJS = lift . liftJS
src/Reflex/Dom/Internal.hs+107−255
@@ -1,272 +1,124 @@
1−{-# LANGUAGE TypeFamilies, FlexibleContexts, FlexibleInstances, MultiParamTypeClasses, RankNTypes, GADTs, ScopedTypeVariables, FunctionalDependencies, RecursiveDo, UndecidableInstances, GeneralizedNewtypeDeriving, StandaloneDeriving, EmptyDataDecls, NoMonomorphismRestriction, TypeOperators, DeriveDataTypeable, PackageImports, TemplateHaskell, LambdaCase, ConstraintKinds, CPP #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE FlexibleInstances #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE LambdaCase #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE NoMonomorphismRestriction #-}
8+{-# LANGUAGE RankNTypes #-}
9+{-# LANGUAGE RecursiveDo #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TypeFamilies #-}
12+{-# LANGUAGE TypeOperators #-}
13+{-# LANGUAGE UndecidableInstances #-}
214module Reflex.Dom.Internal where
315
4−import Prelude hiding (mapM, mapM_, concat, sequence, sequence_)
16+import Prelude hiding (concat, mapM, mapM_, sequence, sequence_)
517
6−import Reflex.Dom.Internal.Foreign
18+import Reflex.Dom.Builder.Immediate
719import Reflex.Dom.Class
8−
9−import GHCJS.DOM hiding (runWebGUI)
10−import GHCJS.DOM.Types hiding (Widget, unWidget, Event)
11−import GHCJS.DOM.Node
12−import GHCJS.DOM.Element
13−import GHCJS.DOM.Document
14−import Reflex.Class
20+import Reflex.Dom.Internal.Foreign
21+import Reflex.Dom.PerformEvent.Base
22+import Reflex.Dom.PostBuild.Class
1523import Reflex.Host.Class
16−import Reflex.Spider (Spider, SpiderHost (..))
24+import Reflex.Spider (Global, Spider, SpiderHost, runSpiderHost)
25+
26+import Control.Concurrent
1727import Control.Lens
18−import Control.Monad hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
19−import Control.Monad.Reader hiding (mapM, mapM_, forM, forM_, sequence, sequence_)
28+import Control.Monad
29+import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
2030import Control.Monad.Ref
21−import Control.Monad.State.Strict hiding (mapM, mapM_, forM, forM_, sequence, sequence_, get)
22−import Control.Monad.Exception
23−import Control.Concurrent
24−import Control.Applicative
2531import Data.ByteString (ByteString)
2632import Data.Dependent.Sum (DSum (..))
27−import Data.Foldable
28−import Data.Traversable
33+import Data.IORef
34+import Data.Maybe
35+import Data.Monoid ((<>))
2936import qualified Data.Text as T
3037import Data.Text.Encoding
31−import Data.Monoid ((<>))
32−
33−data GuiEnv t h
34− = GuiEnv { _guiEnvDocument :: !HTMLDocument
35− , _guiEnvPostGui :: !(h () -> IO ())
36− , _guiEnvRunWithActions :: !([DSum (EventTrigger t) Identity] -> h ())
37− , _guiEnvWebView :: !WebView
38− }
39−
40−--TODO: Poorly named
41−newtype Gui t h m a = Gui { unGui :: ReaderT (GuiEnv t h) m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
42−
43−runGui :: Gui t h m a -> GuiEnv t h -> m a
44−runGui (Gui g) env = runReaderT g env
45−
46−instance MonadTrans (Gui t h) where
47− lift = Gui . lift
48−
49−instance MonadRef m => MonadRef (Gui t h m) where
50− type Ref (Gui t h m) = Ref m
51− newRef = lift . newRef
52− readRef = lift . readRef
53− writeRef r = lift . writeRef r
54−
55−instance MonadAtomicRef m => MonadAtomicRef (Gui t h m) where
56− atomicModifyRef r f = lift $ atomicModifyRef r f
57−
58−instance MonadSample t m => MonadSample t (Gui t h m) where
59− sample b = lift $ sample b
60−
61−instance MonadHold t m => MonadHold t (Gui t h m) where
62− hold a0 e = lift $ hold a0 e
63−
64−instance (Reflex t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (Gui t h m) where
65− newEventWithTrigger = lift . newEventWithTrigger
66− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
67−
68−data WidgetEnv
69− = WidgetEnv { _widgetEnvParent :: !Node
70− }
71−
72−data WidgetState t m
73− = WidgetState { _widgetStatePostBuild :: !(m ())
74− , _widgetStateVoidActions :: ![Event t (m ())] --TODO: Would it help to make this a strict list?
75− }
76−
77−liftM concat $ mapM makeLenses
78− [ ''WidgetEnv
79− , ''WidgetState
80− , ''GuiEnv
81− ]
82−
83−instance Monad m => HasDocument (Gui t h m) where
84− askDocument = Gui $ view guiEnvDocument
85−
86−instance HasDocument m => HasDocument (Widget t m) where
87− askDocument = lift askDocument
88−
89−instance Monad m => HasWebView (Gui t h m) where
90− askWebView = Gui $ view guiEnvWebView
91−
92−instance MonadIORestore m => MonadIORestore (Gui t h m) where
93− askRestore = Gui $ do
94− r <- askRestore
95− return $ Restore $ restore r . unGui
96−
97−instance HasWebView m => HasWebView (Widget t m) where
98− askWebView = lift askWebView
99−
100−instance (MonadRef h, Ref h ~ Ref m, MonadRef m) => HasPostGui t h (Gui t h m) where
101− askPostGui = Gui $ view guiEnvPostGui
102− askRunWithActions = Gui $ view guiEnvRunWithActions
103−
104−instance HasPostGui t h m => HasPostGui t h (Widget t m) where
105− askPostGui = lift askPostGui
106− askRunWithActions = lift askRunWithActions
107−
108−type WidgetInternal t m a = ReaderT WidgetEnv (StateT (WidgetState t m) m) a
109−
110−instance MonadTrans (Widget t) where
111− lift = Widget . lift . lift
112−
113−newtype Widget t m a = Widget { unWidget :: WidgetInternal t m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadException, MonadAsyncException)
114−
115−instance MonadSample t m => MonadSample t (Widget t m) where
116− sample b = lift $ sample b
117−
118−instance MonadHold t m => MonadHold t (Widget t m) where
119− hold v0 e = lift $ hold v0 e
120−
121−-- Need to build FRP circuit first, then elements
122− -- Can't read from FRP until the whole thing is built
123−--TODO: Use JSString when in JS
124−
125−instance MonadRef m => MonadRef (Widget t m) where
126− type Ref (Widget t m) = Ref m
127− newRef = lift . newRef
128− readRef = lift . readRef
129− writeRef r = lift . writeRef r
130−
131−instance MonadAtomicRef m => MonadAtomicRef (Widget t m) where
132− atomicModifyRef r f = lift $ atomicModifyRef r f
133−
134−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (Widget t m) where
135− newEventWithTrigger = lift . newEventWithTrigger
136− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
137−
138−instance ( MonadRef m, Ref m ~ Ref IO, MonadRef h, Ref h ~ Ref IO --TODO: Shouldn't need to be IO
139− , MonadIO m, MonadAsyncException m, MonadIO h, MonadAsyncException h, Functor m
140− , ReflexHost t, MonadReflexCreateTrigger t m, MonadSample t m, MonadHold t m
141− , MonadFix m, HasWebView h, HasPostGui t h h
142− ) => MonadWidget t (Widget t (Gui t h m)) where
143− type WidgetHost (Widget t (Gui t h m)) = Gui t h m
144− type GuiAction (Widget t (Gui t h m)) = h
145− askParent = Widget $ view widgetEnvParent
146− --TODO: Use types to separate cohorts of possibly-recursive events/behaviors
147− -- | 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
148− --schedulePostBuild :: Monad m => m () -> WidgetInternal t m ()
149− liftWidgetHost = Widget . lift . lift
150− schedulePostBuild a = Widget $ widgetStatePostBuild %= (a>>) --TODO: Can this >> be made strict?
151−
152− --addVoidAction :: Monad m => Event t (m ()) -> WidgetInternal t m ()
153− addVoidAction a = Widget $ widgetStateVoidActions %= (a:)
154− subWidget n child = Widget $ local (widgetEnvParent .~ toNode n) $ unWidget child
155− subWidgetWithVoidActions n child = Widget $ do
156− oldActions <- use widgetStateVoidActions
157− widgetStateVoidActions .= []
158− result <- local (widgetEnvParent .~ toNode n) $ unWidget child
159− actions <- use widgetStateVoidActions
160− widgetStateVoidActions .= oldActions
161− return (result, mergeWith (>>) actions)
162−-- runWidget :: (Monad m, IsNode n, Reflex t) => n -> Widget t m a -> m (a, Event t (m ()))
163− getRunWidget = return runWidget
164−
165−getQuitWidget :: MonadWidget t m => m (WidgetHost m ())
166−getQuitWidget = return $ do wv <- askWebView
167− liftIO $ quitWebView wv
168−
169−runWidget :: (Monad m, Reflex t, IsNode n) => n -> Widget t (Gui t h m) a -> WidgetHost (Widget t (Gui t h m)) (a, WidgetHost (Widget t (Gui t h m)) (), Event t (WidgetHost (Widget t (Gui t h m)) ()))
170−runWidget rootElement w = do
171− (result, WidgetState postBuild voidActions) <- runStateT (runReaderT (unWidget w) (WidgetEnv $ toNode rootElement)) (WidgetState (return ()) [])
172− let voidAction = mergeWith (>>) voidActions
173− return (result, postBuild, voidAction)
174−
175−holdOnStartup :: MonadWidget t m => a -> WidgetHost m a -> m (Behavior t a)
176−holdOnStartup a0 ma = do
177− (startupDone, startupDoneTriggerRef) <- newEventWithTriggerRef
178− schedulePostBuild $ do
179− a <- ma
180− runFrameWithTriggerRef startupDoneTriggerRef a
181− hold a0 startupDone
38+import GHCJS.DOM hiding (runWebGUI)
39+import GHCJS.DOM.Document
40+import GHCJS.DOM.Element
41+import GHCJS.DOM.Node
42+import qualified GHCJS.DOM.Types as DOM
18243
183−mainWidget :: Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
184−mainWidget w = runWebGUI $ \webView -> do
185− Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
44+{-# INLINABLE mainWidget #-}
45+mainWidget :: (forall x. Widget x ()) -> IO ()
46+mainWidget w = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
47+ Just doc <- fmap DOM.castToHTMLDocument <$> webViewGetDomDocument webView
18648 Just body <- getBody doc
187− attachWidget body webView w
188−
189−mainWidgetWithHead :: Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
190−mainWidgetWithHead h b = runWebGUI $ \webView -> do
191− Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
192− Just headElement <- liftM (fmap castToHTMLElement) $ getHead doc
193− attachWidget headElement webView h
49+ attachWidget body webViewSing w
50+
51+--TODO: The x's should be unified here
52+{-# INLINABLE mainWidgetWithHead #-}
53+mainWidgetWithHead :: (forall x. Widget x ()) -> (forall x. Widget x ()) -> IO ()
54+mainWidgetWithHead h b = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
55+ Just doc <- fmap DOM.castToHTMLDocument <$> webViewGetDomDocument webView
56+ Just headElement <- fmap DOM.castToHTMLElement <$> getHead doc
57+ attachWidget headElement webViewSing h
19458 Just body <- getBody doc
195− attachWidget body webView b
59+ attachWidget body webViewSing b
19660
197−mainWidgetWithCss :: ByteString -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) () -> IO ()
198−mainWidgetWithCss css w = runWebGUI $ \webView -> do
199− Just doc <- liftM (fmap castToHTMLDocument) $ webViewGetDomDocument webView
200− Just headElement <- liftM (fmap castToHTMLElement) $ getHead doc
61+{-# INLINABLE mainWidgetWithCss #-}
62+mainWidgetWithCss :: ByteString -> (forall x. Widget x ()) -> IO ()
63+mainWidgetWithCss css w = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
64+ Just doc <- fmap DOM.castToHTMLDocument <$> webViewGetDomDocument webView
65+ Just headElement <- fmap DOM.castToHTMLElement <$> getHead doc
20166 setInnerHTML headElement . Just $ "<style>" <> T.unpack (decodeUtf8 css) <> "</style>" --TODO: Fix this
20267 Just body <- getBody doc
203− attachWidget body webView w
204−
205−newtype WithWebView m a = WithWebView { unWithWebView :: ReaderT WebView m a } deriving (Functor, Applicative, Monad, MonadIO, MonadFix, MonadException, MonadAsyncException)
206−
207−instance (Monad m) => HasWebView (WithWebView m) where
208− askWebView = WithWebView ask
209−
210−instance HasPostGui t h m => HasPostGui t (WithWebView h) (WithWebView m) where
211− askPostGui = do
212− postGui <- lift askPostGui
213− webView <- askWebView
214− return $ \h -> postGui $ runWithWebView h webView
215− askRunWithActions = do
216− runWithActions <- lift askRunWithActions
217− return $ lift . runWithActions
218−
219−instance HasPostGui Spider SpiderHost SpiderHost where
220− askPostGui = return $ \h -> liftIO $ runSpiderHost h
221− askRunWithActions = return fireEvents
222−
223−instance MonadTrans WithWebView where
224− lift = WithWebView . lift
225−
226−instance MonadRef m => MonadRef (WithWebView m) where
227− type Ref (WithWebView m) = Ref m
228− newRef = lift . newRef
229− readRef = lift . readRef
230− writeRef r = lift . writeRef r
231−
232−instance MonadAtomicRef m => MonadAtomicRef (WithWebView m) where
233− atomicModifyRef r = lift . atomicModifyRef r
234−
235−instance MonadReflexCreateTrigger t m => MonadReflexCreateTrigger t (WithWebView m) where
236− newEventWithTrigger = lift . newEventWithTrigger
237− newFanEventWithTrigger f = lift $ newFanEventWithTrigger f
238−
239−instance MonadSubscribeEvent t m => MonadSubscribeEvent t (WithWebView m) where
240− subscribeEvent = lift . subscribeEvent
241−
242−instance MonadReflexHost t m => MonadReflexHost t (WithWebView m) where
243− type ReadPhase (WithWebView m) = ReadPhase m
244− fireEventsAndRead dm a = lift $ fireEventsAndRead dm a
245− runHostFrame = lift . runHostFrame
246−
247−runWithWebView :: WithWebView m a -> WebView -> m a
248−runWithWebView = runReaderT . unWithWebView
249−
250−attachWidget :: (IsHTMLElement e) => e -> WebView -> Widget Spider (Gui Spider (WithWebView SpiderHost) (HostFrame Spider)) a -> IO a
251−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
252− Just doc <- liftM (fmap castToHTMLDocument) $ getOwnerDocument rootElement
253− frames <- liftIO newChan
254− rec let guiEnv = GuiEnv doc (writeChan frames . runSpiderHost . flip runWithWebView wv) runWithActions wv :: GuiEnv Spider (WithWebView SpiderHost)
255− runWithActions dm = do
256− voidActionNeeded <- fireEventsAndRead dm $ do
257− sequence =<< readEvent voidActionHandle
258− runHostFrame $ runGui (sequence_ voidActionNeeded) guiEnv
259− Just df <- createDocumentFragment doc
260− (result, voidAction) <- runHostFrame $ flip runGui guiEnv $ do
261− (r, postBuild, va) <- runWidget df w
262− 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
263− return (r, va)
264− setInnerHTML rootElement $ Just ""
265− _ <- appendChild rootElement $ Just df
266− voidActionHandle <- subscribeEvent voidAction --TODO: Should be unnecessary
267− --postGUISync seems to leak memory on GHC (unknown on GHCJS)
268− _ <- liftIO $ forkIO $ forever $ postGUISync =<< readChan frames -- postGUISync is necessary to prevent segfaults in GTK, which is not thread-safe
269− return result
270−
271−--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
272−
68+ attachWidget body webViewSing w
69+
70+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
71+
72+{-# INLINABLE attachWidget #-}
73+attachWidget :: DOM.IsElement e => e -> WebViewSingleton x -> Widget x a -> IO a
74+attachWidget rootElement wv w = fst <$> attachWidget' rootElement wv w
75+
76+{-# INLINABLE attachWidget' #-}
77+attachWidget' :: DOM.IsElement e => e -> WebViewSingleton x -> Widget x a -> IO (a, FireCommand Spider (SpiderHost Global))
78+attachWidget' rootElement wv w = do
79+ Just doc <- getOwnerDocument rootElement
80+ Just df <- createDocumentFragment doc
81+ events <- newChan
82+ (result, fc@(FireCommand fire)) <- runSpiderHost $ do
83+ (postBuild, postBuildTriggerRef) <- newEventWithTriggerRef
84+ let builderEnv = ImmediateDomBuilderEnv
85+ { _immediateDomBuilderEnv_document = doc
86+ , _immediateDomBuilderEnv_parent = toNode df
87+ , _immediateDomBuilderEnv_events = events
88+ }
89+ results@(_, FireCommand fire) <- hostPerformEventT $ runWithWebView (runImmediateDomBuilderT (runPostBuildT w postBuild) builderEnv) wv
90+ mPostBuildTrigger <- readRef postBuildTriggerRef
91+ forM_ mPostBuildTrigger $ \postBuildTrigger -> fire [postBuildTrigger :=> Identity ()] $ return ()
92+ return results
93+ void $ forkIO $ forever $ do
94+ ers <- readChan events
95+ _ <- postGUISync $ runSpiderHost $ do
96+ mes <- liftIO $ forM ers $ \(TriggerRef er :=> TriggerInvocation a _) -> do
97+ me <- readIORef er
98+ return $ fmap (\e -> e :=> Identity a) me
99+ _ <- fire (catMaybes mes) $ return ()
100+ liftIO $ forM_ ers $ \(_ :=> TriggerInvocation _ cb) -> cb
101+ return ()
102+ setInnerHTML rootElement $ Just (""::String)
103+ _ <- appendChild rootElement $ Just df
104+ return (result, fc)
105+
106+data AppInput t = AppInput
107+ { _appInput_window :: Window t
108+ }
109+
110+data AppOutput t = AppOutput --TODO: Add quit event
111+ { _appOutput_windowConfig :: WindowConfig t
112+ }
113+
114+runApp' :: (t ~ Spider) => (forall x. AppInput t -> Widget x (AppOutput t)) -> IO ()
115+runApp' app = runWebGUI $ \webView -> withWebViewSingleton webView $ \webViewSing -> do
116+ Just doc <- fmap DOM.castToHTMLDocument <$> webViewGetDomDocument webView
117+ Just body <- getBody doc
118+ Just win <- getDefaultView doc
119+ rec o <- attachWidget body webViewSing $ do
120+ w <- lift $ wrapWindow win $ _appOutput_windowConfig o
121+ app $ AppInput
122+ { _appInput_window = w
123+ }
124+ return ()
src/Reflex/Dom/Location.hs+11−1
@@ -1,3 +1,13 @@
11module Reflex.Dom.Location (getLocationHost, getLocationProtocol) where
22
3−import Reflex.Dom.Internal.Foreign
3+import Data.Text (Text)
4+import Reflex.Dom.Class
5+import qualified Reflex.Dom.Internal.Foreign as F
6+
7+import Control.Monad.IO.Class
8+
9+getLocationHost :: (MonadIO m) => WebViewSingleton x -> m Text
10+getLocationHost (WebViewSingleton wv) = liftIO $ F.getLocationHost wv
11+
12+getLocationProtocol :: (MonadIO m) => WebViewSingleton x -> m Text
13+getLocationProtocol (WebViewSingleton wv) = liftIO $ F.getLocationProtocol wv
src/Reflex/Dom/Old.hsnew file+46
@@ -0,0 +1,46 @@
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE TypeFamilies #-}
4+module Reflex.Dom.Old where
5+
6+import Control.Monad.Exception
7+import Control.Monad.Fix
8+import Control.Monad.IO.Class
9+import Control.Monad.Ref
10+import Foreign.JavaScript.TH
11+import Reflex
12+import Reflex.Dom.Builder.Class
13+import Reflex.Dom.Builder.Immediate
14+import Reflex.Dom.PerformEvent.Class
15+import Reflex.Dom.PostBuild.Class
16+import Reflex.Host.Class
17+
18+--TODO: HasDocument is still not accounted for
19+type MonadWidget t m =
20+ ( DomBuilder t m
21+ , DomBuilderSpace m ~ GhcjsDomSpace
22+ , MonadFix m
23+ , MonadHold t m
24+ , MonadSample t (Performable m)
25+ , MonadReflexCreateTrigger t m
26+ , PostBuild t m
27+ , PerformEvent t m
28+ , MonadIO m
29+ , MonadIO (Performable m)
30+ , TriggerEvent t m
31+ , HasWebView m
32+ , HasWebView (Performable m)
33+ , MonadAsyncException m
34+ , MonadAsyncException (Performable m)
35+ , MonadRef m
36+ , Ref m ~ Ref IO
37+ , MonadRef (Performable m)
38+ , Ref (Performable m) ~ Ref IO
39+ )
40+
41+type WidgetHost m = Performable m
42+
43+type El = Element EventResult GhcjsDomSpace
44+
45+_el_element :: El t -> RawElement GhcjsDomSpace
46+_el_element = _element_raw
src/Reflex/Dom/PerformEvent/Base.hsnew file+177
@@ -0,0 +1,177 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
4+{-# LANGUAGE MultiParamTypeClasses #-}
5+{-# LANGUAGE RankNTypes #-}
6+{-# LANGUAGE RecursiveDo #-}
7+{-# LANGUAGE ScopedTypeVariables #-}
8+{-# LANGUAGE StandaloneDeriving #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE TypeOperators #-}
11+{-# LANGUAGE UndecidableInstances #-}
12+module Reflex.Dom.PerformEvent.Base where
13+
14+import Foreign.JavaScript.TH
15+import Reflex
16+import Reflex.Dom.Deletable.Class
17+import Reflex.Dom.PerformEvent.Class
18+import Reflex.Host.Class
19+
20+import Control.Lens
21+import Control.Monad.Exception
22+import Control.Monad.Identity
23+import Control.Monad.Ref
24+import Control.Monad.State.Strict
25+import Data.Align
26+import Data.Dependent.Map (DMap)
27+import qualified Data.Dependent.Map as DMap
28+import Data.Dependent.Sum
29+import Data.Functor.Compose
30+import Data.Functor.Misc
31+import Data.Maybe
32+import Data.Semigroup
33+import Data.These
34+import Data.Word
35+
36+newtype EventTriggerRef t m a = EventTriggerRef { unEventTriggerRef :: Ref m (Maybe (EventTrigger t a)) }
37+
38+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?
39+
40+newtype PerformEventT t m a = PerformEventT { unPerformEventT :: StateT [Event t (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())] (HostFrame t) a }
41+
42+deriving instance ReflexHost t => Functor (PerformEventT t m)
43+deriving instance ReflexHost t => Applicative (PerformEventT t m)
44+deriving instance ReflexHost t => Monad (PerformEventT t m)
45+deriving instance ReflexHost t => MonadFix (PerformEventT t m)
46+deriving instance (ReflexHost t, MonadIO (HostFrame t)) => MonadIO (PerformEventT t m)
47+deriving instance (ReflexHost t, MonadException (HostFrame t)) => MonadException (PerformEventT t m)
48+
49+instance (ReflexHost t, MonadAsyncException (HostFrame t)) => MonadAsyncException (PerformEventT t m) where
50+ mask f = PerformEventT $ mask $ \unmask -> unPerformEventT $ f $ PerformEventT . unmask . unPerformEventT
51+
52+instance (ReflexHost t, HasWebView (HostFrame t)) => HasWebView (PerformEventT t m) where
53+ type WebViewPhantom (PerformEventT t m) = WebViewPhantom (HostFrame t)
54+ askWebView = PerformEventT $ lift askWebView
55+
56+instance (ReflexHost t, Ref m ~ Ref IO, MonadRef (HostFrame t), Ref (HostFrame t) ~ Ref IO) => PerformEvent t (PerformEventT t m) where
57+ type Performable (PerformEventT t m) = PerformEventT t m
58+ {-# INLINABLE performEvent_ #-}
59+ performEvent_ = PerformEventT . modify . (:) . fmap (This . (>> return []))
60+ {-# INLINABLE performEvent #-}
61+ performEvent e = PerformEventT $ do
62+ (eResult, reResultTrigger) <- newEventWithTriggerRef
63+ modify $ (:) $ ffor e $ \o -> This $ do
64+ result <- o
65+ return [EventTriggerRef reResultTrigger :=> Identity result]
66+ return eResult
67+
68+instance (ReflexHost t, Monad (HostFrame t), Ref (HostFrame t) ~ Ref IO, Ref m ~ Ref IO) => Deletable t (PerformEventT t m) where
69+ {-# INLINABLE deletable #-}
70+ deletable d a = PerformEventT $ do
71+ (result, reverseEventsToPerform) <- lift $ runStateT (unPerformEventT a) []
72+ let (numberedEvents, _) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
73+ eventToPerform <- lift $ mergePerformEventsAndAdd (DMap.fromList numberedEvents) never
74+ modify (align eventToPerform d :)
75+ return result
76+
77+instance ReflexHost t => MonadReflexCreateTrigger t (PerformEventT t m) where
78+ {-# INLINABLE newEventWithTrigger #-}
79+ newEventWithTrigger = PerformEventT . lift . newEventWithTrigger
80+ {-# INLINABLE newFanEventWithTrigger #-}
81+ newFanEventWithTrigger f = PerformEventT $ lift $ newFanEventWithTrigger f
82+
83+{-# INLINABLE numberWith #-}
84+numberWith :: (Enum n, Traversable t) => (n -> a -> b) -> t a -> n -> (t b, n)
85+numberWith f t n0 = flip runState n0 $ forM t $ \x -> state $ \n -> (f n x, succ n)
86+
87+{-# INLINABLE mergePerformEventsAndAdd #-}
88+mergePerformEventsAndAdd :: forall t m. ReflexHost t
89+ => DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)
90+ -> Event t (PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)))
91+ -> HostFrame t (Event t (PerformEventT t m [DSum (EventTriggerRef t m) Identity]))
92+mergePerformEventsAndAdd initial eAddNew = do
93+ rec events :: Incremental t PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t))
94+ <- holdIncremental initial $ eAddNew <> eDelete --TODO: Eliminate empty firings
95+ let event = mergeWith DMap.union [mergeIncremental events, coincidence (fmap (\(PatchDMap m) -> merge $ DMap.mapMaybeWithKey (const getCompose) m) eAddNew)]
96+ eventToPerform :: Event t (PerformEventT t m [DSum (EventTriggerRef t m) Identity])
97+ eventToPerform = ffor event $
98+ fmap concat .
99+ sequence .
100+ fmapMaybe (\(Const2 _ :=> Identity x) -> x ^? here) .
101+ DMap.toList
102+ eDelete :: Event t (PatchDMap (DMap (Const2 Word64 (These (PerformEventT t m [DSum (EventTriggerRef t m) Identity]) ())) (Event t)))
103+ eDelete = fforMaybe event $
104+ fmap (PatchDMap . DMap.fromList) .
105+ (\l -> if null l then Nothing else Just l) .
106+ fmapMaybe (\(Const2 k :=> Identity x) -> (Const2 k :=> Compose Nothing) <$ x ^? there) .
107+ DMap.toList
108+ return eventToPerform
109+
110+{-# INLINABLE runPerformEventT #-}
111+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]))
112+runPerformEventT (PerformEventT a) = do
113+ (result, reverseEventsToPerform) <- runStateT a []
114+ (eAddNew, reAddNewTrigger) <- newEventWithTriggerRef
115+ let (numberedEvents, nextNumber) = numberWith (\n e -> Const2 n :=> e) (reverse reverseEventsToPerform) (1 :: Word64)
116+ nextNumberRef <- newRef nextNumber
117+ let runResult :: PerformEventT t m [DSum (EventTriggerRef t m) Identity] -> HostFrame t [DSum (EventTriggerRef t m) Identity]
118+ runResult (PerformEventT r) = do
119+ (followups, reverseNewEventsToPerform) <- runStateT r []
120+ oldN <- readRef nextNumberRef
121+ let (numberedNewEvents, newN) = numberWith (\n e -> Const2 n :=> Compose (Just e)) (reverse reverseNewEventsToPerform) oldN
122+ if newN == oldN then return followups else do
123+ writeRef nextNumberRef newN
124+ return $ (EventTriggerRef reAddNewTrigger :=> Identity (PatchDMap (DMap.fromList numberedNewEvents))) : followups
125+ eventToPerform <- mergePerformEventsAndAdd (DMap.fromList numberedEvents) eAddNew
126+ return (result, runResult <$> eventToPerform)
127+
128+{-# INLINABLE hostPerformEventT #-}
129+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)
130+hostPerformEventT a = do
131+ (result, eventToPerform) <- runHostFrame $ runPerformEventT a
132+ eventToPerformHandle <- subscribeEvent eventToPerform
133+ return $ (,) result $ FireCommand $ \triggers (readPhase :: ReadPhase m a') -> do
134+ let go :: [DSum (EventTrigger t) Identity] -> m [a']
135+ go ts = do
136+ (result', mToPerform) <- fireEventsAndRead ts $ do
137+ mToPerform <- sequence =<< readEvent eventToPerformHandle
138+ result' <- readPhase
139+ return (result', mToPerform)
140+ case mToPerform of
141+ Nothing -> return [result']
142+ Just toPerform -> do
143+ followupEventTriggerRefs <- runHostFrame toPerform
144+ followupEventTriggers <- forM followupEventTriggerRefs $ \(EventTriggerRef rt :=> x) -> do
145+ mt <- readRef rt
146+ return $ fmap (:=> x) mt
147+ fmap (result':) $ go $ catMaybes followupEventTriggers
148+ go triggers
149+
150+instance ReflexHost t => MonadSample t (PerformEventT t m) where
151+ {-# INLINABLE sample #-}
152+ sample = PerformEventT . lift . sample
153+
154+instance (ReflexHost t, MonadHold t m) => MonadHold t (PerformEventT t m) where
155+ {-# INLINABLE hold #-}
156+ hold v0 v' = PerformEventT $ lift $ hold v0 v'
157+ {-# INLINABLE holdDyn #-}
158+ holdDyn v0 v' = PerformEventT $ lift $ holdDyn v0 v'
159+ {-# INLINABLE holdIncremental #-}
160+ holdIncremental v0 v' = PerformEventT $ lift $ holdIncremental v0 v'
161+
162+instance (MonadRef (HostFrame t), ReflexHost t) => MonadRef (PerformEventT t m) where
163+ type Ref (PerformEventT t m) = Ref (HostFrame t)
164+ {-# INLINABLE newRef #-}
165+ newRef = PerformEventT . lift . newRef
166+ {-# INLINABLE readRef #-}
167+ readRef = PerformEventT . lift . readRef
168+ {-# INLINABLE writeRef #-}
169+ writeRef r = PerformEventT . lift . writeRef r
170+
171+instance (MonadAtomicRef (HostFrame t), ReflexHost t) => MonadAtomicRef (PerformEventT t m) where
172+ {-# INLINABLE atomicModifyRef #-}
173+ atomicModifyRef r = PerformEventT . lift . atomicModifyRef r
174+
175+instance (HasJS x (HostFrame t), ReflexHost t) => HasJS x (PerformEventT t m) where
176+ type JSM (PerformEventT t m) = JSM (HostFrame t)
177+ liftJS = PerformEventT . lift . liftJS
src/Reflex/Dom/PerformEvent/Class.hsnew file+46
@@ -0,0 +1,46 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE FunctionalDependencies #-}
4+{-# LANGUAGE MultiParamTypeClasses #-}
5+{-# LANGUAGE RankNTypes #-}
6+{-# LANGUAGE ScopedTypeVariables #-}
7+{-# LANGUAGE TypeFamilies #-}
8+{-# LANGUAGE TypeOperators #-}
9+{-# LANGUAGE UndecidableInstances #-}
10+module Reflex.Dom.PerformEvent.Class where
11+
12+import Reflex
13+
14+import Control.Monad.Reader
15+
16+class (Reflex t, Monad (Performable m), Monad m) => PerformEvent t m | m -> t where
17+ type Performable m :: * -> *
18+ performEvent :: Event t (Performable m a) -> m (Event t a)
19+ performEvent_ :: Event t (Performable m ()) -> m ()
20+
21+--TODO: Shouldn't have IO hard-coded
22+class Monad m => TriggerEvent t m | m -> t where
23+ newTriggerEvent :: m (Event t a, a -> IO ())
24+ newTriggerEventWithOnComplete :: m (Event t a, a -> IO () -> IO ()) --TODO: This and newTriggerEvent should be unified somehow
25+ newEventWithLazyTriggerWithOnComplete :: ((a -> IO () -> IO ()) -> IO (IO ())) -> m (Event t a)
26+
27+{-# INLINABLE performEventAsync #-}
28+performEventAsync :: (TriggerEvent t m, PerformEvent t m) => Event t ((a -> IO ()) -> Performable m ()) -> m (Event t a)
29+performEventAsync e = do
30+ (eOut, triggerEOut) <- newTriggerEvent
31+ performEvent_ $ fmap ($ triggerEOut) e
32+ return eOut
33+
34+instance PerformEvent t m => PerformEvent t (ReaderT r m) where
35+ type Performable (ReaderT r m) = ReaderT r (Performable m)
36+ performEvent_ e = do
37+ r <- ask
38+ lift $ performEvent_ $ flip runReaderT r <$> e
39+ performEvent e = do
40+ r <- ask
41+ lift $ performEvent $ flip runReaderT r <$> e
42+
43+instance TriggerEvent t m => TriggerEvent t (ReaderT r m) where
44+ newTriggerEvent = lift newTriggerEvent
45+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
46+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
src/Reflex/Dom/PostBuild/Class.hsnew file+129
@@ -0,0 +1,129 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE FlexibleInstances #-}
3+{-# LANGUAGE FunctionalDependencies #-}
4+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE PolyKinds #-}
7+{-# LANGUAGE RecursiveDo #-}
8+{-# LANGUAGE ScopedTypeVariables #-}
9+{-# LANGUAGE TypeFamilies #-}
10+{-# LANGUAGE TypeOperators #-}
11+{-# LANGUAGE UndecidableInstances #-}
12+module Reflex.Dom.PostBuild.Class where
13+
14+import Foreign.JavaScript.TH
15+import Reflex
16+import Reflex.Dom.Builder.Class
17+import Reflex.Dom.PerformEvent.Class
18+import Reflex.Host.Class
19+
20+import Control.Lens hiding (element)
21+import Control.Monad.Exception
22+import Control.Monad.Reader
23+import Control.Monad.Ref
24+import Control.Monad.Trans.Control
25+
26+class (Reflex t, Monad m) => PostBuild t m | m -> t where
27+ getPostBuild :: m (Event t ())
28+
29+newtype PostBuildT t m a = PostBuildT { unPostBuildT :: ReaderT (Event t ()) m a } deriving (Functor, Applicative, Monad, MonadFix, MonadIO, MonadTrans, MonadException, MonadAsyncException)
30+
31+instance MonadTransControl (PostBuildT t) where
32+ type StT (PostBuildT t) a = StT (ReaderT (Event t ())) a
33+ {-# INLINABLE liftWith #-}
34+ liftWith = defaultLiftWith PostBuildT unPostBuildT
35+ {-# INLINABLE restoreT #-}
36+ restoreT = defaultRestoreT PostBuildT
37+
38+instance (Reflex t, Monad m) => PostBuild t (PostBuildT t m) where
39+ {-# INLINABLE getPostBuild #-}
40+ getPostBuild = PostBuildT ask
41+
42+instance Deletable t m => Deletable t (PostBuildT t m) where
43+ {-# INLINABLE deletable #-}
44+ deletable = liftThrough . deletable
45+
46+{-# INLINABLE liftPostBuildTElementConfig #-}
47+liftPostBuildTElementConfig :: ElementConfig er t (PostBuildT t m) -> ElementConfig er t m
48+liftPostBuildTElementConfig cfg = cfg
49+ { _elementConfig_eventFilters = _elementConfig_eventFilters cfg
50+ , _elementConfig_eventHandler = _elementConfig_eventHandler cfg -- This requires PolyKinds, and will fail to unify types otherwise
51+ }
52+
53+instance (DomBuilder t m, PerformEvent t m, MonadFix m, MonadHold t m) => DomBuilder t (PostBuildT t m) where
54+ type DomBuilderSpace (PostBuildT t m) = DomBuilderSpace m
55+ {-# INLINABLE textNode #-}
56+ textNode = lift . textNode
57+ {-# INLINABLE element #-}
58+ element t cfg child = liftWith $ \run -> element t (liftPostBuildTElementConfig cfg) $ run child
59+ {-# INLINABLE placeholder #-}
60+ placeholder cfg = lift $ do
61+ rec childPostBuild <- deletable (_placeholder_deletedSelf p) $ performEvent $ return () <$ _placeholder_insertedAbove p
62+ p <- placeholder $ cfg
63+ { _placeholderConfig_insertAbove = ffor (_placeholderConfig_insertAbove cfg) $ \a -> runPostBuildT a =<< headE childPostBuild
64+ }
65+ return p
66+ {-# INLINABLE inputElement #-}
67+ inputElement cfg = lift $ inputElement $ cfg & inputElementConfig_elementConfig %~ liftPostBuildTElementConfig
68+ {-# INLINABLE textAreaElement #-}
69+ textAreaElement cfg = lift $ textAreaElement $ cfg & textAreaElementConfig_elementConfig %~ liftPostBuildTElementConfig
70+
71+instance MonadSample t m => MonadSample t (PostBuildT t m) where
72+ {-# INLINABLE sample #-}
73+ sample = lift . sample
74+
75+instance MonadHold t m => MonadHold t (PostBuildT t m) where
76+ {-# INLINABLE hold #-}
77+ hold v0 = lift . hold v0
78+ {-# INLINABLE holdDyn #-}
79+ holdDyn v0 = lift . holdDyn v0
80+ {-# INLINABLE holdIncremental #-}
81+ holdIncremental v0 = lift . holdIncremental v0
82+
83+instance PerformEvent t m => PerformEvent t (PostBuildT t m) where
84+ type Performable (PostBuildT t m) = PostBuildT t (Performable m)
85+ {-# INLINABLE performEvent_ #-}
86+ performEvent_ e = liftWith $ \run -> performEvent_ $ fmap run e
87+ {-# INLINABLE performEvent #-}
88+ performEvent e = liftWith $ \run -> performEvent $ fmap run e
89+
90+instance (ReflexHost t, MonadReflexCreateTrigger t m) => MonadReflexCreateTrigger t (PostBuildT t m) where
91+ {-# INLINABLE newEventWithTrigger #-}
92+ newEventWithTrigger = PostBuildT . lift . newEventWithTrigger
93+ {-# INLINABLE newFanEventWithTrigger #-}
94+ newFanEventWithTrigger f = PostBuildT $ lift $ newFanEventWithTrigger f
95+
96+instance TriggerEvent t m => TriggerEvent t (PostBuildT t m) where
97+ {-# INLINABLE newTriggerEvent #-}
98+ newTriggerEvent = lift newTriggerEvent
99+ {-# INLINABLE newTriggerEventWithOnComplete #-}
100+ newTriggerEventWithOnComplete = lift newTriggerEventWithOnComplete
101+ newEventWithLazyTriggerWithOnComplete = lift . newEventWithLazyTriggerWithOnComplete
102+
103+instance MonadRef m => MonadRef (PostBuildT t m) where
104+ type Ref (PostBuildT t m) = Ref m
105+ {-# INLINABLE newRef #-}
106+ newRef = lift . newRef
107+ {-# INLINABLE readRef #-}
108+ readRef = lift . readRef
109+ {-# INLINABLE writeRef #-}
110+ writeRef r = lift . writeRef r
111+
112+instance MonadAtomicRef m => MonadAtomicRef (PostBuildT t m) where
113+ {-# INLINABLE atomicModifyRef #-}
114+ atomicModifyRef r = lift . atomicModifyRef r
115+
116+instance (HasJS x m, ReflexHost t) => HasJS x (PostBuildT t m) where
117+ type JSM (PostBuildT t m) = JSM m
118+ liftJS = lift . liftJS
119+
120+instance HasWebView m => HasWebView (PostBuildT t m) where
121+ type WebViewPhantom (PostBuildT t m) = WebViewPhantom m
122+ askWebView = lift askWebView
123+
124+{-# INLINABLE runPostBuildT #-}
125+runPostBuildT :: PostBuildT t m a -> Event t () -> m a
126+runPostBuildT (PostBuildT a) = runReaderT a
127+
128+instance PostBuild t m => PostBuild t (ReaderT r m) where
129+ 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+124−13
@@ -1,15 +1,24 @@
1−{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables #-}
1+{-# LANGUAGE DeriveDataTypeable #-}
2+{-# LANGUAGE FlexibleContexts #-}
3+{-# LANGUAGE MultiParamTypeClasses #-}
4+{-# LANGUAGE RecursiveDo #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
27module Reflex.Dom.Time where
38
49import Reflex
5−import Reflex.Dom.Class
10+import Reflex.Dom.PerformEvent.Class
11+import Reflex.Dom.PostBuild.Class
612
713import Control.Concurrent
14+import qualified Control.Concurrent.Thread.Delay as Concurrent
815import Control.Monad
16+import Control.Monad.Fix
917import Control.Monad.IO.Class
1018import Data.Fixed
1119import Data.Time.Clock
1220import Data.Typeable
21+import System.Random
1322
1423data TickInfo
1524 = TickInfo { _tickInfo_lastUTC :: UTCTime
@@ -19,18 +28,18 @@ data TickInfo
1928 , _tickInfo_alreadyElapsed :: NominalDiffTime
2029 -- ^ Amount of time already elapsed in the current tick period.
2130 }
22− deriving (Eq, Ord, Typeable)
31+ deriving (Eq, Ord, Show, Typeable)
2332
2433-- | Special case of tickLossyFrom that uses the post-build event to start the
2534-- tick thread.
26−tickLossy :: MonadWidget t m => NominalDiffTime -> UTCTime -> m (Event t TickInfo)
35+tickLossy :: (PostBuild t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m) => NominalDiffTime -> UTCTime -> m (Event t TickInfo)
2736tickLossy dt t0 = tickLossyFrom dt t0 =<< getPostBuild
2837
2938-- | Send events over time with the given basis time and interval
3039-- If the system starts running behind, occurrences will be dropped rather than buffered
3140-- Each occurrence of the resulting event will contain the index of the current interval, with 0 representing the basis time
3241tickLossyFrom
33− :: MonadWidget t m
42+ :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), MonadFix m)
3443 => NominalDiffTime
3544 -> UTCTime
3645 -> Event t a
@@ -38,13 +47,15 @@ tickLossyFrom
3847 -- be something like the result of getPostBuild that only fires once. But
3948 -- there could be uses for starting multiple timer threads.
4049 -> m (Event t TickInfo)
41−tickLossyFrom dt t0 e = performEventAsync $ fmap callAtNextInterval e
42− where callAtNextInterval _ cb = void $ liftIO $ forkIO $ forever $ do
50+tickLossyFrom dt t0 e = do
51+ rec result <- performEventAsync $ callAtNextInterval <$> leftmost [() <$ e, () <$ result]
52+ return result
53+ where callAtNextInterval _ cb = void $ liftIO $ forkIO $ do
4354 tick <- getCurrentTick dt t0
44− threadDelay $ ceiling $ (dt - _tickInfo_alreadyElapsed tick) * 1000000
55+ Concurrent.delay $ ceiling $ (dt - _tickInfo_alreadyElapsed tick) * 1000000
4556 cb tick
4657
47−clockLossy :: MonadWidget t m => NominalDiffTime -> UTCTime -> m (Dynamic t TickInfo)
58+clockLossy :: (MonadIO m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m), PostBuild t m, MonadHold t m, MonadFix m) => NominalDiffTime -> UTCTime -> m (Dynamic t TickInfo)
4859clockLossy dt t0 = do
4960 initial <- liftIO $ getCurrentTick dt t0
5061 e <- tickLossy dt t0
@@ -58,16 +69,116 @@ getCurrentTick dt t0 = do
5869 return $ TickInfo t n alreadyElapsed
5970
6071-- | Delay an Event's occurrences by a given amount in seconds.
61−delay :: MonadWidget t m => NominalDiffTime -> Event t a -> m (Event t a)
72+delay :: (PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a)
6273delay dt e = performEventAsync $ ffor e $ \a cb -> liftIO $ void $ forkIO $ do
63− threadDelay $ ceiling $ dt * 1000000
74+ Concurrent.delay $ ceiling $ dt * 1000000
6475 cb a
6576
77+-- | Send events with Poisson timing with the given basis and rate
78+-- Each occurence of the resulting event will contain the index of
79+-- the current interval, with 0 representing the basis time
80+poissonLossyFrom
81+ :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m)
82+ => g
83+ -> Double
84+ -- ^ Poisson event rate (Hz)
85+ -> UTCTime
86+ -- ^ Baseline time for events
87+ -> Event t a
88+ -- ^ Event that starts a tick generation thread. Usually you want this to
89+ -- be something like the result of getPostBuild that only fires once. But
90+ -- there could be uses for starting multiple timer threads.
91+ -- Start sending events in response to the event parameter.
92+ -> m (Event t TickInfo)
93+poissonLossyFrom rnd rate = inhomogeneousPoissonFrom rnd (constant rate) rate
94+
95+
96+-- | Send events with Poisson timing with the given basis and rate
97+-- Each occurence of the resulting event will contain the index of
98+-- the current interval, with 0 representing the basis time.
99+-- Automatically begin sending events when the DOM is built
100+poissonLossy
101+ :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, PostBuild t m)
102+ => g
103+ -> Double
104+ -- ^ Poisson event rate (Hz)
105+ -> UTCTime
106+ -- ^ Baseline time for events
107+ -> m (Event t TickInfo)
108+poissonLossy rnd rate t0 = poissonLossyFrom rnd rate t0 =<< getPostBuild
109+
110+-- | Send events with inhomogeneous Poisson timing with the given basis
111+-- and variable rate. Provide a maxRate that you expect to support.
112+inhomogeneousPoissonFrom
113+ :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m)
114+ => g
115+ -> Behavior t Double
116+ -> Double
117+ -> UTCTime
118+ -> Event t a
119+ -> m (Event t TickInfo)
120+inhomogeneousPoissonFrom rnd rate maxRate t0 e = do
121+
122+ -- Create a thread for producing homogeneous poisson events
123+ -- along with random Doubles (usage of Double's explained below)
124+ ticksWithRateRand <- performEventAsync $
125+ fmap callAtNextInterval e
126+
127+ -- Filter homogeneous events according to associated
128+ -- random values and the current rate parameter
129+ return $ attachWithMaybe filterFun rate ticksWithRateRand
130+
131+ where
132+
133+ -- Inhomogeneous poisson processes are built from faster
134+ -- homogeneous ones by randomly dropping events from the
135+ -- fast process. For each fast homogeneous event, choose
136+ -- a uniform random sample from (0, rMax). If the
137+ -- inhomogeneous rate at this moment is greater than the
138+ -- random sample, then keep this event, otherwise drop it
139+ filterFun :: Double -> (TickInfo, Double) -> Maybe TickInfo
140+ filterFun r (tInfo, p)
141+ | r >= p = Just tInfo
142+ | otherwise = Nothing
143+
144+ callAtNextInterval _ cb = void $ liftIO $ forkIO $ go t0 rnd cb 0
145+
146+ go tTargetLast lastGen cb lastN = do
147+ t <- getCurrentTime
148+
149+ -- Generate random numbers for this poisson interval (u)
150+ -- and sample-retention likelihood (p)
151+ let (u, nextGen) = randomR (0,1) lastGen
152+ (p :: Double, nextGen') = randomR (0,maxRate) nextGen
153+
154+ -- Inter-event interval is drawn from exponential
155+ -- distribution accourding to u
156+ let dt = realToFrac $ (-1) * log u / maxRate :: NominalDiffTime
157+ nEvents = lastN + 1
158+ alreadyElapsed = diffUTCTime t tTargetLast
159+ tTarget = addUTCTime dt tTargetLast
160+ thisDelay = realToFrac $ diffUTCTime tTarget t :: Double
161+ Concurrent.delay $ ceiling $ thisDelay * 1000000
162+ _ <- cb (TickInfo t nEvents alreadyElapsed, p)
163+ go tTarget nextGen' cb nEvents
164+
165+-- | Send events with inhomogeneous Poisson timing with the given basis
166+-- and variable rate. Provide a maxRate that you expect to support
167+inhomogeneousPoisson
168+ :: (RandomGen g, MonadIO (Performable m), PerformEvent t m, TriggerEvent t m, PostBuild t m)
169+ => g
170+ -> Behavior t Double
171+ -> Double
172+ -> UTCTime
173+ -> m (Event t TickInfo)
174+inhomogeneousPoisson rnd rate maxRate t0 =
175+ inhomogeneousPoissonFrom rnd rate maxRate t0 =<< getPostBuild
176+
66177-- | Block occurrences of an Event until the given number of seconds elapses without
67178-- the Event firing, at which point the last occurrence of the Event will fire.
68−debounce :: MonadWidget t m => NominalDiffTime -> Event t a -> m (Event t a)
179+debounce :: (MonadFix m, MonadHold t m, PerformEvent t m, TriggerEvent t m, MonadIO (Performable m)) => NominalDiffTime -> Event t a -> m (Event t a)
69180debounce dt e = do
70181 n :: Dynamic t Integer <- count e
71− let tagged = attachDynWith (,) n e
182+ let tagged = attachPromptlyDynWith (,) n e
72183 delayed <- delay dt tagged
73184 return $ attachWithMaybe (\n' (t, v) -> if n' == t then Just v else Nothing) (current n) delayed
src/Reflex/Dom/WebSocket.hs+36−26
@@ -1,28 +1,46 @@
1−{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI, CPP, TemplateHaskell, NoMonomorphismRestriction, EmptyDataDecls, RankNTypes, GADTs, RecursiveDo, ScopedTypeVariables, FlexibleInstances, MultiParamTypeClasses, TypeFamilies, FlexibleContexts, DeriveDataTypeable, GeneralizedNewtypeDeriving, StandaloneDeriving, ConstraintKinds, UndecidableInstances, PolyKinds, AllowAmbiguousTypes #-}
1+{-# LANGUAGE AllowAmbiguousTypes #-}
2+{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE DeriveDataTypeable #-}
4+{-# LANGUAGE EmptyDataDecls #-}
5+{-# LANGUAGE FlexibleContexts #-}
6+{-# LANGUAGE FlexibleInstances #-}
7+{-# LANGUAGE ForeignFunctionInterface #-}
8+{-# LANGUAGE GADTs #-}
9+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
10+{-# LANGUAGE JavaScriptFFI #-}
11+{-# LANGUAGE MultiParamTypeClasses #-}
12+{-# LANGUAGE NoMonomorphismRestriction #-}
13+{-# LANGUAGE PolyKinds #-}
14+{-# LANGUAGE RankNTypes #-}
15+{-# LANGUAGE RecursiveDo #-}
16+{-# LANGUAGE ScopedTypeVariables #-}
17+{-# LANGUAGE StandaloneDeriving #-}
18+{-# LANGUAGE TemplateHaskell #-}
19+{-# LANGUAGE TypeFamilies #-}
20+{-# LANGUAGE UndecidableInstances #-}
221
322module Reflex.Dom.WebSocket where
423
5−import Prelude hiding (div, span, mapM, mapM_, concat, concatMap, all, sequence)
24+import Prelude hiding (all, concat, concatMap, div, mapM, mapM_, sequence, span)
625
726import Reflex
8−import Reflex.Host.Class
927import Reflex.Dom.Class
28+import Reflex.Dom.PerformEvent.Class
29+import Reflex.Dom.PostBuild.Class
1030import Reflex.Dom.WebSocket.Foreign
1131
1232import Control.Concurrent
1333import Control.Concurrent.STM
14−import Control.Exception (catch, SomeException)
34+import Control.Exception (SomeException, catch)
1535import Control.Lens
1636import Control.Monad hiding (forM, forM_, mapM, mapM_, sequence)
1737import Control.Monad.IO.Class
18−import Control.Monad.Ref
1938import Control.Monad.State
20−import Control.Arrow (second)
21−import Data.Maybe (isJust)
2239import Data.ByteString (ByteString)
2340import Data.Default
24−import Data.Dependent.Map (DSum (..))
2541import Data.IORef
42+import Data.Maybe (isJust)
43+import Data.Text
2644
2745data WebSocketConfig t
2846 = WebSocketConfig { _webSocketConfig_send :: Event t [ByteString]
@@ -36,28 +54,20 @@ data WebSocket t
3654 , _webSocket_open :: Event t ()
3755 }
3856
39−webSocket :: forall t m. (HasWebView m, MonadWidget t m) => String -> WebSocketConfig t -> m (WebSocket t)
57+webSocket :: (MonadIO m, MonadIO (Performable m), HasWebView m, PerformEvent t m, TriggerEvent t m, PostBuild t m) => Text -> WebSocketConfig t -> m (WebSocket t)
4058webSocket url config = do
41− wv <- askWebView
42− postGui <- askPostGui
43− runWithActions <- askRunWithActions
44− (eRecv, eRecvTriggerRef) <- newEventWithTriggerRef
59+ wv <- fmap unWebViewSingleton askWebView
60+ (eRecv, onMessage) <- newTriggerEvent
4561 currentSocketRef <- liftIO $ newIORef Nothing
4662 --TODO: Disconnect if value no longer needed
47− (eOpen, eOpenTriggerRef) <- newEventWithTriggerRef
63+ (eOpen, triggerEOpen) <- newTriggerEventWithOnComplete
4864 payloadQueue <- liftIO newTQueueIO
4965 isOpen <- liftIO newEmptyTMVarIO
50− 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 ()]
66+ let onOpen = triggerEOpen () $ do
5767 liftIO $ void $ atomically $ tryPutTMVar isOpen ()
5868 --TODO: Is the fork necessary, or do event handlers run in their own threads automatically?
5969 onClose = void $ forkIO $ do
60− liftIO $ atomically $ tryTakeTMVar isOpen
70+ _ <- liftIO $ atomically $ tryTakeTMVar isOpen
6171 liftIO $ writeIORef currentSocketRef Nothing
6272 liftIO $ threadDelay 1000000
6373 start
@@ -65,23 +75,23 @@ webSocket url config = do
6575 ws <- liftIO $ newWebSocket wv url onMessage onOpen onClose
6676 liftIO $ writeIORef currentSocketRef $ Just ws
6777 return ()
68− schedulePostBuild $ liftIO start
78+ performEvent_ . (liftIO start <$) =<< getPostBuild
6979 performEvent_ $ ffor (_webSocketConfig_send config) $ \payloads -> forM_ payloads $ \payload ->
7080 liftIO $ atomically $ writeTQueue payloadQueue payload
7181
72− liftIO $ forkIO $ forever $ do
82+ _ <- liftIO $ forkIO $ forever $ do
7383 payload <- atomically $ do
7484 pl <- readTQueue payloadQueue
7585 open <- tryReadTMVar isOpen
7686 if isJust open then return pl else retry
7787
7888 mws <- liftIO $ readIORef currentSocketRef
79− succ <- case mws of
89+ success <- case mws of
8090 Nothing -> return False
8191 Just ws -> liftIO ((webSocketSend ws payload >> return True)
8292 `catch`
8393 (\(_ :: SomeException) -> return False))
84− unless succ $ atomically $ unGetTQueue payloadQueue payload
94+ unless success $ atomically $ unGetTQueue payloadQueue payload
8595 return $ WebSocket eRecv eOpen
8696
8797makeLensesWith (lensRules & simpleLenses .~ True) ''WebSocketConfig
src/Reflex/Dom/Widget.hs+5−9
@@ -1,10 +1,6 @@
1−module Reflex.Dom.Widget ( module Reflex.Dom.Widget.Basic
2− , module Reflex.Dom.Widget.Input
3− , module Reflex.Dom.Widget.Lazy
4− , module Reflex.Dom.Widget.Resize
5− ) where
1+module Reflex.Dom.Widget (module X) where
62
7−import Reflex.Dom.Widget.Basic
8−import Reflex.Dom.Widget.Input
9−import Reflex.Dom.Widget.Lazy
10−import Reflex.Dom.Widget.Resize
3+import Reflex.Dom.Widget.Basic as X
4+import Reflex.Dom.Widget.Input as X
5+import Reflex.Dom.Widget.Lazy as X
6+import Reflex.Dom.Widget.Resize as X
src/Reflex/Dom/Widget/Basic.hs+248−734
@@ -1,281 +1,200 @@
1−{-# LANGUAGE ScopedTypeVariables, LambdaCase, ConstraintKinds, TypeFamilies, FlexibleContexts, MultiParamTypeClasses, FlexibleInstances, RecursiveDo, GADTs, DataKinds, RankNTypes, TemplateHaskell #-}
2−
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE LambdaCase #-}
7+{-# LANGUAGE MultiParamTypeClasses #-}
8+{-# LANGUAGE OverloadedStrings #-}
9+{-# LANGUAGE RankNTypes #-}
10+{-# LANGUAGE RecursiveDo #-}
11+{-# LANGUAGE ScopedTypeVariables #-}
12+{-# LANGUAGE TypeFamilies #-}
313module Reflex.Dom.Widget.Basic where
414
15+import Reflex.Dom.Builder.Class
516import Reflex.Dom.Class
617import Reflex.Dom.Internal.Foreign ()
18+import Reflex.Dom.PostBuild.Class
719
8−import Prelude hiding (mapM, mapM_, sequence, sequence_)
9−import Reflex
10−import Reflex.Host.Class
20+import Control.Arrow
21+import Control.Lens hiding (children, element)
22+import Control.Monad.Reader hiding (forM, forM_, mapM, mapM_, sequence, sequence_)
23+import Data.Align
24+import Data.Default
25+import Data.Either
26+import Data.Foldable
1127import Data.Functor.Misc
1228import Data.Map (Map)
1329import qualified Data.Map as Map
30+import Data.Maybe
31+import Data.Semigroup
1432import Data.Set (Set)
1533import qualified Data.Set as Set
16−import Data.Dependent.Sum (DSum (..))
17−import Data.Foldable
18−import Data.Traversable
19−import Control.Monad.Trans
20−import 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
30−import Control.Lens hiding (element, children)
34+import Data.Text (Text)
35+import qualified Data.Text as T
3136import Data.These
32−import Data.Align
33−import Data.Maybe
34−import Data.GADT.Compare.TH
35−import Data.Bitraversable
36−import GHCJS.DOM.MouseEvent
37−import Data.IORef
38−import Data.Default
37+import Data.Traversable
38+import Prelude hiding (mapM, mapM_, sequence, sequence_)
39+import Reflex
40+
41+widgetHoldInternal :: DomBuilder t m => m a -> Event t (m b) -> m (a, Event t b)
42+widgetHoldInternal child0 child' = do
43+ childResult0 <- deletable (void child') child0
44+ childResult' <- placeholder $ def & placeholderConfig_insertAbove .~ fmap (deletable (void child')) child'
45+ return (childResult0, _placeholder_insertedAbove childResult')
46+
47+-- | 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.
48+-- 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.
49+-- No empty pieces will be included in the output.
50+
51+--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
52+partitionMapBySetLT :: forall k v. Ord k => Set k -> Map k v -> Map (Either k ()) (Map k v)
53+partitionMapBySetLT s m0 = Map.fromDistinctAscList $ go (Set.toAscList s) m0
54+ where go :: [k] -> Map k v -> [(Either k (), Map k v)]
55+ go [] m = if Map.null m
56+ then []
57+ else [(Right (), m)]
58+ go (h:t) m = let (lt, eq, gt) = Map.splitLookup h m
59+ geq = maybe id (Map.insert h) eq gt
60+ in if Map.null lt
61+ then go t geq
62+ else (Left h, lt) : go t geq
63+
64+newtype ChildResult t k a = ChildResult { unChildResult :: (a, Event t (Map k (Maybe (ChildResult t k a)))) }
65+
66+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))
67+listHoldWithKey initialChildren modifyChildren buildChild = do
68+ let deleteChildSelector = fanMap $ fmap void modifyChildren
69+ liveChildren :: Dynamic t (Set k) <- foldDyn applyMapKeysSet (Map.keysSet initialChildren) modifyChildren
70+ let placeChildSelector = fanMap $ attachWith partitionMapBySetLT (current liveChildren) $ fmap (Map.mapMaybe id) modifyChildren
71+ buildAugmentedChild :: k -> v -> m (ChildResult t k a)
72+ buildAugmentedChild k v = do
73+ let delete = select deleteChildSelector $ Const2 k
74+ myCfg = def
75+ & insertAbove .~ fmap (imapM buildAugmentedChild) (select placeChildSelector $ Const2 $ Left k)
76+ & deleteSelf .~ delete
77+ ph <- placeholder myCfg
78+ result <- deletable delete $ buildChild k v
79+ 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
80+ rec initialAugmentedResults <- iforM initialChildren buildAugmentedChild
81+ augmentedResults <- foldDyn applyMap initialAugmentedResults $ newInsertedBelow <> newInsertedAbove
82+ let newInsertedAbove = switch $ mconcat . reverse . fmap (snd . unChildResult) . Map.elems <$> current augmentedResults
83+ belowAll <- placeholder $ def & placeholderConfig_insertAbove .~ fmap (imapM buildAugmentedChild) (select placeChildSelector $ Const2 $ Right ())
84+ let newInsertedBelow = fmap Just <$> _placeholder_insertedAbove belowAll
85+ return $ fmap (fmap (fst . unChildResult)) augmentedResults
86+
87+text :: DomBuilder t m => Text -> m ()
88+text t = void $ textNode $ def & textNodeConfig_initialContents .~ t
89+
90+dynText :: forall t m. (PostBuild t m, DomBuilder t m) => Dynamic t Text -> m ()
91+dynText t = do
92+ postBuild <- getPostBuild
93+ void $ textNode $ (def :: TextNodeConfig t) & textNodeConfig_setContents .~ leftmost
94+ [ updated t
95+ , tag (current t) postBuild
96+ ]
97+
98+display :: (PostBuild t m, DomBuilder t m, Show a) => Dynamic t a -> m ()
99+display = dynText . fmap (T.pack . show)
39100
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
101+button :: DomBuilder t m => Text -> m (Event t ())
102+button t = do
103+ (e, _) <- element "button" def $ text t
104+ return $ domEvent Click e
129105
130106--TODO: Should this be renamed to 'widgetView' for consistency with 'widgetHold'?
131107-- | Given a Dynamic of widget-creating actions, create a widget that is recreated whenever the Dynamic updates.
132108-- The returned Event of widget results occurs when the Dynamic does.
133109-- 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)
110+dyn :: (DomBuilder t m, PostBuild t m) => Dynamic t (m a) -> m (Event t a)
135111dyn child = do
136112 postBuild <- getPostBuild
137113 let newChild = leftmost [updated child, tag (current child) postBuild]
138− liftM snd $ widgetHoldInternal (return ()) newChild
114+ snd <$> widgetHoldInternal (return ()) newChild
139115
140116-- | Given an initial widget and an Event of widget-creating actions, create a widget that is recreated whenever the Event fires.
141117-- The returned Dynamic of widget results occurs when the Event does.
142118-- 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)
119+widgetHold :: (DomBuilder t m, MonadHold t m) => m a -> Event t (m a) -> m (Dynamic t a)
144120widgetHold child0 newChild = do
145121 (result0, newResult) <- widgetHoldInternal child0 newChild
146122 holdDyn result0 newResult
147123
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− childVoidAction <- hold childVoidAction0 $ fmap snd newChildBuilt
158− addVoidAction $ switch childVoidAction --TODO: Should this be a switchPromptly?
159− doc <- askDocument
160− runWidget <- getRunWidget
161− addVoidAction $ ffor newChild $ \c -> do
162− Just df <- createDocumentFragment doc
163− (result, postBuild, voidActions) <- runWidget df c
164− runFrameWithTriggerRef newChildBuiltTriggerRef (result, voidActions)
165− postBuild
166− liftIO $ deleteBetweenExclusive startPlaceholder endPlaceholder
167− mp' <- getParentNode endPlaceholder
168− forM_ mp' $ \p' -> insertBefore p' (Just df) (Just endPlaceholder)
169− return ()
170− return (result0, fmap fst newChildBuilt)
171−
172124diffMapNoEq :: (Ord k) => Map k v -> Map k v -> Map k (Maybe v)
173125diffMapNoEq olds news = flip Map.mapMaybe (align olds news) $ \case
174126 This _ -> Just Nothing
175127 These _ new -> Just $ Just new
176128 That new -> Just $ Just new
177129
178−applyMap :: Ord k => Map k v -> Map k (Maybe v) -> Map k v
179−applyMap olds diffs = flip Map.mapMaybe (align olds diffs) $ \case
180− This old -> Just old
181− These _ new -> new
182− That new -> new
130+diffMap :: (Ord k, Eq v) => Map k v -> Map k v -> Map k (Maybe v)
131+diffMap olds news = flip Map.mapMaybe (align olds news) $ \case
132+ This _ -> Just Nothing
133+ These old new
134+ | old == new -> Nothing
135+ | otherwise -> Just $ Just new
136+ That new -> Just $ Just new
137+
138+applyMap :: Ord k => Map k (Maybe v) -> Map k v -> Map k v
139+applyMap patch old = insertions `Map.union` (old `Map.difference` deletions)
140+ where (deletions, insertions) = mapPartitionEithers $ maybeToEither <$> patch
141+ maybeToEither = \case
142+ Nothing -> Left ()
143+ Just r -> Right r
144+
145+mapPartitionEithers :: Map k (Either a b) -> (Map k a, Map k b)
146+mapPartitionEithers m = (fromLeft <$> ls, fromRight <$> rs)
147+ where (ls, rs) = Map.partition isLeft m
148+ fromLeft (Left l) = l
149+ fromLeft _ = error "mapPartitionEithers: fromLeft received a Right value; this should be impossible"
150+ fromRight (Right r) = r
151+ fromRight _ = error "mapPartitionEithers: fromRight received a Left value; this should be impossible"
152+
153+-- | Apply a map patch to a set
154+-- > applyMapKeysSet patch (Map.keysSet m) == Map.keysSet (applyMap patch m)
155+applyMapKeysSet :: Ord k => Map k (Maybe v) -> Set k -> Set k
156+applyMapKeysSet patch old = Map.keysSet insertions `Set.union` (old `Set.difference` Map.keysSet deletions)
157+ where (insertions, deletions) = Map.partition isJust patch
183158
184159--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
185−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))
160+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))
186161listWithKey vals mkChild = do
187162 postBuild <- getPostBuild
188− rec sentVals :: Dynamic t (Map k v) <- foldDyn (flip applyMap) Map.empty changeVals
163+ rec sentVals :: Dynamic t (Map k v) <- foldDyn applyMap Map.empty changeVals
189164 let changeVals :: Event t (Map k (Maybe v))
190165 changeVals = attachWith diffMapNoEq (current sentVals) $ leftmost
191166 [ updated vals
192− , tag (current vals) postBuild
167+ , 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
193168 ]
194169 listWithKeyShallowDiff Map.empty changeVals $ \k v0 dv -> do
195170 mkChild k =<< holdDyn v0 dv
196171
197172{-# DEPRECATED listWithKey' "listWithKey' has been renamed to listWithKeyShallowDiff; also, its behavior has changed to fix a bug where children were always rebuilt (never updated)" #-}
198−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))
173+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))
199174listWithKey' = listWithKeyShallowDiff
200175
201176-- | 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.
202−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))
177+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))
203178listWithKeyShallowDiff initialVals valsChanged mkChild = do
204179 let childValChangedSelector = fanMap $ fmap (Map.mapMaybe id) valsChanged
205− sentVals <- foldDyn (flip applyMap) (() <$ initialVals) $ fmap (fmap (fmap (\_ -> ()))) valsChanged
206− let relevantDiff diff _ = case diff of
180+ sentVals <- foldDyn applyMap Map.empty $ fmap (fmap void) valsChanged
181+ let relevantPatch patch _ = case patch of
207182 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
208183 Just _ -> Nothing -- We don't want to let spurious re-creations of items through
209− listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantDiff)) (current sentVals) valsChanged) $ \k v ->
184+ listHoldWithKey initialVals (attachWith (flip (Map.differenceWith relevantPatch)) (current sentVals) valsChanged) $ \k v ->
210185 mkChild k v $ Reflex.select childValChangedSelector $ Const2 k
211186
212−-- | 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.
213−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))
214−listHoldWithKey initialVals valsChanged mkChild = do
215− doc <- askDocument
216− endPlaceholder <- text' ""
217− (newChildren, newChildrenTriggerRef) <- newEventWithTriggerRef
218−-- performEvent_ $ fmap (const $ return ()) newChildren --TODO: Get rid of this hack
219− runWidget <- getRunWidget
220− let buildChild df k v = runWidget df $ wrapChild k v
221− wrapChild k v = do
222− childStart <- text' ""
223− result <- mkChild k v
224− childEnd <- text' ""
225− return (result, (childStart, childEnd))
226− Just dfOrig <- createDocumentFragment doc
227− 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
228− stateRef <- liftIO $ newIORef initialState
229− children <- holdDyn initialState newChildren
230− addVoidAction $ switch $ fmap (mergeWith (>>) . map snd . Map.elems) $ current children
231− mpOrig <- getParentNode endPlaceholder
232− forM_ mpOrig $ \pOrig -> insertBefore pOrig (Just dfOrig) (Just endPlaceholder)
233− addVoidAction $ flip fmap valsChanged $ \newVals -> do
234− curState <- liftIO $ readIORef stateRef
235− --TODO: Should we remove the parent from the DOM first to avoid reflows?
236− (newState, postBuild) <- flip runStateT (return ()) $ liftM (Map.mapMaybe id) $ iforM (align curState newVals) $ \k -> \case
237− These ((_, (start, end)), _) Nothing -> do -- Deleting child
238− liftIO $ deleteBetweenInclusive start end
239− return Nothing
240− These ((_, (start, end)), _) (Just v) -> do -- Replacing existing child
241− liftIO $ deleteBetweenExclusive start end
242− Just df <- createDocumentFragment doc
243− (childResult, childPostBuild, childVoidAction) <- lift $ buildChild df k v
244− let s = (childResult, childVoidAction)
245− modify (>>childPostBuild)
246− mp <- getParentNode end
247− forM_ mp $ \p -> insertBefore p (Just df) (Just end)
248− return $ Just s
249− That Nothing -> return Nothing -- Deleting non-existent child
250− That (Just v) -> do -- Creating new child
251− Just df <- createDocumentFragment doc
252− (childResult, childPostBuild, childVoidAction) <- lift $ buildChild df k v
253− let s = (childResult, childVoidAction)
254− modify (>>childPostBuild)
255− let placeholder = case Map.lookupGT k curState of
256− Nothing -> endPlaceholder
257− Just (_, ((_, (start, _)), _)) -> start
258− mp <- getParentNode placeholder
259− forM_ mp $ \p -> insertBefore p (Just df) (Just placeholder)
260− return $ Just s
261− This state -> do -- No change
262− return $ Just state
263− liftIO $ writeIORef stateRef newState
264− runFrameWithTriggerRef newChildrenTriggerRef newState
265− postBuild
266− mapDyn (fmap (fst . fst)) children
267−
268187--TODO: Something better than Dynamic t (Map k v) - we want something where the Events carry diffs, not the whole value
269188-- | Create a dynamically-changing set of Event-valued widgets.
270189-- 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'.
271−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))
272−listViewWithKey vals mkChild = liftM (switch . fmap mergeMap) $ listViewWithKey' vals mkChild
190+listViewWithKey :: (Ord k, DomBuilder t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m (Event t a)) -> m (Event t (Map k a))
191+listViewWithKey vals mkChild = switch . fmap mergeMap <$> listViewWithKey' vals mkChild
273192
274−listViewWithKey' :: (Ord k, MonadWidget t m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
275−listViewWithKey' vals mkChild = liftM current $ listWithKey vals mkChild
193+listViewWithKey' :: (Ord k, DomBuilder t m, PostBuild t m, MonadHold t m, MonadFix m) => Dynamic t (Map k v) -> (k -> Dynamic t v -> m a) -> m (Behavior t (Map k a))
194+listViewWithKey' vals mkChild = current <$> listWithKey vals mkChild
276195
277196-- | Create a dynamically-changing set of widgets, one of which is selected at any time.
278−selectViewListWithKey :: forall t m k v a. (MonadWidget t m, Ord k)
197+selectViewListWithKey :: forall t m k v a. (DomBuilder t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
279198 => Dynamic t k -- ^ Current selection key
280199 -> Dynamic t (Map k v) -- ^ Dynamic key/value map
281200 -> (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
@@ -283,465 +202,104 @@ selectViewListWithKey :: forall t m k v a. (MonadWidget t m, Ord k)
283202selectViewListWithKey selection vals mkChild = do
284203 let selectionDemux = demux selection -- For good performance, this value must be shared across all children
285204 selectChild <- listWithKey vals $ \k v -> do
286− selected <- getDemuxed selectionDemux k
205+ let selected = demuxed selectionDemux k
287206 selectSelf <- mkChild k v selected
288207 return $ fmap ((,) k) selectSelf
289− liftM switchPromptlyDyn $ mapDyn (leftmost . Map.elems) selectChild
208+ return $ switchPromptlyDyn $ leftmost . Map.elems <$> selectChild
290209
291−selectViewListWithKey_ :: forall t m k v a. (MonadWidget t m, Ord k)
210+selectViewListWithKey_ :: forall t m k v a. (DomBuilder t m, Ord k, PostBuild t m, MonadHold t m, MonadFix m)
292211 => Dynamic t k -- ^ Current selection key
293212 -> Dynamic t (Map k v) -- ^ Dynamic key/value map
294213 -> (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
295214 -> m (Event t k) -- ^ Event that fires when any child's return Event fires. Contains key of an arbitrary firing widget.
296−selectViewListWithKey_ selection vals mkChild = liftM (fmap fst) $ selectViewListWithKey selection vals mkChild
297−
298−--------------------------------------------------------------------------------
299−-- Basic DOM manipulation helpers
300−--------------------------------------------------------------------------------
301−
302−-- | s and e must both be children of the same node and s must precede e
303−deleteBetweenExclusive :: (IsNode start, IsNode end) => start -> end -> IO ()
304−deleteBetweenExclusive s e = do
305− 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
306− case mCurrentParent of
307− Nothing -> return () --TODO: Is this the right behavior?
308− Just currentParent -> do
309− let go = do
310− Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
311− when (toNode s /= toNode x) $ do
312− _ <- removeChild currentParent $ Just x
313− go
314− go
315−
316−-- | s and e must both be children of the same node and s must precede e
317−deleteBetweenInclusive :: (IsNode start, IsNode end) => start -> end -> IO ()
318−deleteBetweenInclusive s e = do
319− 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
320− case mCurrentParent of
321− Nothing -> return () --TODO: Is this the right behavior?
322− Just currentParent -> do
323− let go = do
324− Just x <- getPreviousSibling e -- This can't be Nothing because we should hit 's' first
325− _ <- removeChild currentParent $ Just x
326− when (toNode s /= toNode x) go
327− go
328− _ <- removeChild currentParent $ Just e
329− return ()
330−
331−nodeClear :: IsNode self => self -> IO ()
332−nodeClear n = do
333− mfc <- getFirstChild n
334− case mfc of
335− Nothing -> return ()
336− Just fc -> do
337− _ <- removeChild n $ Just fc
338− nodeClear n
339−
340−--------------------------------------------------------------------------------
341−-- Adapters
342−--------------------------------------------------------------------------------
343−
344−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)
345−wrapDomEvent element elementOnevent getValue = wrapDomEventMaybe element elementOnevent $ liftM Just getValue
346−
347−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)
348−wrapDomEventMaybe element elementOnevent getValue = do
349− postGui <- askPostGui
350− runWithActions <- askRunWithActions
351− e <- newEventWithTrigger $ \et -> do
352− unsubscribe <- {-# SCC "a" #-} liftIO $ {-# SCC "b" #-} elementOnevent element $ {-# SCC "c" #-} do
353− mv <- {-# SCC "d" #-} getValue
354− forM_ mv $ \v -> liftIO $ postGui $ runWithActions [et :=> Identity v]
355− return $ liftIO $ do
356− {-# SCC "e" #-} unsubscribe
357− return $! {-# SCC "f" #-} e
358−
359−data EventTag
360− = AbortTag
361− | BlurTag
362− | ChangeTag
363− | ClickTag
364− | ContextmenuTag
365− | DblclickTag
366− | DragTag
367− | DragendTag
368− | DragenterTag
369− | DragleaveTag
370− | DragoverTag
371− | DragstartTag
372− | DropTag
373− | ErrorTag
374− | FocusTag
375− | InputTag
376− | InvalidTag
377− | KeydownTag
378− | KeypressTag
379− | KeyupTag
380− | LoadTag
381− | MousedownTag
382− | MouseenterTag
383− | MouseleaveTag
384− | MousemoveTag
385− | MouseoutTag
386− | MouseoverTag
387− | MouseupTag
388− | MousewheelTag
389− | ScrollTag
390− | SelectTag
391− | SubmitTag
392− | WheelTag
393− | BeforecutTag
394− | CutTag
395− | BeforecopyTag
396− | CopyTag
397− | BeforepasteTag
398− | PasteTag
399− | ResetTag
400− | SearchTag
401− | SelectstartTag
402− | TouchstartTag
403− | TouchmoveTag
404− | TouchendTag
405− | TouchcancelTag
406−
407−data EventName :: EventTag -> * where
408− Abort :: EventName 'AbortTag
409− Blur :: EventName 'BlurTag
410− Change :: EventName 'ChangeTag
411− Click :: EventName 'ClickTag
412− Contextmenu :: EventName 'ContextmenuTag
413− Dblclick :: EventName 'DblclickTag
414− Drag :: EventName 'DragTag
415− Dragend :: EventName 'DragendTag
416− Dragenter :: EventName 'DragenterTag
417− Dragleave :: EventName 'DragleaveTag
418− Dragover :: EventName 'DragoverTag
419− Dragstart :: EventName 'DragstartTag
420− Drop :: EventName 'DropTag
421− Error :: EventName 'ErrorTag
422− Focus :: EventName 'FocusTag
423− Input :: EventName 'InputTag
424− Invalid :: EventName 'InvalidTag
425− Keydown :: EventName 'KeydownTag
426− Keypress :: EventName 'KeypressTag
427− Keyup :: EventName 'KeyupTag
428− Load :: EventName 'LoadTag
429− Mousedown :: EventName 'MousedownTag
430− Mouseenter :: EventName 'MouseenterTag
431− Mouseleave :: EventName 'MouseleaveTag
432− Mousemove :: EventName 'MousemoveTag
433− Mouseout :: EventName 'MouseoutTag
434− Mouseover :: EventName 'MouseoverTag
435− Mouseup :: EventName 'MouseupTag
436− Mousewheel :: EventName 'MousewheelTag
437− Scroll :: EventName 'ScrollTag
438− Select :: EventName 'SelectTag
439− Submit :: EventName 'SubmitTag
440− Wheel :: EventName 'WheelTag
441− Beforecut :: EventName 'BeforecutTag
442− Cut :: EventName 'CutTag
443− Beforecopy :: EventName 'BeforecopyTag
444− Copy :: EventName 'CopyTag
445− Beforepaste :: EventName 'BeforepasteTag
446− Paste :: EventName 'PasteTag
447− Reset :: EventName 'ResetTag
448− Search :: EventName 'SearchTag
449− Selectstart :: EventName 'SelectstartTag
450− Touchstart :: EventName 'TouchstartTag
451− Touchmove :: EventName 'TouchmoveTag
452− Touchend :: EventName 'TouchendTag
453− Touchcancel :: EventName 'TouchcancelTag
454−
455−type family EventType en where
456− EventType 'AbortTag = UIEvent
457− EventType 'BlurTag = FocusEvent
458− EventType 'ChangeTag = DOM.Event
459− EventType 'ClickTag = MouseEvent
460− EventType 'ContextmenuTag = MouseEvent
461− EventType 'DblclickTag = MouseEvent
462− EventType 'DragTag = MouseEvent
463− EventType 'DragendTag = MouseEvent
464− EventType 'DragenterTag = MouseEvent
465− EventType 'DragleaveTag = MouseEvent
466− EventType 'DragoverTag = MouseEvent
467− EventType 'DragstartTag = MouseEvent
468− EventType 'DropTag = MouseEvent
469− EventType 'ErrorTag = UIEvent
470− EventType 'FocusTag = FocusEvent
471− EventType 'InputTag = DOM.Event
472− EventType 'InvalidTag = DOM.Event
473− EventType 'KeydownTag = KeyboardEvent
474− EventType 'KeypressTag = KeyboardEvent
475− EventType 'KeyupTag = KeyboardEvent
476− EventType 'LoadTag = UIEvent
477− EventType 'MousedownTag = MouseEvent
478− EventType 'MouseenterTag = MouseEvent
479− EventType 'MouseleaveTag = MouseEvent
480− EventType 'MousemoveTag = MouseEvent
481− EventType 'MouseoutTag = MouseEvent
482− EventType 'MouseoverTag = MouseEvent
483− EventType 'MouseupTag = MouseEvent
484− EventType 'MousewheelTag = MouseEvent
485− EventType 'ScrollTag = UIEvent
486− EventType 'SelectTag = UIEvent
487− EventType 'SubmitTag = DOM.Event
488− EventType 'WheelTag = WheelEvent
489− EventType 'BeforecutTag = DOM.Event
490− EventType 'CutTag = DOM.Event
491− EventType 'BeforecopyTag = DOM.Event
492− EventType 'CopyTag = DOM.Event
493− EventType 'BeforepasteTag = DOM.Event
494− EventType 'PasteTag = DOM.Event
495− EventType 'ResetTag = DOM.Event
496− EventType 'SearchTag = DOM.Event
497− EventType 'SelectstartTag = DOM.Event
498− EventType 'TouchstartTag = TouchEvent
499− EventType 'TouchmoveTag = TouchEvent
500− EventType 'TouchendTag = TouchEvent
501− EventType 'TouchcancelTag = TouchEvent
502−
503−onEventName :: IsElement e => EventName en -> e -> EventM e (EventType en) () -> IO (IO ())
504−onEventName en e = case en of
505− Abort -> on e E.abort
506− Blur -> on e E.blurEvent
507− Change -> on e E.change
508− Click -> on e E.click
509− Contextmenu -> on e E.contextMenu
510− Dblclick -> on e E.dblClick
511− Drag -> on e E.drag
512− Dragend -> on e E.dragEnd
513− Dragenter -> on e E.dragEnter
514− Dragleave -> on e E.dragLeave
515− Dragover -> on e E.dragOver
516− Dragstart -> on e E.dragStart
517− Drop -> on e E.drop
518− Error -> on e E.error
519− Focus -> on e E.focusEvent
520− Input -> on e E.input
521− Invalid -> on e E.invalid
522− Keydown -> on e E.keyDown
523− Keypress -> on e E.keyPress
524− Keyup -> on e E.keyUp
525− Load -> on e E.load
526− Mousedown -> on e E.mouseDown
527− Mouseenter -> on e E.mouseEnter
528− Mouseleave -> on e E.mouseLeave
529− Mousemove -> on e E.mouseMove
530− Mouseout -> on e E.mouseOut
531− Mouseover -> on e E.mouseOver
532− Mouseup -> on e E.mouseUp
533− Mousewheel -> on e E.mouseWheel
534− Scroll -> on e E.scroll
535− Select -> on e E.select
536− Submit -> on e E.submit
537− Wheel -> on e E.wheel
538− Beforecut -> on e E.beforeCut
539− Cut -> on e E.cut
540− Beforecopy -> on e E.beforeCopy
541− Copy -> on e E.copy
542− Beforepaste -> on e E.beforePaste
543− Paste -> on e E.paste
544− Reset -> on e E.reset
545− Search -> on e E.search
546− Selectstart -> on e E.selectStart
547− Touchstart -> on e E.touchStart
548− Touchmove -> on e E.touchMove
549− Touchend -> on e E.touchEnd
550− Touchcancel -> on e E.touchCancel
551−
552−newtype EventResult en = EventResult { unEventResult :: EventResultType en }
553−
554−type family EventResultType (en :: EventTag) :: * where
555− EventResultType 'ClickTag = ()
556− EventResultType 'DblclickTag = ()
557− EventResultType 'KeypressTag = Int
558− EventResultType 'KeydownTag = Int
559− EventResultType 'KeyupTag = Int
560− EventResultType 'ScrollTag = Int
561− EventResultType 'MousemoveTag = (Int, Int)
562− EventResultType 'MousedownTag = (Int, Int)
563− EventResultType 'MouseupTag = (Int, Int)
564− EventResultType 'MouseenterTag = ()
565− EventResultType 'MouseleaveTag = ()
566− EventResultType 'FocusTag = ()
567− EventResultType 'BlurTag = ()
568− EventResultType 'ChangeTag = ()
569− EventResultType 'DragTag = ()
570− EventResultType 'DragendTag = ()
571− EventResultType 'DragenterTag = ()
572− EventResultType 'DragleaveTag = ()
573− EventResultType 'DragoverTag = ()
574− EventResultType 'DragstartTag = ()
575− EventResultType 'DropTag = ()
576− EventResultType 'AbortTag = ()
577− EventResultType 'ContextmenuTag = ()
578− EventResultType 'ErrorTag = ()
579− EventResultType 'InputTag = ()
580− EventResultType 'InvalidTag = ()
581− EventResultType 'LoadTag = ()
582− EventResultType 'MouseoutTag = ()
583− EventResultType 'MouseoverTag = ()
584− EventResultType 'SelectTag = ()
585− EventResultType 'SubmitTag = ()
586− EventResultType 'BeforecutTag = ()
587− EventResultType 'CutTag = ()
588− EventResultType 'BeforecopyTag = ()
589− EventResultType 'CopyTag = ()
590− EventResultType 'BeforepasteTag = ()
591− EventResultType 'PasteTag = ()
592− EventResultType 'ResetTag = ()
593− EventResultType 'SearchTag = ()
594− EventResultType 'SelectstartTag = ()
595− EventResultType 'TouchstartTag = ()
596− EventResultType 'TouchmoveTag = ()
597− EventResultType 'TouchendTag = ()
598− EventResultType 'TouchcancelTag = ()
599− EventResultType 'MousewheelTag = ()
600− EventResultType 'WheelTag = ()
601−
602−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))
603−wrapDomEventsMaybe element handlers = do
604− postGui <- askPostGui
605− runWithActions <- askRunWithActions
606− e <- newFanEventWithTrigger $ \(WrapArg en) et -> do
607− unsubscribe <- onEventName en element $ do
608− mv <- handlers en
609− forM_ mv $ \v -> liftIO $ postGui $ runWithActions [et :=> Identity v]
610− return $ liftIO $ do
611− unsubscribe
612− return $! e
613−
614−getKeyEvent :: EventM e KeyboardEvent Int
615−getKeyEvent = do
616− e <- event
617− which <- getWhich e
618− if which /= 0 then return which else do
619− charCode <- getCharCode e
620− if charCode /= 0 then return charCode else
621− getKeyCode e
622−
623−getMouseEventCoords :: EventM e MouseEvent (Int, Int)
624−getMouseEventCoords = do
625− e <- event
626− bisequence (getClientX e, getClientY e)
627−
628−defaultDomEventHandler :: IsElement e => e -> EventName en -> EventM e (EventType en) (Maybe (EventResult en))
629−defaultDomEventHandler e evt = liftM (Just . EventResult) $ case evt of
630− Click -> return ()
631− Dblclick -> return ()
632− Keypress -> getKeyEvent
633− Scroll -> getScrollTop e
634− Keydown -> getKeyEvent
635− Keyup -> getKeyEvent
636− Mousemove -> getMouseEventCoords
637− Mouseup -> getMouseEventCoords
638− Mousedown -> getMouseEventCoords
639− Mouseenter -> return ()
640− Mouseleave -> return ()
641− Focus -> return ()
642− Blur -> return ()
643− Change -> return ()
644− Drag -> return ()
645− Dragend -> return ()
646− Dragenter -> return ()
647− Dragleave -> return ()
648− Dragover -> return ()
649− Dragstart -> return ()
650− Drop -> return ()
651− Abort -> return ()
652− Contextmenu -> return ()
653− Error -> return ()
654− Input -> return ()
655− Invalid -> return ()
656− Load -> return ()
657− Mouseout -> return ()
658− Mouseover -> return ()
659− Select -> return ()
660− Submit -> return ()
661− Beforecut -> return ()
662− Cut -> return ()
663− Beforecopy -> return ()
664− Copy -> return ()
665− Beforepaste -> return ()
666− Paste -> return ()
667− Reset -> return ()
668− Search -> return ()
669− Selectstart -> return ()
670− Touchstart -> return ()
671− Touchmove -> return ()
672− Touchend -> return ()
673− Touchcancel -> return ()
674− Mousewheel -> return ()
675− Wheel -> return ()
676−
677−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)
678−wrapElement eh e = do
679− es <- wrapDomEventsMaybe e $ eh e
680− return $ El e es
681−
682−{-# INLINABLE elStopPropagationNS #-}
683−elStopPropagationNS :: (MonadWidget t m, IsEvent (EventType en)) => Maybe String -> String -> EventName en -> m a -> m a
684−elStopPropagationNS mns elementTag evt child = do
685− (e, result) <- buildElementNS mns elementTag (Map.empty :: Map String String) child
686− _ <- liftIO $ onEventName evt e stopPropagation
687− return result
215+selectViewListWithKey_ selection vals mkChild = fmap fst <$> selectViewListWithKey selection vals mkChild
688216
217+{-
689218{-# INLINABLE elWith #-}
690−elWith :: (MonadWidget t m, Attributes m attrs) => String -> ElConfig attrs -> m a -> m a
219+elWith :: (DomBuilder t m, Attributes m attrs) => Text -> ElConfig attrs -> m a -> m a
691220elWith elementTag cfg child = do
692221 (_, result) <- buildElementNS (cfg ^. namespace) elementTag (cfg ^. attributes) child
693222 return result
694223
695224{-# INLINABLE elWith' #-}
696−elWith' :: (MonadWidget t m, Attributes m attrs) => String -> ElConfig attrs -> m a -> m (El t, a)
225+elWith' :: (DomBuilder t m, Attributes m attrs) => Text -> ElConfig attrs -> m a -> m (El t, a)
697226elWith' elementTag cfg child = do
698227 (e, result) <- buildElementNS (cfg ^. namespace) elementTag (cfg ^. attributes) child
699228 e' <- wrapElement defaultDomEventHandler e
700229 return (e', result)
701230
702231{-# INLINABLE emptyElWith #-}
703−emptyElWith :: (MonadWidget t m, Attributes m attrs) => String -> ElConfig attrs -> m ()
232+emptyElWith :: (DomBuilder t m, Attributes m attrs) => Text -> ElConfig attrs -> m ()
704233emptyElWith elementTag cfg = do
705234 _ <- buildEmptyElementNS (cfg ^. namespace) elementTag (cfg ^. attributes)
706235 return ()
707236
708237{-# INLINABLE emptyElWith' #-}
709−emptyElWith' :: (MonadWidget t m, Attributes m attrs) => String -> ElConfig attrs -> m (El t)
238+emptyElWith' :: (DomBuilder t m, Attributes m attrs) => Text -> ElConfig attrs -> m (El t)
710239emptyElWith' elementTag cfg = do
711240 wrapElement defaultDomEventHandler =<< buildEmptyElementNS (cfg ^. namespace) elementTag (cfg ^. attributes)
241+-}
712242
713−{-# INLINABLE elDynAttrNS' #-}
714−elDynAttrNS' :: forall t m a. MonadWidget t m => Maybe String -> String -> Dynamic t (Map String String) -> m a -> m (El t, a)
715−elDynAttrNS' mns elementTag attrs = elWith' elementTag $
716− def & namespace .~ mns
717− & elConfig_attributes .~ attrs
718−
719−{-# INLINABLE elDynAttr' #-}
720−elDynAttr' :: forall t m a. MonadWidget t m => String -> Dynamic t (Map String String) -> m a -> m (El t, a)
721−elDynAttr' elementTag attrs = elWith' elementTag $ def & elConfig_attributes .~ attrs
243+{-# INLINABLE el #-}
244+el :: forall t m a. DomBuilder t m => Text -> m a -> m a
245+el elementTag child = snd <$> el' elementTag child
722246
723247{-# INLINABLE elAttr #-}
724−elAttr :: forall t m a. MonadWidget t m => String -> Map String String -> m a -> m a
725−elAttr elementTag attrs = elWith elementTag $ def & attributes .~ attrs
248+elAttr :: forall t m a. DomBuilder t m => Text -> Map Text Text -> m a -> m a
249+elAttr elementTag attrs child = snd <$> elAttr' elementTag attrs child
250+
251+{-# INLINABLE elClass #-}
252+elClass :: forall t m a. DomBuilder t m => Text -> Text -> m a -> m a
253+elClass elementTag c child = snd <$> elClass' elementTag c child
254+
255+{-# INLINABLE elDynAttr #-}
256+elDynAttr :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t (Map Text Text) -> m a -> m a
257+elDynAttr elementTag attrs child = snd <$> elDynAttr' elementTag attrs child
258+
259+{-# INLINABLE elDynClass #-}
260+elDynClass :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t Text -> m a -> m a
261+elDynClass elementTag c child = snd <$> elDynClass' elementTag c child
726262
727263{-# INLINABLE el' #-}
728−el' :: forall t m a. MonadWidget t m => String -> m a -> m (El t, a)
729−el' elementTag = elWith' elementTag def
264+el' :: forall t m a. DomBuilder t m => Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
265+el' elementTag = element elementTag def
730266
731267{-# INLINABLE elAttr' #-}
732−elAttr' :: forall t m a. MonadWidget t m => String -> Map String String -> m a -> m (El t, a)
733−elAttr' elementTag attrs = elWith' elementTag $ def & attributes .~ attrs
268+elAttr' :: forall t m a. DomBuilder t m => Text -> Map Text Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
269+elAttr' elementTag attrs = element elementTag $ def
270+ & initialAttributes .~ Map.mapKeys (\k -> (Nothing, k)) attrs
734271
735−{-# INLINABLE elDynAttr #-}
736−elDynAttr :: forall t m a. MonadWidget t m => String -> Dynamic t (Map String String) -> m a -> m a
737−elDynAttr elementTag attrs = elWith elementTag $ def & elConfig_attributes .~ attrs
272+{-# INLINABLE elClass' #-}
273+elClass' :: forall t m a. DomBuilder t m => Text -> Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
274+elClass' elementTag c = elAttr' elementTag ("class" =: c)
738275
739−{-# INLINABLE el #-}
740−el :: forall t m a. MonadWidget t m => String -> m a -> m a
741−el elementTag = elWith elementTag def
276+{-# INLINABLE elDynAttr' #-}
277+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)
278+elDynAttr' = elDynAttrNS' Nothing
742279
743−elClass :: forall t m a. MonadWidget t m => String -> String -> m a -> m a
744−elClass elementTag c = elWith elementTag $ def & attributes .~ "class" =: c
280+{-# INLINABLE elDynClass' #-}
281+elDynClass' :: forall t m a. (DomBuilder t m, PostBuild t m) => Text -> Dynamic t Text -> m a -> m (Element EventResult (DomBuilderSpace m) t, a)
282+elDynClass' elementTag c = elDynAttr' elementTag (fmap ("class" =:) c)
283+
284+{-# INLINABLE elDynAttrNS' #-}
285+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)
286+elDynAttrNS' mns elementTag attrs child = do
287+ modifyAttrs <- dynamicAttributesToModifyAttributes attrs
288+ let cfg = def
289+ & elementConfig_namespace .~ mns
290+ & modifyAttributes .~ modifyAttrs
291+ element elementTag cfg child
292+
293+dynamicAttributesToModifyAttributes :: PostBuild t m => Dynamic t (Map Text Text) -> m (Event t (Map AttributeName (Maybe Text)))
294+dynamicAttributesToModifyAttributes d = do
295+ postBuild <- getPostBuild
296+ let modificationsNeeded = flip pushAlways (align postBuild $ updated d) $ \case
297+ This () -> fmap (fmap Just) $ sample $ current d
298+ These () new -> return $ fmap Just new
299+ That new -> do
300+ old <- sample $ current d
301+ return $ diffMap old new
302+ return $ Map.fromList . fmap (\(k, v) -> ((Nothing, k), v)) . Map.toList <$> modificationsNeeded
745303
746304--------------------------------------------------------------------------------
747305-- Copied and pasted from Reflex.Widget.Class
@@ -749,98 +307,77 @@ elClass elementTag c = elWith elementTag $ def & attributes .~ "class" =: c
749307
750308-- | Create a dynamically-changing set of widgets from a Dynamic key/value map.
751309-- Unlike the 'withKey' variants, the child widgets are insensitive to which key they're associated with.
752−list :: (MonadWidget t m, Ord k) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
310+list :: (Ord k, DomBuilder t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t (Map k v) -> (Dynamic t v -> m a) -> m (Dynamic t (Map k a))
753311list dm mkChild = listWithKey dm (\_ dv -> mkChild dv)
754312
755313-- | Create a dynamically-changing set of widgets from a Dynamic list.
756−simpleList :: MonadWidget t m => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
757−simpleList xs mkChild = mapDyn (map snd . Map.toList) =<< flip list mkChild =<< mapDyn (Map.fromList . zip [(1::Int)..]) xs
314+simpleList :: (DomBuilder t m, MonadHold t m, PostBuild t m, MonadFix m) => Dynamic t [v] -> (Dynamic t v -> m a) -> m (Dynamic t [a])
315+simpleList xs mkChild = fmap (fmap (map snd . Map.toList)) $ flip list mkChild $ fmap (Map.fromList . zip [(1::Int)..]) xs
758316
759−elDynHtml' :: MonadWidget t m => String -> Dynamic t String -> m (El t)
317+{-
318+schedulePostBuild x = performEvent_ . (x <$) =<< getPostBuild
319+
320+elDynHtml' :: DomBuilder t m => Text -> Dynamic t Text -> m (El t)
760321elDynHtml' elementTag html = do
761− e <- buildEmptyElement elementTag (Map.empty :: Map String String)
322+ e <- buildEmptyElement elementTag (Map.empty :: Map Text Text)
762323 schedulePostBuild $ setInnerHTML e . Just =<< sample (current html)
763− addVoidAction $ fmap (setInnerHTML e . Just) $ updated html
324+ performEvent_ $ fmap (setInnerHTML e . Just) $ updated html
764325 wrapElement defaultDomEventHandler e
765326
766−elDynHtmlAttr' :: MonadWidget t m => String -> Map String String -> Dynamic t String -> m (El t)
327+elDynHtmlAttr' :: DomBuilder t m => Text -> Map Text Text -> Dynamic t Text -> m (El t)
767328elDynHtmlAttr' elementTag attrs html = do
768329 e <- buildEmptyElement elementTag attrs
769330 schedulePostBuild $ setInnerHTML e . Just =<< sample (current html)
770− addVoidAction $ fmap (setInnerHTML e . Just) $ updated html
331+ performEvent_ $ fmap (setInnerHTML e . Just) $ updated html
771332 wrapElement defaultDomEventHandler e
333+-}
772334
773335data Link t
774336 = Link { _link_clicked :: Event t ()
775337 }
776338
777−class HasAttributes a where
778− type Attrs a :: *
779− attributes :: Lens' a (Attrs a)
780−
781−instance HasAttributes (ElConfig attrs) where
782− type Attrs (ElConfig attrs) = attrs
783− attributes = elConfig_attributes
784−
785−class HasNamespace a where
786− namespace :: Lens' a (Maybe String)
787−
788−instance HasNamespace (ElConfig attrs) where
789− namespace = elConfig_namespace
790−
791−class HasDomEvent t a where
792− domEvent :: EventName en -> a -> Event t (EventResultType en)
793−
794−instance Reflex t => HasDomEvent t (El t) where
795− domEvent en e = fmap unEventResult $ Reflex.select (_el_events e) (WrapArg en)
796−
797−linkClass :: MonadWidget t m => String -> String -> m (Link t)
339+linkClass :: DomBuilder t m => Text -> Text -> m (Link t)
798340linkClass s c = do
799341 (l,_) <- elAttr' "a" ("class" =: c) $ text s
800342 return $ Link $ domEvent Click l
801343
802−link :: MonadWidget t m => String -> m (Link t)
344+link :: DomBuilder t m => Text -> m (Link t)
803345link s = linkClass s ""
804346
805−button :: MonadWidget t m => String -> m (Event t ())
806−button s = do
807− (e, _) <- elAttr' "button" (Map.singleton "type" "button") $ text s
808− return $ domEvent Click e
809−
810347newtype Workflow t m a = Workflow { unWorkflow :: m (a, Event t (Workflow t m a)) }
811348
812−workflow :: forall t m a. MonadWidget t m => Workflow t m a -> m (Dynamic t a)
349+workflow :: forall t m a. (DomBuilder t m, MonadFix m, MonadHold t m) => Workflow t m a -> m (Dynamic t a)
813350workflow w0 = do
814− rec eResult <- widgetHold (unWorkflow w0) $ fmap unWorkflow $ switch $ fmap snd $ current eResult
815− mapDyn fst eResult
351+ rec eResult <- widgetHold (unWorkflow w0) $ fmap unWorkflow $ switch $ snd <$> current eResult
352+ return $ fmap fst eResult
816353
817−workflowView :: forall t m a. MonadWidget t m => Workflow t m a -> m (Event t a)
354+workflowView :: forall t m a. (DomBuilder t m, MonadFix m, MonadHold t m, PostBuild t m) => Workflow t m a -> m (Event t a)
818355workflowView w0 = do
819− rec eResult <- dyn =<< mapDyn unWorkflow =<< holdDyn w0 eReplace
820− eReplace <- liftM switch $ hold never $ fmap snd eResult
356+ rec eResult <- dyn . fmap unWorkflow =<< holdDyn w0 eReplace
357+ eReplace <- fmap switch $ hold never $ fmap snd eResult
821358 return $ fmap fst eResult
822359
823−mapWorkflow :: (MonadWidget t m) => (a -> b) -> Workflow t m a -> Workflow t m b
824−mapWorkflow f (Workflow x) = Workflow (fmap (\(v,e) -> (f v, fmap (mapWorkflow f) e)) x)
360+mapWorkflow :: (DomBuilder t m) => (a -> b) -> Workflow t m a -> Workflow t m b
361+mapWorkflow f (Workflow x) = Workflow (fmap (f *** fmap (mapWorkflow f)) x)
825362
826−divClass :: forall t m a. MonadWidget t m => String -> m a -> m a
363+divClass :: forall t m a. DomBuilder t m => Text -> m a -> m a
827364divClass = elClass "div"
828365
829−dtdd :: forall t m a. MonadWidget t m => String -> m a -> m a
366+dtdd :: forall t m a. DomBuilder t m => Text -> m a -> m a
830367dtdd h w = do
831368 el "dt" $ text h
832− el "dd" $ w
369+ el "dd" w
833370
834−blank :: forall t m. MonadWidget t m => m ()
371+blank :: forall m. Monad m => m ()
835372blank = return ()
836373
837374-- | A widget to display a table with static columns and dynamic rows.
838−tableDynAttr :: forall t m r k v. (MonadWidget t m, Show k, Ord k)
839− => String -- ^ Class applied to <table> element
840− -> [(String, k -> Dynamic t r -> m v)] -- ^ Columns of (header, row key -> row value -> child widget)
375+tableDynAttr :: forall t m r k v. (Ord k, DomBuilder t m, MonadHold t m, PostBuild t m, MonadFix m)
376+ => Text -- ^ Class applied to <table> element
377+ -> [(Text, k -> Dynamic t r -> m v)] -- ^ Columns of (header, row key -> row value -> child widget)
841378 -> Dynamic t (Map k r) -- ^ Map from row key to row value
842− -> (k -> m (Dynamic t (Map String String))) -- ^ Function to compute <tr> element attributes from row key
843− -> m (Dynamic t (Map k (El t, [v]))) -- ^ Map from row key to (El, list of widget return values)
379+ -> (k -> m (Dynamic t (Map Text Text))) -- ^ Function to compute <tr> element attributes from row key
380+ -> m (Dynamic t (Map k (Element EventResult (DomBuilderSpace m) t, [v]))) -- ^ Map from row key to (El, list of widget return values)
844381tableDynAttr klass cols dRows rowAttrs = elAttr "div" (Map.singleton "style" "zoom: 1; overflow: auto; background: white;") $ do
845382 elAttr "table" (Map.singleton "class" klass) $ do
846383 el "thead" $ el "tr" $ do
@@ -854,54 +391,31 @@ tableDynAttr klass cols dRows rowAttrs = elAttr "div" (Map.singleton "style" "zo
854391-- | A widget to construct a tabbed view that shows only one of its child widgets at a time.
855392-- Creates a header bar containing a <ul> with one <li> per child; clicking a <li> displays
856393-- the corresponding child and hides all others.
857−tabDisplay :: forall t m k. (MonadFix m, MonadWidget t m, Show k, Ord k)
858− => String -- ^ Class applied to <ul> element
859− -> String -- ^ Class applied to currently active <li> element
860− -> Map k (String, m ()) -- ^ Map from (arbitrary) key to (tab label, child widget)
394+tabDisplay :: forall t m k. (MonadFix m, DomBuilder t m, MonadHold t m, PostBuild t m, Ord k)
395+ => Text -- ^ Class applied to <ul> element
396+ -> Text -- ^ Class applied to currently active <li> element
397+ -> Map k (Text, m ()) -- ^ Map from (arbitrary) key to (tab label, child widget)
861398 -> m ()
862399tabDisplay ulClass activeClass tabItems = do
863− rec dCurrentTab <- holdDyn Nothing (updated dTabClicks)
864− dTabClicks :: Dynamic t (Maybe k) <- elAttr "ul" (Map.singleton "class" ulClass) $ do
865− tabClicksList :: [Event t k] <- (liftM Map.elems) $ imapM (\k (s,_) -> headerBarLink s k =<< mapDyn (== (Just k)) dCurrentTab) tabItems
400+ let t0 = listToMaybe $ Map.keys tabItems
401+ rec currentTab :: Demux t (Maybe k) <- elAttr "ul" ("class" =: ulClass) $ do
402+ tabClicksList :: [Event t k] <- Map.elems <$> imapM (\k (s,_) -> headerBarLink s k $ demuxed currentTab (Just k)) tabItems
866403 let eTabClicks :: Event t k = leftmost tabClicksList
867− holdDyn Nothing $ fmap Just eTabClicks :: m (Dynamic t (Maybe k))
868− divClass "" $ do
869− let dTabs :: Dynamic t (Map k (String, m ())) = constDyn tabItems
870− _ <- listWithKey dTabs (\k dTab -> do
871− dAttrs <- mapDyn (\sel -> do
872− let t1 = listToMaybe $ Map.keys tabItems
873− if sel == Just k || (sel == Nothing && t1 == Just k) then Map.empty else Map.singleton "style" "display:none;") dCurrentTab
874− elDynAttr "div" dAttrs $ dyn =<< mapDyn snd dTab)
404+ fmap demux $ holdDyn t0 $ fmap Just eTabClicks
405+ el "div" $ do
406+ iforM_ tabItems $ \k (_, w) -> do
407+ let isSelected = demuxed currentTab $ Just k
408+ attrs = ffor isSelected $ \s -> if s then Map.empty else Map.singleton "style" "display:none;"
409+ elDynAttr "div" attrs w
875410 return ()
876411 where
877− headerBarLink :: (MonadWidget t m, Ord k) => String -> k -> Dynamic t Bool -> m (Event t k)
878− headerBarLink x k dBool = do
879− dAttributes <- mapDyn (\b -> if b then Map.singleton "class" activeClass else Map.empty) dBool
880− elDynAttr "li" dAttributes $ do
412+ headerBarLink :: Text -> k -> Dynamic t Bool -> m (Event t k)
413+ headerBarLink x k isSelected = do
414+ let attrs = fmap (\b -> if b then Map.singleton "class" activeClass else Map.empty) isSelected
415+ elDynAttr "li" attrs $ do
881416 a <- link x
882417 return $ fmap (const k) (_link_clicked a)
883418
884−-- | 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.
885−unsafePlaceElement :: MonadWidget t m => Element -> m (El t)
886−unsafePlaceElement e = do
887− p <- askParent
888− _ <- appendChild p $ Just e
889− wrapElement defaultDomEventHandler e
890−
891−deriveGEq ''EventName
892−deriveGCompare ''EventName
893−
894−--------------------------------------------------------------------------------
895−-- Deprecated functions
896−
897−{-# DEPRECATED _el_clicked "Use `domEvent Click` instead" #-}
898−_el_clicked :: Reflex t => El t -> Event t ()
899−_el_clicked = domEvent Click
900−
901−{-# DEPRECATED _el_keypress "Use `domEvent Keypress` instead" #-}
902−_el_keypress :: Reflex t => El t -> Event t Int
903−_el_keypress = domEvent Keypress
904−
905−{-# DEPRECATED _el_scrolled "Use `domEvent Scroll` instead" #-}
906−_el_scrolled :: Reflex t => El t -> Event t Int
907−_el_scrolled = domEvent Scroll
419+class HasAttributes a where
420+ type Attrs a :: *
421+ attributes :: Lens' a (Attrs a)
src/Reflex/Dom/Widget/Input.hs+374−124
@@ -1,35 +1,65 @@
1−{-# LANGUAGE ConstraintKinds, TypeFamilies, FlexibleContexts, DataKinds, GADTs, ScopedTypeVariables, FlexibleInstances, RecursiveDo, TemplateHaskell #-}
1+{-# LANGUAGE ConstraintKinds #-}
2+{-# LANGUAGE DataKinds #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE FlexibleInstances #-}
5+{-# LANGUAGE GADTs #-}
6+{-# LANGUAGE MultiParamTypeClasses #-}
7+{-# LANGUAGE OverloadedStrings #-}
8+{-# LANGUAGE PolyKinds #-}
9+{-# LANGUAGE RecursiveDo #-}
10+{-# LANGUAGE ScopedTypeVariables #-}
11+{-# LANGUAGE TemplateHaskell #-}
12+{-# LANGUAGE TypeFamilies #-}
13+{-# LANGUAGE UndecidableInstances #-}
214module Reflex.Dom.Widget.Input (module Reflex.Dom.Widget.Input, def, (&), (.~)) where
315
416import Prelude
517
18+import Reflex.Dom.Builder.Class
19+import Reflex.Dom.Builder.Immediate
620import Reflex.Dom.Class
21+import Reflex.Dom.PostBuild.Class
722import Reflex.Dom.Widget.Basic
823
9−import 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
24+-- For dropdown
25+import Control.Monad.Fix
26+import Control.Monad.IO.Class
27+import Data.Maybe
28+import Data.Semigroup
29+import GHCJS.DOM.HTMLInputElement (HTMLInputElement, castToHTMLInputElement)
30+import GHCJS.DOM.HTMLSelectElement (castToHTMLSelectElement)
31+import qualified GHCJS.DOM.HTMLSelectElement as HTMLSelectElement
32+import GHCJS.DOM.HTMLTextAreaElement (HTMLTextAreaElement, castToHTMLTextAreaElement)
33+import Reflex.Dom.PerformEvent.Class
34+import qualified Text.Read as T
35+
36+-- For fileInput
37+import qualified GHCJS.DOM.Element as Element
38+import GHCJS.DOM.EventM (on)
39+import GHCJS.DOM.Types (File)
40+-- import qualified GHCJS.DOM.File as File
1741import qualified GHCJS.DOM.FileList as FileList
18−import Data.Monoid
19−import qualified Data.Map as Map
20−import Control.Lens hiding (ix)
42+
43+import Control.Lens hiding (element, ix)
2144import Control.Monad hiding (forM_)
22−import Control.Monad.IO.Class
23−import qualified Data.Bimap as B
45+import qualified Data.Bimap as Bimap
2446import Data.Default
25−import Data.Dependent.Sum (DSum (..))
47+import Data.Dependent.Map (DMap)
48+import qualified Data.Dependent.Map as DMap
49+import Data.Functor.Misc
2650import Data.Map (Map)
27−import Data.Maybe
28−import Text.Read
51+import qualified Data.Map as Map
52+import Data.Proxy
53+import Data.Text (Text)
54+import qualified Data.Text as T
55+import Reflex
56+
57+import qualified GHCJS.DOM.Event as Event
58+import qualified GHCJS.DOM.HTMLInputElement as Input
2959
3060data TextInput t
31− = TextInput { _textInput_value :: Dynamic t String
32− , _textInput_input :: Event t String
61+ = TextInput { _textInput_value :: Dynamic t Text
62+ , _textInput_input :: Event t Text
3363 , _textInput_keypress :: Event t Int
3464 , _textInput_keydown :: Event t Int
3565 , _textInput_keyup :: Event t Int
@@ -38,13 +68,14 @@ data TextInput t
3868 }
3969
4070data 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)
71+ = TextInputConfig { _textInputConfig_inputType :: Text
72+ , _textInputConfig_initialValue :: Text
73+ , _textInputConfig_setValue :: Event t Text
74+ , _textInputConfig_attributes :: Dynamic t (Map Text Text)
4575 }
4676
4777instance Reflex t => Default (TextInputConfig t) where
78+ {-# INLINABLE def #-}
4879 def = TextInputConfig { _textInputConfig_inputType = "text"
4980 , _textInputConfig_initialValue = ""
5081 , _textInputConfig_setValue = never
@@ -52,130 +83,259 @@ instance Reflex t => Default (TextInputConfig t) where
5283 }
5384
5485-- | 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)
86+{-# INLINABLE textInput #-}
87+textInput :: (DomBuilder t m, PostBuild t m, DomBuilderSpace m ~ GhcjsDomSpace) => TextInputConfig t -> m (TextInput t)
5688textInput (TextInputConfig inputType initial eSetValue dAttrs) = do
57− e <- liftM castToHTMLInputElement $ buildEmptyElement "input" =<< mapDyn (Map.insert "type" inputType) dAttrs
58− Input.setValue e $ Just initial
59− performEvent_ $ fmap (Input.setValue e . Just) eSetValue
60− eChange <- wrapDomEvent e (`on` input) $ fromMaybe "" <$> Input.getValue e
61− postGui <- askPostGui
62− runWithActions <- askRunWithActions
63− eChangeFocus <- newEventWithTrigger $ \eChangeFocusTrigger -> do
64− unsubscribeOnblur <- on e blurEvent $ liftIO $ do
65− postGui $ runWithActions [eChangeFocusTrigger :=> Identity False]
66− unsubscribeOnfocus <- on e focusEvent $ liftIO $ do
67− postGui $ runWithActions [eChangeFocusTrigger :=> Identity True]
68− return $ liftIO $ unsubscribeOnblur >> unsubscribeOnfocus
69− dFocus <- holdDyn False eChangeFocus
70− eKeypress <- wrapDomEvent e (`on` keyPress) getKeyEvent
71− eKeydown <- wrapDomEvent e (`on` keyDown) getKeyEvent
72− eKeyup <- wrapDomEvent e (`on` keyUp) getKeyEvent
73− dValue <- holdDyn initial $ leftmost [eSetValue, eChange]
74− return $ TextInput dValue eChange eKeypress eKeydown eKeyup dFocus e
75−
89+ modifyAttrs <- dynamicAttributesToModifyAttributes $ fmap (Map.insert "type" inputType) dAttrs
90+ i <- inputElement $ def
91+ & inputElementConfig_initialValue .~ initial
92+ & inputElementConfig_setValue .~ eSetValue
93+ & inputElementConfig_elementConfig . elementConfig_modifyAttributes .~ modifyAttrs
94+ return $ TextInput
95+ { _textInput_value = _inputElement_value i
96+ , _textInput_input = _inputElement_input i
97+ , _textInput_keypress = domEvent Keypress i
98+ , _textInput_keydown = domEvent Keydown i
99+ , _textInput_keyup = domEvent Keyup i
100+ , _textInput_hasFocus = _inputElement_hasFocus i
101+ , _textInput_element = castToHTMLInputElement $ _element_raw $ _inputElement_element i
102+ }
103+
104+{-# INLINABLE textInputGetEnter #-}
76105textInputGetEnter :: Reflex t => TextInput t -> Event t ()
77−textInputGetEnter i = fmapMaybe (\n -> if n == keycodeEnter then Just () else Nothing) $ _textInput_keypress i
106+textInputGetEnter i = fmapMaybe (\n -> if keyCodeLookup n == Enter then Just () else Nothing) $ _textInput_keypress i
107+
108+data RangeInputConfig t
109+ = RangeInputConfig { _rangeInputConfig_initialValue :: Float
110+ , _rangeInputConfig_setValue :: Event t Float
111+ , _rangeInputConfig_attributes :: Dynamic t (Map Text Text)
112+ }
113+
114+instance Reflex t => Default (RangeInputConfig t) where
115+ {-# INLINABLE def #-}
116+ def = RangeInputConfig { _rangeInputConfig_initialValue = 0
117+ , _rangeInputConfig_setValue = never
118+ , _rangeInputConfig_attributes = constDyn mempty
119+ }
120+
121+data RangeInput t
122+ = RangeInput { _rangeInput_value :: Dynamic t Float
123+ , _rangeInput_input :: Event t Float
124+ , _rangeInput_mouseup :: Event t (Int, Int)
125+ , _rangeInput_hasFocus :: Dynamic t Bool
126+ , _rangeInput_element :: HTMLInputElement
127+ }
128+
129+-- | Create an input whose value is a float.
130+-- https://www.w3.org/wiki/HTML/Elements/input/range
131+{-# INLINABLE rangeInput #-}
132+rangeInput :: (DomBuilder t m, PostBuild t m, DomBuilderSpace m ~ GhcjsDomSpace) => RangeInputConfig t -> m (RangeInput t)
133+rangeInput (RangeInputConfig initial eSetValue dAttrs) = do
134+ modifyAttrs <- dynamicAttributesToModifyAttributes $ fmap (Map.insert "type" "range") dAttrs
135+ i <- inputElement $ def
136+ & inputElementConfig_initialValue .~ (T.pack . show $ initial)
137+ & inputElementConfig_setValue .~ (T.pack . show <$> eSetValue)
138+ & inputElementConfig_elementConfig . elementConfig_modifyAttributes .~ modifyAttrs
139+ return $ RangeInput
140+ { _rangeInput_value = read . T.unpack <$> _inputElement_value i
141+ , _rangeInput_input = read . T.unpack <$> _inputElement_input i
142+ , _rangeInput_mouseup = domEvent Mouseup i
143+ , _rangeInput_hasFocus = _inputElement_hasFocus i
144+ , _rangeInput_element = castToHTMLInputElement $ _element_raw $ _inputElement_element i
145+ }
78146
79147data TextAreaConfig t
80− = TextAreaConfig { _textAreaConfig_initialValue :: String
81− , _textAreaConfig_setValue :: Event t String
82− , _textAreaConfig_attributes :: Dynamic t (Map String String)
148+ = TextAreaConfig { _textAreaConfig_initialValue :: Text
149+ , _textAreaConfig_setValue :: Event t Text
150+ , _textAreaConfig_attributes :: Dynamic t (Map Text Text)
83151 }
84152
85153instance Reflex t => Default (TextAreaConfig t) where
154+ {-# INLINABLE def #-}
86155 def = TextAreaConfig { _textAreaConfig_initialValue = ""
87156 , _textAreaConfig_setValue = never
88157 , _textAreaConfig_attributes = constDyn mempty
89158 }
90159
91160data TextArea t
92− = TextArea { _textArea_value :: Dynamic t String
93− , _textArea_input :: Event t String
94− , _textArea_element :: HTMLTextAreaElement
161+ = TextArea { _textArea_value :: Dynamic t Text
162+ , _textArea_input :: Event t Text
95163 , _textArea_hasFocus :: Dynamic t Bool
96164 , _textArea_keypress :: Event t Int
165+ , _textArea_element :: HTMLTextAreaElement
97166 }
98167
99−textArea :: MonadWidget t m => TextAreaConfig t -> m (TextArea t)
168+{-# INLINABLE textArea #-}
169+textArea :: (DomBuilder t m, PostBuild t m, DomBuilderSpace m ~ GhcjsDomSpace) => TextAreaConfig t -> m (TextArea t)
100170textArea (TextAreaConfig initial eSet attrs) = do
101− e <- liftM castToHTMLTextAreaElement $ buildEmptyElement "textarea" attrs
102− TextArea.setValue e $ Just initial
103− postGui <- askPostGui
104− runWithActions <- askRunWithActions
105− eChangeFocus <- newEventWithTrigger $ \eChangeFocusTrigger -> do
106− unsubscribeOnblur <- on e blurEvent $ liftIO $ do
107− postGui $ runWithActions [eChangeFocusTrigger :=> Identity False]
108− unsubscribeOnfocus <- on e focusEvent $ liftIO $ do
109− postGui $ runWithActions [eChangeFocusTrigger :=> Identity True]
110− return $ liftIO $ unsubscribeOnblur >> unsubscribeOnfocus
111− performEvent_ $ fmap (TextArea.setValue e . Just) eSet
112− f <- holdDyn False eChangeFocus
113− ev <- wrapDomEvent e (`on` input) $ fromMaybe "" <$> TextArea.getValue e
114− v <- holdDyn initial $ leftmost [eSet, ev]
115− eKeypress <- wrapDomEvent e (`on` keyPress) getKeyEvent
116− return $ TextArea v ev e f eKeypress
171+ modifyAttrs <- dynamicAttributesToModifyAttributes attrs
172+ i <- textAreaElement $ def
173+ & textAreaElementConfig_initialValue .~ initial
174+ & textAreaElementConfig_setValue .~ eSet
175+ & textAreaElementConfig_elementConfig . elementConfig_modifyAttributes .~ modifyAttrs
176+ return $ TextArea
177+ { _textArea_value = _textAreaElement_value i
178+ , _textArea_input = _textAreaElement_input i
179+ , _textArea_keypress = domEvent Keypress i
180+ , _textArea_hasFocus = _textAreaElement_hasFocus i
181+ , _textArea_element = castToHTMLTextAreaElement $ _element_raw $ _textAreaElement_element i
182+ }
117183
118184data CheckboxConfig t
119185 = CheckboxConfig { _checkboxConfig_setValue :: Event t Bool
120− , _checkboxConfig_attributes :: Dynamic t (Map String String)
186+ , _checkboxConfig_attributes :: Dynamic t (Map Text Text)
121187 }
122188
123189instance Reflex t => Default (CheckboxConfig t) where
190+ {-# INLINABLE def #-}
124191 def = CheckboxConfig { _checkboxConfig_setValue = never
125192 , _checkboxConfig_attributes = constDyn mempty
126193 }
127194
128195data Checkbox t
129196 = Checkbox { _checkbox_value :: Dynamic t Bool
130− , _checkbox_change :: Event t Bool
197+-- , _checkbox_change :: Event t Bool
131198 }
132199
133−--TODO: Make attributes possibly dynamic
134200-- | Create an editable checkbox
135201-- 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)
202+{-# INLINABLE checkbox #-}
203+checkbox :: (DomBuilder t m, PostBuild t m) => Bool -> CheckboxConfig t -> m (Checkbox t)
137204checkbox checked config = do
138− attrs <- mapDyn (\c -> Map.insert "type" "checkbox" $ (if checked then Map.insert "checked" "checked" else Map.delete "checked") c) (_checkboxConfig_attributes config)
139− e <- liftM castToHTMLInputElement $ buildEmptyElement "input" attrs
140− eClick <- wrapDomEvent e (`on` click) $ Input.getChecked e
141− performEvent_ $ fmap (\v -> Input.setChecked e $! v) $ _checkboxConfig_setValue config
142− dValue <- holdDyn checked $ leftmost [_checkboxConfig_setValue config, eClick]
143− return $ Checkbox dValue eClick
144−
145−checkboxView :: MonadWidget t m => Dynamic t (Map String String) -> Dynamic t Bool -> m (Event t Bool)
205+ let permanentAttrs = "type" =: "checkbox"
206+ dAttrs = Map.delete "checked" . Map.union permanentAttrs <$> _checkboxConfig_attributes config
207+ modifyAttrs <- dynamicAttributesToModifyAttributes dAttrs
208+ i <- inputElement $ def
209+ & inputElementConfig_initialChecked .~ checked
210+ & inputElementConfig_setChecked .~ _checkboxConfig_setValue config
211+ & inputElementConfig_elementConfig . elementConfig_initialAttributes .~ Map.mapKeys ((,) Nothing) permanentAttrs
212+ & inputElementConfig_elementConfig . elementConfig_modifyAttributes .~ modifyAttrs
213+ return $ Checkbox
214+ { _checkbox_value = _inputElement_checked i
215+ }
216+
217+type family CheckboxViewEventResultType (en :: EventTag) :: * where
218+ CheckboxViewEventResultType 'ClickTag = Bool
219+ CheckboxViewEventResultType t = EventResultType t
220+
221+regularToCheckboxViewEventType :: EventName t -> EventResultType t -> CheckboxViewEventResultType t
222+regularToCheckboxViewEventType en r = case en of
223+ Click -> error "regularToCheckboxViewEventType: EventName Click should never be encountered"
224+ Abort -> r
225+ Blur -> r
226+ Change -> r
227+ Contextmenu -> r
228+ Dblclick -> r
229+ Drag -> r
230+ Dragend -> r
231+ Dragenter -> r
232+ Dragleave -> r
233+ Dragover -> r
234+ Dragstart -> r
235+ Drop -> r
236+ Error -> r
237+ Focus -> r
238+ Input -> r
239+ Invalid -> r
240+ Keydown -> r
241+ Keypress -> r
242+ Keyup -> r
243+ Load -> r
244+ Mousedown -> r
245+ Mouseenter -> r
246+ Mouseleave -> r
247+ Mousemove -> r
248+ Mouseout -> r
249+ Mouseover -> r
250+ Mouseup -> r
251+ Mousewheel -> r
252+ Scroll -> r
253+ Select -> r
254+ Submit -> r
255+ Wheel -> r
256+ {-
257+ Beforecut -> r
258+ Cut -> r
259+ Beforecopy -> r
260+ Copy -> r
261+ Beforepaste -> r
262+ Paste -> r
263+-}
264+ Reset -> r
265+ Search -> r
266+-- Selectstart -> r
267+ Touchstart -> r
268+ Touchmove -> r
269+ Touchend -> r
270+ Touchcancel -> r
271+
272+newtype CheckboxViewEventResult en = CheckboxViewEventResult { unCheckboxViewEventResult :: CheckboxViewEventResultType en }
273+
274+--TODO
275+{-# INLINABLE checkboxView #-}
276+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)
146277checkboxView dAttrs dValue = do
147− e <- liftM castToHTMLInputElement $ buildEmptyElement "input" =<< mapDyn (Map.insert "type" "checkbox") dAttrs
148− eClicked <- wrapDomEvent e (`on` click) $ do
149− preventDefault
150− Input.getChecked e
151− schedulePostBuild $ do
152− v <- sample $ current dValue
153− when v $ Input.setChecked e True
154− performEvent_ $ fmap (\v -> Input.setChecked e $! v) $ updated dValue
155− return eClicked
278+ let permanentAttrs = "type" =: "checkbox"
279+ modifyAttrs <- dynamicAttributesToModifyAttributes $ fmap (Map.union permanentAttrs) dAttrs
280+ postBuild <- getPostBuild
281+ let filters :: DMap EventName (EventFilter (DomBuilderSpace m) CheckboxViewEventResult)
282+ filters = DMap.singleton Click $ EventFilter $ GhcjsDomHandler $ \(GhcjsDomEvent evt) -> do
283+ Just t <- Event.getTarget evt
284+ b <- Input.getChecked $ Input.castToHTMLInputElement t
285+ return $ (,) preventDefault $ GhcjsDomHandler $ \_ -> return $ Just $ CheckboxViewEventResult b
286+ elementConfig :: ElementConfig CheckboxViewEventResult t m
287+ elementConfig = (def :: ElementConfig EventResult t m)
288+ { _elementConfig_modifyAttributes = modifyAttrs
289+ , _elementConfig_initialAttributes = Map.mapKeys ((,) Nothing) permanentAttrs
290+ , _elementConfig_eventFilters = filters
291+ , _elementConfig_eventHandler = GhcjsDomHandler1 $ \p@(Pair1 en _) -> case en of
292+ Click -> error "impossible"
293+ _ -> do
294+ mr <- unGhcjsDomHandler1 (defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))) p
295+ case mr of
296+ Nothing1 -> return Nothing1
297+ Just1 (EventResult r) -> return $ Just1 $ CheckboxViewEventResult $ regularToCheckboxViewEventType en r
298+ }
299+ inputElementConfig :: InputElementConfig CheckboxViewEventResult t m
300+ inputElementConfig = (def :: InputElementConfig EventResult t m)
301+ & inputElementConfig_setChecked .~ leftmost [updated dValue, tag (current dValue) postBuild]
302+ & inputElementConfig_elementConfig .~ elementConfig
303+ i <- inputElement inputElementConfig
304+ return $ unCheckboxViewEventResult <$> select (_element_events $ _inputElement_element i) (WrapArg Click)
156305
157306data FileInput t
158307 = FileInput { _fileInput_value :: Dynamic t [File]
159− , _fileInput_element :: HTMLInputElement
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)
171−fileInput (FileInputConfig dAttrs) = do
172− e <- liftM castToHTMLInputElement $ buildEmptyElement "input" =<< mapDyn (Map.insert "type" "file") dAttrs
173− eChange <- wrapDomEvent e (flip on change) $ do
174− Just files <- getFiles e
175− len <- FileList.getLength files
176− mapM (liftM (fromMaybe (error "fileInput: fileListItem returned null")) . FileList.item files) $ init [0..len]
318+fileInput :: forall t m. (MonadIO m, MonadFix m, MonadHold t m, TriggerEvent t m, DomBuilder t m, PostBuild t m, DomBuilderSpace m ~ GhcjsDomSpace)
319+ => FileInputConfig t -> m (FileInput t)
320+fileInput config = do
321+ let insertType = Map.insert "type" "file"
322+ dAttrs = insertType <$> _fileInputConfig_attributes config
323+ modifyAttrs <- dynamicAttributesToModifyAttributes dAttrs
324+ let cfg = (def :: ElementConfig EventResult t m)
325+ { _elementConfig_modifyAttributes = modifyAttrs
326+ , _elementConfig_eventFilters = DMap.singleton Change . EventFilter . GhcjsDomHandler $ \_ -> do
327+ return . (,) mempty . GhcjsDomHandler $ \_ -> return . Just $ EventResult ()
328+ }
329+ (eRaw, _) <- element "input" cfg blank
330+ let e = castToHTMLInputElement (_element_raw eRaw)
331+ eChange <- wrapDomEvent e (`on` Element.change) $ do
332+ Just files <- Input.getFiles e
333+ len <- FileList.getLength files
334+ mapM (fmap (fromMaybe (error "fileInput: fileList.item returned null")) . FileList.item files) [0 .. len-1]
177335 dValue <- holdDyn [] eChange
178− return $ FileInput dValue e
336+ return $ FileInput
337+ { _fileInput_value = dValue
338+ }
179339
180340data Dropdown t k
181341 = Dropdown { _dropdown_value :: Dynamic t k
@@ -184,44 +344,122 @@ data Dropdown t k
184344
185345data DropdownConfig t k
186346 = DropdownConfig { _dropdownConfig_setValue :: Event t k
187− , _dropdownConfig_attributes :: Dynamic t (Map String String)
347+ , _dropdownConfig_attributes :: Dynamic t (Map Text Text)
188348 }
189349
190−instance (Reflex t, Ord k, Show k, Read k) => Default (DropdownConfig t k) where
350+instance Reflex t => Default (DropdownConfig t k) where
191351 def = DropdownConfig { _dropdownConfig_setValue = never
192352 , _dropdownConfig_attributes = constDyn mempty
193353 }
194354
355+type family DropdownViewEventResultType (en :: EventTag) :: * where
356+ DropdownViewEventResultType 'ChangeTag = Text
357+ DropdownViewEventResultType t = EventResultType t
358+
359+newtype DropdownViewEventResult en = DropdownViewEventResult { unDropdownViewEventResult :: DropdownViewEventResultType en }
360+
361+regularToDropdownViewEventType :: EventName t -> EventResultType t -> DropdownViewEventResultType t
362+regularToDropdownViewEventType en r = case en of
363+ Change -> error "regularToDropdownViewEventType: EventName Change should never be encountered"
364+ Abort -> r
365+ Blur -> r
366+ Click -> r
367+ Contextmenu -> r
368+ Dblclick -> r
369+ Drag -> r
370+ Dragend -> r
371+ Dragenter -> r
372+ Dragleave -> r
373+ Dragover -> r
374+ Dragstart -> r
375+ Drop -> r
376+ Error -> r
377+ Focus -> r
378+ Input -> r
379+ Invalid -> r
380+ Keydown -> r
381+ Keypress -> r
382+ Keyup -> r
383+ Load -> r
384+ Mousedown -> r
385+ Mouseenter -> r
386+ Mouseleave -> r
387+ Mousemove -> r
388+ Mouseout -> r
389+ Mouseover -> r
390+ Mouseup -> r
391+ Mousewheel -> r
392+ Scroll -> r
393+ Select -> r
394+ Submit -> r
395+ Wheel -> r
396+ {-
397+ Beforecut -> r
398+ Cut -> r
399+ Beforecopy -> r
400+ Copy -> r
401+ Beforepaste -> r
402+ Paste -> r
403+-}
404+ Reset -> r
405+ Search -> r
406+-- Selectstart -> r
407+ Touchstart -> r
408+ Touchmove -> r
409+ Touchend -> r
410+ Touchcancel -> r
411+
195412--TODO: We should allow the user to specify an ordering instead of relying on the ordering of the Map
196413-- | Create a dropdown box
197414-- 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)
415+dropdown :: forall k t m. (DomBuilder t m, MonadFix m, MonadHold t m, PerformEvent t m, MonadIO (Performable m), MonadIO m, TriggerEvent t m, PostBuild t m, DomBuilderSpace m ~ GhcjsDomSpace, Ord k) => k -> Dynamic t (Map k Text) -> DropdownConfig t k -> m (Dropdown t k)
199416dropdown k0 options (DropdownConfig setK attrs) = do
200− optionsWithAddedKeys <- combineDyn Map.union options <=< foldDyn Map.union (k0 =: "") $ fmap (=: "") setK
417+ optionsWithAddedKeys <- fmap (zipDynWith Map.union options) $ foldDyn Map.union (k0 =: "") $ fmap (=: "") setK
201418 defaultKey <- holdDyn k0 setK
202− (indexedOptions, ixKeys) <- splitDyn <=< forDyn optionsWithAddedKeys $ \os ->
203− let xs = map (\(ix, (k, v)) -> ((ix, k), ((ix, k), v))) $ zip [0::Int ..] $ Map.toList os
204− ixVals = Map.fromList $ map snd xs
205− ixKeys = B.fromList $ map fst xs
206− 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
419+ let (indexedOptions, ixKeys) = splitDynPure $ ffor optionsWithAddedKeys $ \os ->
420+ let xs = fmap (\(ix, (k, v)) -> ((ix, k), ((ix, k), v))) $ zip [0::Int ..] $ Map.toList os
421+ in (Map.fromList $ map snd xs, Bimap.fromList $ map fst xs)
422+ modifyAttrs <- dynamicAttributesToModifyAttributes attrs
423+ let cfg = (def :: ElementConfig EventResult t m)
424+ { _elementConfig_modifyAttributes = modifyAttrs
425+ , _elementConfig_eventFilters = DMap.singleton Change $ EventFilter $ GhcjsDomHandler $ \(GhcjsDomEvent evt) -> do
426+ Just t <- Event.getTarget evt
427+ Just v <- HTMLSelectElement.getValue $ HTMLSelectElement.castToHTMLSelectElement t
428+ return $ (,) mempty $ GhcjsDomHandler $ \_ -> return $ Just $ DropdownViewEventResult v
429+ , _elementConfig_eventHandler = GhcjsDomHandler1 $ \p@(Pair1 en _) -> case en of
430+ Click -> error "impossible"
431+ _ -> do
432+ mr <- unGhcjsDomHandler1 (defaultEventHandler (Proxy :: Proxy (DomBuilderSpace m))) p
433+ case mr of
434+ Nothing1 -> return Nothing1
435+ Just1 (EventResult r) -> return $ Just1 $ DropdownViewEventResult $ regularToDropdownViewEventType en r
436+ }
437+ (eRaw, _) <- element "select" cfg $ listWithKey indexedOptions $ \(ix, k) v -> do
438+ let optionAttrs = fmap (\dk -> "value" =: T.pack (show ix) <> if dk == k then "selected" =: "selected" else mempty) defaultKey
209439 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)
440+ let e = castToHTMLSelectElement $ _element_raw eRaw
441+ performEvent_ $ HTMLSelectElement.setValue e . Just . show <$> attachPromptlyDynWithMaybe (flip Bimap.lookupR) ixKeys setK
442+ let lookupSelected ks value = do
443+ v <- value
444+ key <- T.readMaybe v
445+ Bimap.lookup key ks
446+ eChange <- attachPromptlyDynWith lookupSelected ixKeys <$> wrapDomEvent e (`on` Element.change) (HTMLSelectElement.getValue e)
213447 let readKey keys mk = fromMaybe k0 $ do
214448 k <- mk
215− guard $ B.memberR k keys
449+ guard $ Bimap.memberR k keys
216450 return k
217− dValue <- combineDyn readKey ixKeys <=< holdDyn (Just k0) $ leftmost [eChange, fmap Just setK]
218− return $ Dropdown dValue (attachDynWith readKey ixKeys eChange)
451+ dValue <- fmap (zipDynWith readKey ixKeys) $ holdDyn (Just k0) $ leftmost [eChange, fmap Just setK]
452+ return $ Dropdown dValue (attachPromptlyDynWith readKey ixKeys eChange)
219453
220−liftM concat $ mapM makeLenses
454+concat <$> mapM makeLenses
221455 [ ''TextAreaConfig
222456 , ''TextArea
223457 , ''TextInputConfig
224458 , ''TextInput
459+ , ''RangeInputConfig
460+ , ''RangeInput
461+ , ''FileInputConfig
462+ , ''FileInput
225463 , ''DropdownConfig
226464 , ''Dropdown
227465 , ''CheckboxConfig
@@ -229,33 +467,45 @@ liftM concat $ mapM makeLenses
229467 ]
230468
231469instance HasAttributes (TextAreaConfig t) where
232− type Attrs (TextAreaConfig t) = Dynamic t (Map String String)
470+ type Attrs (TextAreaConfig t) = Dynamic t (Map Text Text)
233471 attributes = textAreaConfig_attributes
234472
235473instance HasAttributes (TextInputConfig t) where
236− type Attrs (TextInputConfig t) = Dynamic t (Map String String)
474+ type Attrs (TextInputConfig t) = Dynamic t (Map Text Text)
237475 attributes = textInputConfig_attributes
238476
477+instance HasAttributes (RangeInputConfig t) where
478+ type Attrs (RangeInputConfig t) = Dynamic t (Map Text Text)
479+ attributes = rangeInputConfig_attributes
480+
239481instance HasAttributes (DropdownConfig t k) where
240− type Attrs (DropdownConfig t k) = Dynamic t (Map String String)
482+ type Attrs (DropdownConfig t k) = Dynamic t (Map Text Text)
241483 attributes = dropdownConfig_attributes
242484
243485instance HasAttributes (CheckboxConfig t) where
244− type Attrs (CheckboxConfig t) = Dynamic t (Map String String)
486+ type Attrs (CheckboxConfig t) = Dynamic t (Map Text Text)
245487 attributes = checkboxConfig_attributes
246488
489+instance HasAttributes (FileInputConfig t) where
490+ type Attrs (FileInputConfig t) = Dynamic t (Map Text Text)
491+ attributes = fileInputConfig_attributes
492+
247493class HasSetValue a where
248494 type SetValue a :: *
249495 setValue :: Lens' a (SetValue a)
250496
251497instance HasSetValue (TextAreaConfig t) where
252− type SetValue (TextAreaConfig t) = Event t String
498+ type SetValue (TextAreaConfig t) = Event t Text
253499 setValue = textAreaConfig_setValue
254500
255501instance HasSetValue (TextInputConfig t) where
256− type SetValue (TextInputConfig t) = Event t String
502+ type SetValue (TextInputConfig t) = Event t Text
257503 setValue = textInputConfig_setValue
258504
505+instance HasSetValue (RangeInputConfig t) where
506+ type SetValue (RangeInputConfig t) = Event t Float
507+ setValue = rangeInputConfig_setValue
508+
259509instance HasSetValue (DropdownConfig t k) where
260510 type SetValue (DropdownConfig t k) = Event t k
261511 setValue = dropdownConfig_setValue
@@ -269,11 +519,11 @@ class HasValue a where
269519 value :: a -> Value a
270520
271521instance HasValue (TextArea t) where
272− type Value (TextArea t) = Dynamic t String
522+ type Value (TextArea t) = Dynamic t Text
273523 value = _textArea_value
274524
275525instance HasValue (TextInput t) where
276− type Value (TextInput t) = Dynamic t String
526+ type Value (TextInput t) = Dynamic t Text
277527 value = _textInput_value
278528
279529instance HasValue (FileInput t) where
src/Reflex/Dom/Widget/Lazy.hs+55−45
@@ -1,42 +1,50 @@
1−{-# LANGUAGE RecursiveDo, ScopedTypeVariables #-}
2−
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE MultiParamTypeClasses #-}
3+{-# LANGUAGE OverloadedStrings #-}
4+{-# LANGUAGE RecursiveDo #-}
5+{-# LANGUAGE ScopedTypeVariables #-}
6+{-# LANGUAGE TypeFamilies #-}
37module Reflex.Dom.Widget.Lazy where
48
59import Reflex
10+import Reflex.Dom.Builder.Class
11+import Reflex.Dom.Builder.Immediate
612import Reflex.Dom.Class
13+import Reflex.Dom.PerformEvent.Class
14+import Reflex.Dom.PostBuild.Class
715import Reflex.Dom.Widget.Basic
816
9−import Control.Monad
17+import Control.Monad.Fix
1018import Control.Monad.IO.Class
1119import Data.Fixed
12−import Data.Monoid
13−import qualified Data.Map as Map
1420import Data.Map (Map)
21+import qualified Data.Map as Map
22+import Data.Monoid
23+import Data.Text (Text)
24+import qualified Data.Text as T
1525import GHCJS.DOM.Element
16−import Data.Maybe
17−import Data.Int
1826
1927-- |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)
28+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)
2129 => Dynamic t Int -- ^ The height of the visible region in pixels
2230 -> Int -- ^ The height of each row in pixels
2331 -> Dynamic t Int -- ^ The total number of items
2432 -> Int -- ^ The index of the row to scroll to on initialization
2533 -> 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
34+ -> Text -- ^ The element tag for the list
35+ -> Dynamic t (Map Text Text) -- ^ The attributes of the list
36+ -> Text -- ^ The element tag for a row
37+ -> Dynamic t (Map Text Text) -- ^ The attributes of each row
3038 -> (k -> Dynamic t (Maybe v) -> Dynamic t Bool -> m ()) -- ^ The row child element builder
3139 -> Dynamic t (Map k v) -- ^ The 'Map' of items
3240 -> (Int -> k) -- ^ Index to Key function, used to determine position of Map elements
3341 -> m (Dynamic t (Int, Int), Event t k) -- ^ A tuple containing: a 'Dynamic' of the index (based on the current scroll position) and number of items currently being rendered, and an 'Event' of the selected key
3442virtualListWithSelection heightPx rowPx maxIndex i0 setI listTag listAttrs rowTag rowAttrs itemBuilder items indexToKey = do
35− totalHeightStyle <- mapDyn (toHeightStyle . (*) rowPx) maxIndex
36− containerStyle <- mapDyn toContainer heightPx
37− viewportStyle <- mapDyn toViewport heightPx
43+ let totalHeightStyle = fmap (toHeightStyle . (*) rowPx) maxIndex
44+ containerStyle = fmap toContainer heightPx
45+ viewportStyle = fmap toViewport heightPx
3846 rec (container, sel) <- elDynAttr "div" containerStyle $ elDynAttr' "div" viewportStyle $ do
39− currentTop <- mapDyn (listWrapperStyle . fst) window
47+ let currentTop = fmap (listWrapperStyle . fst) window
4048 (_, lis) <- elDynAttr "div" totalHeightStyle $ tagWrapper listTag listAttrs currentTop $ selectViewListWithKey_ selected itemsInWindow $ \k v s -> do
4149 (li,_) <- tagWrapper rowTag rowAttrs (constDyn $ toHeightStyle rowPx) $ itemBuilder k v s
4250 return $ fmap (const k) (domEvent Click li)
@@ -46,31 +54,32 @@ virtualListWithSelection heightPx rowPx maxIndex i0 setI listTag listAttrs rowTa
4654 scrollPosition <- holdDyn 0 $ leftmost [ domEvent Scroll container
4755 , fmap (const (i0 * rowPx)) pb
4856 ]
49− window <- combineDyn (\h -> findWindow h rowPx) heightPx scrollPosition
50− itemsInWindow <- combineDyn (\(_,(idx,num)) is -> Map.fromList $ map (\i -> let ix = indexToKey i in (ix, Map.lookup ix is)) [idx .. idx + num]) window items
57+ let window = zipDynWith (findWindow rowPx) heightPx scrollPosition
58+ itemsInWindow = zipDynWith (\(_,(idx,num)) is -> Map.fromList $ map (\i -> let ix = indexToKey i in (ix, Map.lookup ix is)) [idx .. idx + num]) window items
5159 postBuild <- getPostBuild
52− performEvent_ $ fmap (\i -> liftIO $ setScrollTop (_el_element container) (i * rowPx)) $ leftmost [setI, fmap (const i0) postBuild]
53− indexAndLength <- mapDyn snd window
60+ performEvent_ $ ffor (leftmost [setI, i0 <$ postBuild]) $ \i -> do
61+ liftIO $ setScrollTop (_element_raw container) (i * rowPx)
62+ let indexAndLength = fmap snd window
5463 return (indexAndLength, sel)
5564 where
56− toStyleAttr m = "style" =: (Map.foldWithKey (\k v s -> k <> ":" <> v <> ";" <> s) "" m)
65+ toStyleAttr m = "style" =: Map.foldWithKey (\k v s -> k <> ":" <> v <> ";" <> s) "" m
5766 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")
67+ "left" =: "0" <> "right" =: "0" <> "height" =: (T.pack (show h) <> "px")
68+ toContainer h = toStyleAttr $ "position" =: "relative" <> "height" =: (T.pack (show h) <> "px")
6069 listWrapperStyle t = toStyleAttr $ "position" =: "relative" <>
61− "top" =: (show t <> "px")
62− toHeightStyle h = toStyleAttr ("height" =: (show h <> "px") <> "overflow" =: "hidden")
70+ "top" =: (T.pack (show t) <> "px")
71+ toHeightStyle h = toStyleAttr ("height" =: (T.pack (show h) <> "px") <> "overflow" =: "hidden")
6372 tagWrapper elTag attrs attrsOverride c = do
64− attrs' <- combineDyn Map.union attrsOverride attrs
73+ let attrs' = zipDynWith Map.union attrsOverride attrs
6574 elDynAttr' elTag attrs' c
66− findWindow windowSize sizeIncrement startingPosition =
75+ findWindow sizeIncrement windowSize startingPosition =
6776 let (startingIndex, topOffsetPx) = startingPosition `divMod'` sizeIncrement
6877 topPx = startingPosition - topOffsetPx
6978 numItems = windowSize `div` sizeIncrement + 1
7079 preItems = min startingIndex numItems
7180 in (topPx - preItems * sizeIncrement, (startingIndex - preItems, preItems + numItems * 2))
7281
73−virtualList :: forall t m k v a. (MonadWidget t m, Ord k)
82+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)
7483 => Dynamic t Int -- ^ A 'Dynamic' of the visible region's height in pixels
7584 -> Int -- ^ The fixed height of each row in pixels
7685 -> Dynamic t Int -- ^ A 'Dynamic' of the total number of items
@@ -82,38 +91,39 @@ virtualList :: forall t m k v a. (MonadWidget t m, Ord k)
8291 -> (k -> v -> Event t v -> m a) -- ^ The row child element builder.
8392 -> m (Dynamic t (Int, Int), Dynamic t (Map k a)) -- ^ A tuple containing: a 'Dynamic' of the index (based on the current scroll position) and number of items currently being rendered, and the 'Dynamic' list result
8493virtualList heightPx rowPx maxIndex i0 setI keyToIndex items0 itemsUpdate itemBuilder = do
85− virtualH <- mapDyn (mkVirtualHeight . (*) rowPx) maxIndex
86− containerStyle <- mapDyn mkContainer heightPx
87− viewportStyle <- mapDyn mkViewport heightPx
94+ let virtualH = fmap (mkVirtualHeight . (*) rowPx) maxIndex
95+ containerStyle = fmap mkContainer heightPx
96+ viewportStyle = fmap mkViewport heightPx
8897 pb <- getPostBuild
8998 rec (viewport, result) <- elDynAttr "div" containerStyle $ elDynAttr' "div" viewportStyle $ elDynAttr "div" virtualH $
9099 listWithKeyShallowDiff items0 itemsUpdate $ \k v e -> elAttr "div" (mkRow k) $ itemBuilder k v e
91100 scrollPosition <- holdDyn 0 $ leftmost [ domEvent Scroll viewport
92101 , fmap (const (i0 * rowPx)) pb
93102 ]
94− 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]
96− return (nubDyn window, result)
103+ let window = zipDynWith (findWindow rowPx) heightPx scrollPosition
104+ performEvent_ $ ffor (leftmost [setI, i0 <$ pb]) $ \i -> do
105+ liftIO $ setScrollTop (_element_raw viewport) (i * rowPx)
106+ return (uniqDyn window, result)
97107 where
98− toStyleAttr m = "style" =: (Map.foldWithKey (\k v s -> k <> ":" <> v <> ";" <> s) "" m)
108+ toStyleAttr m = "style" =: Map.foldWithKey (\k v s -> k <> ":" <> v <> ";" <> s) "" m
99109 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") <>
110+ "left" =: "0" <> "right" =: "0" <> "height" =: (T.pack (show h) <> "px")
111+ mkContainer h = toStyleAttr $ "position" =: "relative" <> "height" =: (T.pack (show h) <> "px")
112+ mkVirtualHeight h = let h' = h * rowPx --TODO: test the use of this
113+ in toStyleAttr $ "height" =: (T.pack (show h') <> "px") <>
104114 "overflow" =: "hidden" <>
105115 "position" =: "relative"
106− mkRow k = toStyleAttr $ "height" =: (show rowPx <> "px") <>
107− "top" =: ((<>"px") $ show $ keyToIndex k * rowPx) <>
116+ mkRow k = toStyleAttr $ "height" =: (T.pack (show rowPx) <> "px") <>
117+ "top" =: ((<>"px") $ T.pack $ show $ keyToIndex k * rowPx) <>
108118 "position" =: "absolute" <>
109119 "width" =: "100%"
110− findWindow windowSize sizeIncrement startingPosition =
111− let (startingIndex, topOffsetPx) = startingPosition `divMod'` sizeIncrement
120+ findWindow sizeIncrement windowSize startingPosition =
121+ let (startingIndex, _) = startingPosition `divMod'` sizeIncrement
112122 numItems = (windowSize + sizeIncrement - 1) `div` sizeIncrement
113123 in (startingIndex, numItems)
114124
115125virtualListBuffered
116− :: (Ord k, MonadWidget t m)
126+ :: (DomBuilder t m, PostBuild t m, MonadHold t m, PerformEvent t m, MonadIO (Performable m), DomBuilderSpace m ~ GhcjsDomSpace, MonadFix m, Ord k)
117127 => Int
118128 -> Dynamic t Int
119129 -> Int
@@ -134,6 +144,6 @@ virtualListBuffered buffer heightPx rowPx maxIndex i0 setI keyToIndex items0 ite
134144 then Nothing
135145 else Just (extendWin winOffset winLimit)) (current winBuffered) (updated win)
136146 winBuffered <- holdDyn (0, 0) $ leftmost [ winHitEdge
137− , fmap (uncurry extendWin) $ tagDyn win pb
147+ , attachPromptlyDynWith (\(x, y) _ -> extendWin x y) win pb
138148 ]
139149 return (updated winBuffered, m)
src/Reflex/Dom/Widget/Resize.hs+29−9
@@ -1,29 +1,49 @@
1+{-# LANGUAGE FlexibleContexts #-}
2+{-# LANGUAGE MultiParamTypeClasses #-}
3+{-# LANGUAGE OverloadedStrings #-}
14{-# LANGUAGE RecursiveDo #-}
5+{-# LANGUAGE TypeFamilies #-}
26module Reflex.Dom.Widget.Resize where
37
48import Reflex
9+import Reflex.Dom.Builder.Class
10+import Reflex.Dom.Builder.Immediate
511import Reflex.Dom.Class
12+import Reflex.Dom.Old
13+import Reflex.Dom.PerformEvent.Class
14+import Reflex.Dom.PostBuild.Class
615import Reflex.Dom.Widget.Basic
716
817import Control.Monad
18+import Control.Monad.Fix
919import Control.Monad.IO.Class
20+import Data.Map (Map)
21+import qualified Data.Map as Map
1022import Data.Monoid
23+import Data.Text (Text)
24+import qualified Data.Text as T
1125import GHCJS.DOM.Element hiding (reset)
1226import GHCJS.DOM.EventM (on)
1327
1428-- | A widget that wraps the given widget in a div and fires an event when resized.
1529-- Adapted from github.com/marcj/css-element-queries
16−resizeDetector :: MonadWidget t m => m a -> m (Event t (), a)
30+resizeDetector :: (DomBuilder t m, PostBuild t m, TriggerEvent t m, PerformEvent t m, MonadHold t m, DomBuilderSpace m ~ GhcjsDomSpace, MonadIO (Performable m), MonadFix m) => m a -> m (Event t (), a)
1731resizeDetector = resizeDetectorWithStyle ""
1832
19−resizeDetectorWithStyle :: MonadWidget t m
20− => String -- ^ A css style string. Warning: It should not contain the "position" style attribute.
33+resizeDetectorWithStyle :: (DomBuilder t m, PostBuild t m, TriggerEvent t m, PerformEvent t m, MonadHold t m, DomBuilderSpace m ~ GhcjsDomSpace, MonadIO (Performable m), MonadFix m)
34+ => Text -- ^ A css style string. Warning: It should not contain the "position" style attribute.
2135 -> m a -- ^ The embedded widget
2236 -> m (Event t (), a) -- ^ An 'Event' that fires on resize, and the result of the embedded widget
23−resizeDetectorWithStyle styleString w = do
37+resizeDetectorWithStyle styleString = resizeDetectorWithAttrs ("style" =: styleString)
38+
39+resizeDetectorWithAttrs :: (DomBuilder t m, PostBuild t m, TriggerEvent t m, PerformEvent t m, MonadHold t m, DomBuilderSpace m ~ GhcjsDomSpace, MonadIO (Performable m), MonadFix m)
40+ => Map Text Text -- ^ A map of attributes. Warning: It should not modify the "position" style attribute.
41+ -> m a -- ^ The embedded widget
42+ -> m (Event t (), a) -- ^ An 'Event' that fires on resize, and the result of the embedded widget
43+resizeDetectorWithAttrs attrs w = do
2444 let childStyle = "position: absolute; left: 0; top: 0;"
2545 containerAttrs = "style" =: "position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: scroll; z-index: -1; visibility: hidden;"
26− (parent, (expand, expandChild, shrink, w')) <- elAttr' "div" ("style" =: ("position: relative;" <> styleString)) $ do
46+ (parent, (expand, expandChild, shrink, w')) <- elAttr' "div" (Map.unionWith (<>) attrs ("style" =: "position: relative;")) $ do
2747 w' <- w
2848 elAttr "div" containerAttrs $ do
2949 (expand, (expandChild, _)) <- elAttr' "div" containerAttrs $ elAttr' "div" ("style" =: childStyle) $ return ()
@@ -36,7 +56,7 @@ resizeDetectorWithStyle styleString w = do
3656 eoh <- getOffsetHeight e
3757 let ecw = eow + 10
3858 ech = eoh + 10
39− setAttribute (_el_element expandChild) "style" (childStyle <> "width: " <> show ecw <> "px;" <> "height: " <> show ech <> "px;")
59+ setAttribute (_el_element expandChild) ("style" :: Text) (childStyle <> "width: " <> T.pack (show ecw) <> "px;" <> "height: " <> T.pack (show ech) <> "px;")
4060 esw <- getScrollWidth e
4161 setScrollLeft e esw
4262 esh <- getScrollHeight e
@@ -52,12 +72,12 @@ resizeDetectorWithStyle styleString w = do
5272 pow <- getOffsetWidth (_el_element parent)
5373 poh <- getOffsetHeight (_el_element parent)
5474 if ds == (Just pow, Just poh)
55− then return Nothing
56− else liftM Just reset
75+ then return Nothing
76+ else fmap Just reset
5777 pb <- getPostBuild
5878 expandScroll <- wrapDomEvent (_el_element expand) (`on` scroll) $ return ()
5979 shrinkScroll <- wrapDomEvent (_el_element shrink) (`on` scroll) $ return ()
6080 size0 <- performEvent $ fmap (const $ liftIO reset) pb
6181 rec resize <- performEventAsync $ fmap (\d cb -> liftIO $ cb =<< resetIfChanged d) $ tag (current dimensions) $ leftmost [expandScroll, shrinkScroll]
6282 dimensions <- holdDyn (Nothing, Nothing) $ leftmost [ size0, fmapMaybe id resize ]
63− return (fmap (const ()) $ fmapMaybe id resize, w')
83+ return (fmapMaybe void resize, w')
src/Reflex/Dom/Xhr.hs+114−76
@@ -1,27 +1,47 @@
11{-# LANGUAGE DeriveDataTypeable #-}
2−
2+{-# LANGUAGE DeriveFunctor #-}
3+{-# LANGUAGE FlexibleContexts #-}
4+{-# LANGUAGE GADTs #-}
5+{-# LANGUAGE MultiParamTypeClasses #-}
6+{-# LANGUAGE OverloadedStrings #-}
7+{-# LANGUAGE TemplateHaskell #-}
38module Reflex.Dom.Xhr
49 ( XMLHttpRequest
5− , XhrRequest(..)
6− , XhrRequestConfig(..)
7− , XhrResponse(..)
8− , XhrResponseBody(..)
9− , XhrResponseType(..)
10− , XhrException(..)
10+ , XhrRequest (..)
11+ , XhrRequestConfig (..)
12+ , XhrResponse (..)
13+ , XhrResponseBody (..)
14+ , XhrResponseType (..)
15+ , XhrException (..)
16+ , IsXhrPayload (..)
1117 , _xhrResponse_body
12− , xhrResponse_body
13− , xhrRequest
18+ , decodeText
19+ , decodeXhrResponse
20+ , getAndDecode
21+ , getMay
1422 , newXMLHttpRequest
1523 , newXMLHttpRequestWithError
24+ , performMkRequestAsync
25+ , performMkRequestsAsync
1626 , performRequestAsync
1727 , performRequestAsyncWithError
1828 , performRequestsAsync
1929 , performRequestsAsyncWithError
20− , getAndDecode
2130 , postJson
22− , getMay
23− , decodeText
24− , decodeXhrResponse
31+ , xhrRequest
32+ , xhrRequestConfig_headers
33+ , xhrRequestConfig_password
34+ , xhrRequestConfig_responseType
35+ , xhrRequestConfig_sendData
36+ , xhrRequestConfig_user
37+ , xhrRequest_config
38+ , xhrRequest_method
39+ , xhrRequest_url
40+ , xhrResponse_body
41+ , xhrResponse_response
42+ , xhrResponse_responseText
43+ , xhrResponse_status
44+ , xhrResponse_statusText
2545 , xmlHttpRequestGetReadyState
2646 , xmlHttpRequestGetResponseText
2747 , xmlHttpRequestGetStatus
@@ -29,21 +49,25 @@ module Reflex.Dom.Xhr
2949 , xmlHttpRequestNew
3050 , xmlHttpRequestOnreadystatechange
3151 , xmlHttpRequestOpen
32− , xmlHttpRequestSend
3352 , xmlHttpRequestSetRequestHeader
3453 , xmlHttpRequestSetResponseType
3554 )
3655where
3756
57+import Reflex
58+import Reflex.Dom.Class
59+import Reflex.Dom.PerformEvent.Class
60+import Reflex.Dom.Xhr.Exception
61+import Reflex.Dom.Xhr.Foreign
62+import Reflex.Dom.Xhr.ResponseType
63+
3864import Control.Concurrent
39−import Control.Exception (catch)
65+import Control.Exception (handle)
4066import Control.Lens
4167import Control.Monad hiding (forM)
4268import Control.Monad.IO.Class
4369import Data.Aeson
4470import Data.Aeson.Encode
45−import qualified Data.Text.Lazy as LT
46−import qualified Data.Text.Lazy.Builder as B
4771import qualified Data.ByteString.Lazy as BL
4872import Data.Default
4973import Data.Map (Map)
@@ -51,30 +75,27 @@ import qualified Data.Map as Map
5175import Data.Maybe
5276import Data.Text (Text)
5377import Data.Text.Encoding
78+import qualified Data.Text.Lazy as LT
79+import qualified Data.Text.Lazy.Builder as B
5480import Data.Traversable
55−import Reflex
56−import Reflex.Dom.Class
57−import Reflex.Dom.Xhr.Exception
58−import Reflex.Dom.Xhr.Foreign
59−import Reflex.Dom.Xhr.ResponseType
6081import Data.Typeable
6182
62−data XhrRequest
63− = XhrRequest { _xhrRequest_method :: String
64− , _xhrRequest_url :: String
65− , _xhrRequest_config :: XhrRequestConfig
83+data XhrRequest a
84+ = XhrRequest { _xhrRequest_method :: Text
85+ , _xhrRequest_url :: Text
86+ , _xhrRequest_config :: XhrRequestConfig a
6687 }
67− deriving (Show, Read, Eq, Ord, Typeable)
88+ deriving (Show, Read, Eq, Ord, Typeable, Functor)
6889
69−data XhrRequestConfig
70− = XhrRequestConfig { _xhrRequestConfig_headers :: Map String String
71− , _xhrRequestConfig_user :: Maybe String
72− , _xhrRequestConfig_password :: Maybe String
90+data XhrRequestConfig a
91+ = XhrRequestConfig { _xhrRequestConfig_headers :: Map Text Text
92+ , _xhrRequestConfig_user :: Maybe Text
93+ , _xhrRequestConfig_password :: Maybe Text
7394 , _xhrRequestConfig_responseType :: Maybe XhrResponseType
74− , _xhrRequestConfig_sendData :: Maybe String
95+ , _xhrRequestConfig_sendData :: a
7596 , _xhrRequestConfig_withCredentials :: Bool
7697 }
77− deriving (Show, Read, Eq, Ord, Typeable)
98+ deriving (Show, Read, Eq, Ord, Typeable, Functor)
7899
79100data XhrResponse
80101 = XhrResponse { _xhrResponse_status :: Word
@@ -92,17 +113,17 @@ _xhrResponse_body = _xhrResponse_responseText
92113xhrResponse_body :: Lens XhrResponse XhrResponse (Maybe Text) (Maybe Text)
93114xhrResponse_body = lens _xhrResponse_responseText (\r t -> r { _xhrResponse_responseText = t })
94115
95−instance Default XhrRequestConfig where
116+instance a ~ () => Default (XhrRequestConfig a) where
96117 def = XhrRequestConfig { _xhrRequestConfig_headers = Map.empty
97118 , _xhrRequestConfig_user = Nothing
98119 , _xhrRequestConfig_password = Nothing
99120 , _xhrRequestConfig_responseType = Nothing
100− , _xhrRequestConfig_sendData = Nothing
121+ , _xhrRequestConfig_sendData = ()
101122 , _xhrRequestConfig_withCredentials = False
102123 }
103124
104125-- | Construct a request object from method, URL, and config record.
105−xhrRequest :: String -> String -> XhrRequestConfig -> XhrRequest
126+xhrRequest :: Text -> Text -> XhrRequestConfig a -> XhrRequest a
106127xhrRequest = XhrRequest
107128
108129-- | Make a new asyncronous XHR request. This does not block (it forks),
@@ -110,19 +131,18 @@ xhrRequest = XhrRequest
110131-- the XHR connection), and will pass an exception ('XhrException') to the
111132-- continuation if the connection cannot be made (or is aborted).
112133newXMLHttpRequestWithError
113− :: (HasWebView m, MonadIO m, HasPostGui t h m)
114− => XhrRequest
134+ :: (HasWebView m, MonadIO m, IsXhrPayload a)
135+ => XhrRequest a
115136 -- ^ The request to make.
116− -> (Either XhrException XhrResponse -> h ())
137+ -> (Either XhrException XhrResponse -> IO ())
117138 -- ^ A continuation to be called once a response comes back, or in
118139 -- case of error.
119140 -> m XMLHttpRequest
120141 -- ^ The XHR request, which could for example be aborted.
121142newXMLHttpRequestWithError req cb = do
122143 wv <- askWebView
123− postGui <- askPostGui
124− xhr <- liftIO $ xmlHttpRequestNew wv
125− void $ liftIO $ forkIO $ flip catch (postGui . cb . Left) $ void $ do
144+ xhr <- liftIO $ xmlHttpRequestNew $ unWebViewSingleton wv
145+ void $ liftIO $ forkIO $ handle (cb . Left) $ void $ do
126146 let c = _xhrRequest_config req
127147 rt = _xhrRequestConfig_responseType c
128148 creds = _xhrRequestConfig_withCredentials c
@@ -140,25 +160,23 @@ newXMLHttpRequestWithError req cb = do
140160 readyState <- liftIO $ xmlHttpRequestGetReadyState xhr
141161 status <- liftIO $ xmlHttpRequestGetStatus xhr
142162 statusText <- liftIO $ xmlHttpRequestGetStatusText xhr
143− if readyState == 4
144− then do
145− t <- if rt == Just XhrResponseType_Text || rt == Nothing
146− then liftIO $ xmlHttpRequestGetResponseText xhr
147− else return Nothing
148− r <- liftIO $ xmlHttpRequestGetResponse xhr
149− _ <- liftIO $ postGui $ cb $ Right $
150− XhrResponse { _xhrResponse_status = status
151− , _xhrResponse_statusText = statusText
152− , _xhrResponse_response = r
153− , _xhrResponse_responseText = t
154− }
155− return ()
156− else return ()
163+ when (readyState == 4) $ do
164+ t <- if rt == Just XhrResponseType_Text || isNothing rt
165+ then liftIO $ xmlHttpRequestGetResponseText xhr
166+ else return Nothing
167+ r <- liftIO $ xmlHttpRequestGetResponse xhr
168+ _ <- liftIO $ cb $ Right $ XhrResponse
169+ { _xhrResponse_status = status
170+ , _xhrResponse_statusText = statusText
171+ , _xhrResponse_response = r
172+ , _xhrResponse_responseText = t
173+ }
174+ return ()
157175 _ <- xmlHttpRequestSend xhr (_xhrRequestConfig_sendData c)
158176 return ()
159177 return xhr
160178
161−newXMLHttpRequest :: (HasWebView m, MonadIO m, HasPostGui t h m) => XhrRequest -> (XhrResponse -> h ()) -> m XMLHttpRequest
179+newXMLHttpRequest :: (HasWebView m, MonadIO m, IsXhrPayload a) => XhrRequest a -> (XhrResponse -> IO ()) -> m XMLHttpRequest
162180newXMLHttpRequest req cb = newXMLHttpRequestWithError req $ mapM_ cb
163181
164182-- | Given Event of requests, issue them when the Event fires.
@@ -167,15 +185,24 @@ newXMLHttpRequest req cb = newXMLHttpRequestWithError req $ mapM_ cb
167185-- The request is processed asynchronously, therefore handling does
168186-- not block or cause a delay while creating the connection.
169187performRequestAsyncWithError
170− :: MonadWidget t m
171− => Event t XhrRequest -> m (Event t (Either XhrException XhrResponse))
172−performRequestAsyncWithError = performRequestAsync' newXMLHttpRequestWithError
188+ :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, IsXhrPayload a)
189+ => Event t (XhrRequest a)
190+ -> m (Event t (Either XhrException XhrResponse))
191+performRequestAsyncWithError = performRequestAsync' newXMLHttpRequestWithError . fmap return
173192
174−performRequestAsync :: MonadWidget t m => Event t XhrRequest -> m (Event t XhrResponse)
175−performRequestAsync = performRequestAsync' newXMLHttpRequest
193+-- | Given Event of request, issue them when the Event fires. Returns Event of corresponding response.
194+performRequestAsync :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, IsXhrPayload a) => Event t (XhrRequest a) -> m (Event t XhrResponse)
195+performRequestAsync = performRequestAsync' newXMLHttpRequest . fmap return
176196
177−performRequestAsync' :: (MonadWidget t m, MonadIO h) => (XhrRequest -> (a -> h ()) -> WidgetHost m XMLHttpRequest) -> Event t XhrRequest -> m (Event t a)
178−performRequestAsync' newXhr req = performEventAsync $ ffor req $ \r cb -> void $ newXhr r $ liftIO . cb
197+-- | Given Event with an action that creates a request, build and issue the request when the Event fires. Returns Event of corresponding response.
198+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)
199+performMkRequestAsync = performRequestAsync' newXMLHttpRequest
200+
201+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)
202+performRequestAsync' newXhr req = performEventAsync $ ffor req $ \hr cb -> do
203+ r <- hr
204+ _ <- newXhr r $ liftIO . cb
205+ return ()
179206
180207-- | Issues a collection of requests when the supplied Event fires.
181208-- When ALL requests from a given firing complete, the results are
@@ -187,16 +214,21 @@ performRequestAsync' newXhr req = performEventAsync $ ffor req $ \r cb -> void $
187214-- Order of request execution and completion is not guaranteed, but
188215-- order of creation and the collection result is preserved.
189216performRequestsAsyncWithError
190− :: (Traversable f, MonadWidget t m)
191− => Event t (f XhrRequest) -> m (Event t (f (Either XhrException XhrResponse)))
192−performRequestsAsyncWithError = performRequestsAsync' newXMLHttpRequestWithError
217+ :: (MonadIO (Performable m), HasWebView (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f, IsXhrPayload a)
218+ => Event t (f (XhrRequest a)) -> m (Event t (f (Either XhrException XhrResponse)))
219+performRequestsAsyncWithError = performRequestsAsync' newXMLHttpRequestWithError . fmap return
193220
221+-- | 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.
222+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))
223+performRequestsAsync = performRequestsAsync' newXMLHttpRequest . fmap return
194224
195−performRequestsAsync :: (Traversable f, MonadWidget t m) => Event t (f XhrRequest) -> m (Event t (f XhrResponse))
196−performRequestsAsync = performRequestsAsync' newXMLHttpRequest
225+-- | 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.
226+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))
227+performMkRequestsAsync = performRequestsAsync' newXMLHttpRequest
197228
198−performRequestsAsync' :: (MonadWidget t m, MonadIO h, Traversable f) => (XhrRequest -> (a -> h ()) -> WidgetHost m XMLHttpRequest) -> Event t (f XhrRequest) -> m (Event t (f a))
199−performRequestsAsync' newXhr req = performEventAsync $ ffor req $ \rs cb -> do
229+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))
230+performRequestsAsync' newXhr req = performEventAsync $ ffor req $ \hrs cb -> do
231+ rs <- hrs
200232 resps <- forM rs $ \r -> do
201233 resp <- liftIO newEmptyMVar
202234 _ <- newXhr r $ liftIO . putMVar resp
@@ -205,21 +237,21 @@ performRequestsAsync' newXhr req = performEventAsync $ ffor req $ \rs cb -> do
205237 return ()
206238
207239-- | Simplified interface to "GET" URLs and return decoded results.
208−getAndDecode :: (FromJSON a, MonadWidget t m) => Event t String -> m (Event t (Maybe a))
240+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))
209241getAndDecode url = do
210242 r <- performRequestAsync $ fmap (\x -> XhrRequest "GET" x def) url
211243 return $ fmap decodeXhrResponse r
212244
213245-- | Create a "POST" request from an URL and thing with a JSON representation
214−postJson :: (ToJSON a) => String -> a -> XhrRequest
246+postJson :: (ToJSON a) => Text -> a -> XhrRequest Text
215247postJson url a =
216248 XhrRequest "POST" url $ def { _xhrRequestConfig_headers = headerUrlEnc
217− , _xhrRequestConfig_sendData = Just body
249+ , _xhrRequestConfig_sendData = body
218250 }
219251 where headerUrlEnc = "Content-type" =: "application/json"
220− body = LT.unpack $ B.toLazyText $ encodeToTextBuilder $ toJSON a
252+ body = LT.toStrict $ B.toLazyText $ encodeToTextBuilder $ toJSON a
221253
222−getMay :: MonadWidget t m => (Event t a -> m (Event t b)) -> Event t (Maybe a) -> m (Event t (Maybe b))
254+getMay :: (Monad m, Reflex t) => (Event t a -> m (Event t b)) -> Event t (Maybe a) -> m (Event t (Maybe b))
223255getMay f e = do
224256 e' <- f (fmapMaybe id e)
225257 return $ leftmost [fmap Just e', fmapMaybe (maybe (Just Nothing) (const Nothing)) e]
@@ -231,3 +263,9 @@ decodeText = decode . BL.fromStrict . encodeUtf8
231263decodeXhrResponse :: FromJSON a => XhrResponse -> Maybe a
232264decodeXhrResponse = join . fmap decodeText . _xhrResponse_responseText
233265
266+concat <$> mapM makeLenses
267+ [ ''XhrRequest
268+ , ''XhrRequestConfig
269+ , ''XhrResponse
270+ ]
271+
src/Reflex/Dom/Xhr/Exception.hs+1−1
@@ -2,8 +2,8 @@
22
33module Reflex.Dom.Xhr.Exception where
44
5+import Control.Exception (Exception (..))
56import Data.Typeable
6−import Control.Exception (Exception(..))
77
88data XhrException = XhrException_Error
99 | XhrException_Aborted
src/Reflex/Dom/Xhr/ResponseType.hs+2−2
@@ -2,10 +2,10 @@
22
33module Reflex.Dom.Xhr.ResponseType where
44
5−import Data.Typeable
5+import Data.ByteString (ByteString)
66import Data.Text (Text)
7+import Data.Typeable
78import GHCJS.DOM.Blob (Blob)
8−import Data.ByteString (ByteString)
99
1010data XhrResponseType = XhrResponseType_Default
1111 | XhrResponseType_ArrayBuffer
test/BrokenHaltSpec.hs+9−10
@@ -2,13 +2,13 @@
22
33module HaltSpec where
44
5−import Control.Monad.IO.Class
6−import Data.Foldable
7−import GHCJS.DOM
5+import Control.Monad.IO.Class
6+import Data.Foldable
7+import GHCJS.DOM
88import qualified Graphics.UI.Gtk as Gtk
9−import Reflex.Dom
10−import Reflex.Spider.Internal (SpiderHostFrame)
11−import Test.Hspec
9+import Reflex.Dom
10+import Reflex.Spider.Internal (SpiderHostFrame)
11+import Test.Hspec
1212
1313spec :: Spec
1414spec = do
@@ -34,7 +34,6 @@ getFirstEventAndHalt action = do
3434 return ()
3535
3636haltGui :: WebView -> IO ()
37−haltGui wv = do
38− Gtk.postGUIAsync $ do
39− w <- Gtk.widgetGetToplevel wv
40− Gtk.widgetDestroy w
37+haltGui wv = Gtk.postGUIAsync $ do
38+ w <- Gtk.widgetGetToplevel wv
39+ Gtk.widgetDestroy w
test/HaltSpec.hs+13−15
@@ -1,29 +1,27 @@
11{-# LANGUAGE FlexibleContexts #-}
22
3−import Control.Monad.IO.Class
4−import Data.Foldable
5−import GHCJS.DOM
3+import Control.Monad.IO.Class
4+import Data.Foldable
5+import GHCJS.DOM
66import qualified Graphics.UI.Gtk as Gtk
7−import Reflex.Dom
8−import Reflex.Spider.Internal (SpiderHostFrame)
7+import Reflex.Dom
8+import Reflex.Spider.Internal (SpiderHostFrame)
99
10+main :: IO ()
1011main = do
1112 getFirstEventAndHalt return
1213 putStrLn "stopped once"
1314
14− forM_ [1 .. 1000] $ \ i -> do
15− getFirstEventAndHalt return
15+ forM_ [1 .. 1000] $ \i -> getFirstEventAndHalt return
1616 putStrLn "stopped many times"
1717
1818type SpiderM = Widget Spider (Gui Spider (WithWebView SpiderHost) SpiderHostFrame)
1919
2020getFirstEventAndHalt :: (Event Spider () -> SpiderM (Event Spider ())) -> IO ()
21−getFirstEventAndHalt action = do
22− _ <- mainWidget $ do
23− tick <- getPostBuild
24− event <- action tick
25− webView <- askWebView
26− quit <- getQuitWidget
27− performEvent_ (quit <$ event)
28− return ()
21+getFirstEventAndHalt action = mainWidget $ do
22+ tick <- getPostBuild
23+ event <- action tick
24+ webView <- askWebView
25+ quit <- getQuitWidget
26+ performEvent_ (quit <$ event)
2927 return ()
test/hlint.hsnew file+12
@@ -0,0 +1,12 @@
1+import Language.Haskell.HLint3 (hlint)
2+import System.Exit (exitFailure, exitSuccess)
3+
4+main :: IO ()
5+main = do
6+ ideas <- hlint
7+ [ "."
8+ , "--ignore=Redundant do"
9+ , "--ignore=Use camelCase"
10+ , "--ignore=Redundant $"
11+ ]
12+ if null ideas then exitSuccess else exitFailure