axiom 0.4.6 → 0.4.7
raw patch · 4 files changed
+483/−302 lines, 4 filesdep ~transientdep ~transient-universePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: transient, transient-universe
API changes (from Hackage documentation)
- GHCJS.HPlay.View: attrs :: FormInput view => view -> Attribs -> view
- GHCJS.HPlay.View: class (Monoid view, Typeable view) => FormInput view where flink1 verb = flink verb (fromStr verb) foption1 val msel = foption val (fromStr val) msel
- GHCJS.HPlay.View: data CheckBoxes a
- GHCJS.HPlay.View: finput :: FormInput view => Name -> Type -> Value -> Checked -> OnClick1 -> view
- GHCJS.HPlay.View: flink :: FormInput view => JSString -> view -> view
- GHCJS.HPlay.View: flink1 :: FormInput view => JSString -> view
- GHCJS.HPlay.View: foption :: FormInput view => JSString -> view -> Bool -> view
- GHCJS.HPlay.View: foption1 :: FormInput view => JSString -> Bool -> view
- GHCJS.HPlay.View: formAction :: FormInput view => JSString -> JSString -> view -> view
- GHCJS.HPlay.View: fromStr :: FormInput view => JSString -> view
- GHCJS.HPlay.View: fromStrNoEncode :: FormInput view => String -> view
- GHCJS.HPlay.View: fselect :: FormInput view => JSString -> view -> view
- GHCJS.HPlay.View: ftag :: FormInput view => JSString -> view -> view
- GHCJS.HPlay.View: ftextarea :: FormInput view => JSString -> JSString -> view
- GHCJS.HPlay.View: inred :: FormInput view => view -> view
- GHCJS.HPlay.View: instance GHC.Base.Monoid a => GHC.Base.Monoid (GHCJS.HPlay.View.Radio a)
- GHCJS.HPlay.View: instance GHC.Classes.Eq GHCJS.HPlay.View.AlternativeBranch
- GHCJS.HPlay.View: instance GHC.Show.Show GHCJS.HPlay.View.AlternativeBranch
- GHCJS.HPlay.View: instance GHC.Show.Show GHCJS.HPlay.View.IDNUM
- GHCJS.HPlay.View: instance GHC.Show.Show GHCJS.HPlay.View.NeedForm
- GHCJS.HPlay.View: instance GHC.Show.Show a => GHC.Show.Show (GHCJS.HPlay.View.CheckBoxes a)
- GHCJS.HPlay.View: instance GHCJS.HPlay.View.FormInput Internal.Perch.Perch
+ GHCJS.HPlay.View: Radio :: a -> Radio a
+ GHCJS.HPlay.View: RadioId :: JSString -> RadioId
+ GHCJS.HPlay.View: data RadioId
+ GHCJS.HPlay.View: newtype CheckBoxes a
+ GHCJS.HPlay.View: newtype Radio a
+ GHCJS.HPlay.View: pack :: () => a
+ GHCJS.HPlay.View: type JSString = String
+ GHCJS.HPlay.View: unpack :: () => a
- GHCJS.HPlay.Cell: calc :: Widget ()
+ GHCJS.HPlay.Cell: calc :: Cloud ()
- GHCJS.HPlay.Cell: gcell :: JSString -> Widget Double
+ GHCJS.HPlay.Cell: gcell :: JSString -> Cloud Double
- GHCJS.HPlay.Cell: get :: Cell a -> TransIO a
+ GHCJS.HPlay.Cell: get :: () => Cell a -> TransIO a
- GHCJS.HPlay.Cell: mkscell :: JSString -> Maybe Double -> Expr Double -> Widget Double
+ GHCJS.HPlay.Cell: mkscell :: JSString -> Expr Double -> Cloud (Cell Double)
- GHCJS.HPlay.View: (<!) :: Widget a -> Attribs -> Widget a
+ GHCJS.HPlay.View: (<!) :: () => Widget a -> [(PropId, JSString)] -> Widget a
- GHCJS.HPlay.View: alert :: JSString -> IO ()
+ GHCJS.HPlay.View: alert :: (Show a, MonadIO m) => a -> m ()
- GHCJS.HPlay.View: edit :: Cloud a -> Cloud a
+ GHCJS.HPlay.View: edit :: () => Cloud a -> Cloud a
- GHCJS.HPlay.View: getRadio :: Monoid a => [Widget (Radio a)] -> Widget a
+ GHCJS.HPlay.View: getRadio :: [Widget (Radio a)] -> Widget a
- GHCJS.HPlay.View: runCont :: EventF -> StateIO (Maybe a)
+ GHCJS.HPlay.View: runCont :: () => EventF -> StateIO Maybe a
- GHCJS.HPlay.View: setRadio :: (Typeable a, Eq a, Show a) => a -> Widget (Radio a)
+ GHCJS.HPlay.View: setRadio :: (Typeable a, Eq a, Show a, Read a) => Bool -> a -> Widget (Radio a)
- GHCJS.HPlay.View: setRadioActive :: (Typeable a, Eq a, Show a) => a -> Widget (Radio a)
+ GHCJS.HPlay.View: setRadioActive :: (Typeable a, Eq a, Show a, Read a) => Bool -> a -> Widget (Radio a)
- GHCJS.HPlay.View: staticNav :: TransIO b -> TransIO b
+ GHCJS.HPlay.View: staticNav :: () => TransIO b -> TransIO b
Files
- README.md +151/−8
- axiom.cabal +4/−6
- src/GHCJS/HPlay/Cell.hs +114/−73
- src/GHCJS/HPlay/View.hs +214/−215
README.md view
@@ -1,14 +1,14 @@  ========== -[](http://hackage.haskell.org/package/ghcjs-hplay) -[](http://stackage.org/lts/package/ghcjs-hplay) -[](http://stackage.org/nightly/package/ghcjs-hplay) +[](http://hackage.haskell.org/package/axiom) +[](http://stackage.org/lts/package/axiom) +[](http://stackage.org/nightly/package/axiom) [](https://travis-ci.org/transient-haskell/axiom) For some examples, see the [transient-examples](https://github.com/transient-haskell/transient-examples) repository: [distributedApps.hs](https://github.com/transient-haskell/transient-examples/blob/master/distributedApps.hs) and [webapp.hs](https://github.com/transient-haskell/transient-examples/blob/master/webapp.hs) -The source code can be executed in the commmand line if you have docker installed. +The source code of these applications can be executed in the commmand line if you have docker installed. [](https://gitter.im/Transient-Transient-Universe-HPlay/Lobby?utm_source=share-link&utm_medium=link&utm_campaign=share-link) @@ -26,7 +26,7 @@ ============ The JS program compiled with GHCJS is sent to the browser, then it opens a websockets connection. Then the most useful primitive is `atRemote` wich execute his argument in the server and return the result back to the client (or viceversa, see below). The communication transport the variables necessary for executing the computation remotely. There is no explicit serialization neither communication. All is done implicitly. -`atRemote` can be executed inside itself, so a computation can jump from server to client and back. So a browser can be controlled by the server or the other way aroud. But the execution starts in the browser. There is very little data to transport to execute remotely. In the other side, streaming and reactivity in both directions is included with no additional considerations with `atRemote` and other primitives. Isn't awesome? +`atRemote` can be executed inside itself, so a computation can jump from server to client and back. So a browser can be controlled by the server or the other way aroud. But the execution starts in the browser. Only the variable values already computed are transported to execute remotely. In the other side, streaming and reactivity in both directions is included since `atRemote` and other primitives are reactive (see the [transient tutorial](https://github.com/transient-haskell/transient/wiki/Transient-tutorial)). In the other side, there is an experimental template editor to generate static HTML templates. The server can execute a rest route and bring the corresponding page template and the JS code to the browser, so web crawlers can find something to read. Also in Axiom everithing compose algebraically with standard applicative, alternative and monoidal operators, and also monadically: @@ -35,13 +35,156 @@ Events do not bubble up to the top like in the case of React. An event within a widget produce a monadic response that executes widgets down trough the monad without affecting the surrounding rendering not affected by the event. That is why Axiom does not need a Virtual DOM, and the logic of the application and the execution flow match, so it produces a clean and understandable code. look at the TODO app (it is client-side only) -tryplayg.herokuapp.com/try/todo.hs/edit +http://tryplayg.herokuapp.com/try/todo.hs/edit Axiom also implement widgets that works as spreadsheet cells, with formulas depending on other cells. These formulas can be executed in the server, so they have full access to databases, mumber crunching, map-reduce etc. This functionality need some testing. +How to install & run fast +========================= +use `initNode` to initalize the application. Example below. + + +If you have docker +------------------ +You can use a docker image that has GHC, GHCJS and Transient installed. First you should create this executable shell with this content and save it to an executable location: +``` +$ cat execthirdline.sh +command=`sed -n '3p' ${1} | sed 's/-- //'` +eval $command $1 $2 $3 +``` +Then add this to the head of your main source file: + +```csh +#!/usr/bin/env ./execthirdline.sh +-- compile an Axiom program with ghcjs and execute it with runghc +-- set -e && port=`echo ${3} | awk -F/ '{print $(3)}'` && docker run -it -p ${port}:${port} -v $(pwd):/work agocorona/transient:24-03-2017 bash -c "cd work && mkdir -p ./static && ghcjs ${1} -o static/out && runghc ${1} ${2} ${3}" +``` + +That header compiles the program with GHCJS and write the javascript code generated to the "static" folder and then executes the server program in interpreted mode with `runghc`. This is useful for rapid development, since you can modify the code and re-execute it very fast. + +To fully compile and execute the program, you can susbstitute `runghc` by `ghc` and execute the binary. The header would look like: + +```csh +#!/usr/bin/env ./execthirdline.sh +-- compile an Axiom program with ghcjs and with ghc, then execute the program +-- set -e && port=`echo ${3} | awk -F/ '{print $(3)}'` && docker run -it -p ${port}:${port} -v $(pwd):/work agocorona/transient:24-03-2017 bash -c "cd work && mkdir -p ./static && ghcjs ${1} -o static/out && ghc ${1} -o program && chmod 777 program && ./program ${2} ${3}" +``` +That header, besides executing the application, it would also create a "program" executable in your host machine (as well as an "static" folder with files needed for the client-side application. You can execute it natively in a linux distro in the way it will be described below. + +More complicated projects can be compiled and executed using `cabal` and `stack`. You can modify the header accordingly. + +For example, this is a program that is directly executable with docker + +```haskell +#!/usr/bin/env ./execthirdline.sh +-- compile it with ghcjs and execute it with runghc +-- set -e && port=`echo ${3} | awk -F/ '{print $(3)}'` && docker run -it -p ${port}:${port} -v $(pwd):/work agocorona/transient:24-03-2017 bash -c "cd work && mkdir -p ./static && ghcjs ${1} -o static/out && runghc ${1} ${2} ${3}" + +import Prelude hiding (div, id, span) +import Transient.Base +import GHCJS.HPlay.View +import Transient.Move +import Transient.Indeterminism +import Data.IORef +import Control.Concurrent (threadDelay) +import Control.Monad.IO.Class +import Data.Monoid + +main= keep . initNode . onBrowser $ do + local . render $ wlink () (h1 "hello fibonacci numbers") + + r <- atRemote $ do + r <- local . threads 1 . choose $ take 10 fibs + localIO $ print r + localIO $ threadDelay 1000000 + return r + + local . render . rawHtml $ (h2 r) + where + fibs = 0 : 1 : zipWith (+) fibs (tail fibs) :: [Int] -- fibonacci numb. definition +``` + +To execute the program: +``` +> chmod 777 YourSource.hs +> ./YourSource.hs -p start/<host>/<port> +``` + +where <host> <port> are defined by you. for example `./YourSource.hs -p start/localhost/8080` + +The program will be accessed from outside docker as a web application. Read the documentation of Docker for your platform about how to invoke it. + +If you want to run it in a host Linux machine, you can generate the browser code and the executable from docker in the way described above. Then in in the host you can execute it: + +``` +> ./program - p start/localhost/8080 +``` + +If you want to install Axiom in your host machine: +-------------------------------------------------- + +You need to install [stack](https://docs.haskellstack.org/en/stable/README/) and [ghcjs](https://github.com/ghcjs/ghcjs). The latter is not an easy task. + +Then install Axiom in stack/ghc: + +``` +> stack install axiom +``` + +This should install ghc and compile everithing. + +Alternatively, you can install [Haskell platform](https://www.haskell.org/platform/) and: + +``` +> cabal install axiom +``` + +In any case you need to install Axiom in GHCJS too: + +``` +> cabal install axiom --ghcjs +``` + +How to compile and run a program +================================ +``` +> mkdir static +> ghcjs yourProgram.hs -o static/out +> ghc yourProgram.hs + +> yourProgram -p start/yourhost/yourport + +``` + +How to run Distributed applications +======================== + +If your program use `inputNodes` to connect N server nodes, you must use additional parameters in the command line: + +in a computer or docker instance: +``` +> yourProgram -p start/host1/port1 +``` +In the same or another computer or docker instance: +``` +> yourProgram -p start/host2/port2/add/host1/port1/y +``` +in the same or another computer or docker instance: +``` +> yourProgram -p start/host3/port3/add/host1/port1/y +``` + +Be sure that the `host:port` ip addresses are reachable from all the machines. + +This connect all the server nodes among them. + +The web browser can point to any host:port of them. You must have the static folder (wich contains the generated javascript files) as well as the executable in all the locations. + +See [distrbutedApps](https://github.com/transient-haskell/transient-examples/blob/master/distributedApps.hs) that contain examples of distributed web applications. + Plans: ====== -Axiom is in the process of becoming a Server-side as well as Client-side library for creating Web application. The last release support page templates for the creation of server-side content. +Axiom web nodes are client side applications. So dHTML rendering happens on the browser. It is intended to implement server side rendering as well as multipage navigation. The last release support page navigation and page templates for the creation of server-side content. -In the future it will manage routes in the server side besides client side, and will generate dinamic HTML content in the server as well as in the client. +
axiom.cabal view
@@ -1,6 +1,6 @@ name: axiom -version: 0.4.6 +version: 0.4.7 cabal-version: >=1.10 build-type: Simple @@ -14,8 +14,6 @@ synopsis: Web EDSL for running in browsers and server nodes using transient description: Client-and Server-side Haskell framework that compiles to javascript with the GHCJS compiler and run over Transient. See homepage category: Web -cabal-version: >=1.10 -build-type: Simple stability: experimental data-dir: "" @@ -23,14 +21,14 @@ source-repository head type: git - location: http://github.com/agocorona/ghcjs-hplay + location: http://github.com/agocorona/axiom library build-depends: base > 4.0 && <6.0 , transformers -any , containers -any - , transient >= 0.5.9.2 - , transient-universe >= 0.4.6.1 + , transient >= 0.6.0.1 + , transient-universe >= 0.5.0.0 , mtl -any , ghcjs-perch >= 0.3.3
src/GHCJS/HPlay/Cell.hs view
@@ -14,23 +14,26 @@ {-# LANGUAGE TypeSynonymInstances, FlexibleInstances, OverloadedStrings, CPP, ScopedTypeVariables #-} module GHCJS.HPlay.Cell(Cell(..),boxCell,bcell,(.=),get,mkscell,scell, gcell, calc) where import Transient.Internals +import Transient.Move --hiding (JSString) import GHCJS.HPlay.View import Data.Typeable import Unsafe.Coerce import qualified Data.Map as M hiding ((!)) -import Control.Monad.IO.Class +import Control.Monad.State hiding (get) import Control.Monad +import Data.Monoid import Data.List import Control.Exception - +import Data.IORef +import System.IO.Unsafe #ifdef ghcjs_HOST_OS import Data.JSString hiding (empty) #else -type JSString = String +-- type JSString = String #endif @@ -73,8 +76,10 @@ typeIO :: (ElemID -> IO (Maybe a)) -> a typeIO = undefined +typestring :: TypeRep typestring= typeOf (undefined :: String) +show1 :: (Show a, Typeable a) => a -> String show1 x= if typeOf x== typestring then unsafeCoerce x else show x @@ -127,66 +132,71 @@ -- | within a `mkscell` formula, `gcell` get the the value of another cell using his name. -- -- see http://tryplayg.herokuapp.com/try/spreadsheet.hs/edit -gcell :: JSString -> Widget Double -gcell n= Widget $ do - Vars vars <- getSData <|> return(Vars M.empty ) -- liftIO $ readIORef rvars +gcell :: JSString -> Cloud Double +gcell n= loggedc $ do + -- onAll $ do + -- cutExceptions + -- reportBack + vars <- getCloudState rvars <|> return M.empty -- liftIO $ readIORef rvars + localIO $ print ("gcell", n) case M.lookup n vars of - Just exp -> do inc n exp; exp + Just exp -> do inc ; exp !> "executing exp" Nothing -> error $ "cell not found: " ++ show n where - inc n exp= do - Tries tries maxtries<- getSData <|> do - Exprs exprs <- getSData - return . Tries 0 $ 3 * (M.size $ exprs) + inc = do + Tries tries maxtries <- getCloudState rtries <|> error "no tries" --do + -- Exprs exprs <- getCloudState + -- return . Tries 0 $ 3 * (M.size $ exprs) + localIO $ print tries if tries <= maxtries - then setData $ Tries (tries+1) maxtries - else back Loop + then localIO $ writeIORef rtries $ Tries (tries+1) maxtries + else local $ do + -- liftIO $ print "back" + back Loop data Loop= Loop deriving (Show,Typeable) instance Exception Loop -- a parameter is a function of all of the rest -type Expr a = TransIO a +type Expr a = Cloud a data Tries= Tries Int Int deriving Typeable ---rtries= unsafePerformIO $ newIORef $ (0::Int) +rtries= unsafePerformIO $ newIORef $ Tries 0 0 --maxtries= 3 * (M.size $ unsafePerformIO $ readIORef rexprs) -newtype Exprs= Exprs (M.Map JSString (Expr Double)) ---rexprs :: IORef (M.Map JSString (Expr Double)) ---rexprs= unsafePerformIO $ newIORef M.empty -- initial expressions +-- newtype Exprs= Exprs (M.Map JSString (Expr Double)) +rexprs :: IORef (M.Map JSString (Expr Double)) +rexprs= unsafePerformIO $ newIORef M.empty -- initial expressions -newtype Vars= Vars (M.Map JSString (Expr Double)) ---rvars :: IORef (M.Map JSString (Expr Double)) ---rvars= unsafePerformIO $ newIORef M.empty -- expressions actually used for each cell. +-- newtype Vars= Vars (M.Map JSString (Expr Double)) +rvars :: IORef (M.Map JSString (Expr Double)) +rvars= unsafePerformIO $ newIORef M.empty -- expressions actually used for each cell. -- initially, A mix of reexprs and rmodified -- and also contains the result of calculation -newtype Modified= Modified (M.Map JSString (Expr Double)) deriving Typeable ---rmodified :: IORef (M.Map JSString (Expr Double)) ---rmodified= unsafePerformIO $ newIORef M.empty -- cells modified by the user or by the loop detection mechanism +-- newtype Modified= Modified (M.Map JSString (Expr Double)) deriving Typeable +rmodified :: IORef (M.Map JSString ( Double)) +rmodified= unsafePerformIO $ newIORef M.empty -- cells modified by the user or by the loop detection mechanism -- | make a spreadsheet cell. a spreadsheet cell is an input-output box that takes input values from -- the user, has an expression associated and display the result value after executing `calc` -- -- see http://tryplayg.herokuapp.com/try/spreadsheet.hs/edit -mkscell :: JSString -> Maybe Double -> Expr Double -> Widget Double -mkscell name val expr= mk (scell name expr) val +mkscell :: JSString -> Expr Double -> Cloud (Cell Double) +mkscell name expr= do + exprs <- onAll $ liftIO (readIORef rexprs) <|> return ( M.empty) -- readIORef rexprs + onAll $ liftIO $ writeIORef rexprs $ M.insert name expr exprs + return $ scell name expr ---both mx= local $ runCloud mx <** runCloud ( atRemote (clustered $ mx >> empty :: Cloud())) scell :: JSString -> Expr Double -> Cell Double scell id expr= Cell{ mk= \mv -> Widget $ do - Exprs exprs <- getSData <|> return (Exprs M.empty) -- readIORef rexprs - setData . Exprs $ M.insert id expr exprs - - r <- norender $ getParam (Just id) "text" mv `fire` OnKeyUp - - Modified mod <- getSData <|> return(Modified M.empty) - setData . Modified $ M.insert id (return r) mod + r <- norender $ getParam (Just id) "text" mv `fire` OnChange + mod <- liftIO (readIORef rmodified) <|> return( M.empty) + liftIO $ writeIORef rmodified $ M.insert id r mod return r , setter= \x -> withElem id $ \e -> setProp e "value" (toJSString $ show1 x) @@ -197,66 +207,97 @@ + -- | executes the spreadsheet adjusting the vaules of the cells created with `mkscell` and solving loops -- -- see http://tryplayg.herokuapp.com/try/spreadsheet.hs/edit -calc :: Widget () -calc= Widget $ do - st <- getCont - return() `onBack` (\(e::Loop) -> do removeVar st e; forward Loop ) +calc :: Cloud () +calc= do + mod <- localIO $ readIORef rmodified + onAll $ liftIO $ print ("LENGTH MOD", M.size mod) + onAll $ liftIO $ print "setCloudState modified" + setCloudState rmodified mod + exprs <- getCloudState rexprs + onAll $ liftIO $ print "setCloudState exprs" + setCloudState rexprs exprs + onAll $ liftIO $ print "setCloudState rvars" - Modified nvs <- getSData <|> error "no modified" -- liftIO $ readIORef rmodified + setCloudState rvars M.empty - when (not $ M.null nvs) $ do - values <- calc1 - mapM_ (\(n,v) -> boxCell n .= v) values + onAll $ return() `onBack` (\(e::Loop) -> runCloud' $ do localIO $ print "REMOVEVAR"; removeVar e; local (forward Loop) ) + exprs <- getCloudState rexprs <|> error "no exprs" + onAll $ liftIO $ print "setCloudState rtries" --- liftIO $ writeIORef rmodified M.empty + setCloudState rtries $ Tries 0 $ 3 * (M.size $ exprs) + nvs <- getCloudState rmodified <|> error "no modified" -- liftIO $ readIORef rmodified + onAll $ liftIO $ print ("LENGTH NVS", M.size nvs) + when (not $ M.null nvs) $ calc1 + --values <- calc1 + --localIO $ print "NEW CALC" + --local $ mapM_ (\(n,v) -> boxCell n .= v) values + onAll $ liftIO $ print "setCloudState modified" + setCloudState rmodified M.empty + where - run' st x= runTransState st x >> return () - calc1 :: TransIO [(JSString,Double)] + --calc1 :: Expr [(JSString,Double)] calc1= do - setData $ Tries 0 -- liftIO $ writeIORef rtries 0 - Exprs cells <- getSData <|> error "no exprs" -- liftIO $ readIORef rexprs - Modified nvs <- getSData <|> error "mo modified2" -- liftIO $ readIORef rmodified - setData . Vars $ M.union nvs cells - solve + return () !> "CALC1" + cells <- getCloudState rexprs <|> error "no exprs" -- liftIO $ readIORef rexprs + nvs <- getCloudState rmodified <|> error "no modified2" -- liftIO $ readIORef rmodified + onAll $ liftIO $ print "setCloudState vars" ---solve :: M.Map JSString (Widget a) -> Widget (M.Map JSString a) -solve :: TransIO [(JSString,Double)] -solve = do - Vars vars <- getSData <|> error "no vars" -- liftIO $ readIORef rvars - mapM (solve1 vars) $ M.toList vars - where + setCloudState rvars $ M.union (M.map return nvs) cells + solve + + --solve :: Expr [(JSString,Double)] + solve = do + vars <- getCloudState rvars <|> error "no vars" -- liftIO $ readIORef rvars + onAll $ liftIO $ print $ ("LENGHT VARS", M.size vars) + mapM_ (solve1 vars) $ M.toList vars + where solve1 vars (k,f)= do + localIO $ print ("solve1",k) x <- f - setData . Vars $ M.insert k (return x) vars - return (k,x) :: TransIO (JSString,Double) + localIO $ print ("setcloudstate var",k,x) + local $ boxCell k .= x + setCloudState rvars $ M.insert k (return x) vars + return () -- (k,x) :: Expr (JSString,Double) +setCloudState r v= allNodes $ writeIORef r v +getCloudState r= onAll . liftIO $ readIORef r --- removeVar :: EventF -> SomeException -> IO () -- [(JSString,Double)] -removeVar st = \(e:: Loop) -> do -- runCloud $ both $ localIO $ do - Modified nvs <- getSData <|> error "no modified 3"-- readIORef rmodified - Exprs exprs <- getSData <|> error " no Exprs2" --readIORef rexprs +-- removeVar ::SomeException -> IO () -- [(JSString,Double)] +removeVar = \(e:: Loop) -> do + nvs <- getCloudState rmodified <|> error "no modified 3"-- readIORef rmodified + -- mapM (\n -> snd n >>= \v -> localIO $ print (fst n,v)) $ M.toList nvs + exprs <- getCloudState rexprs <|> error " no Exprs2" --readIORef rexprs - case M.keys exprs \\ M.keys nvs of - [] -> error "non solvable circularity in cell dependencies" - (name:_) -> do - mv <- liftIO $ getID name + case M.keys exprs \\ M.keys nvs of + [] -> error "non solvable circularity in cell dependencies" + (name:_) -> do + localIO $ print ("removeVar",name) - case mv of - Nothing -> return () - Just v -> do - setData . Modified $ M.insert name ( return v) nvs - return () -- !> ("using",v) - norender calc -- runTransState st (norender calc) - return () + mv <- localIO $ getID name + case mv of + Nothing -> return () + Just v -> do + onAll $ liftIO $ print "setCloudState modified" + setCloudState rmodified $ M.insert name v nvs + return () + +allNodes :: IO () -> Cloud () +allNodes mx= loggedc $ (localIO mx) <> (atRemote $ (localIO $ print "UPDATE" >> mx)) + +--atBrowser mx= if isBrowserInstance then mx else atRemote mx + +--atServer mx= if not isBrowserInstance then mx else atRemote mx + -- http://blog.sigfpe.com/2006/11/from-l-theorem-to-spreadsheet.html -- loeb :: Functor f => f (t -> a) -> f a -- loeb x = fmap (\a -> a (loeb x)) x
src/GHCJS/HPlay/View.hs view
@@ -80,7 +80,6 @@ , pass -- * Low-level and Internals , ElemID - , FormInput(..) , getNextId , genNewId , continuePerch @@ -97,10 +96,12 @@ , getValue -- * Re-exported , module Control.Applicative - , module GHCJS.Perch + , module GHCJS.Perch -- remove ,CheckBoxes(..) ,edit + ,JSString,pack, unpack + ,RadioId(..), Radio(..) ) where @@ -110,7 +111,7 @@ import Transient.Move.Utils import qualified Prelude(id,span,div) #ifndef ghcjs_HOST_OS -import Transient.Move(ParseContext(..)) +import Transient.Parse hiding(parseString) import Data.Char(isSpace) import System.Directory import System.IO.Error @@ -120,7 +121,7 @@ #endif import Control.Monad.State -import qualified Data.Map as M +-- import qualified Data.Map as M import Control.Applicative import Control.Concurrent @@ -147,10 +148,12 @@ import GHCJS.Types import Transient.Move hiding (pack) -import Data.JSString as JS hiding (empty, center,span, strip,foldr,head,tail) +import qualified Data.JSString as JS hiding (empty, center,span, strip,foldr,head) +import Data.JSString (pack,unpack,toLower) #else +import Data.List as JS hiding (span) import GHCJS.Perch hiding (JSVal, JsEvent (..), eventName, option,head, map) -import Transient.Move hiding (JSString, pack) +import Transient.Move #endif #ifndef ghcjs_HOST_OS @@ -195,16 +198,17 @@ getName = undefined #endif -elemBySeq :: (MonadState EventF m,MonadIO m) => JSString -> m (Maybe Elem) +elemBySeq :: (MonadState EventF m, MonadIO m) => JSString -> m (Maybe Elem) #ifdef ghcjs_HOST_OS -elemBySeq id= do - IdLine _ id1 <- getData `onNothing` error ("not found: " ++ show id) -- return (IdLine "none") - return () !> ("elemBySeq",id1, id) - liftIO $ do - re <- elemBySeqDOM id1 $ JS.takeWhile (/='p') id - fromJSVal re +elemBySeq id = do + IdLine _ id1 <- getData `onNothing` error ("not found: " ++ show id) -- return (IdLine "none") + return () !> ("elemBySeq",id1, id) + liftIO $ do + let id2= JS.takeWhile (/='p') id + re <- elemBySeqDOM id1 id2 + fromJSVal re #else -elemBySeq _= return Nothing +elemBySeq _ = return Nothing #endif #ifdef ghcjs_HOST_OS @@ -232,7 +236,7 @@ Nothing -> error ("withElem: not found"++ fromJSString id) Just e -> f e -data NeedForm= HasForm | HasElems | NoElems deriving Show +--data NeedForm= HasForm | HasElems | NoElems deriving Show type ElemID= JSString @@ -241,49 +245,40 @@ instance Functor Widget where fmap f mx= Widget. Transient $ fmap (fmap f) . runTrans $ norender mx ---instance Alternative Widget where --- empty= Widget empty --- (Widget x) <|> (Widget y)= Widget $ Transient $ do --- rx <- runTrans x --- ry <- runTrans y --- return $ rx <|> ry instance Applicative Widget where pure= return - Widget (Transient x) <*> Widget (Transient y) = Widget .Transient $ do - mn <- getData - mrepeat <-getData - - when (isJust mn && mrepeat == Just ExecEvent) $ do - let IDNUM n = fromJust mn in modify $ \s -> s{mfSequence= n} - -- !> ("SET IDMUN", n) - delData $ IDNUM 0 - mx <- x - my <- y - return $ mx <*> my + Widget (Transient x) <*> Widget (Transient y) = Widget . Transient $ do + getData `onNothing` do + cont <- get + let al= Alternative cont + setData $ Alternative cont + return al + mx <- x + my <- y + return $ mx <*> my instance Monoid a => Monoid (Widget a) where mempty= return mempty - mappend x y= (<>) <$> x <*> y + mappend x y= do + (<>) <$> x <*> y instance AdditionalOperators Widget where - Widget (Transient x) <** Widget (Transient y)= - Widget . Transient $ do - mn <- getData - mrepeat <-getData - liftIO $ print ("IDNUM",mn) - when (isJust mn && mrepeat == Just ExecEvent) $ do - let IDNUM n = fromJust mn in modify $ \s -> s{mfSequence= n} --- !> ("SET IDMUN", n) - delData $ IDNUM 0 - mx <- x - y - return mx + Widget (Transient x) <** Widget (Transient y)= Widget . Transient $ do + getData `onNothing` do + cont <- get + let al= Alternative cont + setData $ Alternative cont + return al + + mx <- x + y + return mx (<***) x y= Widget $ norender x <*** norender y @@ -335,22 +330,22 @@ -- instance of this class. -- See "MFlow.Forms.Blaze.Html for the instance for blaze-html" "MFlow.Forms.XHtml" for the instance -- for @Text.XHtml@ and MFlow.Forms.HSP for the instance for Haskell Server Pages. -class (Monoid view,Typeable view) => FormInput view where - fromStr :: JSString -> view - fromStrNoEncode :: String -> view - ftag :: JSString -> view -> view - inred :: view -> view - flink :: JSString -> view -> view - flink1:: JSString -> view - flink1 verb = flink verb (fromStr verb) - finput :: Name -> Type -> Value -> Checked -> OnClick1 -> view - ftextarea :: JSString -> JSString -> view - fselect :: JSString -> view -> view - foption :: JSString -> view -> Bool -> view - foption1 :: JSString -> Bool -> view - foption1 val msel= foption val (fromStr val) msel - formAction :: JSString -> JSString -> view -> view - attrs :: view -> Attribs -> view +-- class (Monoid view,Typeable view) => FormInput view where +-- fromStr :: JSString -> view +-- fromStrNoEncode :: String -> view +-- ftag :: JSString -> view -> view +-- inred :: view -> view +-- flink :: JSString -> view -> view +-- flink1:: JSString -> view +-- flink1 verb = flink verb (fromStr verb) +-- finput :: Name -> Type -> Value -> Checked -> OnClick1 -> view +-- ftextarea :: JSString -> JSString -> view +-- fselect :: JSString -> view -> view +-- foption :: JSString -> view -> Bool -> view +-- foption1 :: JSString -> Bool -> view +-- foption1 val msel= foption val (fromStr val) msel +-- formAction :: JSString -> JSString -> view -> view +-- attrs :: view -> Attribs -> view type Attribs= [(JSString, JSString)] @@ -368,17 +363,18 @@ fromValidated (NotValidated s err)= error $ "fromValidated: NotValidated "++ s getParam1 :: ( Typeable a, Read a, Show a) - => JSString -> StateIO (ParamResult Perch a) -getParam1 par = do + => Bool -> JSString -> StateIO (ParamResult Perch a) +getParam1 exact par = do isTemplate <- liftIO $ readIORef execTemplate if isTemplate then return NoParam else do - me <- elemBySeq par !> par --- !> ("looking for " ++ show par) + + me <- if exact then elemById par else elemBySeq par + !> ("looking for " ++ show par) case me of Nothing -> return NoParam Just e -> do - v <- getValue e -- !!> ("exist" ++ show par) - readParam v -- !!> ("getParam for "++ show v) + v <- getValue e -- !!> ("exist" ++ show par) + readParam v -- !!> ("getParam for "++ show v) type Params= Attribs @@ -397,14 +393,18 @@ maybeRead str= do let typeofx = typeOf x - if typeofx == typeOf ( undefined :: String) then + if typeofx == typeOf ( undefined :: String) then return . Validated $ unsafeCoerce str -- !!> ("maybread string " ++ str) - else case reads $ str of -- -- !!> ("read " ++ str) of + else if typeofx == typeOf(undefined :: JSString) then + return . Validated $ unsafeCoerce $ pack str + else case reads $ str of -- -- !!> ("read " ++ str) of [(x,"")] -> return $ Validated x -- !!> ("readsprec" ++ show x) _ -> do - let err= inred . fromStr $ toJSString $ "can't read \"" ++ str ++ "\" as type " ++ show (typeOf x) + let err= inred $ "can't read \"" ++ str ++ "\" as type " ++ show (typeOf x) return $ NotValidated str err + + -- | Validates a form or widget result against a validating procedure -- -- @getOdd= getInt Nothing `validate` (\x -> return $ if mod x 2==0 then Nothing else Just "only odd numbers, please")@ @@ -420,7 +420,7 @@ me <- val x case me of Just str -> do - liftIO $ withElem idn $ build $ clear >> inred str + liftIO $ withElem idn $ build $ clear >> (inred str) return Nothing Nothing -> do liftIO $ withElem idn $ build clear @@ -439,7 +439,6 @@ #ifdef ghcjs_HOST_OS genNewId :: (MonadState EventF m, MonadIO m) => m JSString genNewId= do --- Prefix pre <- getData `onNothing` return (Prefix "") r <- liftIO $ atomicModifyIORef rprefix (\n -> (n+1,n)) n <- genId let nid= toJSString $ ('n':show n) ++ ('p':show r) @@ -502,69 +501,56 @@ inputPassword :: Widget String inputPassword= getPassword -newtype Radio a= Radio a deriving Monoid - +newtype Radio a= Radio a +data RadioId= RadioId JSString deriving Typeable -- | Implement a radio button --- the parameter is the name of the radio group -setRadio :: (Typeable a, Eq a, Show a) => - a -> Widget (Radio a) -setRadio v = Widget $ Transient $ do - RadioId n <- getData `onNothing` error "setRadio out of getRadio" +setRadio :: (Typeable a, Eq a, Show a,Read a) => + Bool -> a -> Widget (Radio a) +setRadio ch v = Widget $ Transient $ do + RadioId name <- getData `onNothing` error "setRadio out of getRadio" id <- genNewId - st <- get --- setData HasElems -- only for MFlow - me <- elemBySeq id + me <- elemBySeq id checked <- case me of - Nothing -> return "" - Just e -> liftIO $ getProp e "checked" - let strs= if checked=="true" then Just v else Nothing --- let mn= if null strs then False else True - ret= fmap Radio strs - str = if typeOf v == typeOf(undefined :: String) + Nothing -> return "" + Just e -> liftIO $ getProp e "checked" + + let str = if typeOf v == typeOf(undefined :: String) then unsafeCoerce v else show v - addSData - ( finput id "radio" (toJSString str) ( isJust strs ) Nothing `attrs` [("name",n)] :: Perch) + ( finput id "radio" (toJSString str) ch Nothing `attrs` [("name",name)] :: Perch) + + if checked == "true" !> ("val",v) then Just . Radio . read1 . unpack <$> liftIO (getProp (fromJust me) "value") else return Nothing + where + read1 x=r + where + r= if typeOf r== typeOf (undefined :: String) then unsafeCoerce x + else read x - return ret +setRadioActive :: (Typeable a, Eq a, Show a,Read a) => + Bool -> a -> Widget (Radio a) +setRadioActive ch rs = setRadio ch rs `raiseEvent` OnClick -setRadioActive :: (Typeable a, Eq a, Show a) => - a -> Widget (Radio a) -setRadioActive rs = setRadio rs `raiseEvent` OnClick -data RadioId= RadioId JSString deriving Typeable - -- | encloses a set of Radio boxes. Return the option selected getRadio - :: Monoid a => [Widget (Radio a)] -> Widget a -getRadio ws = Widget $ Transient $ do - id <- genNewId - setData $ RadioId id - fs <- mapM runView ws - let mx = mconcat fs - delData $ RadioId id - return $ fmap (\(Radio r) -> r) mx + :: [Widget (Radio a)] -> Widget a +getRadio ws = do + id <- genNewId + setData $ RadioId id + Radio x <- foldr (<|>) empty ws <*** delData (RadioId id) + return x -data CheckBoxes a= CheckBoxes [a] deriving Show - -instance Monoid (CheckBoxes a) where - mappend (CheckBoxes xs) (CheckBoxes ys)= CheckBoxes $ xs ++ ys - mempty= CheckBoxes [] - +newtype CheckBoxes a= CheckBoxes [a] deriving Monoid --- | Display a text box and return the value entered if it is readable( Otherwise, fail the validation) +-- | present a checkbox setCheckBox :: (Typeable a , Show a) => Bool -> a -> Widget (CheckBoxes a) setCheckBox checked' v= Widget . Transient $ do n <- genNewId - st <- get --- setData HasElems me <- elemBySeq n - - let showv= toJSString (if typeOf v == typeOf (undefined :: String) then unsafeCoerce v else show v) @@ -577,27 +563,26 @@ checked <- liftIO $ getProp e "checked" return . Just . CheckBoxes $ if checked=="true" then [v] else [] - +-- Read the checkboxes getCheckBoxes :: Show a => Widget (CheckBoxes a) -> Widget [a] -getCheckBoxes w = Widget $ Transient $ do - mrs <- runView w - case mrs of - Nothing -> return Nothing - Just(CheckBoxes rs ) -> return $ Just rs +getCheckBoxes w = do + CheckBoxes rs <- w + return rs + whidden :: (Read a, Show a, Typeable a) => a -> Widget a whidden x= res where res= Widget . Transient $ do - n <- genNewId - let showx= case cast x of - Just x' -> x' - Nothing -> show x - r <- getParam1 n `asTypeOf` typef res - addSData (finput n "hidden" (toJSString showx) False Nothing :: Perch) - return (valToMaybe r) - where - typef :: Widget a -> StateIO (ParamResult Perch a) - typef = undefined + n <- genNewId + let showx= case cast x of + Just x' -> x' + Nothing -> show x + r <- getParam1 False n `asTypeOf` typef res + addSData (finput n "hidden" (toJSString showx) False Nothing :: Perch) + return (valToMaybe r) + where + typef :: Widget a -> StateIO (ParamResult Perch a) + typef = undefined @@ -628,9 +613,9 @@ else if typeOf v== typeOf (undefined :: JSString) then unsafeCoerce v else toJSString $ show v -- !!> "show" - setData HasElems - r <- getParam1 tolook - + -- setData HasElems + r <- getParam1 (isJust look) tolook + case r of Validated x -> do addSData (finput tolook type1 (nvalue $ Just x) False Nothing :: Perch) ; return $ Just x -- !!> "validated" NotValidated s err -> do addSData (finput tolook type1 (toJSString s) False Nothing <> err :: Perch); return Nothing @@ -645,7 +630,7 @@ getMultilineText nvalue = res where res= Widget. Transient $ do tolook <- genNewId !> "GETMULTI" - r <- getParam1 tolook `asTypeOf` typef res + r <- getParam1 False tolook `asTypeOf` typef res case r of Validated x -> do addSData (ftextarea tolook $ toJSString x :: Perch); return $ Just x !> "VALIDATED" NotValidated s err -> do addSData (ftextarea tolook (toJSString s) :: Perch); return Nothing !> "NOTVALIDATED" @@ -677,7 +662,7 @@ tolook <- genNewId -- st <- get -- setData HasElems - r <- getParam1 tolook `asTypeOf` typef res + r <- getParam1 False tolook `asTypeOf` typef res -- setData $ fmap MFOption $ valToMaybe r runView $ fselect tolook <<< opts -- @@ -799,10 +784,11 @@ staticNav x= do Path paths <- getState <|> return (Path []) x <*** setState (Path paths) + -- | template link. Besides the wlink behaviour, it loads the page from the server if there is any -- --- the page many have been saved with `edit` +-- the page may have been saved with `edit` tlink :: (Show a, Typeable a) => a -> Perch -> Widget a tlink x v= Widget $ @@ -897,7 +883,6 @@ else do -- setData ExecTemplate !> "SET EXECTEMPLATE 2" -- liftIO $ writeIORef execTemplate True - liftIO $ print (pathelem, segment) if unpack pathelem /= segment then empty else do liftIO $ writeIORef rReadIndexPath $ l + 1 asynchronous @@ -960,28 +945,28 @@ where isSeparator c= c == '>' - dropSpaces= parse $ \str ->((),BS.dropWhile isSpace str) + --dropSpaces= parse $ \str ->((),BS.dropWhile isSpace str) -tTakeWhile :: (Char -> Bool) -> TransIO BS.ByteString -tTakeWhile cond= parse (span' cond) - where - span' cond s= - let (h,t) = BS.span cond s - c= BS.head t - in (BS.snoc h c,BS.drop 1 t) +-- tTakeWhile :: (Char -> Bool) -> TransIO BS.ByteString +-- tTakeWhile cond= parse (span' cond) +-- where +-- span' cond s= +-- let (h,t) = BS.span cond s +-- c= BS.head t +-- in (BS.snoc h c,BS.drop 1 t) -parse :: (BS.ByteString -> (b, BS.ByteString)) -> TransIO b -parse split= do - ParseContext readit str <- getSData - <|> error "parse: ParseContext not found" - :: TransIO (ParseContext BS.ByteString) +-- parse :: (BS.ByteString -> (b, BS.ByteString)) -> TransIO b +-- parse split= do +-- ParseContext readit str <- getSData +-- <|> error "parse: ParseContext not found" +-- :: TransIO (ParseContext BS.ByteString) - if BS.null str then empty else do - let (ret,str3) = split str - setData $ ParseContext readit str3 - return ret +-- if BS.null str then empty else do +-- let (ret,str3) = split str +-- setData $ ParseContext readit str3 +-- return ret @@ -1061,7 +1046,7 @@ rest <- getData `onNothing` return mempty delData rest mx <- runView widget - fs <- getData `onNothing` return mempty + fs <- getData `onNothing` return (mempty :: Perch) setData $ rest <> (fs `attrs` attribs :: Perch) return mx @@ -1112,41 +1097,42 @@ ------------------------- -instance FormInput Perch where - fromStr = toElem - fromStrNoEncode = toElem - ftag n v = nelem n `child` v +fromStr = toElem +-- fromStrNoEncode = toElem +ftag n v = nelem n `child` v - attrs tag [] = tag - attrs tag (nv:attribs) = attrs (attr tag nv) attribs +attrs tag [] = tag +attrs tag (nv:attribs) = attrs (attr tag nv) attribs - inred msg= ftag "b" msg `attrs` [("style","color:red")] +inred msg= ftag "b" msg `attrs` [("style","color:red")] - finput n t v f c= +finput n t v f c= let - tag= ftag "input" mempty `attrs` [("type", t), ("id", n), ("value", v)] - tag1= if f then tag `attrs` [("checked", "")] else tag - in case c of Just s -> tag1 `attrs` [("onclick", s)] ; _ -> tag1 + tag= input ! atr "type" t ! id n ! atr "value" v + tag1= if f then tag ! atr "checked" "" else tag + in case c of Just s -> tag1 ! atr "onclick" s; _ -> tag1 - ftextarea nam text= - ftag "textarea" mempty `attrs` [("id", nam)] `child` text +ftextarea nam text= + textarea ! id nam $ text - fselect nam list = ftag "select" mempty `attrs` [("id", nam)] `child` list - foption name v msel= - let tag= ftag "option" mempty `attrs` [("value", name)] `child` v - in if msel then tag `attrs` [("selected", "")] else tag +fselect nam list = select ! id nam $ list - formAction action method1 form = ftag "form" mempty `attrs` [("acceptCharset", "UTF-8") - ,( "action", action) - ,("method", method1)] - `child` form +foption name v msel= + let tag= nelem "option" ! atr "value" name `child` v + in if msel then tag ! atr "selected" "" else tag - flink v str = ftag "a" mempty `attrs` [("href", v)] `child` str +-- formAction action method1 form = ftag "form" mempty `attrs` [("acceptCharset", "UTF-8") +-- ,( "action", action) +-- ,("method", method1)] +-- `child` form +-- flink v str = ftag "a" mempty `attrs` [("href", v)] `child` str + + --------------------------- data EvData = NoData | Click Int (Int, Int) | Mouse (Int, Int) | MouseOut | Key Int deriving (Show,Eq,Typeable) @@ -1369,7 +1355,7 @@ -- first identifier for an applicative widget expression -- needed for applictives in the widget monad that are executed differently than in the TransIO monad -newtype IDNUM = IDNUM Int deriving Show +-- newtype IDNUM = IDNUM Int deriving Show data Event= forall ev.IsEvent ev => Event ev @@ -1399,7 +1385,7 @@ raiseEvent :: IsEvent event => Widget a -> event -> Widget a #ifdef ghcjs_HOST_OS raiseEvent w event = Widget . Transient $ do - cont <- get -- !> "raise" + Alternative cont <- getData `onNothing` (Alternative <$> get) let iohandler :: EventData -> IO () iohandler eventdata =do runStateT (setData eventdata >> runCont' cont) cont -- !> "runCont INIT" @@ -1408,17 +1394,19 @@ id <- genNewId let id'= JS.takeWhile (/='p') id addEventList id' event iohandler - - me <- elemBySeq id' -- !> ("adding event to", id') - case me of + template <-liftIO $ readIORef execTemplate + if not template then runView $ addEvent id event iohandler <<< w + else do + me <- elemBySeq id' -- !> ("adding event to", id') + case me of - Nothing -> runView $ addEvent id event iohandler <<< w -- !> "do not exist, creating elem" - Just e -> do - mr <- getData -- !> "exist adding event to current element" - when (mr /= Just ExecEvent) $ liftIO (buildHandler e event iohandler) - r <- runView w - delData noHtml - return r + Nothing -> runView $ addEvent id event iohandler <<< w !> "do not exist, creating elem" + Just e -> do + mr <- getData !> "exist adding event to current element" + when (mr /= Just ExecEvent) $ liftIO (buildHandler e event iohandler) + r <- runView w + delData noHtml + return r where -- to restore event handlers when a new template is loaded @@ -1433,9 +1421,8 @@ runCont' cont= do --- mn <- getData --- when (isJust mn) $ let IDNUM n = fromJust mn in modify $ \s -> s{mfSequence= n} setData ExecEvent -- !> "REPEAT: SET EXECEVENT" + liftIO $ writeIORef execTemplate False mr <- runClosure cont return () @@ -1484,6 +1471,7 @@ wraw v `wake` event e@(EventData typ _) <- getEventData guard (eventName event== typ) + return e @@ -1528,7 +1516,7 @@ runWidget w body -data AlternativeBranch= Alternative deriving (Typeable, Eq, Show) +newtype AlternativeBranch= Alternative EventF deriving Typeable -- | executes the computation and add the effect of "hanging" the generated rendering from the one generated by the -- previous `render` sentence, or from the body of the document, if there isn't any. If an event happens within @@ -1555,7 +1543,7 @@ ma <- getData mw <- getData - id1 <- if (ma == Just Alternative || mw == Just WasParallel) !> (ma,mw) + id1 <- if (isJust (ma :: Maybe AlternativeBranch) || mw == Just WasParallel ) !> (mw) then do id3 <- do id3 <- genNewId !> "ALTERNATIVE" @@ -1568,19 +1556,19 @@ me <- liftIO $ elemById id1' >>= \x -> case x of Nothing -> return Nothing - Just x -> nextSibling x + Just x -> nextSibling x case me of Nothing -> return id3 -- should not happen - Just e -> attribute e "id" >>= return . fromJust + Just e -> attribute e "id" >>= return . fromJust - setData (IdLine level id3) -- !> ("setDataAL1",id3) - delData Alternative -- !> ("alternative, creating", id3) + setData (IdLine level id3) !> ("setDataAL1",id3) + delData $ Alternative undefined !> ("alternative, creating", id3) return id3 else setData idline1 >> return id1' id2 <- genNewId n <- gets mfSequence - setData $ IDNUM n + -- setData $ IDNUM n @@ -1590,7 +1578,7 @@ (Transient $ do - meid2 <- elemBySeq id2 -- !> ("checking",id1,id2) + meid2 <- elemBySeq id2 !> ("checking",id1,id2) case meid2 of Nothing -> return () @@ -1599,7 +1587,7 @@ id2' <- attribute eid2 "id" >>= return . fromJust -- let n= read (tail $ JS.unpack $ JS.dropWhile (/= 'p') id2') + 1 -- liftIO $ writeIORef rprefix n !> ("N",n) - (setData (IdLine (level +1) id2')) -- !> ("set IdLine",id2') + (setData (IdLine (level +1) id2')) !> ("set IdLine",id2') execmode <- getData @@ -1607,7 +1595,7 @@ Just ExecEvent -> do -- an event has happened. Clean previous rendering when (isJust meid2) $ liftIO $ do - deleteSiblings $ fromJust meid2 -- !> "EVENT" + deleteSiblings $ fromJust meid2 !> "EVENT" clearChildren $ fromJust meid2 delData ExecEvent @@ -1616,7 +1604,7 @@ _ -> do - return () -- !> ("EXECTEMPLATE", isTemplate) + return () !> ("EXECTEMPLATE", isTemplate) if isTemplate then delData noHtml else do render <- getData `onNothing` (return noHtml) -- !> "TEMPLATE" @@ -1627,13 +1615,15 @@ delData render return $ Just ()) if(isJust r) - then delData Alternative >> setData (IdLine (level +1) id2 ) -- !> ("setDataAl",id2) - else setData Alternative !> "SETDATA ALTERNATIVE" + then delData (Alternative undefined) >> setData (IdLine (level +1) id2 ) -- !> ("setDataAl",id2) + else do + cont <- get + setData (Alternative cont) !> "SETDATA ALTERNATIVE" return r #else -render (Widget x)= x +render (Widget x)= empty #endif @@ -1661,11 +1651,19 @@ -- be appended, prepended to the previous content or it can erase the previous content depending on the -- update method. at :: JSString -> UpdateMethod -> Widget a -> Widget a -at id method w= setAt id method <<< w - +at id method w= setAt id method <<< do + original@(IdLine level i) <- Widget $ getState <|> error "IdLine not defined" + setState $ IdLine level $ JS.tail id -- "n0p0" + w `with` setState original + where + with (Widget (Transient x)) (Widget (Transient y))= + Widget . Transient $ do + mx <- x + y + return mx setAt :: JSString -> UpdateMethod -> Perch -> Perch -setAt id method render = liftIO $ case method of +setAt id method render = liftIO $ case method of Insert -> do forElems_ id $ clear >> render return () @@ -1705,9 +1703,10 @@ foreign import javascript unsafe "$1[$2] = $3" setProp :: Elem -> JSString -> JSString -> IO () -foreign import javascript unsafe "alert($1)" alert :: JSString -> IO () - +foreign import javascript unsafe "alert($1)" js_alert :: JSString -> IO () +alert :: (Show a,MonadIO m) => a -> m () +alert= liftIO . js_alert . pack . show foreign import javascript unsafe "document.getElementById($1)" elemByIdDOM :: JSString -> IO JSVal @@ -1725,11 +1724,11 @@ #else unpack= undefined getProp :: Elem -> JSString -> IO JSString -getProp = undefined +getProp = error "getProp: undefined in server" setProp :: Elem -> JSString -> JSString -> IO () -setProp = undefined -alert :: JSString -> IO () -alert= undefined +setProp = error "setProp: undefined in server" +alert :: (Show a,MonadIO m) => a -> m () +alert= liftIO . print data Callback a= Callback a data ContinueAsync=ContinueAsync syncCallback1= undefined