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
+
+ + + + + +
testresult