Permalink
Browse files

Working!!

  • Loading branch information...
1 parent e3d8408 commit 5304d5fec89926a51297cb304225bf2be7764013 @serras serras committed with Alejandro Jan 21, 2011
Showing with 326 additions and 17 deletions.
  1. +18 −1 Example-HTML.html
  2. +107 −13 Example.hs
  3. +177 −0 JThread.js
  4. +24 −3 lib.js
View
@@ -39,11 +39,28 @@
<script type="text/javascript" src="AFRPDiagnostics.mjs"></script>
<script type="text/javascript" src="AFRPEvent.mjs"></script>
<script type="text/javascript" src="AFRP.mjs"></script>
+<script type="text/javascript" src="JThread.js"></script>
<script type="text/javascript" src="Example.no-arrows.js"></script>
<script type="text/javascript">
-$(document).ready(jQueryMain);
+
+var loopBody, exitCondition, onEnd, onKill;
+loopBody = function(t){
+ jQueryMain();
+};
+exitCondition = function(t){
+ return true;
+};
+onEnd = onKill = function(t){
+ alert("Something strange happened");
+};
+
+var jThread = new JThread(loopBody, exitCondition, onEnd, onKill);
+$(document).ready(jThread.run());
</script>
</head>
<body>
+
+<div id="example_count"> </div>
+
</body>
</html>
View
@@ -18,9 +18,12 @@ data JSRawInput = JSRawInput {
deriving Show
data JSResponse = JSInit
| JSLabelCreateResp
+ | JSTimeoutCreateResp
+ | JSTimeoutTickResp
| JSDeComp (Event JSResponse,Event JSResponse)
data JSRequest = JSLabelCreateReq JSLabelState
| JSLabelSetReq JSLabelState
+ | JSTimeoutReq JSTimeoutState
| JSComp (Event JSRequest,Event JSRequest)
type Responder a b = SF (JSRawInput, Event JSResponse, a) (Event JSRequest, b)
@@ -101,7 +104,7 @@ label text ls = ls {labelText = text}
div_ :: String -> JSLabelConf
div_ div ls = ls {labelDiv = div}
--- The label GUI
+-- The label widget
jsLabel :: JSLabelConf -> JSGUI JSLabelConf ()
jsLabel conf0 =
@@ -127,10 +130,70 @@ jsLabel conf0 =
(tag stateChanged (JSLabelSetReq state))
returnA -< (req,())
+-- The mouse widget
+
jsMouse :: JSGUI () (Int, Int)
jsMouse = JSGUI $ proc (inp,_,_) -> do
returnA -< (noEvent, jsMousePosition inp)
+-- The timeout widget
+
+data JSTimeoutState = JSTimeoutState {
+ timeoutId :: String,
+ timeoutTime :: Int
+ } deriving (Eq, Show)
+
+type JSTimeoutConf = JSTimeoutState -> JSTimeoutState
+
+timeId :: String -> JSTimeoutConf
+timeId id ts = ts {timeoutId = id}
+
+time :: Int -> JSTimeoutConf
+time t ts = ts {timeoutTime = t}
+
+jsTimeout :: JSTimeoutConf -> JSGUI JSTimeoutConf (Event ())
+jsTimeout conf0 =
+ let -- Initial state
+ defState = JSTimeoutState {timeoutId = "timeout", timeoutTime = 1000}
+ initState = conf0 defState
+
+ -- Detect creation
+ maybeCreate JSTimeoutCreateResp = Just True
+ maybeCreate _ = Nothing
+
+ -- Detect timeout tick
+ maybeTick JSTimeoutTickResp = Just ()
+ maybeTick _ = Nothing
+
+ in JSGUI $ proc (_,resp,conf) -> do
+ -- Keep track of the state.
+ rec state <- iPre initState -< conf state
+
+ -- Has the state changed? If so, generate set request.
+ stateChanged <- edgeBy maybeChanged initState -< state
+
+ isCreated <- hold False -< mapFilterE maybeCreate resp
+
+ -- Send a creation request if we haven't been created yet.
+ -- WARNING: this is probably a bad way to do this. It can
+ -- create an infinitely-dense stream of Events, which is a
+ -- big no-no in the AFRP world (at least, conceptually).
+ -- We used to use WXWInit for this purpose, but with the
+ -- dynamic switching of widgets, the system wouldn't know
+ -- when to send the WXWInit events to widgets that have
+ -- been switched-into.
+ let doCreate = if isCreated then noEvent else Event (JSTimeoutReq state)
+
+ -- Merge create/set requests.
+ let req = tag doCreate (JSTimeoutReq state)
+
+ -- Pass button presses through.
+ let tick = mapFilterE maybeTick resp
+
+ returnA -< (req,tick)
+
+-- Internal GUI state
+
type JSGUIState = Int
type JSGUIRef = IORef JSGUIState
@@ -140,8 +203,7 @@ startGUI (JSGUI g) = do
epoch <- getCurrentTime
gsr <- newIORef epoch
rh <- reactInit initSense (actuate gsr) g
- addEvent "timeout" $ respond gsr rh NoEvent
- -- addEvent "timeout" $ putStrLn "hola"
+ -- addEvent "timeout" $ respond gsr rh NoEvent
return ()
-- Get an input sample from the OS.
@@ -162,9 +224,9 @@ respond :: JSGUIRef -> JSRHandle -> Event JSResponse -> IO ()
respond gsr rh resp = do
-- Obtain input sample.
prevt <- readIORef gsr
- putStrLn $ show prevt
+ -- putStrLn $ show prevt
inp <- getRawInput
- putStrLn $ show inp
+ -- putStrLn $ show inp
-- Make sure time's elapsed since the last call to react.
-- With the timer set up in startGUI, this is probably
@@ -180,10 +242,10 @@ respond gsr rh resp = do
-- Process an output sample (i.e. a widget request).
actuate :: JSGUIRef -> JSRHandle -> Bool -> (Event JSRequest,()) -> IO Bool
actuate gsr rh _ (wre,_) =
- do -- Handle requests, if any.
+ do -- Handle requests, if any.
t <- readIORef gsr
- putStrLn "actuate"
- putStrLn $ show t
+ -- putStrLn "actuate"
+ -- putStrLn $ show t
resp <- handleWidgetReq gsr rh wre
-- Reset layout if contents changed.
@@ -198,12 +260,41 @@ actuate gsr rh _ (wre,_) =
handleWidgetReq :: JSGUIRef -> JSRHandle -> (Event JSRequest) -> IO (Event JSResponse)
-handleWidgetReq _ _ NoEvent = return NoEvent
+handleWidgetReq _ _ NoEvent = do
+ -- alert $ stringToJSString "No event"
+ return NoEvent
handleWidgetReq _ _ (Event (JSLabelCreateReq t)) = do
- changeText (stringToJSString . labelDiv $ t) (stringToJSString . labelText $ t)
+ -- alert $ stringToJSString "Label create"
+ let ldiv = labelDiv t
+ -- alert $ stringToJSString "Label div obtained"
+ let ltext = labelText t
+ -- alert $ stringToJSString "Label text obtained"
+ changeText (stringToJSString ldiv) (stringToJSString ltext)
+ -- alert $ stringToJSString "Label created"
return $ Event JSLabelCreateResp
handleWidgetReq _ _ (Event (JSLabelSetReq t)) = do
- changeText (stringToJSString . labelDiv $ t) (stringToJSString . labelText $ t)
+ -- alert $ stringToJSString "Label set"
+ -- alert $ stringToJSString "Label create"
+ let ldiv = labelDiv t
+ -- alert $ stringToJSString "Label div obtained"
+ let ltext = labelText t
+ -- alert $ stringToJSString "Label text obtained"
+ changeText (stringToJSString ldiv) (stringToJSString ltext)
+ -- alert $ stringToJSString "Label set"
+ return NoEvent
+handleWidgetReq gsr rh (Event (JSTimeoutReq t)) = do
+ -- alert $ stringToJSString "timeout"
+ addEvent "timeout" $ respond gsr rh (Event JSTimeoutTickResp)
+ return $ Event JSTimeoutCreateResp
+handleWidgetReq gsr rh (Event (JSComp (lreq, rreq))) = do
+ -- alert $ stringToJSString "comp"
+ lresp <- handleWidgetReq gsr rh lreq
+ rresp <- handleWidgetReq gsr rh rreq
+ return $ case (lresp, rresp) of
+ (NoEvent, NoEvent) -> noEvent
+ resp -> Event (JSDeComp resp)
+handleWidgetReq _ _ _ = do
+ -- alert $ stringToJSString "nothing"
return NoEvent
------------------------------------------------------------------
@@ -313,8 +404,11 @@ ensureTimeElapses t0 t1 getTime = do
example :: JSGUI () ()
example = proc _ -> do
- mpos <- jsMouse -< ()
- -- _ <- jsLabel (div_ "example") -< (label $ show (fst mpos))
+ rec mpos <- jsMouse -< ()
+ tick <- jsTimeout ((timeId "example_time") . (time 3000)) -< id
+ -- sum <- edgeTag (+1) -< tick
+ -- count <- accum 1 -< sum
+ _ <- jsLabel (div_ "example_count") -< (label $ show mpos)
returnA -< ()
jQueryMain :: IO ()
View
@@ -0,0 +1,177 @@
+/**
+ * Summary:
+ * Most of the current browsers run all javascript functions/loops within a
+ * single thread causing the browser to freeze while executing a long
+ * running loop or function, and may cause the browser to display the
+ * "Unresponsive script" message. JThread attempts to solve those issues.
+ * JThread has the following features: -
+ * 1- Doesn't freeze the browser while running a loop.
+ * 2- Allows multiple threads to run concurrently.
+ * 3- Can be killed at any time.
+ *
+ * Example 1:
+ * var jThread = new JThread(loopBody, exitCondition);//instantiate
+ * ...
+ * jThread.run();//run
+ * ...
+ * jThread.kill();//kill
+ * ...
+ * ...
+ * var jThread = new JThread(loopBody, exitCondition).run();//instantiate and run
+ * ...
+ * jThread.kill();//kill
+ * ...
+ *
+ * Example 2:
+ * var bigArr = [];
+ * var jThread = JThread.forEach(bigArr, callback);//instantiate and run
+ * ...
+ * jThread.kill();//kill
+ *
+ * Drawbacks:
+ * Slower than regular loops. But this can be controlled byt "_freezeIter"
+ * variable that controls the number of iterations to be executed during
+ * which the browser will freeze.
+ *
+ */
+
+/**
+ * JThread arguments: -
+ *
+ * loopBody: (Function)
+ * A function to be executed every time the "exitCondition" returns
+ * true. Takes one argument:
+ * jThread: The current running jThread object.
+ *
+ * exitCondition: (Function)
+ * A function to be executed to return false to exit the loop, or
+ * true to execute another iteration of the loop. Takes one argument:
+ * jThread: The current running jThread object.
+ *
+ * onEnd: (Function - optional)
+ * A function to be executed when ending the loop normally. Takes one
+ * argument:
+ * jThread: The current running jThread object.
+ *
+ * onKill: (Function - optional)
+ * A function to be executed when "kill" function is called to end the
+ * loop. Takes one argument:
+ * jThread: The current running jThread object.
+ *
+ * scope: (Object - optional)
+ * A scoping object. The scope in which the functions will be executed.
+ *
+ */
+var JThread = function (/*Function*/loopBody, /*Function*/exitCondition, /*[Function]*/onEnd, /*[Function]*/onKill, /*[Object]*/scope){
+ //private attributes
+ var _threadId = new Date().getTime() + ((++JThread.id) % 1000),
+ _freezeIter = 10,
+ _kill = false,
+ _killed = false,
+ _running = false,
+ _sTime, _eTime;
+
+ scope = scope?scope:window;
+
+ //private functions
+ var _reset = function(){
+ _kill = false;
+ _killed = false;
+ _running = false;
+ };
+
+ return {
+ //public functions
+ getThreadId: function(){
+ return _threadId;
+ },
+ isRunning: function(){
+ return _running;
+ },
+ isKilled: function(){
+ return _killed;
+ },
+ getTime: function(){
+ return (_eTime.getTime() - _sTime.getTime())/1000;
+ },
+ kill: function(){
+ _kill = true;
+ },
+ run: function(){
+ var jThread = this;
+ _sTime = new Date();
+ _reset();
+ var f = function(){
+ if(_kill){
+ _killed = true;
+ _running = false;
+ _eTime = new Date();
+ if(onKill){
+ onKill.apply(scope,[jThread]);
+ }
+ return;
+ }
+ var i = _freezeIter;
+ while(i--){
+ if(exitCondition.apply(scope,[jThread])){
+ _running = true;
+ loopBody.apply(scope,[jThread]);
+ if(!i){
+ setTimeout(f,0);
+ }
+ }else{
+ _running = false;
+ _eTime = new Date();
+ if(onEnd){
+ onEnd.apply(scope,[jThread]);
+ }
+ break;
+ }
+ }
+ };
+ f();
+ return jThread;
+ }
+ };
+};
+
+/**
+ * JThread.forEach arguments: -
+ *
+ * arr: (Array)
+ * An array to be traversed.
+ *
+ * callback: (Function)
+ * A function to be executed with every iteration while traversing an
+ * array. Takes three arguments:
+ * jThread: The current running jThread object.
+ * element: The current array element.
+ * index: The current array index.
+ *
+ * onEnd: (Function - optional)
+ * A function to be executed when ending the loop normally. Takes one
+ * argument:
+ * jThread: The current running jThread object.
+ *
+ * onKill: (Function - optional)
+ * A function to be executed when "kill" function is called to end the
+ * loop. Takes one argument:
+ * jThread: The current running jThread object.
+ *
+ * scope: (Object - optional)
+ * A scoping object. The scope in which the functions will be executed.
+ *
+ */
+JThread.forEach = function(/*Array*/arr, /*Function*/callback, /*[Function]*/onEnd, /*[Function]*/onKill, /*[Object]*/scope){
+ arr = arr?(arr instanceof Array?arr:[arr]):[];
+ var i = 0;
+ var loopBody = function(t){
+ callback.apply(scope, [/*jThread*/t, /*element*/arr[i], /*index*/i++]);
+ }
+ var exitCondition = function(t){
+ return i<arr.length;
+ }
+ return new JThread(loopBody, exitCondition, onEnd, onKill, scope).run();
+};
+
+JThread.id = 0;
Oops, something went wrong.

0 comments on commit 5304d5f

Please sign in to comment.