Skip to content
This repository has been archived by the owner on Oct 11, 2022. It is now read-only.

Commit

Permalink
Working!!
Browse files Browse the repository at this point in the history
  • Loading branch information
serras authored and Alejandro committed Jan 21, 2011
1 parent e3d8408 commit 5304d5f
Show file tree
Hide file tree
Showing 4 changed files with 326 additions and 17 deletions.
19 changes: 18 additions & 1 deletion Example-HTML.html
Expand Up @@ -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>
120 changes: 107 additions & 13 deletions Example.hs
Expand Up @@ -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)
Expand Down Expand Up @@ -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 =
Expand All @@ -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

Expand All @@ -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.
Expand All @@ -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
Expand All @@ -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.
Expand All @@ -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

------------------------------------------------------------------
Expand Down Expand Up @@ -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 ()
Expand Down
177 changes: 177 additions & 0 deletions JThread.js
@@ -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;

0 comments on commit 5304d5f

Please sign in to comment.