jsaddle (empty) → 0.1.1.3
raw patch · 17 files changed
+2090/−0 lines, 17 filesdep +basedep +glibdep +gtksetup-changed
Dependencies added: base, glib, gtk, gtk3, hslogger, jmacro, jsaddle, lens, template-haskell, text, transformers, webkit, webkit-javascriptcore, webkitgtk3, webkitgtk3-javascriptcore
Files
- LICENSE +21/−0
- Setup.lhs +6/−0
- jsaddle.cabal +101/−0
- src/Language/Javascript/JSaddle.hs +81/−0
- src/Language/Javascript/JSaddle/Arguments.hs +58/−0
- src/Language/Javascript/JSaddle/Classes.hs +52/−0
- src/Language/Javascript/JSaddle/Evaluate.hs +79/−0
- src/Language/Javascript/JSaddle/Exception.hs +35/−0
- src/Language/Javascript/JSaddle/JMacro.hs +43/−0
- src/Language/Javascript/JSaddle/Monad.hs +100/−0
- src/Language/Javascript/JSaddle/Object.hs +523/−0
- src/Language/Javascript/JSaddle/PropRef.hs +25/−0
- src/Language/Javascript/JSaddle/Properties.hs +196/−0
- src/Language/Javascript/JSaddle/String.hs +74/−0
- src/Language/Javascript/JSaddle/Types.hs +53/−0
- src/Language/Javascript/JSaddle/Value.hs +502/−0
- tests/TestJSaddle.hs +141/−0
+ LICENSE view
@@ -0,0 +1,21 @@+Copyright (c) 2010-2012 Hamish Mackenzie, Victor Nazarov, Luite Stegeman++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.+
+ Setup.lhs view
@@ -0,0 +1,6 @@+#!/usr/bin/runhaskell +> module Main where+> import Distribution.Simple+> main :: IO ()+> main = defaultMain+
+ jsaddle.cabal view
@@ -0,0 +1,101 @@+name: jsaddle+version: 0.1.1.3+cabal-version: >=1.10+license: MIT+license-file: LICENSE+maintainer: Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+synopsis: High level interface for webkit-javascriptcore+description: This package provides an EDSL for calling JavaScript code using+ the JavaScriptCore engine and low level Haskell bindings+ in the webkit-javascriptcore library <https://github.com/ghcjs/webkit-javascriptcore>.+author: Hamish Mackenzie+data-dir: ""+category: Web, Javascript+build-type: Simple++Source-Repository head+ type: git+ location: https://github.com/ghcjs/jsaddle++flag gtk3+ Description: Use GTK3 rather than GTK2+ Default: True++flag jsffi+ description: Use JavaScriptFFI extension for GHCJS JavaScript output+ default: True++flag webkit+ description: Use WebKitGtk and JavaScriptCore for GHCJS output+ default: False++flag jmacro+ description: Include some JMacro support+ default: True++library+-- if impl(ghcjs) && flag(jsffi)+-- cpp-options: -DUSE_JAVASCRIPTFFI+-- build-depends: ghcjs-base++-- if !impl(ghcjs) || flag(webkit)+ if impl(ghc)+ cpp-options: -DUSE_WEBKIT+ if flag(gtk3)+ build-depends: webkitgtk3 >=0.12.5 && <0.13,+ webkitgtk3-javascriptcore >=0.12.5 && <0.13+ else+ build-depends: webkit >=0.12.5 && <0.13,+ webkit-javascriptcore >=0.12.5 && <0.13++ if flag(jmacro)+ exposed-modules: Language.Javascript.JSaddle.JMacro+ build-depends: jmacro >=0.6.3 && <0.8++ build-depends: template-haskell -any, base <5, lens >=3.8.5 && <4.1,+ text >=0.11.2.3 && <1.2, transformers >=0.3.0.0 && <0.4++ exposed-modules: Language.Javascript.JSaddle+ Language.Javascript.JSaddle.Arguments Language.Javascript.JSaddle.Classes+ Language.Javascript.JSaddle.Evaluate Language.Javascript.JSaddle.Exception+ Language.Javascript.JSaddle.Monad+ Language.Javascript.JSaddle.Object Language.Javascript.JSaddle.Properties+ Language.Javascript.JSaddle.String Language.Javascript.JSaddle.Value+ Language.Javascript.JSaddle.Types+ exposed: True+ buildable: True+ default-language: Haskell2010+ hs-source-dirs: src+ other-modules: Language.Javascript.JSaddle.PropRef++test-suite test-tool+ if flag(jsffi)+ buildable: False++-- if impl(ghcjs) && flag(jsffi)+-- cpp-options: -DUSE_JAVASCRIPTFFI+-- build-depends: ghcjs-base++-- if !impl(ghcjs) || flag(webkit)+ if impl(ghc)+ cpp-options: -DUSE_WEBKIT+ build-depends: glib >=0.12.3.1 && <0.13+ if flag(gtk3)+ build-depends: gtk3 >= 0.12.4.1 && <0.13,+ webkitgtk3 >=0.12.5 && <0.13,+ webkitgtk3-javascriptcore >=0.12.5 && <0.13+ else+ build-depends: gtk >= 0.12.4.1 && <0.13,+ webkit >=0.12.5 && <0.13,+ webkit-javascriptcore >=0.12.5 && <0.13++ if flag(jmacro)+ build-depends: jmacro >=0.6.3 && <0.8++ build-depends: template-haskell -any, base <5, lens >=3.8.5 && <4.1,+ text >=0.11.2.3 && <1.2, transformers >=0.3.0.0 && <0.4,+ hslogger -any, jsaddle ==0.1.1.3+ type: exitcode-stdio-1.0+ main-is: TestJSaddle.hs+ default-language: Haskell2010+ hs-source-dirs: tests
+ src/Language/Javascript/JSaddle.hs view
@@ -0,0 +1,81 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | This package provides an EDSL for calling JavaScript code using+-- the JavaScriptCore engine and low level Haskell bindings+-- in the webkit-javascriptcore library <https://github.com/ghcjs/webkit-javascriptcore>.+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle (+ -- * JSaddle EDSL+ -- | The 'JSM' monad gives us the context for evaluation. In keeping+ -- with JavaScript the EDSL has+ --+ -- * /Weakish typing/ - type classes are used to convert to JSValueRef+ -- and JSObjectRef types+ --+ -- * /Strict evaluation/ - function in the 'JSM' monad can be passed in+ -- place of a value and will evaluated and converted to JSValueRef or+ -- JSObjectRef and then passed on to JavaScript+ --+ -- JSaddle should be used to write wrappers for JavaScript libraries that provide+ -- more type safety.++ -- * Code Examples+ -- | The code examples in this documentation are executed with a 'runjs'+ -- function that executes the example code in the JSM monad and converts+ -- the result to 'Text' with 'valToText'. It also catches unhandled+ -- exceptions with 'catch'. The source code can be found in tests/TestJSaddle.hs+ --+ -- Where it makes sense code examples are given in two forms. One+ -- that uses 'eval' to run a purely JavaScript version and one that+ -- uses more of the JSaddle EDSL feature being demonstated.++ -- * Calling Haskell from JavaScript+ -- | You can call back into haskell from JavaScript using 'fun' to+ -- convert a Haskell function in the JSM monad into a javascript+ -- value.++ -- * JMacro Support+ -- | If you want a more JavaScript like syntax we also have a+ -- JMacro based QuasiQuoter 'evalJM'. This converts your JMacro+ -- code into a string literal and a call to @eval@ to evaluate it+ -- using JavaScriptCore.++ -- * GHCJS Support+ -- | Because it uses webkit-javascriptcore you can compile your JSaddle code to+ -- JavaScript using GHCJS <https://github.com/ghcjs/ghcjs> and run it+ -- in a web browser. Calls to the WebKitGTK+ JavaScriptCore C functions+ -- will be replaced with JavaScript function calls.++ -- * Modules+ module Language.Javascript.JSaddle.Monad+ , module Language.Javascript.JSaddle.Exception+ , module Language.Javascript.JSaddle.Value+ , module Language.Javascript.JSaddle.Arguments+ , module Language.Javascript.JSaddle.Properties+ , module Language.Javascript.JSaddle.Object+ , module Language.Javascript.JSaddle.Evaluate+#ifdef MIN_VERSION_jmacro+ , module Language.Javascript.JSaddle.JMacro+#endif+ , module Language.Javascript.JSaddle.String+) where++import Language.Javascript.JSaddle.Monad+import Language.Javascript.JSaddle.Exception+import Language.Javascript.JSaddle.Value+import Language.Javascript.JSaddle.Arguments+import Language.Javascript.JSaddle.Properties+import Language.Javascript.JSaddle.Object+import Language.Javascript.JSaddle.Evaluate+#ifdef MIN_VERSION_jmacro+import Language.Javascript.JSaddle.JMacro+#endif+import Language.Javascript.JSaddle.String
+ src/Language/Javascript/JSaddle/Arguments.hs view
@@ -0,0 +1,58 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Arguments+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- |+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Arguments (+ MakeArgRefs(..)+) where++import Language.Javascript.JSaddle.Classes+ (MakeValueRef(..), MakeValueRef, MakeArgRefs(..), MakeArgRefs)+import Language.Javascript.JSaddle.Monad (JSM)++instance MakeArgRefs arg => MakeArgRefs (JSM arg) where+ makeArgRefs arg = arg >>= makeArgRefs++instance MakeValueRef arg => MakeArgRefs [arg] where+ makeArgRefs = mapM makeValueRef++instance (MakeValueRef arg1, MakeValueRef arg2) => MakeArgRefs (arg1, arg2) where+ makeArgRefs (arg1, arg2) = do+ rarg1 <- makeValueRef arg1+ rarg2 <- makeValueRef arg2+ return [rarg1, rarg2]++instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3) => MakeArgRefs (arg1, arg2, arg3) where+ makeArgRefs (arg1, arg2, arg3) = do+ rarg1 <- makeValueRef arg1+ rarg2 <- makeValueRef arg2+ rarg3 <- makeValueRef arg3+ return [rarg1, rarg2, rarg3]++instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3, MakeValueRef arg4) => MakeArgRefs (arg1, arg2, arg3, arg4) where+ makeArgRefs (arg1, arg2, arg3, arg4) = do+ rarg1 <- makeValueRef arg1+ rarg2 <- makeValueRef arg2+ rarg3 <- makeValueRef arg3+ rarg4 <- makeValueRef arg4+ return [rarg1, rarg2, rarg3, rarg4]++instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3, MakeValueRef arg4, MakeValueRef arg5) => MakeArgRefs (arg1, arg2, arg3, arg4, arg5) where+ makeArgRefs (arg1, arg2, arg3, arg4, arg5) = do+ rarg1 <- makeValueRef arg1+ rarg2 <- makeValueRef arg2+ rarg3 <- makeValueRef arg3+ rarg4 <- makeValueRef arg4+ rarg5 <- makeValueRef arg5+ return [rarg1, rarg2, rarg3, rarg4, rarg5]+
+ src/Language/Javascript/JSaddle/Classes.hs view
@@ -0,0 +1,52 @@+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Classes+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | These classes are used to make various JavaScript types+-- out of whatever we have. Functions in jsaddle take these as inputs.+-- This alows implicit casting and eager evaluation.+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Classes (+ -- * Type classes to convert Haskell data to JavaScript+ MakeValueRef(..)+ , MakeStringRef(..)+ , MakeArgRefs(..)+ , MakeObjectRef(..)+ , MakePropRef(..)+) where++import Control.Monad.IO.Class (MonadIO)+import Language.Javascript.JSaddle.Types+ (JSObjectRef, JSStringRef, JSValueRef)+import Language.Javascript.JSaddle.Monad (JSM)+import Language.Javascript.JSaddle.PropRef (JSPropRef)++-- | Anything that can be used to make a JavaScript value reference+class MakeValueRef a where+ makeValueRef :: a -> JSM JSValueRef++-- | Anything that can be used to make a JavaScript string reference+class MakeStringRef a where+ makeStringRef :: a -> JSStringRef++-- | Anything that can be used to make a list of JavaScript value+-- references for use as function arguments+class MakeArgRefs this where+ makeArgRefs :: this -> JSM [JSValueRef]++-- | Anything that can be used to make a JavaScript object reference+class MakeObjectRef this where+ makeObjectRef :: this -> JSM JSObjectRef++-- | Anything that can be used to make a JavaScript property reference+class MakePropRef this where+ makePropRef :: this -> JSM JSPropRef+++
+ src/Language/Javascript/JSaddle/Evaluate.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE CPP #-}+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI #-}+#endif+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Evaluate+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | If you just want to run some JavaScript that you have as a string this is+-- you can use 'eval' or 'evaluateScript'.+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Evaluate (+ evaluateScript+ , eval+) where++import Control.Monad.Trans.Reader (ask)+import Control.Monad.IO.Class (MonadIO(..))+import Language.Javascript.JSaddle.Types+ (JSStringRef, JSValueRef, JSObjectRef,+ JSValueRefRef)+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+import GHCJS.Types (nullRef)+#else+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase+ (jsevaluatescript)+import Foreign (nullPtr)+#endif+import Language.Javascript.JSaddle.Exception (rethrow)+import Language.Javascript.JSaddle.Value ()+import Language.Javascript.JSaddle.Object ()+import Language.Javascript.JSaddle.Classes+ (MakeObjectRef(..), MakeStringRef(..))+import Language.Javascript.JSaddle.Monad (JSM)+++-- | Evaluates a script (like eval in java script). Unlike 'eval' this function lets you+-- specify a source URL and starting line number for beter error information.+--+-- >>> testJSaddle $ (evaluateScript "\n\n{" global "FileName" 53 >>= valToText) `catch` \(JSException e) -> array (e,e!"sourceURL", e!"line") >>= valToText+-- SyntaxError: Expected token '}',FileName,55+evaluateScript :: (MakeStringRef script, MakeObjectRef this, MakeStringRef url)+ => script -- ^ JavaScript to evaluate+ -> this+ -> url+ -> Int -- ^ The Line number of the first line of the script+ -> JSM JSValueRef+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+evaluateScript script this url line = liftIO $ js_eval (makeStringRef script)+foreign import javascript unsafe "$r = eval(s);"+ js_eval :: JSStringRef -> IO JSValueRef+#elif defined(USE_WEBKIT)+evaluateScript script this url line = do+ gctxt <- ask+ thisr <- makeObjectRef this+ rethrow $ liftIO . jsevaluatescript gctxt (makeStringRef script) thisr (makeStringRef url) line+#else+evaluateScript = undefined+#endif++-- | Evaluates a script (like eval in java script)+--+-- >>> testJSaddle $ eval "1+1"+-- 2+eval :: MakeStringRef script+ => script -- ^ JavaScript to evaluate+ -> JSM JSValueRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+eval script = evaluateScript script (nullRef::JSObjectRef) (nullRef::JSStringRef) 1+#else+eval script = evaluateScript script (nullPtr::JSObjectRef) (nullPtr::JSStringRef) 1+#endif+
+ src/Language/Javascript/JSaddle/Exception.hs view
@@ -0,0 +1,35 @@+{-# LANGUAGE DeriveDataTypeable #-}+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Exception+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- |+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Exception (+ JSException(..)+ , rethrow+) where++import qualified Control.Exception as E (throwIO, Exception)+import Language.Javascript.JSaddle.Types+ (JSValueRefRef, JSValueRef)+import Data.Typeable (Typeable)+import Language.Javascript.JSaddle.Monad (catchval, JSM)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Text (Text)++data JSException = JSException JSValueRef deriving (Show, Typeable)++instance E.Exception JSException++-- | Catch JavaScript exceptions and rethrow Haskell ones+rethrow :: (JSValueRefRef -> JSM a) -> JSM a+rethrow f = f `catchval` \e -> do+ liftIO . E.throwIO $ JSException e+
+ src/Language/Javascript/JSaddle/JMacro.hs view
@@ -0,0 +1,43 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.JMacro+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | Helper function to make it easy to embed JMacro in your code and+-- use it from JSaddle+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.JMacro (+ evalJM+ , evalJME+) where++import Language.Haskell.TH.Quote (QuasiQuoter(..))+import Language.Haskell.TH+ (varE, appE, Lit(..), Exp(..), mkName, ExpQ)+import Language.Javascript.JMacro (jmacro, jmacroE, renderJs)++-- | Quasi quoter that creates a JavaScript string from JMacro expression+-- and stores it in a string litteral+evalJME :: QuasiQuoter+evalJME = jmacroE {quoteExp = quoteEvalJME}++quoteEvalJME :: String -> ExpQ+quoteEvalJME s =+ appE (varE 'return) [|AppE (VarE $ mkName "Language.Javascript.JSaddle.eval")+ (LitE . StringL . show $ renderJs $(quoteExp jmacroE s))|]++-- | Quasi quoter that creates a JavaScript string from JMacro statement+-- and stores it in a string litteral+evalJM :: QuasiQuoter+evalJM = jmacro {quoteExp = quoteEvalJM}++quoteEvalJM :: String -> ExpQ+quoteEvalJM s =+ appE (varE 'return) [|AppE (VarE $ mkName "Language.Javascript.JSaddle.eval")+ (LitE . StringL . show $ renderJs $(quoteExp jmacro s))|]
+ src/Language/Javascript/JSaddle/Monad.hs view
@@ -0,0 +1,100 @@+{-# LANGUAGE CPP #-}+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Monad+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | JSM monad keeps track of the JavaScript context+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Monad (+ -- * Types+ JSM(..)+ , JSContextRef++ -- * Running JSaddle given a DOM Window+ , runJSaddle+ , runJSaddle_++ -- * Exception Handling+ , catchval+ , catch+) where++import Prelude hiding (catch)+import Control.Monad.Trans.Reader (runReaderT, ask, ReaderT(..))+import Language.Javascript.JSaddle.Types+ (JSValueRefRef, JSValueRef, JSContextRef)+import Control.Monad.IO.Class (MonadIO(..))+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+import GHCJS.Types (isUndefined, isNull)+import GHCJS.Foreign (newArray, indexArray)+#else+import Foreign (nullPtr, alloca)+import Foreign.Storable (Storable(..))+import Graphics.UI.Gtk.WebKit.Types+ (WebView(..))+import Graphics.UI.Gtk.WebKit.WebView+ (webViewGetMainFrame)+import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame+ (webFrameGetGlobalContext)+#endif+import qualified Control.Exception as E (Exception, catch)++-- | The @JSM@ monad keeps track of the JavaScript context.+--+-- Given a @JSM@ function and a 'JSContextRef' you can run the+-- function like this...+--+-- > runReaderT jsmFunction javaScriptContext+--+-- For an example of how to set up WebKitGTK+ see tests/TestJSaddle.hs+type JSM = ReaderT JSContextRef IO++-- | Wrapped version of 'E.catch' that runs in a MonadIO that works+-- a bit better with 'JSM'+catch :: (MonadIO m, E.Exception e)+ => ReaderT r IO b+ -> (e -> ReaderT r IO b)+ -> ReaderT r m b+t `catch` c = do+ r <- ask+ liftIO (runReaderT t r `E.catch` \e -> runReaderT (c e) r)++-- | Handle JavaScriptCore functions that take a JSValueRefRef in order+-- to throw exceptions.+catchval :: (JSValueRefRef -> JSM a) -> (JSValueRef -> JSM a) -> JSM a+catchval f catcher = do+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+ pexc <- liftIO $ newArray+ result <- f pexc+ exc <- liftIO $ indexArray 0 pexc+ if isUndefined exc || isNull exc+ then return result+ else catcher exc+#else+ gctxt <- ask+ liftIO . alloca $ \pexc -> flip runReaderT gctxt $ do+ liftIO $ poke pexc nullPtr+ result <- f pexc+ exc <- liftIO $ peek pexc+ if exc == nullPtr+ then return result+ else catcher exc+#endif++#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+runJSaddle :: w -> JSM a -> IO a+runJSaddle _ f = runReaderT f ()+#else+runJSaddle :: WebView -> JSM a -> IO a+runJSaddle webView f = do+ gctxt <- webViewGetMainFrame webView >>= webFrameGetGlobalContext+ runReaderT f gctxt+#endif++runJSaddle_ w f = runJSaddle w f >> return ()
+ src/Language/Javascript/JSaddle/Object.hs view
@@ -0,0 +1,523 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE Rank2Types #-}+{-# LANGUAGE ForeignFunctionInterface #-}+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+{-# LANGUAGE JavaScriptFFI #-}+#endif+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Object+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | Interface to JavaScript object+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Object (+ JSObjectRef+ , MakeObjectRef(..)++ -- * Property lookup+ , (!)+ , (!!)+ , js+ , JSF(..)+ , jsf+ , js0+ , js1+ , js2+ , js3+ , js4+ , js5+ , jsg++ -- * Setting the value of a property+ , (<#)++ -- * Calling JavaSctipt+ , (#)+ , new+ , call+ , obj++ -- * Calling Haskell From JavaScript+ , function+ , fun+ , JSCallAsFunction(..)+ -- ** Object Constructors++ -- | There is no good way to support calling haskell code as a JavaScript+ -- constructor for the same reason that the return type of+ -- 'JSCallAsFunction' is 'JSUndefined'.+ --+ -- Instead of writing a constructor in Haskell write a function+ -- that takes a continuation. Create the JavaScript object+ -- and pass it to the continuation.++ -- * Arrays+ , array++ -- * Global Object+ , global++ -- * Enumerating Properties+ , propertyNames++ -- * Low level+ , objCallAsFunction+ , objCallAsConstructor+) where++import Prelude hiding ((!!))+import Language.Javascript.JSaddle.Types+ (JSPropertyNameArrayRef, JSStringRef, JSObjectRef, JSValueRefRef,+ JSValueRef, JSContextRef, Index)+import Foreign.C.Types (CSize(..), CULong(..), CUInt(..))+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+import GHCJS.Types (nullRef, castRef, JSArray, JSFun)+import GHCJS.Foreign (newObj, toArray, fromArray, syncCallback2)+import Control.Monad (liftM)+import Control.Applicative ((<$>))+#else+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef+ (jsobjectmake, jspropertynamearraygetnameatindex,+ jspropertynamearraygetcount, jsobjectcopypropertynames,+ jsobjectsetpropertyatindex, jsobjectgetpropertyatindex,+ jsobjectcallasconstructor, jsobjectmakearray,+ jsobjectcallasfunction, jsobjectgetproperty, jsobjectsetproperty,+ JSPropertyAttributes, JSObjectCallAsFunctionCallback,+ jsobjectmakefunctionwithcallback, JSObjectCallAsFunctionCallback')+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef+ (jsvaluemakeundefined)+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSContextRef+ (jscontextgetglobalobject)+import Foreign (peekArray, nullPtr, withArrayLen)+#endif+import Language.Javascript.JSaddle.Exception (rethrow)+import Language.Javascript.JSaddle.Value+ (JSUndefined, valMakeUndefined, valToObject)+import Language.Javascript.JSaddle.PropRef (JSPropRef(..))+import Language.Javascript.JSaddle.Classes+ (MakeValueRef(..), MakeStringRef(..), MakeArgRefs(..), MakePropRef(..),+ MakeObjectRef(..))+import Language.Javascript.JSaddle.Monad+ (JSM)+import Control.Monad.Trans.Reader (runReaderT, ask)+import Control.Monad.IO.Class (MonadIO(..))+import qualified Control.Exception as E (catch)+import Control.Exception (SomeException)+import qualified Data.Text as T (pack)+import Foreign.Storable (Storable(..))+import Language.Javascript.JSaddle.Properties+import Control.Lens+ (IndexPreservingGetter, to, (^.))+import Language.Javascript.JSaddle.String (textToStr)++-- | If we already have a JSObjectRef we are fine+instance MakeObjectRef JSObjectRef where+ makeObjectRef = return++-- | JSObjectRef can be made by evaluating a function in 'JSM' as long+-- as it returns something we can make into a JSObjectRef.+instance MakeObjectRef v => MakeObjectRef (JSM v) where+ makeObjectRef v = v >>= makeObjectRef++-- | Lookup a property based on its name. This function just constructs a JSPropRef+-- the lookup is delayed until we use the JSPropRef. This makes it a bit lazy compared+-- to JavaScript's @.@ operator.+--+-- >>> testJSaddle $ eval "'Hello World'.length"+-- >>> testJSaddle $ val "Hello World" ! "length"+-- 11+(!) :: (MakeObjectRef this, MakeStringRef name)+ => this -- ^ Object to look on+ -> name -- ^ Name of the property to find+ -> JSM JSPropRef -- ^ Property reference+this ! name = do+ rthis <- makeObjectRef this+ return (JSPropRef rthis rname)+ where+ rname = makeStringRef name++-- | Lookup a property based on its index. This function just constructs a JSPropRef+-- the lookup is delayed until we use the JSPropRef. This makes it a bit lazy compared+-- to JavaScript's @[]@ operator.+--+-- >>> testJSaddle $ eval "'Hello World'[6]"+-- >>> testJSaddle $ val "Hello World" !! 6+-- W+(!!) :: (MakeObjectRef this)+ => this -- ^ Object to look on+ -> Index -- ^ Index of the property to lookup+ -> JSM JSPropRef -- ^ Property reference+this !! index = do+ rthis <- makeObjectRef this+ return (JSPropIndexRef rthis index)++-- | Makes a getter for a particular property name.+--+-- > js name = to (!name)+--+-- >>> testJSaddle $ eval "'Hello World'.length"+-- >>> testJSaddle $ val "Hello World" ^. js "length"+-- 11+js :: (MakeObjectRef s, MakeStringRef name)+ => name -- ^ Name of the property to find+ -> IndexPreservingGetter s (JSM JSPropRef)+js name = to (!name)++-- | Java script function applications have this type+type JSF = MakeObjectRef o => IndexPreservingGetter o (JSM JSValueRef)++-- | Handy way to call a function+--+-- > jsf name = js name . to (# args)+--+-- >>> testJSaddle $ val "Hello World" ^. jsf "indexOf" ["World"]+-- 6+jsf :: (MakeStringRef name, MakeArgRefs args) => name -> args -> JSF+jsf name args = function . to (# args)+ where+ function = js name++-- | Handy way to call a function that expects no arguments+--+-- > js0 name = jsf name ()+--+-- >>> testJSaddle $ val "Hello World" ^. js0 "toLowerCase"+-- hello world+js0 :: (MakeStringRef name) => name -> JSF+js0 name = jsf name ()++-- | Handy way to call a function that expects one argument+--+-- > js1 name a0 = jsf name [a0]+--+-- >>> testJSaddle $ val "Hello World" ^. js1 "indexOf" "World"+-- 6+js1 :: (MakeStringRef name, MakeValueRef a0) => name -> a0 -> JSF+js1 name a0 = jsf name [a0]++-- | Handy way to call a function that expects two arguments+js2 :: (MakeStringRef name, MakeValueRef a0, MakeValueRef a1) => name -> a0 -> a1 -> JSF+js2 name a0 a1 = jsf name (a0, a1)++-- | Handy way to call a function that expects three arguments+js3 :: (MakeStringRef name, MakeValueRef a0, MakeValueRef a1, MakeValueRef a2)+ => name -> a0 -> a1 -> a2 -> JSF+js3 name a0 a1 a2 = jsf name (a0, a1, a2)++-- | Handy way to call a function that expects four arguments+js4 :: (MakeStringRef name, MakeValueRef a0, MakeValueRef a1, MakeValueRef a2,+ MakeValueRef a3)+ => name -> a0 -> a1 -> a2 -> a3 -> JSF+js4 name a0 a1 a2 a3 = jsf name (a0, a1, a2, a3)++-- | Handy way to call a function that expects five arguments+js5 :: (MakeStringRef name, MakeValueRef a0, MakeValueRef a1, MakeValueRef a2,+ MakeValueRef a3, MakeValueRef a4)+ => name -> a0 -> a1 -> a2 -> a3 -> a4 -> JSF+js5 name a0 a1 a2 a3 a4 = jsf name (a0, a1, a2, a3, a4)+++-- | Handy way to get and hold onto a reference top level javascript+--+-- >>> testJSaddle $ eval "w = console; w.log('Hello World')"+-- >>> testJSaddle $ do w <- jsg "console"; w ^. js "log" # ["Hello World"]+-- 11+jsg :: MakeStringRef a => a -> JSM JSPropRef+jsg name = global ! name++-- | Call a JavaScript function+--+-- >>> testJSaddle $ eval "'Hello World'.indexOf('World')"+-- >>> testJSaddle $ val "Hello World" ! "indexOf" # ["World"]+-- 6+infixr 2 #+(#) :: (MakePropRef prop, MakeArgRefs args)+ => prop -> args -> JSM JSValueRef+prop # args = do+ rprop <- makePropRef prop+ (this, f) <- objGetProperty' rprop+ rethrow $ objCallAsFunction f this args++-- | Call a JavaScript function+--+-- >>> testJSaddle $ eval "var j = {}; j.x = 1; j.x"+-- >>> testJSaddle $ do {j <- eval "({})"; j!"x" <# 1; j!"x"}+-- 1+infixr 0 <#+(<#) :: (MakePropRef prop, MakeValueRef val)+ => prop -- ^ Property to set+ -> val -- ^ Value to set it to+ -> JSM JSPropRef -- ^ Reference to the property set+prop <# val = do+ p <- makePropRef prop+ objSetProperty p val+ return p++-- | Use this to create a new JavaScript object+--+-- >>> testJSaddle $ new "Date" (2013, 1, 1)+-- Fri Feb 01 2013 00:00:00 GMT+1300 (NZDT)+new :: (MakeObjectRef constructor, MakeArgRefs args)+ => constructor+ -> args+ -> JSM JSValueRef+new constructor args = do+ f <- makeObjectRef constructor+ rethrow $ objCallAsConstructor f args++-- | Call function with a given @this@. In most cases you should use '#'.+--+-- >>> testJSaddle $ eval "(function(){return this;}).apply('Hello', [])"+-- >>> testJSaddle $ do { test <- eval "(function(){return this;})"; call test (val "Hello") () }+-- Hello+call :: (MakeObjectRef function, MakeObjectRef this, MakeArgRefs args)+ => function -> this -> args -> JSM JSValueRef+call function this args = do+ rfunction <- makeObjectRef function+ rthis <- makeObjectRef this+ rethrow $ objCallAsFunction rfunction rthis args++-- | Make an empty object using the default constuctor+--+-- >>> testJSaddle $ eval "var a = {}; a.x = 'Hello'; a.x"+-- >>> testJSaddle $ do { a <- obj; a ^. js "x" <# "Hello"; a ^. js "x" }+-- Hello+obj :: JSM JSObjectRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+obj = liftIO $ newObj+#else+obj = do+ gctxt <- ask+ liftIO $ jsobjectmake gctxt nullPtr nullPtr+#endif++-- | Type used for Haskell functions called from JavaScript.+type JSCallAsFunction = JSValueRef -- ^ Function object+ -> JSValueRef -- ^ this+ -> [JSValueRef] -- ^ Function arguments+ -> JSM JSUndefined -- ^ Only 'JSUndefined' can be returned because+ -- the function may need to be executed in a+ -- different thread. If you need to get a+ -- value out pass in a continuation function+ -- as an argument and invoke it from haskell.++-- | Short hand @::JSCallAsFunction@ so a haskell function can be passed to+-- a to a JavaScipt one.+--+-- >>> testJSaddle $ eval "(function(f) {f('Hello');})(function (a) {console.log(a)})"+-- >>> testJSaddle $ call (eval "(function(f) {f('Hello');})") global [fun $ \ _ _ args -> valToText (head args) >>= (liftIO . putStrLn . T.unpack) ]+-- Hello+-- undefined+fun :: JSCallAsFunction -> JSCallAsFunction+fun = id++#if (!defined(ghcjs_HOST_OS) || !defined(USE_JAVASCRIPTFFI)) && defined(USE_WEBKIT)+foreign import ccall "wrapper"+ mkJSObjectCallAsFunctionCallback :: JSObjectCallAsFunctionCallback' -> IO JSObjectCallAsFunctionCallback+#endif++-- ^ Make a JavaScript function object that wraps a Haskell function.+function :: MakeStringRef name+ => name -- ^ Name of the function+ -> JSCallAsFunction -- ^ Haskell function to call+ -> JSM JSObjectRef -- ^ Returns a JavaScript function object that will+ -- call the Haskell one when it is called+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+function name f = liftIO $ do+ callback <- syncCallback2 False True $ \this args -> do+ rargs <- fromArray args+ runReaderT (f this this rargs) () -- TODO pass function object through+ makeFunctionWithCallback (makeStringRef name) callback+foreign import javascript unsafe "$r = function () { $2(this, arguments); }"+ makeFunctionWithCallback :: JSStringRef -> JSFun (JSValueRef -> JSValueRefRef -> IO ()) -> IO JSObjectRef+#elif defined(USE_WEBKIT)+function name f = do+ gctxt <- ask+ callback <- liftIO $ mkJSObjectCallAsFunctionCallback wrap+ liftIO $ jsobjectmakefunctionwithcallback gctxt (makeStringRef name) callback+ where+ wrap ctx fobj this argc argv exception = do+ args <- peekArray (fromIntegral argc) argv+ (`runReaderT` ctx) $+ f fobj this args >>= makeValueRef+ `E.catch` \(e :: SomeException) -> do+ str <- runReaderT (makeValueRef . T.pack $ show e) ctx+ poke exception str+ jsvaluemakeundefined ctx+#else+function = undefined+#endif++-- | A callback to Haskell can be used as a JavaScript value. This will create+-- an anonymous JavaScript function object. Use 'function' to create one with+-- a name.+instance MakeValueRef JSCallAsFunction where+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+ makeValueRef = function (nullRef::JSStringRef)+#else+ makeValueRef = function (nullPtr::JSStringRef)+#endif++instance MakeArgRefs JSCallAsFunction where+ makeArgRefs f = do+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+ rarg <- function (nullRef::JSStringRef) f+#else+ rarg <- function (nullPtr::JSStringRef) f+#endif+ return [rarg]++makeArray :: MakeArgRefs args => args -> JSValueRefRef -> JSM JSObjectRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+makeArray args exceptions = makeArgRefs args >>= liftM castRef . liftIO . toArray+#else+makeArray args exceptions = do+ gctxt <- ask+ rargs <- makeArgRefs args+ liftIO $ withArrayLen rargs $ \ len ptr ->+ jsobjectmakearray gctxt (fromIntegral len) ptr exceptions+#endif++-- | Make an JavaScript array from a list of values+--+-- >>> testJSaddle $ eval "['Hello', 'World'][1]"+-- >>> testJSaddle $ array ["Hello", "World"] !! 1+-- World+-- >>> testJSaddle $ eval "['Hello', null, undefined, true, 1]"+-- >>> testJSaddle $ array ("Hello", JSNull, (), True, 1.0::Double)+-- Hello,,,true,1+array :: MakeArgRefs args => args -> JSM JSObjectRef+array = rethrow . makeArray++-- | JavaScript's global object+global :: JSM JSObjectRef+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+global = liftIO js_window+foreign import javascript unsafe "$r = window"+ js_window :: IO JSObjectRef+#elif defined(USE_WEBKIT)+global = ask >>= (liftIO . jscontextgetglobalobject)+#else+global = undefined+#endif++-- | Get an array containing the property names present on a given object+#if (!defined(ghcjs_HOST_OS) || !defined(USE_JAVASCRIPTFFI)) && defined(USE_WEBKIT)+copyPropertyNames :: MakeObjectRef this => this -> JSM JSPropertyNameArrayRef+copyPropertyNames this = do+ gctxt <- ask+ rthis <- makeObjectRef this+ liftIO $ jsobjectcopypropertynames gctxt rthis++-- | Get the number of names in a property name array+propertyNamesCount :: MonadIO m => JSPropertyNameArrayRef -> m CSize+propertyNamesCount names = liftIO $ jspropertynamearraygetcount names++-- | Get a name out of a property name array+propertyNamesAt :: MonadIO m => JSPropertyNameArrayRef -> CSize -> m JSStringRef+propertyNamesAt names index = liftIO $ jspropertynamearraygetnameatindex names index++-- | Convert property array to a list+propertyNamesList :: MonadIO m => JSPropertyNameArrayRef -> m [JSStringRef]+propertyNamesList names = do+ count <- propertyNamesCount names+ mapM (propertyNamesAt names) $ enumFromTo 0 (count - 1)+#endif++-- | Get a list containing the property names present on a given object+propertyNames :: MakeObjectRef this => this -> JSM [JSStringRef]+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+propertyNames this = makeObjectRef this >>= liftIO . js_propertyNames >>= liftIO . fromArray+foreign import javascript unsafe "$r = []; h$forIn($1, function(n){$r.push(n);})"+ js_propertyNames :: JSObjectRef -> IO (JSArray a)+#elif defined(USE_WEBKIT)+propertyNames this = copyPropertyNames this >>= propertyNamesList+#else+propertyNames = undefined+#endif++-- | Get a list containing references to all the properties present on a given object+properties :: MakeObjectRef this => this -> JSM [JSPropRef]+properties this = propertyNames this >>= mapM (this !)++-- | Call a JavaScript object as function. Consider using '#'.+objCallAsFunction :: MakeArgRefs args+ => JSObjectRef+ -> JSObjectRef+ -> args+ -> JSValueRefRef+ -> JSM JSValueRef+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+objCallAsFunction function this args exceptions = do+ rargs <- makeArgRefs args >>= liftIO . toArray+ liftIO $ js_apply function this rargs exceptions+foreign import javascript unsafe "try { $r = $1.apply($2, $3) } catch(e) { $4[0] = e }"+ js_apply :: JSObjectRef -> JSObjectRef -> JSValueRefRef -> JSValueRefRef -> IO JSValueRef+#elif defined(USE_WEBKIT)+objCallAsFunction function this args exceptions = do+ gctxt <- ask+ rargs <- makeArgRefs args+ liftIO $ withArrayLen rargs $ \ largs pargs ->+ jsobjectcallasfunction gctxt function this (fromIntegral largs) pargs exceptions+#else+objCallAsFunction = undefined+#endif++-- | Call a JavaScript object as a constructor. Consider using 'new'.+objCallAsConstructor :: MakeArgRefs args+ => JSObjectRef+ -> args+ -> JSValueRefRef+ -> JSM JSValueRef+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+objCallAsConstructor function args exceptions = do+ rargs <- makeArgRefs args >>= liftIO . toArray+ liftIO $ js_new function rargs exceptions+foreign import javascript unsafe "\+ try {\+ switch($2.length) {\+ case 0 : $r = new $1();\+ case 1 : $r = new $1($2[0]);\+ case 2 : $r = new $1($2[0],$2[1]);\+ case 3 : $r = new $1($2[0],$2[1],$2[2]);\+ case 4 : $r = new $1($2[0],$2[1],$2[2],$2[3]);\+ case 5 : $r = new $1($2[0],$2[1],$2[2],$2[3],$2[4]);\+ case 6 : $r = new $1($2[0],$2[1],$2[2],$2[3],$2[4],$2[5]);\+ case 6 : $r = new $1($2[0],$2[1],$2[2],$2[3],$2[4],$2[5],$2[6]);\+ default:\+ var ret;\+ var temp = function() {\+ ret = $1.apply(this, $2);\+ };\+ temp.prototype = f.prototype;\+ var i = new temp();\+ $r = ret instanceof Object ? ret : i;\+ }\+ }\+ catch(e) {\+ $3[0] = e;\+ }"+ js_new :: JSObjectRef -> JSValueRefRef -> JSValueRefRef -> IO JSValueRef+#elif defined(USE_WEBKIT)+objCallAsConstructor function args exceptions = do+ gctxt <- ask+ rargs <- makeArgRefs args+ liftIO $ withArrayLen rargs $ \ largs pargs ->+ jsobjectcallasconstructor gctxt function (fromIntegral largs) pargs exceptions+#else+objCallAsConstructor = undefined+#endif+++++
+ src/Language/Javascript/JSaddle/PropRef.hs view
@@ -0,0 +1,25 @@+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.PropRef+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | Implements a reference to a property+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.PropRef (+ JSPropRef(..)+) where++import Language.Javascript.JSaddle.Types+ (JSStringRef, JSObjectRef, Index)++-- | A reference to a property.+-- Implemented as a reference to an object and something to find the property.+data JSPropRef = JSPropRef JSObjectRef JSStringRef -- ^ Object and property name.+ | JSPropIndexRef JSObjectRef Index -- ^ Object and property index.++
+ src/Language/Javascript/JSaddle/Properties.hs view
@@ -0,0 +1,196 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI #-}+#endif+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Properties+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | Low level JavaScript object property access. In most cases you+-- should use "Language.Javascript.JSaddle.Object" instead.+--+-- This module is mostly here to implement functions needed to use+-- 'JSPropRef'.+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Properties (+ -- * Propery Reference+ JSPropRef(..)+ , MakePropRef(..)++ -- * Getting Property Values+ , objGetPropertyByName+ , objGetPropertyAtIndex+ , objGetProperty+ , objGetProperty'+ -- * Setting Property Values+ , objSetPropertyByName+ , objSetPropertyAtIndex+ , objSetProperty+) where++import Control.Applicative ((<$>))+import Language.Javascript.JSaddle.PropRef (JSPropRef(..))+import Language.Javascript.JSaddle.Classes+ (MakePropRef(..), MakeObjectRef(..), MakeValueRef(..),+ MakeArgRefs(..), MakeStringRef(..))+import Language.Javascript.JSaddle.Monad (JSM)+import Language.Javascript.JSaddle.Types+ (JSValueRefRef, JSObjectRef, JSPropertyAttributes,+ Index(..), JSStringRef)+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+#else+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef+ (jsobjectgetpropertyatindex, jsobjectgetproperty,+ jsobjectsetpropertyatindex, jsobjectsetproperty,+ JSPropertyAttributes)+#endif+import Control.Monad.Trans.Reader (ask)+import Control.Monad.IO.Class (MonadIO(..))+import Language.Javascript.JSaddle.Exception (rethrow)+import Language.Javascript.JSaddle.Value (JSValueRef)+import Language.Javascript.JSaddle.Arguments ()+import Language.Javascript.JSaddle.String ()++-- | If we already have a JSPropRef we are fine+instance MakePropRef JSPropRef where+ makePropRef = return++-- | JSPropRef can be made by evaluating a function in 'JSM' as long+-- as it returns something we can make into a JSPropRef.+instance MakePropRef prop => MakePropRef (JSM prop) where+ makePropRef prop = prop >>= makePropRef++-- | We can use a property as an object.+instance MakeObjectRef JSPropRef where+ makeObjectRef = objGetProperty++-- | We can use a property as a value.+instance MakeValueRef JSPropRef where+ makeValueRef = objGetProperty++-- | We can pass a property as the only paramter to a function.+instance MakeArgRefs JSPropRef where+ makeArgRefs p = do+ rarg <- objGetProperty p+ return [rarg]++-- | Get a property value given the object and the name of the property.+objGetPropertyByName :: MakeStringRef name+ => JSObjectRef -- ^ object to find the property on.+ -> name -- ^ name of the property.+ -> JSValueRefRef -- ^ exception if one is raised.+ -> JSM JSValueRef -- ^ returns the property value.+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+objGetPropertyByName this name = liftIO . js_tryGetProp (makeStringRef name) this+foreign import javascript unsafe "try { $r=$2[$1] } catch(e) { $3[0] = e }"+ js_tryGetProp :: JSStringRef -> JSObjectRef -> JSValueRefRef -> IO JSValueRef+#elif defined(USE_WEBKIT)+objGetPropertyByName this name exceptions = do+ gctxt <- ask+ liftIO $ jsobjectgetproperty gctxt this (makeStringRef name) exceptions+#else+objGetPropertyByName = undefined+#endif++-- | Get a property value given the object and the index of the property.+objGetPropertyAtIndex :: JSObjectRef -- ^ object to find the property on.+ -> Index -- ^ index of the property.+ -> JSValueRefRef -- ^ exception if one is raised.+ -> JSM JSValueRef -- ^ returns the property value.+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+objGetPropertyAtIndex this index = liftIO . js_tryIndex index this+foreign import javascript unsafe "try { $r=$2[$1] } catch(e) { $3[0] = e }"+ js_tryIndex :: Index -> JSObjectRef -> JSValueRefRef -> IO JSValueRef+#elif defined(USE_WEBKIT)+objGetPropertyAtIndex this index exceptions = do+ gctxt <- ask+ liftIO $ jsobjectgetpropertyatindex gctxt this index exceptions+#else+objGetPropertyAtIndex = undefined+#endif++-- | Gets the value of a property given a 'JSPropRef'.+objGetProperty :: JSPropRef -- ^ property reference.+ -> JSM JSValueRef -- ^ returns the property value.+objGetProperty (JSPropRef this name ) =+ rethrow $ objGetPropertyByName this name+objGetProperty (JSPropIndexRef this index) =+ rethrow $ objGetPropertyAtIndex this index++-- | This version of 'objGetProperty' is handy when you also need to perform.+-- another operation on the object the property is on.+objGetProperty' :: JSPropRef -- ^ property reference.+ -> JSM (JSObjectRef, JSValueRef) -- ^ returns the object and property value.+objGetProperty' (JSPropRef this name) = do+ p <- rethrow $ objGetPropertyByName this name+ return (this, p)+objGetProperty' (JSPropIndexRef this index) = do+ p <- rethrow $ objGetPropertyAtIndex this index+ return (this, p)+++-- | Set a property value given the object and the name of the property.+objSetPropertyByName :: (MakeStringRef name, MakeValueRef val)+ => JSObjectRef -- ^ object to set the property on.+ -> name -- ^ name of the property.+ -> val -- ^ new value to set the property to.+ -> JSPropertyAttributes -- ^ property attributes to give the property.+ -> JSValueRefRef -- ^ exception if one is raised.+ -> JSM ()+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+objSetPropertyByName this name val attributes exceptions = do+ vref <- makeValueRef val+ liftIO $ js_trySetProp (makeStringRef name) this vref exceptions+foreign import javascript unsafe "try { $2[$1]=$3 } catch(e) { $4[0] = e }"+ js_trySetProp :: JSStringRef -> JSObjectRef -> JSValueRef -> JSValueRefRef -> IO ()+#elif defined(USE_WEBKIT)+objSetPropertyByName this name val attributes exceptions = do+ gctxt <- ask+ vref <- makeValueRef val+ liftIO $ jsobjectsetproperty gctxt this (makeStringRef name) vref attributes exceptions+#else+objSetPropertyByName = undefined+#endif++-- | Set a property value given the object and the index of the property.+objSetPropertyAtIndex :: (MakeValueRef val)+ => JSObjectRef -- ^ object to find property on.+ -> Index -- ^ index of the property.+ -> val -- ^ new value to set the property to.+ -> JSValueRefRef -- ^ exception if one is raised.+ -> JSM ()+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+objSetPropertyAtIndex this index val exceptions = do+ vref <- makeValueRef val+ liftIO $ js_trySetAtIndex index this vref exceptions+foreign import javascript unsafe "try { $2[$1]=$3 } catch(e) { $4[0] = e }"+ js_trySetAtIndex :: Index -> JSObjectRef -> JSValueRef -> JSValueRefRef -> IO ()+#elif defined(USE_WEBKIT)+objSetPropertyAtIndex this index val exceptions = do+ gctxt <- ask+ vref <- makeValueRef val+ liftIO $ jsobjectsetpropertyatindex gctxt this index vref exceptions+#else+objSetPropertyAtIndex = undefined+#endif++-- | Sets the value of a property given a 'JSPropRef'.+objSetProperty :: (MakeValueRef val)+ => JSPropRef -- ^ property reference.+ -> val -- ^ new value to set the property to.+ -> JSM ()+objSetProperty (JSPropRef this name ) val = rethrow $ objSetPropertyByName this name val 0+objSetProperty (JSPropIndexRef this index) val = rethrow $ objSetPropertyAtIndex this index val+++++
+ src/Language/Javascript/JSaddle/String.hs view
@@ -0,0 +1,74 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.String+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | JavaScript string conversion functions+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.String (+ JSStringRef+ , MakeStringRef(..)++ -- * Data.Text helpers+ , strToText+ , textToStr+) where++import Data.Text (Text)+import qualified Data.Text as T (pack)+import Control.Monad.IO.Class (MonadIO(..))+import Language.Javascript.JSaddle.Types (JSStringRef)+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+import GHCJS.Foreign (fromJSString, toJSString)+#else+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef+ (jsstringcreatewithcharacters, jsstringgetcharactersptr,+ jsstringgetlength)+#endif+import qualified Data.Text.Foreign as T (fromPtr)+import Foreign (castPtr)+import Data.Text.Foreign (useAsPtr)+import Language.Javascript.JSaddle.Classes (MakeStringRef(..))+import System.IO.Unsafe (unsafePerformIO)++-- | If we already have a JSStringRef we are fine+instance MakeStringRef JSStringRef where+ makeStringRef = id++instance MakeStringRef Text where+ makeStringRef = textToStr++instance MakeStringRef String where+ makeStringRef = textToStr . T.pack++-- | Convert a JavaScript string to a Haskell 'Text'+strToText :: MonadIO m => JSStringRef -> m Text+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+strToText = return . fromJSString+#else+strToText jsstring = liftIO $ do+ l <- jsstringgetlength jsstring+ p <- jsstringgetcharactersptr jsstring+ T.fromPtr (castPtr p) (fromIntegral l)+#endif++-- | Convert a Haskell 'Text' to a JavaScript string+textToStr :: Text -> JSStringRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+textToStr = toJSString+#else+textToStr text = unsafePerformIO $+ useAsPtr text $ \p l ->+ jsstringcreatewithcharacters (castPtr p) (fromIntegral l)+#endif+++
+ src/Language/Javascript/JSaddle/Types.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE CPP, FlexibleInstances #-}+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Properties+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- |+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Types (+ JSValueRef(..)+ , JSValueRefRef(..)+ , JSObjectRef(..)+ , JSPropertyNameArrayRef(..)+ , JSPropertyAttributes(..)+ , JSContextRef(..)+ , JSStringRef(..)+ , Index(..)+) where++#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+import GHCJS.Types+import Data.Word (Word(..))+#else+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase+ (JSValueRef, JSValueRefRef, JSObjectRef, JSStringRef,+ JSContextRef, JSPropertyNameArrayRef)+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSObjectRef (JSPropertyAttributes)+import Foreign.C (CUInt(..))+#endif++#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+data ValueOrObject+data Object+data PropertyNameArray+type JSValueRef = JSRef ValueOrObject+type JSValueRefRef = JSArray ValueOrObject+type JSObjectRef = JSRef ValueOrObject+type JSPropertyNameArrayRef = JSRef PropertyNameArray+type JSPropertyAttributes = Word+type JSContextRef = ()+type JSStringRef = JSString+type Index = Int++instance Show (JSRef ValueOrObject)+instance Eq (JSRef ValueOrObject)+#else+type Index = CUInt+#endif
+ src/Language/Javascript/JSaddle/Value.hs view
@@ -0,0 +1,502 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeSynonymInstances #-}+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+{-# LANGUAGE ForeignFunctionInterface, JavaScriptFFI #-}+#endif+-----------------------------------------------------------------------------+--+-- Module : Language.Javascript.JSaddle.Value+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- | Deals with JavaScript values. These can be+--+-- * null+--+-- * undefined+--+-- * true | false+--+-- * a double precision floating point number+--+-- * a string+--+-- * an object+--+-----------------------------------------------------------------------------++module Language.Javascript.JSaddle.Value (+ -- * JavaScript value references+ JSValueRef+ , MakeValueRef(..)++ -- * Haskell types for JavaScript values+ , JSNull(..)+ , JSUndefined(..)+ , JSBool(..)+ , JSNumber(..)+ , JSString(..)+ , JSValue(..)++ -- * Converting JavaScript values+ , valToBool+ , valToNumber+ , valToStr+ , valToObject+ , valToText+ , valToJSON++ -- * Make JavaScript values from Haskell ones+ , val+ , valMakeNull+ , valMakeUndefined+ , valMakeBool+ , valMakeNumber+ , valMakeString++ -- * Conver to and from JSValue+ , deRefVal+ , valMakeRef+) where++import Prelude hiding (catch)+import Language.Javascript.JSaddle.Types+ (JSValueRefRef, JSObjectRef, JSStringRef, JSValueRef)+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+import GHCJS.Types (castRef, JSRef(..))+import GHCJS.Foreign (toJSBool, fromJSBool', jsNull, jsUndefined, toJSString)+import GHCJS.Marshal (toJSRef)+#else+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef+ (jsvaluecreatejsonstring, JSType(..), jsvaluegettype,+ jsvaluemakestring, jsvaluemakenumber, jsvaluemakeboolean,+ jsvaluemakeundefined, jsvaluemakenull, jsvaluetoobject,+ jsvaluetostringcopy, jsvaluetonumber, jsvaluetoboolean)+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSStringRef+ (jsstringcreatewithcharacters, jsstringgetcharactersptr,+ jsstringgetlength)+#endif+import Language.Javascript.JSaddle.Monad (JSM, catchval)+import Language.Javascript.JSaddle.Exception (rethrow)+import Control.Monad.Trans.Reader (ask)+import Control.Monad.IO.Class (MonadIO, MonadIO(..))+import qualified Data.Text.Foreign as T (fromPtr)+import Foreign (castPtr)+import Data.Text.Foreign (useAsPtr)+import Control.Applicative ((<$>))+import Data.Text (Text)+import qualified Data.Text as T (pack)+import Language.Javascript.JSaddle.Classes+ (MakeObjectRef(..), MakeStringRef(..), MakeValueRef(..),+ MakeArgRefs(..))+import Language.Javascript.JSaddle.String (strToText, textToStr)+import Language.Javascript.JSaddle.Arguments ()+import Data.Word (Word)++data JSNull = JSNull -- ^ Type that represents a value that can only be null.+ -- Haskell of course has no null so we are adding this type.+type JSUndefined = () -- ^ A type that can only be undefined in JavaScript. Using ()+ -- because functions in JavaScript that have no return, impicitly+ -- return undefined.+type JSBool = Bool -- ^ JavaScript boolean values map the 'Bool' haskell type.+type JSNumber = Double -- ^ A number in JavaScript maps nicely to 'Double'.+type JSString = Text -- ^ JavaScript strings can be represented with the Haskell 'Text' type.++-- | An algebraic data type that can represent a JavaScript value. Any JavaScriptCore+-- 'JSValueRef' can be converted into this type.+data JSValue = ValNull -- ^ null+ | ValUndefined -- ^ undefined+ | ValBool JSBool -- ^ true or false+ | ValNumber JSNumber -- ^ a number+ | ValString JSString -- ^ a string+ | ValObject JSObjectRef -- ^ an object+ deriving(Show, Eq)++-- | Given a JavaScript value get its boolean value.+-- All values in JavaScript convert to bool.+--+-- >>> testJSaddle $ valToBool JSNull+-- false+-- >>> testJSaddle $ valToBool ()+-- false+-- >>> testJSaddle $ valToBool True+-- true+-- >>> testJSaddle $ valToBool False+-- false+-- >>> testJSaddle $ valToBool (1.0 :: Double)+-- true+-- >>> testJSaddle $ valToBool (0.0 :: Double)+-- false+-- >>> testJSaddle $ valToBool ""+-- false+-- >>> testJSaddle $ valToBool "1"+-- true+valToBool :: MakeValueRef val => val -> JSM JSBool+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+valToBool val = fromJSBool' <$> makeValueRef val+#else+valToBool val = do+ gctxt <- ask+ rval <- makeValueRef val+ liftIO $ jsvaluetoboolean gctxt rval+#endif++-- | Given a JavaScript value get its numeric value.+-- May throw JSException.+--+-- >>> testJSaddle $ show <$> valToNumber JSNull+-- 0.0+-- >>> testJSaddle $ show <$> valToNumber ()+-- NaN+-- >>> testJSaddle $ show <$> valToNumber True+-- 1.0+-- >>> testJSaddle $ show <$> valToNumber False+-- 0.0+-- >>> testJSaddle $ show <$> valToNumber (1.0 :: Double)+-- 1.0+-- >>> testJSaddle $ show <$> valToNumber (0.0 :: Double)+-- 0.0+-- >>> testJSaddle $ show <$> valToNumber ""+-- 0.0+-- >>> testJSaddle $ show <$> valToNumber "1"+-- 1.0+valToNumber :: MakeValueRef val => val -> JSM JSNumber+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+valToNumber val = jsrefToNumber <$> makeValueRef val+foreign import javascript unsafe "$r = Number($1);" jsrefToNumber :: JSRef a -> Double+#elif defined(USE_WEBKIT)+valToNumber val = do+ gctxt <- ask+ rval <- makeValueRef val+ rethrow $ liftIO . jsvaluetonumber gctxt rval+#else+valToNumber = undefined+#endif++-- | Given a JavaScript value get its string value (as a JavaScript string).+-- May throw JSException.+--+-- >>> testJSaddle $ valToStr JSNull >>= strToText+-- null+-- >>> testJSaddle $ valToStr () >>= strToText+-- undefined+-- >>> testJSaddle $ valToStr True >>= strToText+-- true+-- >>> testJSaddle $ valToStr False >>= strToText+-- false+-- >>> testJSaddle $ valToStr (1.0 :: Double) >>= strToText+-- 1+-- >>> testJSaddle $ valToStr (0.0 :: Double) >>= strToText+-- 0+-- >>> testJSaddle $ valToStr "" >>= strToText+--+-- >>> testJSaddle $ valToStr "1" >>= strToText+-- 1+valToStr :: MakeValueRef val => val -> JSM JSStringRef+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+valToStr val = jsrefToString <$> makeValueRef val+foreign import javascript unsafe "$r = $1.toString();" jsrefToString :: JSRef a -> JSStringRef+#elif defined(USE_WEBKIT)+valToStr val = do+ gctxt <- ask+ rval <- makeValueRef val+ rethrow $ liftIO . jsvaluetostringcopy gctxt rval+#else+valToStr = undefined+#endif++-- | Given a JavaScript value get its string value (as a Haskell 'Text').+-- May throw JSException.+--+-- >>> testJSaddle $ show <$> valToText JSNull+-- "null"+-- >>> testJSaddle $ show <$> valToText ()+-- "undefined"+-- >>> testJSaddle $ show <$> valToText True+-- "true"+-- >>> testJSaddle $ show <$> valToText False+-- "false"+-- >>> testJSaddle $ show <$> valToText (1.0 :: Double)+-- "1"+-- >>> testJSaddle $ show <$> valToText (0.0 :: Double)+-- "0"+-- >>> testJSaddle $ show <$> valToText ""+-- ""+-- >>> testJSaddle $ show <$> valToText "1"+-- "1"+valToText :: MakeValueRef val => val -> JSM Text+valToText jsvar = valToStr jsvar >>= strToText++-- | Given a JavaScript value get a JSON string value.+-- May throw JSException.+--+-- >>> testJSaddle $ valToJSON 0 JSNull >>= strToText+-- null+-- >>> testJSaddle $ valToJSON 0 () >>= strToText+--+-- >>> testJSaddle $ valToJSON 0 True >>= strToText+-- true+-- >>> testJSaddle $ valToJSON 0 False >>= strToText+-- false+-- >>> testJSaddle $ valToJSON 0 (1.0 :: Double) >>= strToText+-- 1+-- >>> testJSaddle $ valToJSON 0 (0.0 :: Double) >>= strToText+-- 0+-- >>> testJSaddle $ valToJSON 0 "" >>= strToText+-- ""+-- >>> testJSaddle $ valToJSON 0 "1" >>= strToText+-- "1"+-- >>> testJSaddle $ obj >>= valToJSON 0 >>= strToText+-- {}+valToJSON :: MakeValueRef val => Word -> val -> JSM JSStringRef+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+valToJSON indent val = jsrefToJSON <$> makeValueRef val+foreign import javascript unsafe "$r = JSON.stringify($1);" jsrefToJSON :: JSRef a -> JSStringRef+#elif defined(USE_WEBKIT)+valToJSON indent val = do+ gctxt <- ask+ rval <- makeValueRef val+ rethrow $ liftIO . jsvaluecreatejsonstring gctxt rval (fromIntegral indent)+#else+valToJSON = undefined+#endif++-- | Given a JavaScript value get its object value.+-- May throw JSException.+--+-- >>> testJSaddle $ (valToObject JSNull >>= valToText) `catch` \ (JSException e) -> valToText e+-- TypeError: 'null' is not an object+-- >>> testJSaddle $ (valToObject () >>= valToText) `catch` \ (JSException e) -> valToText e+-- TypeError: 'undefined' is not an object+-- >>> testJSaddle $ valToObject True+-- true+-- >>> testJSaddle $ valToObject False+-- false+-- >>> testJSaddle $ valToObject (1.0 :: Double)+-- 1+-- >>> testJSaddle $ valToObject (0.0 :: Double)+-- 0+-- >>> testJSaddle $ valToObject ""+--+-- >>> testJSaddle $ valToObject "1"+-- 1+valToObject :: MakeValueRef val => val -> JSM JSObjectRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+valToObject val = castRef <$> makeValueRef val+#else+valToObject val = do+ gctxt <- ask+ rval <- makeValueRef val+ rethrow $ liftIO . jsvaluetoobject gctxt rval+#endif++-- | Convert to a JavaScript value (just an alias for 'makeValueRef')+val :: MakeValueRef value+ => value -- ^ value to convert to a JavaScript value+ -> JSM JSValueRef+val = makeValueRef++-- | If we already have a JSValueRef we are fine+instance MakeValueRef JSValueRef where+ makeValueRef = return++-- | A single JSValueRef can be used as the argument list+instance MakeArgRefs JSValueRef where+ makeArgRefs arg = return [arg]++-- | JSValueRef can be made by evaluating a function in 'JSM' as long+-- as it returns something we can make into a JSValueRef.+instance MakeValueRef v => MakeValueRef (JSM v) where+ makeValueRef v = v >>= makeValueRef++----------- null ---------------+-- | Make a @null@ JavaScript value+valMakeNull :: JSM JSValueRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+valMakeNull = return jsNull+#else+valMakeNull = ask >>= (liftIO . jsvaluemakenull)+#endif++-- | Makes a @null@ JavaScript value+instance MakeValueRef JSNull where+ makeValueRef = const valMakeNull++-- | Makes an argument list with just a single @null@ JavaScript value+instance MakeArgRefs JSNull where+ makeArgRefs _ = valMakeNull >>= (\ref -> return [ref])++----------- undefined ---------------+-- | Make an @undefined@ JavaScript value+valMakeUndefined :: JSM JSValueRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+valMakeUndefined = return jsUndefined+#else+valMakeUndefined = ask >>= (liftIO . jsvaluemakeundefined)+#endif++-- | Makes an @undefined@ JavaScript value+instance MakeValueRef JSUndefined where+ makeValueRef = const valMakeUndefined++--We can't allow this if JSUndefined is () as () is no args not "(null)".+--Use [()] instead.+--instance MakeArgRefs JSUndefined where+-- makeArgRefs _ = valMakeUndefined >>= (\ref -> return [ref])++-- | This allows us to pass no arguments easily (altenative would be to use @[]::[JSValueRef]@).+instance MakeArgRefs () where+ makeArgRefs _ = return []++----------- booleans ---------------+-- | Make a JavaScript boolean value+valMakeBool :: JSBool -> JSM JSValueRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+valMakeBool b = return . castRef $ toJSBool b+#else+valMakeBool b = do+ gctxt <- ask+ liftIO $ jsvaluemakeboolean gctxt b+#endif++-- | Make a JavaScript boolean value+instance MakeValueRef Bool where+ makeValueRef = valMakeBool++-- | Makes an argument list with just a single JavaScript boolean value+instance MakeArgRefs Bool where+ makeArgRefs b = valMakeBool b >>= (\ref -> return [ref])++----------- numbers ---------------+-- | Make a JavaScript number+valMakeNumber :: JSNumber -> JSM JSValueRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+valMakeNumber n = liftIO $ castRef <$> toJSRef n+#else+valMakeNumber n = do+ gctxt <- ask+ liftIO $ jsvaluemakenumber gctxt n+#endif++-- | Makes a JavaScript number+instance MakeValueRef Double where+ makeValueRef = valMakeNumber++-- | Makes an argument list with just a single JavaScript number+instance MakeArgRefs Double where+ makeArgRefs n = valMakeNumber n >>= (\ref -> return [ref])++----------- numbers ---------------+-- | Make a JavaScript string+valMakeString :: Text -> JSM JSValueRef+#if (defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)) || !defined(USE_WEBKIT)+valMakeString = return . castRef . toJSString+#else+valMakeString text = do+ gctxt <- ask+ liftIO $ jsvaluemakestring gctxt (textToStr text)+#endif++-- | Makes a JavaScript string+instance MakeValueRef Text where+ makeValueRef = valMakeString++-- | Makes an argument list with just a single JavaScript string+instance MakeArgRefs Text where+ makeArgRefs t = valMakeString t >>= (\ref -> return [ref])++-- | Makes a JavaScript string+instance MakeValueRef String where+ makeValueRef = valMakeString . T.pack++-- | Derefernce a value reference.+--+-- >>> testJSaddle $ show <$> deRefVal JSNull+-- ValNull+-- >>> testJSaddle $ show <$> deRefVal ()+-- ValUndefined+-- >>> testJSaddle $ show <$> deRefVal True+-- ValBool True+-- >>> testJSaddle $ show <$> deRefVal False+-- ValBool False+-- >>> testJSaddle $ show <$> deRefVal (1.0 :: Double)+-- ValNumber 1.0+-- >>> testJSaddle $ show <$> deRefVal (0.0 :: Double)+-- ValNumber 0.0+-- >>> testJSaddle $ show <$> deRefVal ""+-- ValString ""+-- >>> testJSaddle $ show <$> deRefVal "1"+-- ValString "1"+-- >>> testJSaddle $ show <$> valToObject True >>= deRefVal+-- ValObject 0x...+deRefVal :: MakeValueRef val => val -> JSM JSValue+#if defined(ghcjs_HOST_OS) && defined(USE_JAVASCRIPTFFI)+deRefVal val = do+ gctxt <- ask+ valref <- makeValueRef val+ case (jsrefGetType valref :: Int) of+ 0 -> return ValUndefined+ 1 -> return ValNull+ 2 -> ValBool <$> valToBool valref+ 3 -> ValNumber <$> valToNumber valref+ 4 -> ValString <$> (valToStr valref >>= strToText)+ 5 -> ValObject <$> valToObject valref+foreign import javascript unsafe "$r = ($1 === undefined)?0:\+ ($1===null)?1:\+ (typeof $1===\"boolean\")?2:\+ (typeof $1===\"number\")?3:\+ (typeof $1===\"string\")?4:\+ (typeof $1===\"object\")?5:-1;" jsrefGetType :: JSValueRef -> Int+#elif defined(USE_WEBKIT)+deRefVal val = do+ gctxt <- ask+ valref <- makeValueRef val+ t <- liftIO $ jsvaluegettype gctxt valref+ case t of+ Kjstypenull -> return ValNull+ Kjstypeundefined -> return ValUndefined+ Kjstypeboolean -> ValBool <$> valToBool valref+ Kjstypenumber -> ValNumber <$> valToNumber valref+ Kjstypestring -> ValString <$> (valToStr valref >>= strToText)+ Kjstypeobject -> ValObject <$> valToObject valref+#else+deRefVal = undefined+#endif++-- | Make a JavaScript value out of a 'JSValue' ADT.+--+-- >>> testJSaddle $ valMakeRef ValNull+-- "null"+-- >>> testJSaddle $ valMakeRef ValUndefined+-- "undefined"+-- >>> testJSaddle $ valMakeRef (ValBool True)+-- "true"+-- >>> testJSaddle $ valMakeRef (ValNumber 1)+-- "1"+-- >>> testJSaddle $ valMakeRef (ValString $ pack "Hello")+-- "Hello"+valMakeRef :: JSValue -> JSM JSValueRef+valMakeRef val =+ case val of+ ValNull -> valMakeNull+ ValUndefined -> valMakeUndefined+ ValBool b -> valMakeBool b+ ValNumber n -> valMakeNumber n+ ValString s -> valMakeString s+ ValObject o -> return o++-- | Makes a JavaScript value from a 'JSValue' ADT.+instance MakeValueRef JSValue where+ makeValueRef = valMakeRef++-- | Makes an argument list with just a single JavaScript value from a 'JSValue' ADT.+instance MakeArgRefs JSValue where+ makeArgRefs v = valMakeRef v >>= (\ref -> return [ref])++instance MakeObjectRef JSNull where+ makeObjectRef = const valMakeNull+
+ tests/TestJSaddle.hs view
@@ -0,0 +1,141 @@+{-# LANGUAGE RecordWildCards #-}+-----------------------------------------------------------------------------+--+-- Module : TestJSC+-- Copyright : (c) Hamish Mackenzie+-- License : MIT+--+-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>+--+-- |+--+-----------------------------------------------------------------------------++module Main (+ main+) where++import Prelude hiding((!!), catch)+import Graphics.UI.Gtk+ (Window, widgetDestroy, postGUIAsync, postGUISync, widgetShowAll,+ mainGUI, mainQuit, on, objectDestroy, containerAdd, scrolledWindowNew,+ windowSetPosition, windowSetDefaultSize, timeoutAddFull, windowNew,+ initGUI)+import Control.Concurrent+ (tryTakeMVar, forkIO, newMVar, putMVar, takeMVar, newEmptyMVar,+ yield)+import System.Glib.MainLoop (priorityHigh)+import Graphics.UI.Gtk.General.Enums (WindowPosition(..))+import Graphics.UI.Gtk.WebKit.WebView+ (webViewGetMainFrame, webViewNew)+import System.IO.Unsafe (unsafePerformIO)+import Control.Monad.Trans.Reader (runReaderT)+import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase (JSContextRef)+import Graphics.UI.Gtk.WebKit.JavaScriptCore.WebFrame+ (webFrameGetGlobalContext)+import Language.Javascript.JSaddle+import Data.Text (Text)+import qualified Data.Text as T+import Control.Applicative+import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad (forM, when)+import System.Log.Logger (debugM)+import Control.Lens.Getter ((^.))+import Data.Monoid ((<>))++data TestState = TestState { jsContext :: JSContextRef, window :: Window }++state = unsafePerformIO $ newMVar Nothing+done = unsafePerformIO $ newEmptyMVar++-- >>> testJSaddle $ ((global ^. js "console" . js "log") # ["Hello"])+testJSaddle :: MakeValueRef val => JSM val -> IO ()+testJSaddle = testJSaddle' False++-- >>> showJSaddle $ eval "document.body.innerHTML = 'Test'"+showJSaddle :: MakeValueRef val => JSM val -> IO ()+showJSaddle = testJSaddle' True++debugLog = debugM "jsaddle"++testJSaddle' :: MakeValueRef val => Bool -> JSM val -> IO ()+testJSaddle' showWindow f = do+ debugLog "taking done"+ tryTakeMVar done+ debugLog "taking state"+ mbState <- takeMVar state+ TestState {..} <- case mbState of+ Nothing -> do+ debugLog "newState"+ newState <- newEmptyMVar+ debugLog "fork"+ forkIO $ do+ debugLog "initGUI"+ initGUI+ debugLog "windowNew"+ window <- windowNew+ debugLog "timeoutAdd"+ timeoutAddFull (yield >> return True) priorityHigh 10+ windowSetDefaultSize window 900 600+ windowSetPosition window WinPosCenter+ scrollWin <- scrolledWindowNew Nothing Nothing+ webView <- webViewNew+ window `containerAdd` scrollWin+ scrollWin `containerAdd` webView+ on window objectDestroy $ do+ debugLog "onDestroy"+ tryTakeMVar state+ debugLog "put state"+ putMVar state Nothing+ debugLog "mainQuit"+ mainQuit+ debugLog "put done"+ putMVar done ()+ return True+ debugLog "get context"+ jsContext <- webViewGetMainFrame webView >>= webFrameGetGlobalContext+ debugLog "put initial state"+ putMVar newState TestState {..}+ debugLog "maybe show"+ when showWindow $ widgetShowAll window+ debugLog "mainGUI"+ mainGUI+ debugLog "mainGUI exited"+ takeMVar newState+ Just s@TestState {..} -> do+ debugLog "maybe show (2)"+ when showWindow . postGUISync $ widgetShowAll window+ return s+ x <- postGUISync $ runReaderT ((f >>= valToText >>= liftIO . putStrLn . T.unpack)+ `catch` \ (JSException e) -> valToText e >>= liftIO . putStrLn . T.unpack) jsContext+ debugLog "put state"+ putMVar state $ Just TestState {..}+ return x++main = do+ testJSaddle $ return ()+ Just TestState{..} <- takeMVar state+ postGUIAsync $ widgetDestroy window+ takeMVar done++listWindowProperties = testJSaddle $ T.pack . show <$> do+ window <- jsg "window"+ names <- propertyNames window+ forM names $ \name -> do+ v <- window ^. js name >>= valToText+ n <- strToText name+ return (n, v)+ `catch`+ \(JSException e) -> do+ n <- strToText name+ msg <- valToText e+ return (n, (T.pack $ " error ") <> msg)+++++++++