react-flux 1.1.1 → 1.2.0
raw patch · 12 files changed
+286/−230 lines, 12 files
Files
- ChangeLog.md +12/−1
- example/README.md +1/−1
- example/purecss-side-menu/index.html +2/−2
- example/routing/route-example.html +2/−1
- example/todo/todo-dev.html +2/−2
- example/todo/todo.html +2/−2
- jsbits/ajax.js +13/−2
- react-flux.cabal +2/−1
- src/React/Flux.hs +10/−2
- src/React/Flux/Ajax.hs +238/−0
- src/React/Flux/Combinators.hs +0/−214
- test/client/test-client.html +2/−2
ChangeLog.md view
@@ -1,3 +1,14 @@+# 1.2.0++* (Breaking Change) Add timeout support to the AJAX functionality. This has three changes. First, the AJAX code moved+ into it's own module `React.Flux.Ajax` which is not re-exported by `React.Flux`. Second, the+ `AjaxRequest` has a new entry `reqTimeout` to optionally specify a timeout value. Finally, the+ `jsonAjax` function now takes a `RequestTimeout` as the first parameter.++* `reactRender` and `reactRenderToString` now error when executed if you call them when compiling with GHC+ instead of GHCJS. These functions only work when using GHCJS to compile because for the actual rendering+ we rely on the React javascript code. In addition, there are a few haddock updates about this change.+ # 1.1.1 * When compiling with GHC (not GHCJS), add fake instances for FromJSVal to allow more programs to compile@@ -15,7 +26,7 @@ (from the `Data.JSString` module in `ghcjs-base`). If a value was just going to be passed straight into react, I made it a JSString and if the value was intended to be manipulated in Haskell I used Text. The main changes are:- + * `elemText` used to (confusingly) take a `String`. Now there are three functions: `elemString`, `elemText`, and `elemJSString` that take a `String`, `Text`, and `JSString` value respectively.
example/README.md view
@@ -36,7 +36,7 @@ ~~~ cd example/todo-npm install react@0.14.6+npm install react@15.3.0 react-dom@15.3.0 node run-in-node.js ~~~
@@ -9,8 +9,8 @@ </head> <body> <div id="side-menu-app"/>- <script src="https://fb.me/react-15.1.0.min.js"></script>- <script src="https://fb.me/react-dom-15.1.0.min.js"></script>+ <script src="https://fb.me/react-15.3.0.min.js"></script>+ <script src="https://fb.me/react-dom-15.3.0.min.js"></script> <script src="purecss-side-menu.js"></script> </body> </html>
example/routing/route-example.html view
@@ -7,7 +7,8 @@ </head> <body> <section id="route-example"/>- <script src="https://fb.me/react-0.13.3.js"></script>+ <script src="https://fb.me/react-15.3.0.js"></script>+ <script src="https://fb.me/react-dom-15.3.0.js"></script> <script src="../../js-build/install-root/bin/route-example.jsexe/all.js"></script> </body> </html>
example/todo/todo-dev.html view
@@ -7,8 +7,8 @@ </head> <body> <section id="todoapp"/>- <script src="https://fb.me/react-0.15.1.js"></script>- <script src=" https://fb.me/react-dom-0.15.1.js"></script>+ <script src="https://fb.me/react-15.3.0.js"></script>+ <script src=" https://fb.me/react-dom-15.3.0.js"></script> <script src="../../js-build/install-root/bin/todo.jsexe/all.js"></script> </body> </html>
example/todo/todo.html view
@@ -7,8 +7,8 @@ </head> <body> <section id="todoapp"/>- <script src="https://fb.me/react-15.0.1.js"></script>- <script src="https://fb.me/react-dom-15.0.1.js"></script>+ <script src="https://fb.me/react-15.3.0.js"></script>+ <script src="https://fb.me/react-dom-15.3.0.js"></script> <script src="../../js-build/todo.min.js"></script> </body> </html>
jsbits/ajax.js view
@@ -8,14 +8,25 @@ function hsreact$ajax(req, handler) { var xhr = new XMLHttpRequest();+ xhr['open'](req['reqMethod'], req['reqURI']);+ var lst = req['reqHeaders']; for (var i = 0; i < lst.length; i++) { xhr['setRequestHeader'](lst[i][0], lst[i][1]); }++ if (req['reqTimeout']) {+ xhr['timeout'] = req['reqTimeout'];+ xhr['ontimeout'] = function() {+ hsreact$ajaxCallback(xhr, {hs: handler, timedOut: true});+ }+ }+ xhr['onreadystatechange'] = function() {- if (xhr['readyState'] === XMLHttpRequest['DONE']) {- hsreact$ajaxCallback(xhr, {hs: handler});+ // a status of 0 with DONE occurs when the request is timed out+ if (xhr['readyState'] === XMLHttpRequest['DONE'] && xhr['status'] !== 0) {+ hsreact$ajaxCallback(xhr, {hs: handler, timedOut: false}); } }; xhr['send'](req['reqBody']);
react-flux.cabal view
@@ -1,5 +1,5 @@ name: react-flux-version: 1.1.1+version: 1.2.0 synopsis: A binding to React based on the Flux application architecture for GHCJS category: Web homepage: https://bitbucket.org/wuzzeb/react-flux@@ -60,6 +60,7 @@ React.Flux.PropertiesAndEvents React.Flux.Combinators React.Flux.Lifecycle+ React.Flux.Ajax React.Flux.Internal React.Flux.Addons.React React.Flux.Addons.Bootstrap
src/React/Flux.hs view
@@ -131,6 +131,8 @@ ---------------------------------------------------------------------------------------------------- -- | Render your React application into the DOM. Use this from your @main@ function, and only in the browser.+-- 'reactRender' only works when compiled with GHCJS (not GHC), because we rely on the React javascript code+-- to actually perform the rendering. reactRender :: Typeable props => String -- ^ The ID of the HTML element to render the application into. -- (This string is passed to @document.getElementById@)@@ -150,13 +152,19 @@ #else -reactRender _ _ _ = return ()+reactRender _ _ _ = error "reactRender only works when compiled with GHCJS, because we rely on the javascript React code." #endif -- | Render your React application to a string using either @ReactDOMServer.renderToString@ if the first -- argument is false or @ReactDOMServer.renderToStaticMarkup@ if the first argument is true. -- Use this only on the server when running with node.+-- 'reactRenderToString' only works when compiled with GHCJS (not GHC), because we rely on the React javascript code+-- to actually perform the rendering.+--+-- If you are interested in isomorphic React, I suggest instead of using 'reactRenderToString' you use+-- 'exportViewToJavaScript' and then write a small top-level JavaScript view which can then integrate with+-- all the usual isomorphic React tools. reactRenderToString :: Typeable props => Bool -- ^ Render to static markup? If true, this won't create extra DOM attributes -- that React uses internally.@@ -184,7 +192,7 @@ #else -reactRenderToString _ _ _ = return ""+reactRenderToString _ _ _ = error "reactRenderToString only works when compiled with GHCJS, because we rely on the javascript React code." #endif
+ src/React/Flux/Ajax.hs view
@@ -0,0 +1,238 @@+module React.Flux.Ajax (+ initAjax+ , RequestTimeout(..)+ , jsonAjax+ , AjaxRequest(..)+ , AjaxResponse(..)+ , ajax+ ) where++import Data.Aeson+import Data.Text (Text)+import Data.Typeable (Typeable)+import GHC.Generics (Generic)+import React.Flux.Internal+import React.Flux.Store+import React.Flux.Views++#ifdef __GHCJS__+import Control.Arrow ((***))+import Control.DeepSeq (deepseq)+import GHCJS.Foreign.Callback+import GHCJS.Foreign (jsNull)+import GHCJS.Marshal (ToJSVal(..), toJSVal_aeson, FromJSVal(..))+import GHCJS.Types (JSVal)+import qualified Data.Text as T+import qualified Data.JSString.Text as JSS++import React.Flux.Export+#endif++-- | An optional timeout to use for @XMLHttpRequest.timeout@.+-- When a request times out, a status code of 504 is set in 'respStatus' and the+-- response handler executes.+data RequestTimeout = TimeoutMilliseconds Int | NoTimeout++instance ToJSVal RequestTimeout where+ toJSVal (TimeoutMilliseconds i) = toJSVal i+ toJSVal NoTimeout = pure jsNull++-- | The input to an AJAX request built using @XMLHttpRequest@.+data AjaxRequest = AjaxRequest+ { reqMethod :: JSString+ , reqURI :: JSString+ , reqTimeout :: RequestTimeout+ , reqHeaders :: [(JSString, JSString)]+ , reqBody :: JSVal+ } deriving (Typeable, Generic)++-- | The response after @XMLHttpRequest@ indicates that the @readyState@ is done.+data AjaxResponse = AjaxResponse+ { respStatus :: Int+ , respResponseText :: JSString+ , respResponseXHR :: JSVal -- ^ The raw @XMLHttpRequest@ object. Use this if you want more status about the response,+ -- such as response headers.+ } deriving (Typeable, Generic)++#ifdef __GHCJS__+-- | GHCJS panics if we export the function directly, so wrap it in a data struct+data HandlerWrapper = HandlerWrapper (AjaxResponse -> IO [SomeStoreAction])+ deriving Typeable++-- | This is broken out as a separate function because if the code is inlined (either manually or by+-- ghc), there is a runtime error.+useHandler :: AjaxResponse -> HandlerWrapper -> IO ()+useHandler r (HandlerWrapper h) = do+ actions <- h r+ actions `deepseq` mapM_ executeAction actions+{-# NOINLINE useHandler #-} -- if this is inlined, there is a bug in ghcjs+#endif++-- | If you are going to use 'ajax' or 'jsonAjax', you must call 'initAjax' once from your main+-- function. The call should appear before the call to 'reactRender'.+initAjax :: IO ()+#ifdef __GHCJS__+initAjax = do+ return ()+ a <- asyncCallback2 $ \rawXhr h -> do+ resp <- if js_reqTimedOut h+ then return $ AjaxResponse 504 "Timeout" rawXhr+ else AjaxResponse <$> js_xhrStatus rawXhr <*> js_xhrResponseText rawXhr <*> pure rawXhr+ e <- js_getHandlerWrapper h+ js_release e+ parseExport e >>= useHandler resp++ js_setAjaxCallback a+#else+initAjax = return ()+#endif++-- | Use @XMLHttpRequest@ to send a request to the backend. Once the response arrives+-- and the @readyState@ is done, the response will be passed to the given handler and the resulting+-- actions will be executed. Note that 'ajax' returns immedietly and does not wait for the request+-- to finish.+ajax :: AjaxRequest -> (AjaxResponse -> IO [SomeStoreAction]) -> IO ()+#ifdef __GHCJS__+ajax req handler = do+ reqJson <- toJSVal req+ handlerE <- export $ HandlerWrapper handler+ js_ajax reqJson handlerE+#else+ajax _ _ = return ()+#endif++-- | Use @XMLHttpRequest@ to send a request with a JSON body, parse the response body as JSON, and+-- then dispatch some actions with the response. This should be used from within the transform+-- function of your store. For example,+--+-- >data Target = AlienShip | AlienPlanet+-- > deriving (Show, Typeable, Generic, ToJSON, FromJSON)+-- >+-- >data Trajectory = Trajectory+-- > { x :: Double, y :: Double, z :: Double, vx :: Double, vy :: Double, vz :: Double }+-- > deriving (Show, Typeable, Generic, ToJSON, FromJSON)+-- >+-- >data UpdatePending = NoUpdatePending | UpdatePending Text | PreviousUpdateHadError Text+-- >+-- >data MyStore = MyStore { currentTrajectory :: Maybe Trajectory, launchUpdate :: UpdatePending }+-- >+-- >data MyStoreAction = LaunchTheMissiles Target+-- > | MissilesLaunched Trajectory+-- > | UnableToLaunchMissiles Text+-- > deriving (Typeable, Generic, NFData)+-- >+-- >instance StoreData MyStore where+-- > type StoreAction MyStore = MyStoreAction+-- >+-- > transform (LaunchTheMissiles t) s = do+-- > jsonAjax NoTimeout "PUT" "/launch-the-missiles" [] t $ \case+-- > Left (_, msg) -> return [SomeStoreAction myStore $ UnableToLaunchMissiles msg]+-- > Right traj -> return [SomeStoreAction myStore $ MissilesLaunched traj]+-- > return s { launchUpdate = UpdatePending ("Requesting missle launch against " ++ T.pack (show t)) }+-- >+-- > transform (MissilesLaunched traj) s =+-- > return s { currentTrajectory = Just traj, launchUpdate = NoUpdatePending }+-- >+-- > transform (UnableToLaunchMissiles err) s =+-- > return s { launchUpdate = PreviousUpdateHadError err }+-- >+-- >myStore :: ReactStore MyStore+-- >myStore = mkStore $ MyStore Nothing NoUpdatePending+--+-- And then in your view, you can render this using something like:+--+-- >myView :: ReactView ()+-- >myView = defineControllerView "launch the missles" myStore $ \s () -> do+-- > case launchUpdate s of+-- > NoUpdatePending -> return ()+-- > UpdatePending msg -> span_ $ faIcon_ "rocket" <> elemText msg+-- > PreviousUpdateHadErroer err -> span_ $ faIcon_ "exclamation" <> elemText err+-- > clbutton_ ["pure-button button-success"] ([SomeStoreAction myStore $ LaunchTheMissiles AlienShip]) $ do+-- > faIcon_ "rocket"+-- > "Launch the missles against the alien ship!"+-- > p_ $ elemString $ "Current trajectory " ++ show (currentTrajectory s)+--+jsonAjax :: (ToJSON body, FromJSON response)+ => RequestTimeout+ -> Text -- ^ the method+ -> Text -- ^ the URI+ -> [(Text, Text)] -- ^ the headers. In addition to these headers, 'jsonAjax' adds two headers:+ -- @Content-Type: application/json@ and @Accept: application/json@.+ -> body -- ^ the body+ -> (Either (Int, Text) response -> IO [SomeStoreAction])+ -- ^ Once @XMLHttpRequest@ changes the @readyState@ to done this handler will be+ -- executed and the resulting actions dispatched to the stores.+ --+ -- * If the response status is @200@, the body will be parsed as JSON and a+ -- 'Right' value will be passed to this handler. If there is an+ -- error parsing the JSON response, a 'Left' value with @500@ and the error message+ -- from aeson is given to the handler. + --+ -- * If the response status is anything besides @200@, a 'Left' value with a pair+ -- of the response status and response text is passed to the handler.+ -> IO ()+#ifdef __GHCJS__+jsonAjax timeout method uri headers body handler = do+ bodyRef <- toJSVal_aeson body >>= js_JSONstringify+ let extraHeaders = [("Content-Type", "application/json"), ("Accept", "application/json")]+ let req = AjaxRequest+ { reqMethod = JSS.textToJSString method+ , reqURI = JSS.textToJSString uri+ , reqHeaders = extraHeaders ++ map (JSS.textToJSString *** JSS.textToJSString) headers+ , reqTimeout = timeout+ , reqBody = bodyRef+ }+ ajax req $ \resp ->+ if respStatus resp == 200+ then do+ j <- js_JSONParse $ respResponseText resp+ mv <- fromJSVal j+ case mv of+ Nothing -> handler $ Left (500, "Unable to convert response body")+ Just v -> case fromJSON v of+ Success v' -> handler $ Right v'+ Error e -> handler $ Left (500, T.pack e)+ else handler $ Left (respStatus resp, JSS.textFromJSString $ respResponseText resp)+#else+jsonAjax _ _ _ _ _ = return ()+#endif++#ifdef __GHCJS__+foreign import javascript unsafe+ "hsreact$ajax($1, $2)"+ js_ajax :: JSVal -> Export HandlerWrapper -> IO ()++foreign import javascript unsafe+ "hsreact$setAjaxCallback($1)"+ js_setAjaxCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO ()++foreign import javascript unsafe+ "JSON['parse']($1)"+ js_JSONParse :: JSString -> IO JSVal++foreign import javascript unsafe+ "JSON['stringify']($1)"+ js_JSONstringify :: JSVal -> IO JSVal++foreign import javascript unsafe+ "$1.hs"+ js_getHandlerWrapper :: JSVal -> IO (Export HandlerWrapper)++foreign import javascript unsafe+ "$r = $1.timedOut"+ js_reqTimedOut :: JSVal -> Bool++foreign import javascript unsafe+ "$1['status']"+ js_xhrStatus :: JSVal -> IO Int++foreign import javascript unsafe+ "$1['responseText']"+ js_xhrResponseText :: JSVal -> IO JSString++foreign import javascript unsafe+ "h$release($1)"+ js_release :: Export HandlerWrapper -> IO ()++instance ToJSVal AjaxRequest+#endif
src/React/Flux/Combinators.hs view
@@ -9,36 +9,17 @@ , foreign_ , labeledInput_ , style- -- * Ajax- , initAjax- , jsonAjax- , AjaxRequest(..)- , AjaxResponse(..)- , ajax ) where -import Data.Aeson import Data.Monoid ((<>))-import Data.Text (Text)-import Data.Typeable (Typeable)-import GHC.Generics (Generic) import React.Flux.DOM import React.Flux.Internal import React.Flux.PropertiesAndEvents-import React.Flux.Store import React.Flux.Views #ifdef __GHCJS__-import Control.Arrow ((***))-import Control.DeepSeq (deepseq)-import GHCJS.Foreign.Callback-import GHCJS.Marshal (ToJSVal(..), toJSVal_aeson, FromJSVal(..)) import GHCJS.Types (JSVal)-import qualified Data.Text as T-import qualified Data.JSString.Text as JSS -import React.Flux.Export- foreign import javascript unsafe "$r = window[$1]" js_lookupWindow :: JSString -> JSVal@@ -132,198 +113,3 @@ -- on elements. style :: [(JSString,JSString)] -> PropertyOrHandler handler style = nestedProperty "style" . map (\(n,a) -> (n &= a))-------------------------------------------------------------------------------------- Ajax------------------------------------------------------------------------------------- | The input to an AJAX request built using @XMLHttpRequest@.-data AjaxRequest = AjaxRequest- { reqMethod :: JSString- , reqURI :: JSString- , reqHeaders :: [(JSString, JSString)]- , reqBody :: JSVal- } deriving (Typeable, Generic)---- | The response after @XMLHttpRequest@ indicates that the @readyState@ is done.-data AjaxResponse = AjaxResponse- { respStatus :: Int- , respResponseText :: JSString- , respResponseXHR :: JSVal -- ^ The raw @XMLHttpRequest@ object. Use this if you want more status about the response,- -- such as response headers.- } deriving (Typeable, Generic)--#ifdef __GHCJS__--- | GHCJS panics if we export the function directly, so wrap it in a data struct-data HandlerWrapper = HandlerWrapper (AjaxResponse -> IO [SomeStoreAction])- deriving Typeable---- | This is broken out as a separate function because if the code is inlined (either manually or by--- ghc), there is a runtime error.-useHandler :: AjaxResponse -> HandlerWrapper -> IO ()-useHandler r (HandlerWrapper h) = do- actions <- h r- actions `deepseq` mapM_ executeAction actions-{-# NOINLINE useHandler #-} -- if this is inlined, there is a bug in ghcjs-#endif---- | If you are going to use 'ajax' or 'jsonAjax', you must call 'initAjax' once from your main--- function. The call should appear before the call to 'reactRender'.-initAjax :: IO ()-#ifdef __GHCJS__-initAjax = do- return ()- a <- asyncCallback2 $ \rawXhr h -> do- resp <- AjaxResponse <$> js_xhrStatus rawXhr <*> js_xhrResponseText rawXhr <*> pure rawXhr- e <- js_getHandlerWrapper h- js_release e- parseExport e >>= useHandler resp-- js_setAjaxCallback a-#else-initAjax = return ()-#endif---- | Use @XMLHttpRequest@ to send a request to the backend. Once the response arrives--- and the @readyState@ is done, the response will be passed to the given handler and the resulting--- actions will be executed. Note that 'ajax' returns immedietly and does not wait for the request--- to finish.-ajax :: AjaxRequest -> (AjaxResponse -> IO [SomeStoreAction]) -> IO ()-#ifdef __GHCJS__-ajax req handler = do- reqJson <- toJSVal req- handlerE <- export $ HandlerWrapper handler- js_ajax reqJson handlerE-#else-ajax _ _ = return ()-#endif---- | Use @XMLHttpRequest@ to send a request with a JSON body, parse the response body as JSON, and--- then dispatch some actions with the response. This should be used from within the transform--- function of your store. For example,------ >data Target = AlienShip | AlienPlanet--- > deriving (Show, Typeable, Generic, ToJSON, FromJSON)--- >--- >data Trajectory = Trajectory--- > { x :: Double, y :: Double, z :: Double, vx :: Double, vy :: Double, vz :: Double }--- > deriving (Show, Typeable, Generic, ToJSON, FromJSON)--- >--- >data UpdatePending = NoUpdatePending | UpdatePending Text | PreviousUpdateHadError Text--- >--- >data MyStore = MyStore { currentTrajectory :: Maybe Trajectory, launchUpdate :: UpdatePending }--- >--- >data MyStoreAction = LaunchTheMissiles Target--- > | MissilesLaunched Trajectory--- > | UnableToLaunchMissiles Text--- > deriving (Typeable, Generic, NFData)--- >--- >instance StoreData MyStore where--- > type StoreAction MyStore = MyStoreAction--- >--- > transform (LaunchTheMissiles t) s = do--- > jsonAjax "PUT" "/launch-the-missiles" [] t $ \case--- > Left (_, msg) -> return [SomeStoreAction myStore $ UnableToLaunchMissiles msg]--- > Right traj -> return [SomeStoreAction myStore $ MissilesLaunched traj]--- > return s { launchUpdate = UpdatePending ("Requesting missle launch against " ++ T.pack (show t)) }--- >--- > transform (MissilesLaunched traj) s =--- > return s { currentTrajectory = Just traj, launchUpdate = NoUpdatePending }--- >--- > transform (UnableToLaunchMissiles err) s =--- > return s { launchUpdate = PreviousUpdateHadError err }--- >--- >myStore :: ReactStore MyStore--- >myStore = mkStore $ MyStore Nothing NoUpdatePending------ And then in your view, you can render this using something like:--- --- >myView :: ReactView ()--- >myView = defineControllerView "launch the missles" myStore $ \s () -> do--- > case launchUpdate s of--- > NoUpdatePending -> return ()--- > UpdatePending msg -> span_ $ faIcon_ "rocket" <> elemText (T.unpack msg)--- > PreviousUpdateHadErroer err -> span_ $ faIcon_ "exclamation" <> elemText (T.unpack err)--- > clbutton_ ["pure-button button-success"] ([SomeStoreAction myStore $ LaunchTheMissiles AlienShip]) $ do--- > faIcon_ "rocket"--- > "Launch the missles against the alien ship!"--- > p_ $ elemText $ "Current trajectory " ++ show (currentTrajectory s)----jsonAjax :: (ToJSON body, FromJSON response)- => Text -- ^ the method- -> Text -- ^ the URI- -> [(Text, Text)] -- ^ the headers. In addition to these headers, 'jsonAjax' adds two headers:- -- @Content-Type: application/json@ and @Accept: application/json@.- -> body -- ^ the body- -> (Either (Int, Text) response -> IO [SomeStoreAction])- -- ^ Once @XMLHttpRequest@ changes the @readyState@ to done this handler will be- -- executed and the resulting actions dispatched to the stores.- --- -- * If the response status is @200@, the body will be parsed as JSON and a- -- 'Right' value will be passed to this handler. If there is an- -- error parsing the JSON response, a 'Left' value with @500@ and the error message- -- from aeson is given to the handler. - --- -- * If the response status is anything besides @200@, a 'Left' value with a pair- -- of the response status and response text is passed to the handler.- -> IO ()-#ifdef __GHCJS__-jsonAjax method uri headers body handler = do- bodyRef <- toJSVal_aeson body >>= js_JSONstringify- let extraHeaders = [("Content-Type", "application/json"), ("Accept", "application/json")]- let req = AjaxRequest- { reqMethod = JSS.textToJSString method- , reqURI = JSS.textToJSString uri- , reqHeaders = extraHeaders ++ map (JSS.textToJSString *** JSS.textToJSString) headers- , reqBody = bodyRef- }- ajax req $ \resp ->- if respStatus resp == 200- then do- j <- js_JSONParse $ respResponseText resp- mv <- fromJSVal j- case mv of- Nothing -> handler $ Left (500, "Unable to convert response body")- Just v -> case fromJSON v of- Success v' -> handler $ Right v'- Error e -> handler $ Left (500, T.pack e)- else handler $ Left (respStatus resp, JSS.textFromJSString $ respResponseText resp)-#else-jsonAjax _ _ _ _ _ = return ()-#endif--#ifdef __GHCJS__-foreign import javascript unsafe- "hsreact$ajax($1, $2)"- js_ajax :: JSVal -> Export HandlerWrapper -> IO ()--foreign import javascript unsafe- "hsreact$setAjaxCallback($1)"- js_setAjaxCallback :: Callback (JSVal -> JSVal -> IO ()) -> IO ()--foreign import javascript unsafe- "JSON['parse']($1)"- js_JSONParse :: JSString -> IO JSVal--foreign import javascript unsafe- "JSON['stringify']($1)"- js_JSONstringify :: JSVal -> IO JSVal--foreign import javascript unsafe- "$1.hs"- js_getHandlerWrapper :: JSVal -> IO (Export HandlerWrapper)--foreign import javascript unsafe- "$1['status']"- js_xhrStatus :: JSVal -> IO Int--foreign import javascript unsafe- "$1['responseText']"- js_xhrResponseText :: JSVal -> IO JSString--foreign import javascript unsafe- "h$release($1)"- js_release :: Export HandlerWrapper -> IO ()--instance ToJSVal AjaxRequest-#endif
test/client/test-client.html view
@@ -4,8 +4,8 @@ </head> <body> <div id="app"></div>- <script src="https://fb.me/react-with-addons-15.0.1.js"></script>- <script src="https://fb.me/react-dom-15.0.1.js"></script>+ <script src="https://fb.me/react-with-addons-15.3.0.js"></script>+ <script src="https://fb.me/react-dom-15.3.0.js"></script> <script src="node_modules/react-intl/dist/react-intl.js"></script> <script src="node_modules/react-intl/locale-data/en.js"></script> <script src="../../js-build/install-root/bin/test-client.jsexe/all.js"></script>