diff --git a/README.md b/README.md
index 6c26c38..03847fa 100644
--- a/README.md
+++ b/README.md
@@ -73,7 +73,6 @@ Still to do
-----------
- Data
-- Deferred Object
- Effects (Custom, Sliding)
- Forms
- Internals
diff --git a/fay-jquery.cabal b/fay-jquery.cabal
index f3ce093..8fa479a 100644
--- a/fay-jquery.cabal
+++ b/fay-jquery.cabal
@@ -16,6 +16,7 @@ extra-source-files:
README.md
CHANGELOG.md
data-files: src/JQuery.hs
+ , src/JQuery/Deferred.hs
source-repository head
type: git
@@ -26,5 +27,6 @@ library
hs-source-dirs: src
ghc-options: -Wall
exposed-modules: JQuery
+ , JQuery.Deferred
build-depends: fay-base >= 0.18
, fay-text >= 0.2
diff --git a/src/JQuery.hs b/src/JQuery.hs
index 4fa5b4c..0bdc41f 100644
--- a/src/JQuery.hs
+++ b/src/JQuery.hs
@@ -292,6 +292,9 @@ noConflictBool = ffi "jQuery['noConflict'](%1)"
-- TODO: jQuery['when'](): figure out Deferred first
+promise :: Defined Text -> Defined a -> JQuery -> Fay JQuery
+promise = ffi "%3.promise(%1, %2)"
+
----
---- CSS
----
@@ -373,10 +376,6 @@ setWidthWith = ffi "%2['width'](%1)"
---- Data
----
-----
----- Deferred Object
-----
-
----
---- Effects
----
diff --git a/src/JQuery/Deferred.hs b/src/JQuery/Deferred.hs
new file mode 100644
index 0000000..e7b20a5
--- /dev/null
+++ b/src/JQuery/Deferred.hs
@@ -0,0 +1,107 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+
+module JQuery.Deferred where
+----
+---- Deferred Object
+----
+
+---- Assume JQuery > 1.8
+
+import Fay.Text (Text, fromString, pack, unpack)
+import FFI
+
+data Deferred t = Deferred t
+data DeferredState = Pending | Resolved | Rejected
+
+deferred :: Defined (Deferred t -> Fay ()) -> Fay (Deferred t)
+deferred (Defined beforeStart) = (ffi "jQuery.Deferred(%1)" :: (Deferred t -> Fay ()) -> Fay (Deferred t)) beforeStart
+deferred Undefined = ffi "jQuery.Deferred()" :: Fay (Deferred t)
+
+always :: Fay ()
+ -> Deferred t
+ -> Fay (Deferred t)
+always = ffi "%2.always(%1)"
+
+done :: (t -> Fay ())
+ -> Deferred t
+ -> Fay (Deferred t)
+done = ffi "%2.done(%1)"
+
+fail_ :: Fay ()
+ -> Deferred t
+ -> Fay (Deferred t)
+fail_ = ffi "%2.fail(%1)"
+
+--notify :: t -> Deferred t -> Fay ()
+--notify = ffi "%2.notify(%1)"
+
+-- TODO - store *this* into Deferred
+-- notifyWith :: c -> t -> Deferred t -> Fay ()
+-- notifyWith = ffi "%3.notify(%1,%2)"
+
+reject :: t -> Deferred t -> Fay (Deferred t)
+reject = ffi "%2.reject(%1)"
+
+-- TODO - store *this* into Deferred
+rejectWith :: c -> t -> Deferred t -> Fay ()
+rejectWith = ffi "%3.rejectWith(%1,%2)"
+
+resolve :: t -> Deferred t -> Fay ()
+resolve = ffi "%2.resolve(%1)"
+
+-- TODO - store *this* into Deferred
+--resolveWith :: c -> t -> Deferred t -> Fay ()
+--resolveWith = ffi "%3.resolveWith(%1,%2)"
+
+-- TODO - Promise not yet implemented. The hidden methods on Promise will fail at runtime ATM
+-- TODO - Promise with target untested
+promise_ :: Defined target -> Deferred t -> Fay (Deferred t)
+promise_ (Defined targ) def = (ffi "%2.promise(%1)" :: target -> Deferred t -> Fay (Deferred t)) targ def
+promise_ Undefined def = (ffi "%1.promise()" :: Deferred t -> Fay (Deferred t)) def
+
+--
+-- Tested up to here
+--
+
+deferredStateToText :: DeferredState -> Text
+deferredStateToText ds = case ds of
+ Pending -> pack "pending" :: Text
+ Rejected -> pack "rejected" :: Text
+ Resolved -> pack "resolved" :: Text
+
+-- TODO - the case statement here with the strings as Text does not work, it just chooses the first value
+-- I haven't been able to replicate the problem outside of this file though, so I've left it here
+state :: Deferred t -> Fay DeferredState
+state def = do
+ stateStr <- (ffi "%1.state()" :: Deferred t -> Fay Text) def
+-- (ffi "window.foo = %1" :: Text -> Fay ()) stateStr
+-- (ffi "window.bar = %1" :: Text -> Fay ()) resolvedStr
+ return $ case unpack stateStr of
+ "pending" -> Pending
+ "resolved" -> Resolved
+ "rejected" -> Rejected
+
+-- case stateStr of
+-- rejectedStr -> (ffi "console.log(\"rejected\")" :: Fay ())
+-- resolvedStr -> (ffi "console.log(\"resolved\")" :: Fay ())
+-- pendingStr -> (ffi "console.log(\"pending\")" :: Fay ())
+-- return $ case stateStr of
+-- pendingStr -> Pending
+-- resolvedStr -> Resolved
+-- rejectedStr -> Rejected
+-- where pendingStr = pack "pending" :: Text
+-- resolvedStr = pack "resolved" :: Text
+-- rejectedStr = pack "rejected" :: Text
+
+then_ :: Defined (t -> Fay s)
+ -> Defined (Fay ())
+ -> Defined (Fay ())
+ -> Deferred t
+ -> Fay (Deferred s)
+then_ = ffi "%4.then(%1,%2,%3)"
+
+-- Need heterogeneous array to implement this properly
+when_ :: [Deferred t] -> Fay (Deferred [t])
+when_ = ffi "jQuery.when.apply(jQuery,%1).then(function() { return Array.prototype.slice.call(arguments).sort() })"
+
diff --git a/test.hs b/test.hs
index ad057b7..e954e91 100644
--- a/test.hs
+++ b/test.hs
@@ -8,8 +8,10 @@ module Test (main) where
import Fay.Text (Text, fromString)
import JQuery
+import JQuery.Deferred
import Prelude hiding (div)
import qualified Fay.Text as T
+import FFI
myMapM :: (a -> Fay b) -> [a] -> Fay [b]
myMapM f as = mySequence (map f as)
@@ -51,19 +53,20 @@ main = ready $ do
testAnimations
testAjax
+ testDeferred
return ()
testAnimations :: Fay ()
testAnimations = do
- body <- select "body"
- container <- select "
" >>= appendTo body
+ animationsTest <- select "#animationsTest"
+ container <- select "" >>= appendTo animationsTest
thing <- select "Hello
" >>= appendTo container
select "" >>= click (const $ hide Slow thing >> return ()) >>= appendTo container
select "" >>= click (const $ jshow Instantly thing >> return ()) >>= appendTo container
select "" >>= click (const $ toggle (Speed 100) thing >> return ()) >>= appendTo container
select "" >>= click (const $ runAnimation $ chainedAnimation thing) >>= appendTo container
- select ("" `T.append` "Thunk" `T.append` "
") >>= appendTo body
+ select ("" `T.append` "Thunk" `T.append` "
") >>= appendTo animationsTest
return ()
where
@@ -83,3 +86,219 @@ addZebraStriping table = do
addClass "even" evenRows
oddRows <- findSelector "tr:odd" table
addClass "odd" oddRows
+
+-- Set up minimal test DSL and results box
+data TestResultBox = TestResultBox { selector :: JQuery }
+
+passTest :: TestResultBox -> Fay ()
+passTest r = do
+ setHtml "Pass" $ selector r
+ return ()
+
+failTest :: TestResultBox -> Fay ()
+failTest r = do
+ setHtml "Fail" $ selector r
+ return ()
+
+newResultBox :: Text -> Fay TestResultBox
+newResultBox name = do
+ table <- select "#deferredTest"
+ row <- select ("" `T.append` name `T.append` " |
") >>= appendTo table
+ box <- select "Pending | " >>= appendTo row
+ return $ TestResultBox box
+
+-- Not for use in actual lib...
+isDeferred :: Deferred a -> Fay Bool
+isDeferred = ffi "!!(%1.then && %1.always && %1.done && %1.fail)"
+
+-- Deferred object tests
+
+testDeferredConstructor :: Fay ()
+testDeferredConstructor = do
+ out <- newResultBox "Deferred constructor with no args"
+ def <- deferred Undefined :: Fay (Deferred Text)
+ resultIsDeferred <- isDeferred def
+ case resultIsDeferred of
+ True -> passTest out
+ _ -> failTest out
+ return ()
+
+testDeferredConstructorBefore :: Fay ()
+testDeferredConstructorBefore = do
+ out1 <- newResultBox "Deferred constructor calls func arg with Deferred"
+ out2 <- newResultBox "Deferred constructor with func arg returns Deferred"
+ def <- ((deferred $ Defined (\d -> do
+ argumentIsDeferred <- isDeferred d
+ case argumentIsDeferred of
+ True -> passTest out1
+ _ -> failTest out1)) :: Fay (Deferred Text))
+ resultIsDeferred <- isDeferred def
+ case resultIsDeferred of
+ True -> passTest out2
+ _ -> failTest out2
+ return ()
+
+testDeferredAlways :: Fay ()
+testDeferredAlways = do
+ called <- newResultBox "Deferred always callback triggered"
+ correctRV <- newResultBox "Deferred always returns Deferred"
+ def <- (deferred Undefined) :: Fay (Deferred Text)
+ def1 <- always (passTest called) def
+ def1IsDeferred <- isDeferred def1
+ case def1IsDeferred of
+ True -> passTest correctRV
+ _ -> failTest correctRV
+ resolve "foo" def1
+ return ()
+
+testDeferredDone :: Fay ()
+testDeferredDone = do
+ called <- newResultBox "Deferred done callback triggered"
+ correctArg <- newResultBox "Deferred done passed correct value"
+ correctRV <- newResultBox "Deferred done returns Deferred"
+ def <- deferred Undefined :: Fay (Deferred Text)
+ rv <- done (\v -> do
+ passTest called
+ case v of
+ "Wee timorous beastie!" -> passTest correctArg
+ _ -> failTest correctArg) def
+ rvIsDeferred <- isDeferred rv
+ case rvIsDeferred of
+ True -> passTest correctRV
+ _ -> failTest correctRV
+ resolve "Wee timorous beastie!" def
+ return ()
+
+testDeferredFail :: Fay ()
+testDeferredFail = do
+ called <- newResultBox "Deferred fail callback triggered"
+ correctRV <- newResultBox "Deferred fail returns Deferred"
+ def <- (deferred Undefined) :: Fay (Deferred Text)
+ def1 <- fail_ (passTest called) def
+ def1IsDeferred <- isDeferred def1
+ case def1IsDeferred of
+ True -> passTest correctRV
+ _ -> failTest correctRV
+ reject "foo" def1
+ return ()
+
+testDeferredPromise :: Fay ()
+testDeferredPromise = do
+ promiseReturned <- newResultBox "Deferred promise returns something Deferred"
+ def <- (deferred Undefined) :: Fay (Deferred Text)
+ prom <- promise_ Undefined def :: Fay (Deferred Text)
+ promIsDeferred <- isDeferred prom
+ case promIsDeferred of
+ True -> passTest promiseReturned
+ _ -> failTest promiseReturned
+
+testDeferredState :: Fay ()
+testDeferredState = do
+ statePendingAssertion <- newResultBox "Deferred state returns Pending constructor"
+ stateRejectedAssertion <- newResultBox "Deferred state returns Rejected constructor"
+ stateResolvedAssertion <- newResultBox "Deferred state returns Resolved constructor"
+ defSuccessExample <- (deferred Undefined) :: Fay (Deferred Text)
+ defFailureExample <- (deferred Undefined) :: Fay (Deferred Text)
+ pendingState <- state defSuccessExample
+ resolve "foo" defSuccessExample
+ reject "foo" defFailureExample
+
+ successState <- state defSuccessExample
+ failureState <- state defFailureExample
+
+ case pendingState of
+ Pending -> passTest statePendingAssertion
+ _ -> failTest statePendingAssertion
+ case failureState of
+ Rejected -> passTest stateRejectedAssertion
+ _ -> failTest stateRejectedAssertion
+ case successState of
+ Resolved -> passTest stateResolvedAssertion
+ _ -> failTest stateResolvedAssertion
+
+testDeferredThen :: Fay ()
+testDeferredThen = do
+ doneCalled <- newResultBox "Deferred then calls doneFilter"
+ failCalled <- newResultBox "Deferred then calls failFilter"
+ progressCalled <- newResultBox "Deferred then progressFilter"
+ doneArgCorrect <- newResultBox "Deferred then passes correct argument to doneFilter"
+ doneRVCorrect <- newResultBox "Deferred then returns correct value"
+
+ defSuccessExample <- (deferred Undefined) :: Fay (Deferred Text)
+ defFailureExample <- (deferred Undefined) :: Fay (Deferred Text)
+
+ then_ Undefined (Defined $ passTest failCalled) Undefined defFailureExample
+
+ filtered <- then_ (Defined $ \v -> do
+ passTest doneCalled
+
+ case v of
+ "Great chieftain o the pudding race" -> passTest doneArgCorrect
+ _ -> failTest doneArgCorrect
+
+ return "Painch, tripe, or thairm" :: Fay Text
+ ) Undefined Undefined defSuccessExample
+
+ done (\v -> do
+ case v of
+ "Painch, tripe, or thairm" -> passTest doneRVCorrect
+ _ -> failTest doneRVCorrect
+ ) filtered
+
+ resolve "Great chieftain o the pudding race" defSuccessExample
+ reject "bar" defFailureExample
+
+ return ()
+
+testDeferredWhen :: Fay ()
+testDeferredWhen = do
+ dependentResolved <- newResultBox "Deferred when resolves dependent Deferred"
+ dependentArgCorrect <- newResultBox "Deferred when returned Deferred receives correct argument"
+
+ d1 <- (deferred Undefined) :: Fay (Deferred Text)
+ d2 <- (deferred Undefined) :: Fay (Deferred Text)
+
+ dependent <- when_ [d1, d2] >>= done (\v -> do
+ passTest dependentResolved
+ if all (\(vi, ti) -> vi == ti) (zip v ["Burns night", "Hogmanay"]) then
+ passTest dependentArgCorrect
+ else
+ -- TODO - Bug somewhere. Please see console output
+ putStrLn (show v)
+ >>
+ putStrLn (show ["Burns night", "Hogmanay"])
+ >>
+ failTest dependentArgCorrect
+ return ()
+ )
+
+ resolve "Burns night" d1
+ resolve "Hogmanay" d2
+
+
+testDeferred :: Fay ()
+testDeferred = do
+ testDeferredConstructorBefore
+ testDeferredConstructor
+ testDeferredAlways
+ testDeferredDone
+ testDeferredFail
+ testDeferredPromise
+ testDeferredState
+ testDeferredThen
+ testDeferredWhen
+
+
+ s1 <- (ffi "\"resolved\"" :: Fay Text)
+ s2 <- (ffi "\"pending\"" :: Fay Text)
+ (ffi "console.log(%1)" :: Bool -> Fay ()) $ (s1 == s2)
+
+ case s1 of
+ s1 -> (ffi "console.log(\"OK\")" :: Fay ())
+ s2 -> (ffi "console.log(\"Weird\")" :: Fay ())
+
+
+
+
+-- testDeferredAlways
+
diff --git a/test.html b/test.html
index 54557e7..10f6939 100644
--- a/test.html
+++ b/test.html
@@ -15,10 +15,29 @@
{
background-color: #ddf;
}
+ #deferredTest .success
+ {
+ color: #080;
+ }
+ #deferredTest .failure
+ {
+ color: #F00
+ }
+ #deferredTest .pending
+ {
+ color: #F80
+ }
Replace this
+
+