javascript-extras 0.3.2.0 → 0.4.0.0
raw patch · 11 files changed
+207/−162 lines, 11 filesdep ~deepseqdep ~ghcjs-base-stubdep ~ghcjs-primPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: deepseq, ghcjs-base-stub, ghcjs-prim, parallel, text
API changes (from Hackage documentation)
- JavaScript.Extras.JSVar: JSVar :: JSVal -> JSVar
- JavaScript.Extras.JSVar: fromJS' :: FromJS a => JSVar -> Maybe a
- JavaScript.Extras.JSVar: instance Control.DeepSeq.NFData JavaScript.Extras.JSVar.JSVar
- JavaScript.Extras.JSVar: instance Data.String.IsString JavaScript.Extras.JSVar.JSVar
- JavaScript.Extras.JSVar: instance GHC.Show.Show JavaScript.Extras.JSVar.JSVar
- JavaScript.Extras.JSVar: instance GHCJS.Internal.Types.IsJSVal JavaScript.Extras.JSVar.JSVar
- JavaScript.Extras.JSVar: instance GHCJS.Marshal.Internal.PToJSVal JavaScript.Extras.JSVar.JSVar
- JavaScript.Extras.JSVar: instance JavaScript.Extras.Cast.FromJS JavaScript.Extras.JSVar.JSVar
- JavaScript.Extras.JSVar: instance JavaScript.Extras.Cast.ToJS JavaScript.Extras.JSVar.JSVar
- JavaScript.Extras.JSVar: js_stringify :: JSVar -> JSString
- JavaScript.Extras.JSVar: newtype JSVar
- JavaScript.Extras.JSVar: toJS' :: ToJS a => a -> JSVar
- JavaScript.Extras.JSVar.Unsafe: instance GHC.Read.Read JavaScript.Extras.JSVar.JSVar
- JavaScript.Extras.JSVar.Unsafe: js_eval :: JSString -> JSVar
+ JavaScript.Extras.JSRep: JSRep :: JSVal -> JSRep
+ JavaScript.Extras.JSRep: fromJSR :: FromJS a => JSRep -> Maybe a
+ JavaScript.Extras.JSRep: instance Control.DeepSeq.NFData JavaScript.Extras.JSRep.JSRep
+ JavaScript.Extras.JSRep: instance Data.String.IsString JavaScript.Extras.JSRep.JSRep
+ JavaScript.Extras.JSRep: instance GHC.Show.Show JavaScript.Extras.JSRep.JSRep
+ JavaScript.Extras.JSRep: instance GHCJS.Internal.Types.IsJSVal JavaScript.Extras.JSRep.JSRep
+ JavaScript.Extras.JSRep: instance GHCJS.Marshal.Internal.PToJSVal JavaScript.Extras.JSRep.JSRep
+ JavaScript.Extras.JSRep: instance JavaScript.Extras.Cast.FromJS JavaScript.Extras.JSRep.JSRep
+ JavaScript.Extras.JSRep: instance JavaScript.Extras.Cast.ToJS JavaScript.Extras.JSRep.JSRep
+ JavaScript.Extras.JSRep: js_stringify :: JSRep -> JSString
+ JavaScript.Extras.JSRep: newtype JSRep
+ JavaScript.Extras.JSRep: toJSR :: ToJS a => a -> JSRep
+ JavaScript.Extras.JSRep.Unsafe: instance GHC.Read.Read JavaScript.Extras.JSRep.JSRep
+ JavaScript.Extras.JSRep.Unsafe: js_eval :: JSString -> JSRep
+ JavaScript.Extras.Number: minSafeInteger :: Int
+ JavaScript.Extras.Number: safeDecrement :: Int -> Int
+ JavaScript.Extras.Number: safeIncrement :: Int -> Int
+ JavaScript.Extras.Property: classNames :: [(JSString, Bool)] -> JSRep
- JavaScript.Extras.Cast: class ToJS a where toJS = jsval
+ JavaScript.Extras.Cast: class ToJS a
- JavaScript.Extras.Property: getProperty :: Coercible a JSVal => JSString -> a -> IO JSVar
+ JavaScript.Extras.Property: getProperty :: ToJS a => a -> JSString -> IO JSRep
- JavaScript.Extras.Property: setProperty :: Coercible a JSVal => Property -> a -> IO ()
+ JavaScript.Extras.Property: setProperty :: ToJS a => a -> Property -> IO ()
- JavaScript.Extras.Property: type Property = (JSString, JSVar)
+ JavaScript.Extras.Property: type Property = (JSString, JSRep)
Files
- README.md +15/−0
- javascript-extras.cabal +10/−10
- src/JavaScript/Extras.hs +2/−2
- src/JavaScript/Extras/JSRep.hs +53/−0
- src/JavaScript/Extras/JSRep/Unsafe.hs +26/−0
- src/JavaScript/Extras/JSVar.hs +0/−53
- src/JavaScript/Extras/JSVar/Unsafe.hs +0/−26
- src/JavaScript/Extras/Number.hs +21/−0
- src/JavaScript/Extras/Property.hs +33/−24
- test/haskell/Main.hs +0/−47
- test/hs/Main.hs +47/−0
README.md view
@@ -1,3 +1,18 @@ [](https://hackage.haskell.org/package/javascript-extras) Extra javascript functions when using GHCJS++# Changelog++* 0.4.0.0+ - Renamed `JSVar` to `JSRep` to avoid confusion with `JSVal`+ - Renamed `toJS'` to `toJSR`+ - Renamed `fromJS'` to `fromJSR`+ - `getProperty` and `setProperty` uses `JE.ToJS a` instead of `Coercible a J.JSVal`+ - flipped the args of `getProperty` and `setProperty`+ - Moved `justSnds` to esoteric-extras `Data.Maybe.Esoteric.keepMaybes`+ - Renamed `safeModularIncrement` to `safeIncrement`+ - Renamed `safeModularDecrement` to `safeDecrement`++* 0.3.3.0+ - Added `classNames`, `justSnds`
javascript-extras.cabal view
@@ -1,5 +1,5 @@ name: javascript-extras-version: 0.3.2.0+version: 0.4.0.0 synopsis: Extra javascript functions when using GHCJS description: Extra javascript functions when using GHCJS homepage: https://github.com/louispan/javascript-extras#readme@@ -20,21 +20,21 @@ JavaScript.Extras.Cast JavaScript.Extras.Number JavaScript.Extras.Property- JavaScript.Extras.JSVar- JavaScript.Extras.JSVar.Unsafe+ JavaScript.Extras.JSRep+ JavaScript.Extras.JSRep.Unsafe build-depends: base >= 4.7 && < 5- , deepseq >= 1.4 && < 2- , parallel >= 3.2 && < 4- , text >= 1.2 && < 1.3+ , deepseq >= 1.4+ , parallel >= 3.2+ , text >= 1.2 default-language: Haskell2010 ghc-options: -Wall if impl(ghcjs) build-depends: ghcjs-base if !impl(ghcjs)- build-depends: ghcjs-base-stub >= 0.1.0.1 && < 1+ build-depends: ghcjs-base-stub >= 0.1.0.1 executable javascript-extras-test- hs-source-dirs: test/haskell+ hs-source-dirs: test/hs main-is: Main.hs ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N cpp-options: -DGHCJS_BROWSER@@ -44,9 +44,9 @@ default-extensions: ApplicativeDo if impl(ghcjs) build-depends: ghcjs-base- , ghcjs-prim >= 0.1 && < 0.2+ , ghcjs-prim >= 0.1 if !impl(ghcjs)- build-depends: ghcjs-base-stub >= 0.1.0.2 && < 1+ build-depends: ghcjs-base-stub >= 0.1.0.2 source-repository head type: git
src/JavaScript/Extras.hs view
@@ -1,11 +1,11 @@ module JavaScript.Extras ( module JavaScript.Extras.Cast- , module JavaScript.Extras.JSVar+ , module JavaScript.Extras.JSRep , module JavaScript.Extras.Number , module JavaScript.Extras.Property ) where import JavaScript.Extras.Cast-import JavaScript.Extras.JSVar+import JavaScript.Extras.JSRep import JavaScript.Extras.Number import JavaScript.Extras.Property
+ src/JavaScript/Extras/JSRep.hs view
@@ -0,0 +1,53 @@+{-# LANGUAGE CPP #-}++module JavaScript.Extras.JSRep where++import Control.DeepSeq+import Data.Coerce+import Data.JSString as JS+import Data.String+import qualified GHCJS.Marshal.Pure as J+import qualified GHCJS.Types as J+import JavaScript.Extras.Cast as JE++-- | Wrapper to have a JSVal that also have an IString instance+-- This is helpful when using OverloadedStrings+newtype JSRep = JSRep J.JSVal++instance Show JSRep where+ show = JS.unpack . js_stringify++instance J.IsJSVal JSRep++instance J.PToJSVal JSRep where+ pToJSVal = J.jsval++instance JE.ToJS JSRep++instance JE.FromJS JSRep where+ fromJS v = coerce (fromJS v :: Maybe J.JSVal)++instance IsString JSRep where+ fromString = JSRep . J.jsval . JS.pack++instance NFData JSRep where+ rnf (JSRep v) = rnf v++toJSR :: JE.ToJS a => a -> JSRep+toJSR = JSRep . toJS++fromJSR :: JE.FromJS a => JSRep -> Maybe a+fromJSR (JSRep v) = fromJS v++#ifdef __GHCJS__++foreign import javascript unsafe+ "$r = hje$stringify($1);"+ js_stringify :: JSRep -> J.JSString++#else++js_stringify :: JSRep -> J.JSString+js_stringify _ = JS.empty++#endif
+ src/JavaScript/Extras/JSRep/Unsafe.hs view
@@ -0,0 +1,26 @@+{-# OPTIONS_GHC -fno-warn-orphans #-}+{-# LANGUAGE CPP #-}++module JavaScript.Extras.JSRep.Unsafe where++import Data.JSString as JS+import qualified GHCJS.Types as J+import JavaScript.Extras.JSRep as JE++-- | Injection attack! Use with care+instance Read JE.JSRep where+ readsPrec _ str = [(js_eval (JS.pack str), [])]++#ifdef __GHCJS__++-- | Injection attack! Use with care+foreign import javascript unsafe+ "$r = hje$unstringify($1);"+ js_eval :: J.JSString -> JSRep++#else++js_eval :: J.JSString -> JSRep+js_eval _ = JSRep J.nullRef++#endif
− src/JavaScript/Extras/JSVar.hs
@@ -1,53 +0,0 @@-{-# LANGUAGE CPP #-}--module JavaScript.Extras.JSVar where--import Control.DeepSeq-import Data.Coerce-import Data.JSString as JS-import Data.String-import qualified GHCJS.Marshal.Pure as J-import qualified GHCJS.Types as J-import JavaScript.Extras.Cast as JE---- | Wrapper to have a JSVal that also have an IString instance--- This is helpful when using OverloadedStrings-newtype JSVar = JSVar J.JSVal--instance Show JSVar where- show = JS.unpack . js_stringify--instance J.IsJSVal JSVar--instance J.PToJSVal JSVar where- pToJSVal = J.jsval--instance JE.ToJS JSVar--instance JE.FromJS JSVar where- fromJS v = coerce (fromJS v :: Maybe J.JSVal)--instance IsString JSVar where- fromString = JSVar . J.jsval . JS.pack--instance NFData JSVar where- rnf (JSVar v) = rnf v--toJS' :: JE.ToJS a => a -> JSVar-toJS' = JSVar . toJS--fromJS' :: JE.FromJS a => JSVar -> Maybe a-fromJS' (JSVar v) = fromJS v--#ifdef __GHCJS__--foreign import javascript unsafe- "$r = hje$stringify($1);"- js_stringify :: JSVar -> J.JSString--#else--js_stringify :: JSVar -> J.JSString-js_stringify _ = JS.empty--#endif
− src/JavaScript/Extras/JSVar/Unsafe.hs
@@ -1,26 +0,0 @@-{-# OPTIONS_GHC -fno-warn-orphans #-}-{-# LANGUAGE CPP #-}--module JavaScript.Extras.JSVar.Unsafe where--import Data.JSString as JS-import qualified GHCJS.Types as J-import JavaScript.Extras.JSVar as JE---- | Injection attack! Use with care-instance Read JE.JSVar where- readsPrec _ str = [(js_eval (JS.pack str), [])]--#ifdef __GHCJS__---- | Injection attack! Use with care-foreign import javascript unsafe- "$r = hje$unstringify($1);"- js_eval :: J.JSString -> JSVar--#else--js_eval :: J.JSString -> JSVar-js_eval _ = JSVar J.nullRef--#endif
src/JavaScript/Extras/Number.hs view
@@ -2,20 +2,41 @@ module JavaScript.Extras.Number ( maxSafeInteger+ , minSafeInteger+ , safeIncrement+ , safeDecrement ) where maxSafeInteger :: Int maxSafeInteger = js_maxSafeInteger +minSafeInteger :: Int+minSafeInteger = js_minSafeInteger++-- | always returns a number between [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]+safeIncrement :: Int -> Int+safeIncrement i = if i >= maxSafeInteger then minSafeInteger else i + 1++-- | always returns a number between [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER]+safeDecrement :: Int -> Int+safeDecrement i = if i <= minSafeInteger then maxSafeInteger else i - 1+ #ifdef __GHCJS__ foreign import javascript unsafe "Number.MAX_SAFE_INTEGER" js_maxSafeInteger :: Int +foreign import javascript unsafe+ "Number.MIN_SAFE_INTEGER"+ js_minSafeInteger :: Int+ #else js_maxSafeInteger :: Int js_maxSafeInteger = 0++js_minSafeInteger :: Int+js_minSafeInteger = 0 #endif
src/JavaScript/Extras/Property.hs view
@@ -1,9 +1,11 @@ {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE GHCForeignImportPrim #-}+{-# LANGUAGE TupleSections #-} module JavaScript.Extras.Property- ( Property+ ( classNames+ , Property , getProperty , setProperty , fromProperties@@ -12,71 +14,78 @@ import Control.DeepSeq import Control.Parallel-import Data.Coerce+import qualified Data.JSString as JS import qualified GHC.Exts as Exts import qualified GHCJS.Marshal.Pure as J import qualified GHCJS.Types as J+import qualified JavaScript.Extras.Cast as JE+import qualified JavaScript.Extras.JSRep as JE import qualified JavaScript.Object as JO import qualified JavaScript.Object.Internal as JOI-import qualified JavaScript.Extras.JSVar as JE import Unsafe.Coerce -type Property = (J.JSString, JE.JSVar)+type Property = (J.JSString, JE.JSRep) +-- | Creates a JE.JSRep single string for "className" property from a list of (JSString, Bool)+-- Idea from https://github.com/JedWatson/classnames+classNames :: [(J.JSString, Bool)] -> JE.JSRep+classNames = JE.toJSR . JS.unwords . fmap fst . filter snd+ -- | get a property of any JSVal. If a null or undefined is queried, the result will also be null-getProperty :: Coercible a J.JSVal => J.JSString -> a -> IO JE.JSVar-getProperty k a = let k' = J.pToJSVal k- x = coerce a+getProperty :: JE.ToJS a => a -> J.JSString -> IO JE.JSRep+getProperty a k = let k' = J.pToJSVal k+ x = JE.toJS a in if J.isUndefined x || J.isNull x || J.isUndefined k' || J.isNull k'- then pure $ JE.JSVar J.nullRef- else js_unsafeGetProperty k x+ then pure $ JE.JSRep J.nullRef+ else js_unsafeGetProperty x k -- | set a property of any JSVal-setProperty :: Coercible a J.JSVal => Property -> a -> IO ()-setProperty (k, v) a = let k' = J.pToJSVal k- x = coerce a+setProperty :: JE.ToJS a => a -> Property -> IO ()+setProperty a (k, v) = let k' = J.pToJSVal k+ x = JE.toJS a in if J.isUndefined x || J.isNull x || J.isUndefined k' || J.isNull k' then pure ()- else js_unsafeSetProperty k v x+ else js_unsafeSetProperty x k v fromProperties :: [Property] -> JO.Object fromProperties xs =- let (names, props) = unzip xs- in (rnf names `seq` rnf props) `pseq` js_toJSObjectPure (unsafeCoerce names) (unsafeCoerce props)+ let (names, values) = unzip xs+ in (rnf names `seq` rnf values) `pseq` js_toJSObjectPure (unsafeCoerce names) (unsafeCoerce values) toProperties :: JO.Object -> IO [Property] toProperties obj = do props <- JO.listProps obj- traverse (\k -> (\v -> (k, JE.JSVar v)) <$> JO.unsafeGetProp k obj) props+ traverse (\k -> (\v -> (k, JE.JSRep v)) <$> JO.unsafeGetProp k obj) props #ifdef __GHCJS__ -- | throws an exception if undefined or null foreign import javascript unsafe- "$2[$1]"- js_unsafeGetProperty :: J.JSString -> J.JSVal -> IO JE.JSVar+ "$1[$2]"+ js_unsafeGetProperty :: J.JSVal -> J.JSString -> IO JE.JSRep -- | throws an exception if undefined or null foreign import javascript unsafe- "$3[$1] = $2;"- js_unsafeSetProperty :: J.JSString -> JE.JSVar -> J.JSVal -> IO ()+ "$1[$2] = $3;"+ js_unsafeSetProperty :: J.JSVal -> J.JSString -> JE.JSRep -> IO () -- | zip list of string and JSVal to object, lists must have been completely forced first -- Using the idea from JavaScript.Array.Internal.fromList h$fromHsListJSVal-foreign import javascript unsafe "hje$fromHsZipListJSVal($1, $2)"+foreign import javascript unsafe+ "hje$fromHsZipListJSVal($1, $2)" js_toJSObjectPure :: Exts.Any -> Exts.Any -> JO.Object #else -- | throws an exception if undefined or null-js_unsafeGetProperty :: J.JSString -> J.JSVal -> IO JE.JSVar-js_unsafeGetProperty _ _ = pure $ JE.JSVar J.nullRef+js_unsafeGetProperty :: J.JSVal -> J.JSString -> IO JE.JSRep+js_unsafeGetProperty _ _ = pure $ JE.JSRep J.nullRef -- | throws an exception if undefined or null-js_unsafeSetProperty :: J.JSString -> JE.JSVar -> J.JSVal -> IO ()+js_unsafeSetProperty :: J.JSVal -> J.JSString -> JE.JSRep -> IO () js_unsafeSetProperty _ _ _ = pure () -- | zip list of string and JSVal to object, lists must have been completely forced first
− test/haskell/Main.hs
@@ -1,47 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE OverloadedStrings #-}--module Main (main) where--import qualified Data.JSString as JS-import qualified GHCJS.Foreign.Callback as J-import qualified GHCJS.Types as J-import qualified JavaScript.Extras as JE-import qualified JavaScript.Extras.JSVar.Unsafe as JE--#ifdef __GHCJS__--test :: JE.JSVar -> IO ()-test x = do- let str = show x- x' = read str :: JE.JSVar- str' = show x'- js_write $ "Test: " `mappend` (JS.pack str) `mappend` " == " `mappend` (JS.pack str')--main :: IO ()-main = do- test js_int- test js_obj- cb <- J.syncCallback' (pure (JE.toJS js_int))- test (JE.JSVar (J.jsval cb))- J.releaseCallback cb- test (JE.JSVar (J.jsval cb))--foreign import javascript unsafe- "document.write('<p>' + $1 + '</p>')"- js_write :: JS.JSString -> IO ()--foreign import javascript unsafe- "5"- js_int :: JE.JSVar--foreign import javascript unsafe- "{ 'hello': 'world' }"- js_obj :: JE.JSVar--#else--main :: IO ()-main = pure ()--#endif
+ test/hs/Main.hs view
@@ -0,0 +1,47 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}++module Main (main) where++#ifdef __GHCJS__++import qualified Data.JSString as JS+import qualified GHCJS.Foreign.Callback as J+import qualified GHCJS.Types as J+import qualified JavaScript.Extras as JE+import JavaScript.Extras.JSRep.Unsafe ()++test :: JE.JSRep -> IO ()+test x = do+ let str = show x+ x' = read str :: JE.JSRep+ str' = show x'+ js_write $ "Test: " `mappend` (JS.pack str) `mappend` " == " `mappend` (JS.pack str')++main :: IO ()+main = do+ test js_int+ test js_obj+ cb <- J.syncCallback' (pure (JE.toJS js_int))+ test (JE.JSRep (J.jsval cb))+ J.releaseCallback cb+ test (JE.JSRep (J.jsval cb))++foreign import javascript unsafe+ "document.write('<p>' + $1 + '</p>')"+ js_write :: JS.JSString -> IO ()++foreign import javascript unsafe+ "5"+ js_int :: JE.JSRep++foreign import javascript unsafe+ "{ 'hello': 'world' }"+ js_obj :: JE.JSRep++#else++main :: IO ()+main = pure ()++#endif