@@ -1,7 +1,5 @@
1−{-# LANGUAGE ForeignFunctionInterface
1+{-# LANGUAGE FlexibleInstances #-}
2− , TypeSynonymInstances
2+{-# LANGUAGE TypeSynonymInstances #-}
4− , DeriveDataTypeable #-}
5-----------------------------------------------------------------------------
3-----------------------------------------------------------------------------
7-- Module : Language.Javascript.JSC.Value
5-- Module : Language.Javascript.JSC.Value
@@ -10,38 +8,43 @@
11-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
9-- Maintainer : Hamish Mackenzie <Hamish.K.Mackenzie@googlemail.com>
13−-- |
11+-- | Deals with JavaScript values. These can be
19+-- * a double precision floating point number
15-----------------------------------------------------------------------------
25-----------------------------------------------------------------------------
17module Language.Javascript.JSC.Value (
27module Language.Javascript.JSC.Value (
18− JSNull(..)
28+ -- * JavaScript value references
19− , JSUndefined
29+ JSValueRef
20− , JSBool
30+ , MakeValueRef(..)
25− , JSStringRef
32+ -- * Haskell types for JavaScript values
26− , JSObjectRef
33+ , JSNull(..)
27− , JSValueRef
34+ , JSUndefined(..)
40+ -- * Converting JavaScript values
29 , valToBool
41 , valToBool
30 , valToNumber
42 , valToNumber
31 , valToStr
43 , valToStr
32 , valToObject
44 , valToObject
37 , valToText
45 , valToText
39− , rethrow
47+ -- * Make JavaScript values from Haskell ones
46 , valMakeNull
49 , valMakeNull
47 , valMakeUndefined
50 , valMakeUndefined
@@ -49,6 +52,7 @@ module Language.Javascript.JSC.Value (
49 , valMakeNumber
52 , valMakeNumber
50 , valMakeString
53 , valMakeString
55+ -- * Conver to and from JSValue
52 , deRefVal
56 , deRefVal
53 , valMakeRef
57 , valMakeRef
@@ -56,7 +60,8 @@ module Language.Javascript.JSC.Value (
56import Prelude hiding (catch)
60import Prelude hiding (catch)
57import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
61import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSBase
58 (JSValueRefRef, JSObjectRef, JSStringRef, JSValueRef)
62 (JSValueRefRef, JSObjectRef, JSStringRef, JSValueRef)
59−import Language.Javascript.JSC.Monad (JSC, catch)
63+import Language.Javascript.JSC.Monad (JSC, catchval)
64+import Language.Javascript.JSC.Exception (rethrow)
60import Control.Monad.Trans.Reader (ask)
65import Control.Monad.Trans.Reader (ask)
61import Control.Monad.IO.Class (MonadIO, MonadIO(..))
66import Control.Monad.IO.Class (MonadIO, MonadIO(..))
62import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
67import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
@@ -73,191 +78,273 @@ import Data.Text.Foreign (useAsPtr)
73import Control.Applicative ((<$>))
78import Control.Applicative ((<$>))
74import Data.Text (Text)
79import Data.Text (Text)
75import qualified Data.Text as T (pack)
80import qualified Data.Text as T (pack)
76−import qualified Control.Exception as E (throwIO, Exception)
81+import Language.Javascript.JSC.Classes
77−import Data.Typeable (Typeable)
82+ (MakeObjectRef(..), MakeStringRef(..), MakeValueRef(..),
84+import Language.Javascript.JSC.String (strToText, textToStr)
85+import Language.Javascript.JSC.Arguments ()
79−data JSNull = JSNull
87+data JSNull = JSNull -- ^ Type that represents a value that can only be null.
88+ -- Haskell of course has no null so we are adding this type.
80−type JSUndefined = ()
89+type JSUndefined = () -- ^ A type that can only be undefined in JavaScript. Using ()
81−type JSBool = Bool
90+ -- because functions in JavaScript that have no return, impicitly
92+type JSBool = Bool -- ^ JavaScript boolean values map the 'Bool' haskell type.
82−type JSNumber = Double
93+type JSNumber = Double -- ^ A number in JavaScript maps nicely to 'Double'.
83−type JSString = Text
94+type JSString = Text -- ^ JavaScript strings can be represented with the Haskell 'Text' type.
96+-- | An algebraic data type that can represent a JavaScript value. Any JavaScriptCore
97+-- 'JSValueRef' can be converted into this type.
85−data JSValue = ValNull
98+data JSValue = ValNull -- ^ null
86− | ValUndefined
99+ | ValUndefined -- ^ undefined
87− | ValBool JSBool
100+ | ValBool JSBool -- ^ true or false
88− | ValNumber JSNumber
101+ | ValNumber JSNumber -- ^ a number
89− | ValString JSString
102+ | ValString JSString -- ^ a string
90− | ValObject JSObjectRef deriving(Show, Eq)
103+ | ValObject JSObjectRef -- ^ an object
91−
104+ deriving(Show, Eq)
92−valToBool :: JSValueRef -> JSC JSBool
105+
106+-- | Given a JavaScript value get its boolean value.
107+-- All values in JavaScript convert to bool.
109+-- >>> runjs $ valToBool JSNull
111+-- >>> runjs $ valToBool ()
93−valToBool jsvar = do
113+-- >>> runjs $ valToBool True
94− gctxt <- ask
114+-- True
95− liftIO $ jsvaluetoboolean gctxt jsvar
115+-- >>> runjs $ valToBool False
97−valToNumber :: JSValueRef -> JSC JSNumber
117+-- >>> runjs $ valToBool (1.0 :: Double)
98−valToNumber jsvar = do
118+-- True
119+-- >>> runjs $ valToBool (0.0 :: Double)
121+-- >>> runjs $ valToBool ""
123+-- >>> runjs $ valToBool "1"
125+valToBool :: MakeValueRef val => val -> JSC JSBool
99 gctxt <- ask
127 gctxt <- ask
100− rethrow $ liftIO . jsvaluetonumber gctxt jsvar
128+ rval <- makeValueRef val
129+ liftIO $ jsvaluetoboolean gctxt rval
102−valToStr :: JSValueRef -> JSC JSStringRef
131+-- | Given a JavaScript value get its numeric value.
103−valToStr jsvar = do
132+-- May throw JSException.
134+-- >>> runjs $ valToNumber JSNull
136+-- >>> runjs $ valToNumber ()
138+-- >>> runjs $ valToNumber True
140+-- >>> runjs $ valToNumber False
142+-- >>> runjs $ valToNumber (1.0 :: Double)
144+-- >>> runjs $ valToNumber (0.0 :: Double)
146+-- >>> runjs $ valToNumber ""
148+-- >>> runjs $ valToNumber "1"
150+valToNumber :: MakeValueRef val => val -> JSC JSNumber
104 gctxt <- ask
152 gctxt <- ask
153+ rval <- makeValueRef val
105− rethrow $ liftIO . jsvaluetostringcopy gctxt jsvar
154+ rethrow $ liftIO . jsvaluetonumber gctxt rval
107−valToObject :: JSValueRef -> JSC JSObjectRef
156+-- | Given a JavaScript value get its string value (as a JavaScript string).
108−valToObject jsvar = do
157+-- May throw JSException.
159+-- >>> runjs $ valToStr JSNull >>= strToText
161+-- >>> runjs $ valToStr () >>= strToText
163+-- >>> runjs $ valToStr True >>= strToText
165+-- >>> runjs $ valToStr False >>= strToText
167+-- >>> runjs $ valToStr (1.0 :: Double) >>= strToText
169+-- >>> runjs $ valToStr (0.0 :: Double) >>= strToText
171+-- >>> runjs $ valToStr "" >>= strToText
173+-- >>> runjs $ valToStr "1" >>= strToText
175+valToStr :: MakeValueRef val => val -> JSC JSStringRef
109 gctxt <- ask
177 gctxt <- ask
178+ rval <- makeValueRef val
110− rethrow $ liftIO . jsvaluetoobject gctxt jsvar
179+ rethrow $ liftIO . jsvaluetostringcopy gctxt rval
112−strToText :: MonadIO m => JSStringRef -> m Text
113−strToText jsstring = liftIO $ do
114− l <- jsstringgetlength jsstring
115− p <- jsstringgetcharactersptr jsstring
116− T.fromPtr (castPtr p) (fromIntegral l)
118−textToStr :: MonadIO m => Text -> m JSStringRef
181+-- | Given a JavaScript value get its string value (as a Haskell 'Text').
119−textToStr text = do
182+-- May throw JSException.
120− liftIO $ useAsPtr text $ \p l -> do
183+--
121− jsstringcreatewithcharacters (castPtr p) (fromIntegral l)
184+-- >>> runjs $ valToText JSNull
123−valToText :: JSValueRef -> JSC Text
186+-- >>> runjs $ valToText ()
188+-- >>> runjs $ valToText True
190+-- >>> runjs $ valToText False
192+-- >>> runjs $ valToText (1.0 :: Double)
194+-- >>> runjs $ valToText (0.0 :: Double)
196+-- >>> runjs $ valToText ""
198+-- >>> runjs $ valToText "1"
200+valToText :: MakeValueRef val => val -> JSC Text
124valToText jsvar = valToStr jsvar >>= strToText
201valToText jsvar = valToStr jsvar >>= strToText
126−data JSException = JSException String JSValueRef deriving (Show, Typeable)
203+-- | Given a JavaScript value get its object value.
128−instance E.Exception JSException
204+-- May throw JSException.
130−rethrow :: (JSValueRefRef -> JSC a) -> JSC a
206+-- >>> runjs $ (valToObject JSNull >>= valToText) `catch` \ (JSException e) -> valToText e
131−rethrow f = f `catch` \e -> do
207+-- "TypeError: 'null' is not an object"
132− s <- deRefVal e
208+-- >>> runjs $ (valToObject () >>= valToText) `catch` \ (JSException e) -> valToText e
133− liftIO . E.throwIO $ JSException (show s) e
209+-- "TypeError: 'undefined' is not an object"
134−
210+-- >>> runjs $ valToObject True
135−class MakeValueRef a where
211+-- "true"
136− makeValueRef :: a -> JSC JSValueRef
212+-- >>> runjs $ valToObject False
138−class MakeStringRef a where
214+-- >>> runjs $ valToObject (1.0 :: Double)
139− makeStringRef :: MonadIO m => a -> m JSStringRef
215+-- "1"
140−
216+-- >>> runjs $ valToObject (0.0 :: Double)
141−---- Arguments ----
217+-- "0"
142−class MakeArgRefs this where
218+-- >>> runjs $ valToObject ""
143− makeArgRefs :: this -> JSC [JSValueRef]
219+-- ""
144−
220+-- >>> runjs $ valToObject "1"
145−instance MakeArgRefs arg => MakeArgRefs (JSC arg) where
221+-- "1"
146− makeArgRefs arg = arg >>= makeArgRefs
222+valToObject :: MakeValueRef val => val -> JSC JSObjectRef
147−
223+valToObject val = do
148−instance MakeValueRef arg => MakeArgRefs [arg] where
224+ gctxt <- ask
149− makeArgRefs = mapM makeValueRef
225+ rval <- makeValueRef val
150−
226+ rethrow $ liftIO . jsvaluetoobject gctxt rval
151−instance MakeArgRefs () where
152− makeArgRefs _ = return []
154−instance (MakeValueRef arg1, MakeValueRef arg2) => MakeArgRefs (arg1, arg2) where
228+-- | Convert to a JavaScript value (just an alias for 'makeValueRef')
155− makeArgRefs (arg1, arg2) = do
156− rarg1 <- makeValueRef arg1
157− rarg2 <- makeValueRef arg2
158− return [rarg1, rarg2]
160−instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3) => MakeArgRefs (arg1, arg2, arg3) where
161− makeArgRefs (arg1, arg2, arg3) = do
162− rarg1 <- makeValueRef arg1
163− rarg2 <- makeValueRef arg2
164− rarg3 <- makeValueRef arg3
165− return [rarg1, rarg2, rarg3]
167−instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3, MakeValueRef arg4) => MakeArgRefs (arg1, arg2, arg3, arg4) where
168− makeArgRefs (arg1, arg2, arg3, arg4) = do
169− rarg1 <- makeValueRef arg1
170− rarg2 <- makeValueRef arg2
171− rarg3 <- makeValueRef arg3
172− rarg4 <- makeValueRef arg4
173− return [rarg1, rarg2, rarg3, rarg4]
175−instance (MakeValueRef arg1, MakeValueRef arg2, MakeValueRef arg3, MakeValueRef arg4, MakeValueRef arg5) => MakeArgRefs (arg1, arg2, arg3, arg4, arg5) where
176− makeArgRefs (arg1, arg2, arg3, arg4, arg5) = do
177− rarg1 <- makeValueRef arg1
178− rarg2 <- makeValueRef arg2
179− rarg3 <- makeValueRef arg3
180− rarg4 <- makeValueRef arg4
181− rarg5 <- makeValueRef arg5
182− return [rarg1, rarg2, rarg3, rarg4, rarg5]
184−val :: MakeValueRef v => v -> JSC JSValueRef
229+val :: MakeValueRef value
230+ => value -- ^ value to convert to a JavaScript value
185val = makeValueRef
232val = makeValueRef
234+-- | If we already have a JSValueRef we are fine
187instance MakeValueRef JSValueRef where
235instance MakeValueRef JSValueRef where
188 makeValueRef = return
236 makeValueRef = return
238+-- | A single JSValueRef can be used as the argument list
190instance MakeArgRefs JSValueRef where
239instance MakeArgRefs JSValueRef where
191 makeArgRefs arg = return [arg]
240 makeArgRefs arg = return [arg]
242+-- | JSValueRef can be made by evaluating a function in 'JSC' as long
243+-- as it returns something we can make into a JSValueRef.
193instance MakeValueRef v => MakeValueRef (JSC v) where
244instance MakeValueRef v => MakeValueRef (JSC v) where
194 makeValueRef v = v >>= makeValueRef
245 makeValueRef v = v >>= makeValueRef
196−instance MakeStringRef JSStringRef where
247+----------- null ---------------
197− makeStringRef = return
248+-- | Make a @null@ JavaScript value
199−instance MakeStringRef Text where
200− makeStringRef = textToStr
202−instance MakeStringRef String where
203− makeStringRef = textToStr . T.pack
205valMakeNull :: JSC JSValueRef
249valMakeNull :: JSC JSValueRef
206valMakeNull = ask >>= (liftIO . jsvaluemakenull)
250valMakeNull = ask >>= (liftIO . jsvaluemakenull)
252+-- | Makes a @null@ JavaScript value
208instance MakeValueRef JSNull where
253instance MakeValueRef JSNull where
209 makeValueRef = const valMakeNull
254 makeValueRef = const valMakeNull
256+-- | Makes an argument list with just a single @null@ JavaScript value
211instance MakeArgRefs JSNull where
257instance MakeArgRefs JSNull where
212 makeArgRefs _ = valMakeNull >>= (\ref -> return [ref])
258 makeArgRefs _ = valMakeNull >>= (\ref -> return [ref])
260+----------- undefined ---------------
261+-- | Make an @undefined@ JavaScript value
214valMakeUndefined :: JSC JSValueRef
262valMakeUndefined :: JSC JSValueRef
215valMakeUndefined = ask >>= (liftIO . jsvaluemakeundefined)
263valMakeUndefined = ask >>= (liftIO . jsvaluemakeundefined)
265+-- | Makes an @undefined@ JavaScript value
217instance MakeValueRef JSUndefined where
266instance MakeValueRef JSUndefined where
218 makeValueRef = const valMakeUndefined
267 makeValueRef = const valMakeUndefined
220−--We can't allow this if JSUndefined is () as () is now args not "(null)"
269+--We can't allow this if JSUndefined is () as () is no args not "(null)".
221--instance MakeArgRefs JSUndefined where
271--instance MakeArgRefs JSUndefined where
222-- makeArgRefs _ = valMakeUndefined >>= (\ref -> return [ref])
272-- makeArgRefs _ = valMakeUndefined >>= (\ref -> return [ref])
274+-- | This allows us to pass no arguments easily (altenative would be to use @[]::[JSValueRef]@).
275+instance MakeArgRefs () where
276+ makeArgRefs _ = return []
278+----------- booleans ---------------
279+-- | Make a JavaScript boolean value
224valMakeBool :: JSBool -> JSC JSValueRef
280valMakeBool :: JSBool -> JSC JSValueRef
225valMakeBool b = do
281valMakeBool b = do
226 gctxt <- ask
282 gctxt <- ask
227 liftIO $ jsvaluemakeboolean gctxt b
283 liftIO $ jsvaluemakeboolean gctxt b
285+-- | Make a JavaScript boolean value
229instance MakeValueRef Bool where
286instance MakeValueRef Bool where
230 makeValueRef = valMakeBool
287 makeValueRef = valMakeBool
289+-- | Makes an argument list with just a single JavaScript boolean value
232instance MakeArgRefs Bool where
290instance MakeArgRefs Bool where
233 makeArgRefs b = valMakeBool b >>= (\ref -> return [ref])
291 makeArgRefs b = valMakeBool b >>= (\ref -> return [ref])
293+----------- numbers ---------------
294+-- | Make a JavaScript number
235valMakeNumber :: JSNumber -> JSC JSValueRef
295valMakeNumber :: JSNumber -> JSC JSValueRef
236valMakeNumber n = do
296valMakeNumber n = do
237 gctxt <- ask
297 gctxt <- ask
238 liftIO $ jsvaluemakenumber gctxt n
298 liftIO $ jsvaluemakenumber gctxt n
300+-- | Makes a JavaScript number
240instance MakeValueRef Double where
301instance MakeValueRef Double where
241 makeValueRef = valMakeNumber
302 makeValueRef = valMakeNumber
304+-- | Makes an argument list with just a single JavaScript number
243instance MakeArgRefs Double where
305instance MakeArgRefs Double where
244 makeArgRefs n = valMakeNumber n >>= (\ref -> return [ref])
306 makeArgRefs n = valMakeNumber n >>= (\ref -> return [ref])
308+----------- numbers ---------------
309+-- | Make a JavaScript string
246valMakeString :: Text -> JSC JSValueRef
310valMakeString :: Text -> JSC JSValueRef
247valMakeString text = do
311valMakeString text = do
248 gctxt <- ask
312 gctxt <- ask
249 s <- textToStr text
313 s <- textToStr text
250 liftIO $ jsvaluemakestring gctxt s
314 liftIO $ jsvaluemakestring gctxt s
316+-- | Makes a JavaScript string
252instance MakeValueRef Text where
317instance MakeValueRef Text where
253 makeValueRef = valMakeString
318 makeValueRef = valMakeString
320+-- | Makes an argument list with just a single JavaScript string
255instance MakeArgRefs Text where
321instance MakeArgRefs Text where
256 makeArgRefs t = valMakeString t >>= (\ref -> return [ref])
322 makeArgRefs t = valMakeString t >>= (\ref -> return [ref])
324+-- | Makes a JavaScript string
258instance MakeValueRef String where
325instance MakeValueRef String where
259 makeValueRef = valMakeString . T.pack
326 makeValueRef = valMakeString . T.pack
328+-- | Derefernce a value reference.
330+-- >>> runjs $ deRefVal JSNull
332+-- >>> runjs $ deRefVal ()
334+-- >>> runjs $ deRefVal True
336+-- >>> runjs $ deRefVal False
338+-- >>> runjs $ deRefVal (1.0 :: Double)
340+-- >>> runjs $ deRefVal (0.0 :: Double)
342+-- >>> runjs $ deRefVal ""
344+-- >>> runjs $ deRefVal "1"
346+-- >>> runjs $ valToObject True >>= deRefVal
261deRefVal :: MakeValueRef val => val -> JSC JSValue
348deRefVal :: MakeValueRef val => val -> JSC JSValue
262deRefVal val = do
349deRefVal val = do
263 gctxt <- ask
350 gctxt <- ask
@@ -271,8 +358,20 @@ deRefVal val = do
271 Kjstypestring -> ValString <$> (valToStr valref >>= strToText)
358 Kjstypestring -> ValString <$> (valToStr valref >>= strToText)
272 Kjstypeobject -> ValObject <$> valToObject valref
359 Kjstypeobject -> ValObject <$> valToObject valref
361+-- | Make a JavaScript value out of a 'JSValue' ADT.
363+-- >>> runjs $ valMakeRef ValNull
365+-- >>> runjs $ valMakeRef ValUndefined
367+-- >>> runjs $ valMakeRef (ValBool True)
369+-- >>> runjs $ valMakeRef (ValNumber 1)
371+-- >>> runjs $ valMakeRef (ValString $ pack "Hello")
274valMakeRef :: JSValue -> JSC JSValueRef
373valMakeRef :: JSValue -> JSC JSValueRef
275−valMakeRef val = do
374+valMakeRef val =
276 case val of
375 case val of
277 ValNull -> valMakeNull
376 ValNull -> valMakeNull
278 ValUndefined -> valMakeUndefined
377 ValUndefined -> valMakeUndefined
@@ -281,9 +380,14 @@ valMakeRef val = do
281 ValString s -> valMakeString s
380 ValString s -> valMakeString s
282 ValObject o -> return o
381 ValObject o -> return o
383+-- | Makes a JavaScript value from a 'JSValue' ADT.
284instance MakeValueRef JSValue where
384instance MakeValueRef JSValue where
285 makeValueRef = valMakeRef
385 makeValueRef = valMakeRef
387+-- | Makes an argument list with just a single JavaScript value from a 'JSValue' ADT.
287instance MakeArgRefs JSValue where
388instance MakeArgRefs JSValue where
288 makeArgRefs v = valMakeRef v >>= (\ref -> return [ref])
389 makeArgRefs v = valMakeRef v >>= (\ref -> return [ref])
391+instance MakeObjectRef JSNull where
392+ makeObjectRef = const valMakeNull