{-# LANGUAGE DeriveDataTypeable #-}
module Graphics.UI.Threepenny.Internal (
Window, disconnect,
startGUI, loadFile, loadDirectory,
UI, runUI, MonadUI(..), liftIOLater, askWindow, liftJSWindow,
FFI, FromJS, ToJS, JSFunction, JSObject, ffi,
runFunction, callFunction,
CallBufferMode(..), setCallBufferMode, flushCallBuffer,
ffiExport, debug, timestamp,
Element(toJSObject), fromJSObject, getWindow,
mkElementNamespace, mkElement, delete, appendChild, clearChildren,
EventData, domEvent, unsafeFromJSON,
) where
import Control.Applicative (Applicative(..))
import Control.Monad
import Control.Monad.Catch
import Control.Monad.Fix
import Control.Monad.IO.Class
import qualified Control.Monad.Trans.RWS.Lazy as Monad
import Data.Dynamic (Typeable)
import qualified Data.Aeson as JSON
import qualified Foreign.JavaScript as JS
import qualified Foreign.RemotePtr as Foreign
import qualified Reactive.Threepenny as RB
import Foreign.JavaScript hiding
(runFunction, callFunction, setCallBufferMode, flushCallBuffer
,debug, timestamp, Window, loadFile, loadDirectory)
data Window = Window
{ Window -> Window
jsWindow :: JS.Window
, Window -> Event ()
eDisconnect :: RB.Event ()
, Window -> Vendor Events
wEvents :: Foreign.Vendor Events
, Window -> Vendor ()
wChildren :: Foreign.Vendor ()
}
startGUI
:: Config
-> (Window -> UI ())
-> IO ()
startGUI :: Config -> (Window -> UI ()) -> IO ()
startGUI Config
config Window -> UI ()
init = Config -> (Window -> IO ()) -> IO ()
JS.serve Config
config forall a b. (a -> b) -> a -> b
$ \Window
w -> do
(Event ()
eDisconnect, Handler ()
handleDisconnect) <- forall a. IO (Event a, Handler a)
RB.newEvent
Window -> IO () -> IO ()
JS.onDisconnect Window
w forall a b. (a -> b) -> a -> b
$ Handler ()
handleDisconnect ()
Vendor Events
wEvents <- forall a. IO (Vendor a)
Foreign.newVendor
Vendor ()
wChildren <- forall a. IO (Vendor a)
Foreign.newVendor
let window :: Window
window = Window
{ jsWindow :: Window
jsWindow = Window
w
, eDisconnect :: Event ()
eDisconnect = Event ()
eDisconnect
, wEvents :: Vendor Events
wEvents = Vendor Events
wEvents
, wChildren :: Vendor ()
wChildren = Vendor ()
wChildren
}
forall a. Window -> UI a -> IO a
runUI Window
window forall a b. (a -> b) -> a -> b
$ Window -> UI ()
init Window
window
disconnect :: Window -> RB.Event ()
disconnect :: Window -> Event ()
disconnect = Window -> Event ()
eDisconnect
loadFile
:: String
-> FilePath
-> UI String
loadFile :: String -> String -> UI String
loadFile String
x String
y = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> Server -> String -> String -> IO String
JS.loadFile (Window -> Server
JS.getServer Window
w) String
x String
y
loadDirectory :: FilePath -> UI String
loadDirectory :: String -> UI String
loadDirectory String
x = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> Server -> String -> IO String
JS.loadDirectory (Window -> Server
JS.getServer Window
w) String
x
type Events = String -> RB.Event JSON.Value
type Children = Foreign.RemotePtr ()
data Element = Element
{ Element -> JSObject
toJSObject :: JS.JSObject
, Element -> Events
elEvents :: Events
, Element -> Children
elChildren :: Children
, Element -> Window
elWindow :: Window
} deriving (Typeable)
instance ToJS Element where
render :: Element -> IO JSCode
render = forall a. ToJS a => a -> IO JSCode
render forall b c a. (b -> c) -> (a -> b) -> a -> c
. Element -> JSObject
toJSObject
getWindow :: Element -> IO Window
getWindow :: Element -> IO Window
getWindow = forall (m :: * -> *) a. Monad m => a -> m a
return forall b c a. (b -> c) -> (a -> b) -> a -> c
. Element -> Window
elWindow
getChildren :: JS.JSObject -> Window -> IO Children
getChildren :: JSObject -> Window -> IO Children
getChildren JSObject
el window :: Window
window@Window{ wChildren :: Window -> Vendor ()
wChildren = Vendor ()
wChildren } =
forall a b. RemotePtr a -> (Coupon -> a -> IO b) -> IO b
Foreign.withRemotePtr JSObject
el forall a b. (a -> b) -> a -> b
$ \Coupon
coupon JSPtr
_ -> do
Maybe Children
mptr <- forall a. Coupon -> Vendor a -> IO (Maybe (RemotePtr a))
Foreign.lookup Coupon
coupon Vendor ()
wChildren
case Maybe Children
mptr of
Maybe Children
Nothing -> do
Children
ptr <- forall a. Coupon -> a -> Vendor a -> IO (RemotePtr a)
Foreign.newRemotePtr Coupon
coupon () Vendor ()
wChildren
forall a b. RemotePtr a -> RemotePtr b -> IO ()
Foreign.addReachable JSObject
el Children
ptr
forall (m :: * -> *) a. Monad m => a -> m a
return Children
ptr
Just Children
p ->
forall (m :: * -> *) a. Monad m => a -> m a
return Children
p
fromJSObject0 :: JS.JSObject -> Window -> IO Element
fromJSObject0 :: JSObject -> Window -> IO Element
fromJSObject0 JSObject
el Window
window = do
Events
events <- JSObject -> Window -> IO Events
getEvents JSObject
el Window
window
Children
children <- JSObject -> Window -> IO Children
getChildren JSObject
el Window
window
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ JSObject -> Events -> Children -> Window -> Element
Element JSObject
el Events
events Children
children Window
window
fromJSObject :: JS.JSObject -> UI Element
fromJSObject :: JSObject -> UI Element
fromJSObject JSObject
el = do
Window
window <- UI Window
askWindow
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
forall a b. RemotePtr a -> RemotePtr b -> IO ()
Foreign.addReachable (Window -> Children
JS.root forall a b. (a -> b) -> a -> b
$ Window -> Window
jsWindow Window
window) JSObject
el
JSObject -> Window -> IO Element
fromJSObject0 JSObject
el Window
window
addEvents :: JS.JSObject -> Window -> IO Events
addEvents :: JSObject -> Window -> IO Events
addEvents JSObject
el Window{ jsWindow :: Window -> Window
jsWindow = Window
w, wEvents :: Window -> Vendor Events
wEvents = Vendor Events
wEvents } = do
let initializeEvent :: (t, b, a) -> IO ()
initializeEvent (t
name,b
_,a
handler) = do
JSObject
handlerPtr <- forall a. IsHandler a => Window -> a -> IO JSObject
JS.exportHandler Window
w a
handler
forall a b. RemotePtr a -> RemotePtr b -> IO ()
Foreign.addReachable JSObject
el JSObject
handlerPtr
Window -> JSFunction () -> IO ()
JS.runFunction Window
w forall a b. (a -> b) -> a -> b
$
forall a. FFI a => String -> a
ffi String
"Haskell.on(%1,%2,%3)" JSObject
el t
name JSObject
handlerPtr
Events
events <- forall name a.
Ord name =>
Handler (name, Event a, Handler a) -> IO (name -> Event a)
RB.newEventsNamed forall {a} {t} {b}. (IsHandler a, ToJS t) => (t, b, a) -> IO ()
initializeEvent
forall a b. RemotePtr a -> (Coupon -> a -> IO b) -> IO b
Foreign.withRemotePtr JSObject
el forall a b. (a -> b) -> a -> b
$ \Coupon
coupon JSPtr
_ -> do
RemotePtr Events
ptr <- forall a. Coupon -> a -> Vendor a -> IO (RemotePtr a)
Foreign.newRemotePtr Coupon
coupon Events
events Vendor Events
wEvents
forall a b. RemotePtr a -> RemotePtr b -> IO ()
Foreign.addReachable JSObject
el RemotePtr Events
ptr
forall (m :: * -> *) a. Monad m => a -> m a
return Events
events
getEvents :: JS.JSObject -> Window -> IO Events
getEvents :: JSObject -> Window -> IO Events
getEvents JSObject
el window :: Window
window@Window{ wEvents :: Window -> Vendor Events
wEvents = Vendor Events
wEvents } = do
forall a b. RemotePtr a -> (Coupon -> a -> IO b) -> IO b
Foreign.withRemotePtr JSObject
el forall a b. (a -> b) -> a -> b
$ \Coupon
coupon JSPtr
_ -> do
Maybe (RemotePtr Events)
mptr <- forall a. Coupon -> Vendor a -> IO (Maybe (RemotePtr a))
Foreign.lookup Coupon
coupon Vendor Events
wEvents
case Maybe (RemotePtr Events)
mptr of
Maybe (RemotePtr Events)
Nothing -> JSObject -> Window -> IO Events
addEvents JSObject
el Window
window
Just RemotePtr Events
p -> forall a b. RemotePtr a -> (Coupon -> a -> IO b) -> IO b
Foreign.withRemotePtr RemotePtr Events
p forall a b. (a -> b) -> a -> b
$ \Coupon
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return
type EventData = JSON.Value
unsafeFromJSON :: JSON.FromJSON a => EventData -> a
unsafeFromJSON :: forall a. FromJSON a => Value -> a
unsafeFromJSON Value
x = let JSON.Success a
y = forall a. FromJSON a => Value -> Result a
JSON.fromJSON Value
x in a
y
domEvent
:: String
-> Element
-> RB.Event EventData
domEvent :: String -> Element -> Event Value
domEvent String
name Element
el = Element -> Events
elEvents Element
el String
name
mkElement :: String -> UI Element
mkElement :: String -> UI Element
mkElement = Maybe String -> String -> UI Element
mkElementNamespace forall a. Maybe a
Nothing
mkElementNamespace :: Maybe String -> String -> UI Element
mkElementNamespace :: Maybe String -> String -> UI Element
mkElementNamespace Maybe String
namespace String
tag = do
Window
window <- UI Window
askWindow
let w :: Window
w = Window -> Window
jsWindow Window
window
forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall a b. (a -> b) -> a -> b
$ do
JSObject
el <- Window -> JSFunction NewJSObject -> IO JSObject
JS.unsafeCreateJSObject Window
w forall a b. (a -> b) -> a -> b
$ case Maybe String
namespace of
Maybe String
Nothing -> forall a. FFI a => String -> a
ffi String
"document.createElement(%1)" String
tag
Just String
ns -> forall a. FFI a => String -> a
ffi String
"document.createElementNS(%1,%2)" String
ns String
tag
JSObject -> Window -> IO Element
fromJSObject0 JSObject
el Window
window
delete :: Element -> UI ()
delete :: Element -> UI ()
delete Element
el = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> do
Window -> JSFunction () -> IO ()
JS.runFunction Window
w forall a b. (a -> b) -> a -> b
$ forall a. FFI a => String -> a
ffi String
"$(%1).detach()" Element
el
forall a. RemotePtr a -> IO ()
Foreign.destroy forall a b. (a -> b) -> a -> b
$ Element -> JSObject
toJSObject Element
el
clearChildren :: Element -> UI ()
clearChildren :: Element -> UI ()
clearChildren Element
element = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> do
let el :: JSObject
el = Element -> JSObject
toJSObject Element
element
forall a b. RemotePtr a -> (Coupon -> a -> IO b) -> IO b
Foreign.withRemotePtr JSObject
el forall a b. (a -> b) -> a -> b
$ \Coupon
_ JSPtr
_ -> do
Window -> JSFunction () -> IO ()
JS.runFunction Window
w forall a b. (a -> b) -> a -> b
$ forall a. FFI a => String -> a
ffi String
"$(%1).contents().detach()" JSObject
el
forall a. RemotePtr a -> IO ()
Foreign.clearReachable (Element -> Children
elChildren Element
element)
appendChild :: Element -> Element -> UI ()
appendChild :: Element -> Element -> UI ()
appendChild Element
parent Element
child = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> do
forall a b. RemotePtr a -> RemotePtr b -> IO ()
Foreign.addReachable (Element -> Children
elChildren Element
parent) (Element -> JSObject
toJSObject Element
child)
Window -> JSFunction () -> IO ()
JS.runFunction Window
w forall a b. (a -> b) -> a -> b
$ forall a. FFI a => String -> a
ffi String
"$(%1).append($(%2))" (Element -> JSObject
toJSObject Element
parent) (Element -> JSObject
toJSObject Element
child)
newtype UI a = UI { forall a. UI a -> RWST Window [IO ()] () IO a
unUI :: Monad.RWST Window [IO ()] () IO a }
deriving (Typeable)
class (Monad m) => MonadUI m where
liftUI :: UI a -> m a
instance MonadUI UI where
liftUI :: forall a. UI a -> UI a
liftUI = forall a. a -> a
id
liftJSWindow :: (JS.Window -> IO a) -> UI a
liftJSWindow :: forall a. (Window -> IO a) -> UI a
liftJSWindow Window -> IO a
f = UI Window
askWindow forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO forall b c a. (b -> c) -> (a -> b) -> a -> c
. Window -> IO a
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. Window -> Window
jsWindow
instance Functor UI where
fmap :: forall a b. (a -> b) -> UI a -> UI b
fmap a -> b
f = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap a -> b
f forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. UI a -> RWST Window [IO ()] () IO a
unUI
instance Applicative UI where
pure :: forall a. a -> UI a
pure = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (f :: * -> *) a. Applicative f => a -> f a
pure
<*> :: forall a b. UI (a -> b) -> UI a -> UI b
(<*>) = forall (m :: * -> *) a b. Monad m => m (a -> b) -> m a -> m b
ap
instance Monad UI where
return :: forall a. a -> UI a
return = forall (f :: * -> *) a. Applicative f => a -> f a
pure
UI a
m >>= :: forall a b. UI a -> (a -> UI b) -> UI b
>>= a -> UI b
k = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall a b. (a -> b) -> a -> b
$ forall a. UI a -> RWST Window [IO ()] () IO a
unUI UI a
m forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= forall a. UI a -> RWST Window [IO ()] () IO a
unUI forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> UI b
k
instance MonadIO UI where
liftIO :: forall a. IO a -> UI a
liftIO = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) a. MonadIO m => IO a -> m a
liftIO
instance MonadFix UI where
mfix :: forall a. (a -> UI a) -> UI a
mfix a -> UI a
f = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) a. MonadFix m => (a -> m a) -> m a
mfix (forall a. UI a -> RWST Window [IO ()] () IO a
unUI forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> UI a
f)
instance MonadThrow UI where
throwM :: forall e a. Exception e => e -> UI a
throwM = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall (m :: * -> *) e a. (MonadThrow m, Exception e) => e -> m a
throwM
instance MonadCatch UI where
catch :: forall e a. Exception e => UI a -> (e -> UI a) -> UI a
catch UI a
m e -> UI a
f = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) e a.
(MonadCatch m, Exception e) =>
m a -> (e -> m a) -> m a
catch (forall a. UI a -> RWST Window [IO ()] () IO a
unUI UI a
m) (forall a. UI a -> RWST Window [IO ()] () IO a
unUI forall b c a. (b -> c) -> (a -> b) -> a -> c
. e -> UI a
f)
runUI :: Window -> UI a -> IO a
runUI :: forall a. Window -> UI a -> IO a
runUI Window
window UI a
m = do
(a
a, ()
_, [IO ()]
actions) <- forall r w s (m :: * -> *) a.
RWST r w s m a -> r -> s -> m (a, s, w)
Monad.runRWST (forall a. UI a -> RWST Window [IO ()] () IO a
unUI UI a
m) Window
window ()
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, Monad m) =>
t (m a) -> m ()
sequence_ [IO ()]
actions
forall (m :: * -> *) a. Monad m => a -> m a
return a
a
askWindow :: UI Window
askWindow :: UI Window
askWindow = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall w (m :: * -> *) r s. (Monoid w, Monad m) => RWST r w s m r
Monad.ask
liftIOLater :: IO () -> UI ()
liftIOLater :: IO () -> UI ()
liftIOLater IO ()
x = forall a. RWST Window [IO ()] () IO a -> UI a
UI forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) w r s. Monad m => w -> RWST r w s m ()
Monad.tell [IO ()
x]
runFunction :: JSFunction () -> UI ()
runFunction :: JSFunction () -> UI ()
runFunction JSFunction ()
fun = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> Window -> JSFunction () -> IO ()
JS.runFunction Window
w JSFunction ()
fun
callFunction :: JSFunction a -> UI a
callFunction :: forall a. JSFunction a -> UI a
callFunction JSFunction a
fun = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> forall a. Window -> JSFunction a -> IO a
JS.callFunction Window
w JSFunction a
fun
setCallBufferMode :: CallBufferMode -> UI ()
setCallBufferMode :: CallBufferMode -> UI ()
setCallBufferMode CallBufferMode
x = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> Window -> CallBufferMode -> IO ()
JS.setCallBufferMode Window
w CallBufferMode
x
flushCallBuffer :: UI ()
flushCallBuffer :: UI ()
flushCallBuffer = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> Window -> IO ()
JS.flushCallBuffer Window
w
ffiExport :: JS.IsHandler a => a -> UI JSObject
ffiExport :: forall a. IsHandler a => a -> UI JSObject
ffiExport a
fun = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> do
JSObject
handlerPtr <- forall a. IsHandler a => Window -> a -> IO JSObject
JS.exportHandler Window
w a
fun
forall a b. RemotePtr a -> RemotePtr b -> IO ()
Foreign.addReachable (Window -> Children
JS.root Window
w) JSObject
handlerPtr
forall (m :: * -> *) a. Monad m => a -> m a
return JSObject
handlerPtr
debug :: String -> UI ()
debug :: String -> UI ()
debug String
s = forall a. (Window -> IO a) -> UI a
liftJSWindow forall a b. (a -> b) -> a -> b
$ \Window
w -> Window -> String -> IO ()
JS.debug Window
w String
s
timestamp :: UI ()
timestamp :: UI ()
timestamp = forall a. (Window -> IO a) -> UI a
liftJSWindow Window -> IO ()
JS.timestamp