@@ -2,6 +2,8 @@
22{-# LANGUAGE FlexibleInstances #-}
33{-# LANGUAGE TypeSynonymInstances #-}
44{-# LANGUAGE ScopedTypeVariables #-}
5+{-# LANGUAGE NoMonomorphismRestriction #-}
6+{-# LANGUAGE Rank2Types #-}
57-----------------------------------------------------------------------------
68--
79-- Module : Language.Javascript.JSC.Object
@@ -21,6 +23,8 @@ module Language.Javascript.JSC.Object (
2123 -- * Property lookup
2224 , (!)
2325 , (!!)
26+ , js
27+ , jsg
2428
2529 -- * Setting the value of a property
2630 , (<#)
@@ -92,6 +96,9 @@ import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSValueRef
9296import Graphics.UI.Gtk.WebKit.JavaScriptCore.JSContextRef
9397 (jscontextgetglobalobject)
9498import Language.Javascript.JSC.Properties
99+import Control.Lens
100+ (IndexPreservingGetter, to, (^.))
101+import Language.Javascript.JSC.String (textToStr)
95102
96103-- | If we already have a JSObjectRef we are fine
97104instance MakeObjectRef JSObjectRef where
@@ -106,8 +113,8 @@ instance MakeObjectRef v => MakeObjectRef (JSC v) where
106113-- the lookup is delayed until we use the JSPropRef. This makes it a bit lazy compared
107114-- to JavaScript's @.@ operator.
108115--
109−-- >>> runjs $ eval "'Hello World'.length"
110−-- >>> runjs $ val "Hello World" ! "length"
116+-- >>> testJSC $ eval "'Hello World'.length"
117+-- >>> testJSC $ val "Hello World" ! "length"
111118-- "11"
112119(!) :: (MakeObjectRef this, MakeStringRef name)
113120 => this -- ^ Object to look on
@@ -115,15 +122,16 @@ instance MakeObjectRef v => MakeObjectRef (JSC v) where
115122 -> JSC JSPropRef -- ^ Property reference
116123this ! name = do
117124 rthis <- makeObjectRef this
118− rname <- makeStringRef name
119125 return (JSPropRef rthis rname)
126+ where
127+ rname = makeStringRef name
120128
121129-- | Lookup a property based on its index. This function just constructs a JSPropRef
122130-- the lookup is delayed until we use the JSPropRef. This makes it a bit lazy compared
123131-- to JavaScript's @[]@ operator.
124132--
125−-- >>> runjs $ eval "'Hello World'[6]"
126−-- >>> runjs $ val "Hello World" !! 6
133+-- >>> testJSC $ eval "'Hello World'[6]"
134+-- >>> testJSC $ val "Hello World" !! 6
127135-- "W"
128136(!!) :: (MakeObjectRef this)
129137 => this -- ^ Object to look on
@@ -133,11 +141,32 @@ this !! index = do
133141 rthis <- makeObjectRef this
134142 return (JSPropIndexRef rthis index)
135143
144+-- | Makes a getter for a particular property name.
145+--
146+-- > js name = to (!name)
147+--
148+-- >>> testJSC $ eval "'Hello World'.length"
149+-- >>> testJSC $ val "Hello World" ^. js "length"
150+-- "11"
151+js :: (MakeObjectRef s, MakeStringRef name)
152+ => name -- ^ Name of the property to find
153+ -> IndexPreservingGetter s (JSC JSPropRef)
154+js name = to (!name)
155+
156+-- | Handy way to get and hold onto a reference top level javascript
157+--
158+-- >>> testJSC $ eval "w = console; w.log('Hello World')"
159+-- >>> testJSC $ do w <- jsg "console"; w ^. js "log" # ["Hello World"]
160+-- "11"
161+jsg :: MakeStringRef a => a -> JSC JSPropRef
162+jsg name = global ! name
163+
136164-- | Call a JavaScript function
137165--
138−-- >>> runjs $ eval "'Hello World'.indexOf('World')"
139−-- >>> runjs $ val "Hello World" ! "indexOf" # ["World"]
166+-- >>> testJSC $ eval "'Hello World'.indexOf('World')"
167+-- >>> testJSC $ val "Hello World" ! "indexOf" # ["World"]
140168-- "6"
169+infixr 2 #
141170(#) :: (MakePropRef prop, MakeArgRefs args)
142171 => prop -> args -> JSC JSValueRef
143172prop # args = do
@@ -147,8 +176,8 @@ prop # args = do
147176
148177-- | Call a JavaScript function
149178--
150−-- >>> runjs $ eval "var j = {}; j.x = 1; j.x"
151−-- >>> runjs $ do {j <- eval "({})"; j!"x" <# 1; j!"x"}
179+-- >>> testJSC $ eval "var j = {}; j.x = 1; j.x"
180+-- >>> testJSC $ do {j <- eval "({})"; j!"x" <# 1; j!"x"}
152181-- "1"
153182infixr 0 <#
154183(<#) :: (MakePropRef prop, MakeValueRef val)
@@ -162,7 +191,7 @@ prop <# val = do
162191
163192-- | Use this to create a new JavaScript object
164193--
165−-- >>> runjs $ new "Date" (2013, 1, 1)
194+-- >>> testJSC $ new "Date" (2013, 1, 1)
166195-- "Fri Feb 01 2013 00:00:00 GMT+1300 (NZDT)"
167196new :: (MakeObjectRef constructor, MakeArgRefs args)
168197 => constructor
@@ -174,8 +203,8 @@ new constructor args = do
174203
175204-- | Call function with a given @this@. In most cases you should use '#'.
176205--
177−-- >>> runjs $ eval "(function(){return this;}).apply('Hello', [])"
178−-- >>> runjs $ do { test <- eval "(function(){return this;})"; call test (val "Hello") () }
206+-- >>> testJSC $ eval "(function(){return this;}).apply('Hello', [])"
207+-- >>> testJSC $ do { test <- eval "(function(){return this;})"; call test (val "Hello") () }
179208-- "Hello"
180209call :: (MakeObjectRef function, MakeObjectRef this, MakeArgRefs args)
181210 => function -> this -> args -> JSC JSValueRef
@@ -206,8 +235,8 @@ type JSCallAsFunction = JSValueRef -- ^ Function object
206235-- | Short hand @::JSCallAsFunction@ so a haskell function can be passed to
207236-- a to a JavaScipt one.
208237--
209−-- >>> runjs $ eval "(function(f) {f('Hello');})(function (a) {console.log(a)})"
210−-- >>> runjs $ call (eval "(function(f) {f('Hello');})") global [fun $ \ _ _ args -> valToText (head args) >>= (liftIO . print) ]
238+-- >>> testJSC $ eval "(function(f) {f('Hello');})(function (a) {console.log(a)})"
239+-- >>> testJSC $ call (eval "(function(f) {f('Hello');})") global [fun $ \ _ _ args -> valToText (head args) >>= (liftIO . print) ]
211240-- "Hello"
212241-- "undefined"
213242fun :: JSCallAsFunction -> JSCallAsFunction
@@ -225,8 +254,7 @@ function :: MakeStringRef name
225254function name f = do
226255 gctxt <- ask
227256 callback <- liftIO $ mkJSObjectCallAsFunctionCallback wrap
228− sName <- makeStringRef name
229− liftIO $ jsobjectmakefunctionwithcallback gctxt sName callback
257+ liftIO $ jsobjectmakefunctionwithcallback gctxt (makeStringRef name) callback
230258 where
231259 wrap ctx fobj this argc argv exception = do
232260 args <- peekArray (fromIntegral argc) argv
@@ -257,11 +285,11 @@ makeArray args exceptions = do
257285
258286-- | Make an JavaScript array from a list of values
259287--
260−-- >>> runjs $ eval "['Hello', 'World'][1]"
261−-- >>> runjs $ array ["Hello", "World"] !! 1
288+-- >>> testJSC $ eval "['Hello', 'World'][1]"
289+-- >>> testJSC $ array ["Hello", "World"] !! 1
262290-- "World"
263−-- >>> runjs $ eval "['Hello', null, undefined, true, 1]"
264−-- >>> runjs $ array ("Hello", JSNull, (), True, 1.0::Double)
291+-- >>> testJSC $ eval "['Hello', null, undefined, true, 1]"
292+-- >>> testJSC $ array ("Hello", JSNull, (), True, 1.0::Double)
265293-- "Hello,,,true,1"
266294array :: MakeArgRefs args => args -> JSC JSObjectRef
267295array = rethrow . makeArray
@@ -270,20 +298,11 @@ array = rethrow . makeArray
270298global :: JSC JSObjectRef
271299global = ask >>= (liftIO . jscontextgetglobalobject)
272300
273−---- String global scope ----
274−-- | A string on its own is assumed to be the name of a property in the global object
275−instance MakePropRef String where
276− makePropRef name = do
277− this <- global
278− rname <- makeStringRef name
279− return (JSPropRef this rname)
280−
281301-- | A string on its own is assumed to be the name of a property in the global object
282302instance MakeObjectRef String where
283303 makeObjectRef name = do
284304 this <- global
285− rname <- makeStringRef name
286− rethrow (objGetPropertyByName this rname) >>= valToObject
305+ rethrow (objGetPropertyByName this (makeStringRef name)) >>= valToObject
287306
288307-- | Get an array containing the property names present on a given object
289308copyPropertyNames :: MakeObjectRef this => this -> JSC JSPropertyNameArrayRef