Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First attempt at game interface (no UI links let)

  • Loading branch information...
commit 4f2683e2199077b6b2a4691432e3184c6ebecde6 1 parent dad346e
@cdsmith authored
View
21 gloss-web-adapters/GlossAdapters.hs
@@ -5,6 +5,8 @@ module GlossAdapters where
import Graphics.Gloss
import Graphics.Gloss.Interface.Simulate
+import Graphics.Gloss.Interface.Game
+
data Simulation = forall a. Simulation
a
@@ -20,3 +22,22 @@ advanceSimulation t (Simulation c s p) = Simulation (s v t c) s p
simulationToPicture :: Simulation -> Picture
simulationToPicture (Simulation c s p) = p c
+
+data Game = forall a. Game
+ a
+ (Event -> a -> a)
+ (Float -> a -> a)
+ (a -> Picture)
+
+
+advanceGame :: Float -> Game -> Game
+advanceGame t (Game w e a d) = Game (a t w) e a d
+
+
+signalGame :: Event -> Game -> Game
+signalGame ev (Game w e a d) = Game (e ev w) e a d
+
+
+gameToPicture :: Game -> Picture
+gameToPicture (Game w e a d) = d w
+
View
2  gloss-web.cabal
@@ -15,7 +15,7 @@ Cabal-version: >=1.2
Executable gloss-web
Hs-source-dirs: src
Main-is: Main.hs
- Ghc-options: -O2
+ Ghc-options: -O2 -threaded -rtsopts -with-rtsopts=-N
Ghc-prof-options: -DPROFILE_SUBST -auto-all -rtsopts
Build-depends: aeson,
array,
View
13 src/App.hs
@@ -17,16 +17,19 @@ import GlossAdapters
import ClientManager
-type Anim = (UTCTime, Float -> Picture)
-type Sim = MVar (UTCTime, Simulation)
+type Anim = (UTCTime, Float -> Picture)
+type Sim = MVar (UTCTime, Simulation)
+type RunningGame = MVar (UTCTime, Game)
data App = App {
appHeist :: TemplateState Snap,
appAnimations :: ClientManager Anim,
appSimulations :: ClientManager Sim,
+ appGames :: ClientManager RunningGame,
appCompiledPictures :: MVar (Map ByteString (Either [String] Picture)),
appCompiledAnimations :: MVar (Map ByteString (Either [String] (Float -> Picture))),
- appCompiledSimulations :: MVar (Map ByteString (Either [String] Simulation))
+ appCompiledSimulations :: MVar (Map ByteString (Either [String] Simulation)),
+ appCompiledGames :: MVar (Map ByteString (Either [String] Game))
}
@@ -34,10 +37,12 @@ newApp :: TemplateState Snap -> IO App
newApp heist = do
animMgr <- newClientManager
simMgr <- newClientManager
+ gameMgr <- newClientManager
cpic <- newMVar M.empty
canim <- newMVar M.empty
csim <- newMVar M.empty
- return (App heist animMgr simMgr cpic canim csim)
+ cgame <- newMVar M.empty
+ return (App heist animMgr simMgr gameMgr cpic canim csim cgame)
{-|
View
9 src/ClientManager.hs
@@ -9,11 +9,8 @@ import Data.Map (Map)
import qualified Data.Map as M
-type Key = Int
-
-
data ClientManager a = Mgr {
- clientMap :: MVar (Map Key (Client a))
+ clientMap :: MVar (Map Int (Client a))
}
@@ -38,7 +35,7 @@ newClientManager = do
return (t1 `diffUTCTime` t < 30)
-newClient :: ClientManager a -> a -> IO Key
+newClient :: ClientManager a -> a -> IO Int
newClient (Mgr m) c = modifyMVar m $ go
where go cmap = do
k <- randomIO
@@ -50,7 +47,7 @@ newClient (Mgr m) c = modifyMVar m $ go
return (M.insert k cval cmap, k)
-getClient :: ClientManager a -> Key -> IO (Maybe (a, IO ()))
+getClient :: ClientManager a -> Int -> IO (Maybe (a, IO ()))
getClient (Mgr m) k = withMVar m $ \cmap -> do
case M.lookup k cmap of
Nothing -> do return Nothing
View
179 src/Main.hs
@@ -11,6 +11,7 @@ import Data.Aeson.Encode
import Data.IORef
import Data.Time
import Graphics.Gloss
+import Graphics.Gloss.Interface.Game
import Snap.Http.Server
import Snap.Types
import Snap.Util.FileServe
@@ -41,12 +42,16 @@ main = do
quickHttpServe $
route [ ("draw", draw app),
("anim", anim app),
- ("sim", sim app),
+ ("sim", sim app),
+ ("game", game app),
("displayInBrowser", displayInBrowser app),
("animateInBrowser", animateInBrowser app),
("animateStream", animateStream app),
("simulateInBrowser", simulateInBrowser app),
- ("simulateStream", simulateStream app) ]
+ ("simulateStream", simulateStream app),
+ ("gameInBrowser", gameInBrowser app),
+ ("gameStream", gameStream app),
+ ("gameEvent", gameEvent app) ]
<|> serveDirectory "web"
@@ -135,6 +140,41 @@ sim app = do
]]
+game :: App -> Snap ()
+game app = do
+ Just (b,t) <- renderTemplate
+ (addSplices (appHeist app))
+ "editor"
+ modifyResponse (setContentType t)
+ writeBuilder b
+ where
+ addSplices = bindSplices [
+ ("intro", introSplice),
+ ("action", actionSplice),
+ ("defaults", defaultsSplice)
+ ]
+ introSplice = return [
+ TextNode "Define variables called ",
+ Element "code" [] [TextNode "initial"],
+ TextNode ", ",
+ Element "code" [] [TextNode "event"],
+ TextNode ", ",
+ Element "code" [] [TextNode "step"],
+ TextNode ", and ",
+ Element "code" [] [TextNode "draw"],
+ TextNode " describing your game's initial state, event handler, step function, and appearance."
+ ]
+ actionSplice = return [ TextNode "gameInBrowser" ]
+ defaultsSplice = return [ Element "script" [("type", "text/javascript")] [
+ TextNode "var sourceCookie = 'gameSource';",
+ TextNode "var initialSource = 'import Graphics.Gloss\\n\\n",
+ TextNode "initial = undefined\\n",
+ TextNode "event e = undefined\\n",
+ TextNode "step t g = undefined\\n",
+ TextNode "draw g = undefined';"
+ ]]
+
+
displayInBrowser :: App -> Snap ()
displayInBrowser app = do
src <- maybe pass return =<< getParam "source"
@@ -180,7 +220,7 @@ animate app f = do
writeBuilder b
where
scrSplice k = return [ Element "script" [("type", "text/javascript")] [
- TextNode "eventURI = \'animateStream?key=",
+ TextNode "streamURI = \'animateStream?key=",
TextNode $ T.pack $ show k,
TextNode "\';"
]]
@@ -229,7 +269,7 @@ simulate app sim = do
writeBuilder b
where
scrSplice k = return [ Element "script" [("type", "text/javascript")] [
- TextNode "eventURI = \'simulateStream?key=",
+ TextNode "streamURI = \'simulateStream?key=",
TextNode $ T.pack $ show k,
TextNode "\';"
]]
@@ -256,6 +296,137 @@ simulateStream app = do
targetInterval = 0.1
+gameInBrowser :: App -> Snap ()
+gameInBrowser app = do
+ src <- maybe pass return =<< getParam "source"
+ res <- liftIO $ getGame app src
+ case res of
+ Left errs -> errors app errs
+ Right pic -> runGame app pic
+
+
+runGame :: App -> Game -> Snap ()
+runGame app game = do
+ t <- liftIO getCurrentTime
+ gvar <- liftIO $ newMVar (t, game)
+ k <- liftIO $ newClient (appGames app) gvar
+ Just (b, t) <- renderTemplate
+ (bindSplice "displayScript" (scrSplice k) (appHeist app))
+ "display"
+ modifyResponse (setContentType t)
+ writeBuilder b
+ where
+ scrSplice k = return [ Element "script" [("type", "text/javascript")] [
+ TextNode "streamURI = \'gameStream?key=",
+ TextNode $ T.pack $ show k,
+ TextNode "\';",
+ TextNode "eventURI = \'gameEvent?key=",
+ TextNode $ T.pack $ show k,
+ TextNode "\';"
+ ]]
+
+
+gameStream :: App -> Snap ()
+gameStream app = do
+ k <- maybe pass return
+ =<< getParam "key"
+ (var, touch) <- maybe pass return
+ =<< liftIO (getClient (appGames app) (read (BC.unpack k)))
+ eventStreamPull $ modifyMVar var $ \(t0, game) -> do
+ touch
+ t1 <- getCurrentTime
+ let interval = t1 `diffUTCTime` t0
+ when (interval < targetInterval) $
+ threadDelay $ round $ 1000000 * (targetInterval - interval)
+ t1 <- getCurrentTime
+ let t = realToFrac (t1 `diffUTCTime` t0)
+ let game' = advanceGame t game
+ let pic = gameToPicture game'
+ return ((t1, game'), ServerEvent Nothing Nothing [ base64 $ fromPicture pic ])
+ where
+ targetInterval = 0.1
+
+
+gameEvent :: App -> Snap ()
+gameEvent app = do
+ liftIO . print . BC.unpack =<< fmap rqQueryString getRequest
+ typ <- maybe pass return =<< getParam "type"
+ case typ of
+ "k" -> key
+ "m" -> move
+ _ -> pass
+ where
+ key = do
+ k <- toKey =<< maybe pass return =<< getParam "btn"
+ d <- toKeyState =<< maybe pass return =<< getParam "state"
+ shift <- toKeyState =<< maybe pass return =<< getParam "shift"
+ alt <- toKeyState =<< maybe pass return =<< getParam "alt"
+ ctrl <- toKeyState =<< maybe pass return =<< getParam "ctrl"
+ x <- bsToNum =<< maybe pass return =<< getParam "x"
+ y <- bsToNum =<< maybe pass return =<< getParam "y"
+ dispatch $ EventKey k d (Modifiers shift ctrl alt) (x,y)
+
+ move = do
+ x <- bsToNum =<< maybe pass return =<< getParam "x"
+ y <- bsToNum =<< maybe pass return =<< getParam "y"
+ dispatch $ EventMotion (x,y)
+
+ dispatch event = do
+ k <- maybe pass return
+ =<< getParam "key"
+ (var, touch) <- maybe pass return
+ =<< liftIO (getClient (appGames app) (read (BC.unpack k)))
+ liftIO $ modifyMVar var $
+ \ (t0, game) -> return ((t0, signalGame event game), ())
+
+
+bsToNum :: ByteString -> Snap Float
+bsToNum bs = case reads (BC.unpack bs) of
+ [(f,"")] -> return f
+ _ -> pass
+
+
+toKeyState :: ByteString -> Snap KeyState
+toKeyState "1" = return Down
+toKeyState "0" = return Up
+toKeyState _ = pass
+
+
+toKey :: ByteString -> Snap Key
+toKey bs = case bs of
+ -- Special keys and mouse events; always > 1 character
+ "F1" -> return (SpecialKey KeyF1)
+ "F2" -> return (SpecialKey KeyF2)
+ "F3" -> return (SpecialKey KeyF3)
+ "F4" -> return (SpecialKey KeyF4)
+ "F5" -> return (SpecialKey KeyF5)
+ "F6" -> return (SpecialKey KeyF6)
+ "F7" -> return (SpecialKey KeyF7)
+ "F8" -> return (SpecialKey KeyF8)
+ "F9" -> return (SpecialKey KeyF9)
+ "F10" -> return (SpecialKey KeyF10)
+ "F11" -> return (SpecialKey KeyF11)
+ "F12" -> return (SpecialKey KeyF12)
+ "Left" -> return (SpecialKey KeyLeft)
+ "Up" -> return (SpecialKey KeyUp)
+ "Right" -> return (SpecialKey KeyRight)
+ "Down" -> return (SpecialKey KeyDown)
+ "PageUp" -> return (SpecialKey KeyPageUp)
+ "PageDown" -> return (SpecialKey KeyPageDown)
+ "Home" -> return (SpecialKey KeyHome)
+ "End" -> return (SpecialKey KeyEnd)
+ "Insert" -> return (SpecialKey KeyInsert)
+ "Del" -> return (SpecialKey KeyDelete)
+ "NumLock" -> return (SpecialKey KeyNumLock)
+ "lbtn" -> return (MouseButton LeftButton)
+ "rbtn" -> return (MouseButton RightButton)
+ "mbtn" -> return (MouseButton MiddleButton)
+ "mwup" -> return (MouseButton WheelUp)
+ "mwdn" -> return (MouseButton WheelDown)
+ _ | B.length bs == 1 -> return (Char (BC.head bs))
+ | otherwise -> pass
+
+
errors :: App -> [String] -> Snap ()
errors app errs = do
Just (b, t) <- renderTemplate
View
17 src/ProfileSubst.hs
@@ -9,11 +9,14 @@
module ProfileSubst (
picture,
animation,
- simulation
+ simulation,
+ game
)
where
import Graphics.Gloss
+import Graphics.Gloss.Interface.Simulate
+import Graphics.Gloss.Interface.Game
import GlossAdapters
-----------------------------------------------------------------------
@@ -73,11 +76,19 @@ greener c = mixColors 1 10 green c
-----------------------------------------------------------------------
-simulation = Simulation initial step draw
+simulation = Simulation initial (const step) draw
data Ball = Ball Float Float
initial = Ball 100 0
-step _ t (Ball x v) = Ball (x + v*t) (v - x*t)
+step t (Ball x v) = Ball (x + v*t) (v - x*t)
draw (Ball x v) = translate x 0 (circle 20)
+-----------------------------------------------------------------------
+
+game = Game initial event step draw
+
+event (EventKey (Char '1') _ _ _) (Ball x v) = Ball x (v - 10)
+event (EventKey (Char '2') _ _ _) (Ball x v) = Ball x (v + 10)
+event _ (Ball x v) = Ball x v
+
View
11 src/Source.hs
@@ -51,6 +51,9 @@ getAnimation _ _ = return (Right animation)
getSimulation :: App -> ByteString -> IO (Either [String] Simulation)
getSimulation _ _ = return (Right simulation)
+getGame :: App -> ByteString -> IO (Either [String] Game)
+getGame _ _ = return (Right game)
+
#else
getPicture :: App -> ByteString -> IO (Either [String] Picture)
@@ -76,6 +79,14 @@ getSimulation app src = do
"Simulation"
src
+
+getGame :: App -> ByteString -> IO (Either [String] Game)
+getGame app src = do
+ getCompileResult (appCompiledGames app)
+ "Game initial event step draw"
+ "Game"
+ src
+
#endif
View
36 web/display.tpl
@@ -2,11 +2,6 @@
<html>
<head>
<title>Drawing</title>
- <!--[if IE]>
- <script type="text/javascript" src="excanvas.js"></script>
- <![endif]-->
- <script type="text/javascript" src="draw.js"></script>
- <displayScript/>
<link rel="stylesheet" type="text/css" href="tooltip.css">
<style type="text/css">
body {
@@ -14,36 +9,19 @@
text-align: center;
}
</style>
- <script type="text/javascript">
- function init()
- {
- var canvas = document.getElementById("screen");
-
- if (window.picture)
- {
- displayInCanvas(canvas, picture);
- }
-
- if (window.eventURI)
- {
- var eventSource = new EventSource(eventURI);
- eventSource.onmessage = function(event) {
- displayInCanvas(canvas, event.data);
- }
- }
- }
- </script>
+ <!--[if IE]>
+ <script type="text/javascript" src="excanvas.js"></script>
+ <![endif]-->
+ <displayScript/>
+ <script type="text/javascript" src="draw.js"></script>
</head>
<body onload="init()" style="overflow:hidden">
<canvas id="screen"
width="500"
height="500"
- style="border:solid black 1px"
- onmouseover="tooltip.show();"
- onmouseout="tooltip.hide();"
- onclick="tooltip.pin();">
+ tabindex="0"
+ style="border:solid black 1px">
</canvas>
- <script type="text/javascript" src="tooltip.js"></script>
</body>
</html>
View
365 web/draw.js
@@ -1,3 +1,136 @@
+/*
+ * Tooltip function originally by Michael Leigeber, but modified here to
+ * include pinning and to simplify in a few places.
+ */
+var tooltip = function() {
+ var top = 3;
+ var left = 0;
+ var speed = 10;
+ var timer = 20;
+ var endalpha = 90;
+ var alpha = 0;
+ var tt = null;
+ var c, h;
+
+ return {
+ show: function() {
+ if(tt == null)
+ {
+ tt = document.createElement('div');
+ tt.className = 'tt';
+ c = document.createElement('div');
+ c.className = 'ttcont';
+ tt.appendChild(c);
+ document.body.appendChild(tt);
+ tt.style.opacity = 0;
+ tt.style.filter = 'alpha(opacity=0)';
+ document.onmousemove = this.pos;
+ }
+
+ tt.style.display = 'block';
+ c.innerHTML = '()';
+ tt.style.width = '90px';
+ tt.style.whiteSpace = 'nowrap';
+ h = parseInt(tt.offsetHeight) + top;
+ clearInterval(tt.timer);
+ tt.timer = setInterval(function() { tooltip.fade(1) }, timer);
+ },
+
+ pos: function(e) {
+ if (tt == null)
+ {
+ tooltip.show();
+ return;
+ }
+
+ var u,l;
+
+ if (e.pageX)
+ {
+ var u = e.pageY;
+ var l = e.pageX;
+ }
+ else
+ {
+ var u = event.clientY + document.documentElement.scrollTop;
+ var l = event.clientX + document.documentElement.scrollLeft;
+ }
+
+ tt.style.top = (u - h) + 'px';
+ tt.style.left = (l + left) + 'px';
+ c.innerHTML = "(" + (l - 264) + "," + (252 - u) + ")";
+ },
+
+ fade: function(d) {
+ if (tt == null) return;
+
+ var a = alpha;
+ if((a != endalpha && d == 1) || (a != 0 && d == -1))
+ {
+ var i = speed;
+ if(endalpha - a < speed && d == 1)
+ {
+ i = endalpha - a;
+ }
+ else if(alpha < speed && d == -1)
+ {
+ i = a;
+ }
+ alpha = a + (i * d);
+ tt.style.opacity = alpha * .01;
+ tt.style.filter = 'alpha(opacity=' + alpha + ')';
+ }
+ else
+ {
+ clearInterval(tt.timer);
+ if(d == -1){tt.style.display = 'none'}
+ }
+ },
+
+ hide: function() {
+ if (tt != null)
+ {
+ clearInterval(tt.timer);
+ tt.timer = setInterval(function(){tooltip.fade(-1)},timer);
+ }
+ },
+
+ pin: function() {
+ if (tt == null || tt.style.display == 'none') return;
+
+ var pinned = tt;
+ tt = null;
+ alpha = 0;
+
+ var u,l;
+ if (event.pageX)
+ {
+ u = event.pageY;
+ l = event.pageX;
+ }
+ else
+ {
+ u = event.clientY + document.documentElement.scrollTop;
+ l = event.clientX + document.documentElement.scrollLeft;
+ }
+
+ pinned.style.left = (l - 6) + 'px';
+ pinned.style.top = (u - h + 1) + 'px';
+ pinned.style.filter = 'alpha(opacity=' + endalpha + ')';
+
+ pinned.onclick = function()
+ {
+ pinned.parentNode.removeChild(pinned);
+ }
+ }
+ };
+}();
+
+
+/*
+ * Stream class represents a binary stream from which various kinds of data can
+ * be read.
+ */
function Stream(b)
{
var index = 0;
@@ -87,6 +220,9 @@ function Stream(b)
}
}
+/*
+ * Base64 decoding, in case the browser doesn't implement it (looking at you, IE)
+ */
if (!window.atob)
{
window.atob = function(a)
@@ -141,8 +277,14 @@ if (!window.atob)
}
}
+/*
+ * Cached drawing context.
+ */
var ctx = null;
+/*
+ * Top-level function to draw a picture in a given canvas.
+ */
function displayInCanvas(c, pic)
{
var decoded = atob(pic);
@@ -160,6 +302,9 @@ function displayInCanvas(c, pic)
ctx.restore();
}
+/*
+ * The recursive drawing function.
+ */
function display(ctx, p, a, b, c, d, e, f)
{
var tag = p.popByte();
@@ -320,3 +465,223 @@ function display(ctx, p, a, b, c, d, e, f)
}
}
+/*
+ * Initialization. Examines the variables defined at the top level and
+ * sets up the drawing, streaming, and event handling as appropriate.
+ */
+function init()
+{
+ var canvas = document.getElementById("screen");
+
+ if (window.picture)
+ {
+ displayInCanvas(canvas, picture);
+ }
+
+ if (window.streamURI)
+ {
+ var eventSource = new EventSource(streamURI);
+ eventSource.onmessage = function(event) {
+ displayInCanvas(canvas, event.data);
+ }
+ }
+
+ if (window.eventURI)
+ {
+ var fireEvent = function(str)
+ {
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", window.eventURI + str, true);
+ xhr.send(null);
+ };
+
+ var lastMove = 0;
+ var lastKeyCode = -1;
+
+ var states = function(e)
+ {
+ var s = e.shiftKey ? "1" : "0";
+ var a = e.altKey ? "1" : "0";
+ var c = e.ctrlKey ? "1" : "0";
+ return "&shift=" + s + "&alt=" + a + "&ctrl=" + c;
+ };
+
+ /*
+ * Keys that should be recognized from the JavaScript key property, if
+ * and when major browsers ever implement it.
+ */
+ var specialKeys = [
+ "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8", "F9", "F10", "F11", "F12",
+ "Up", "Down", "Left", "Right", "PageUp", "PageDown", "Home", "End", "Insert",
+ "Del", "NumLock"
+ ];
+
+ /*
+ * Keys that should be recognized from the JavaScript keyCode property.
+ * These are actually, by some miracle, consistent across browsers.
+ */
+ var specialKeyCodes = [
+ 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123,
+ 38, 40, 37, 39, 33, 34, 36, 35, 45, 46, 144
+ ]
+
+ /*
+ * Since Firefox doesn't deliver key codes with the keypress event, we
+ * remember the last code from a keydown event, in the hopes that it
+ * can be matched up, and we can deliver the event for releasing a
+ * char-type key.
+ */
+ var lastKeyCode;
+
+ /*
+ * Cached map from key codes to characters. This is so that when the
+ * key is released, we can deliver the right event with the right
+ * character even though several browsers don't fill in the charCode
+ * property.
+ */
+ var cachedCodes = {};
+
+ /*
+ * Look up a special key from a key event, and return the identifier
+ * string, or null if there is none.
+ */
+ var getSpecialKey = function(e)
+ {
+ if (e.key)
+ {
+ var i = specialKeys.indexOf(e.key);
+
+ if (i == -1) return null;
+ else return e.key;
+ }
+
+ if (e.keyCode)
+ {
+ var i = specialKeyCodes.indexOf(e.keyCode);
+
+ if (i == -1) return null;
+ else return specialKeys[i];
+ }
+
+ return null;
+ };
+
+ window.onkeydown = function(e) {
+ lastKeyCode = e.keyCode;
+
+ var kname = getSpecialKey(e);
+ if (kname == null) return true;
+
+ fireEvent("&type=k&btn=" + kname + "&state=1&x=0&y=0" + states(e));
+
+ if (e.preventDefault) e.preventDefault();
+ if (e.stopPropogation) e.stopPropogation();
+ if (e.cancelBubble) e.cancelBubble();
+ return false;
+ };
+
+ window.onkeypress = function(e)
+ {
+ if (e.charCode == 0) return true;
+
+ cachedCodes[lastKeyCode.toString()] = String.fromCharCode(e.charCode);
+
+ var kname = encodeURIComponent(String.fromCharCode(e.charCode));
+ fireEvent("&type=k&btn=" + kname + "&state=1&x=0&y=0" + states(e));
+
+ if (e.preventDefault) e.preventDefault();
+ if (e.stopPropogation) e.stopPropogation();
+ if (e.cancelBubble) e.cancelBubble();
+ return false;
+ };
+
+ window.onkeyup = function(e) {
+ var kname = getSpecialKey(e);
+
+ if (kname == null && e.charCode && e.charCode != 0)
+ {
+ kname = encodeURIComponent(String.fromCharCode(e.charCode));
+ }
+
+ if (kname == null && e.keyCode
+ && cachedCodes.hasOwnProperty(e.keyCode.toString()))
+ {
+ kname = encodeURIComponent(cachedCodes[e.keyCode.toString()]);
+ }
+
+ if (kname == null) return true;
+
+ fireEvent("&type=k&btn=" + kname + "&state=0&x=0&y=0" + states(e));
+
+ if (e.preventDefault) e.preventDefault();
+ if (e.stopPropogation) e.stopPropogation();
+ if (e.cancelBubble) e.cancelBubble();
+ return false;
+ };
+
+ canvas.onmousedown = function(e) {
+ var box = canvas.getBoundingClientRect();
+ var x = e.clientX - box.left - 250;
+ var y = 250 - e.clientY + box.top;
+
+ var btn;
+ if (e.button == 0) btn = "lbtn";
+ else if (e.button == 1) btn = "mbtn";
+ else if (e.button == 2) btn = "rbtn";
+ else return true;
+
+ fireEvent("&type=k&btn=" + btn + "&state=1&x=" + x + "&y=" + y + states(e));
+
+ if (e.preventDefault) e.preventDefault();
+ if (e.stopPropogation) e.stopPropogation();
+ if (e.cancelBubble) e.cancelBubble();
+ return false;
+ };
+
+ canvas.onmouseup = function(e) {
+ var box = canvas.getBoundingClientRect();
+ var x = e.clientX - box.left - 250;
+ var y = 250 - e.clientY + box.top;
+
+ var btn;
+ if (e.button == 0) btn = "lbtn";
+ else if (e.button == 1) btn = "mbtn";
+ else if (e.button == 2) btn = "rbtn";
+ else return true;
+
+ fireEvent("&type=k&btn=" + btn + "&state=0&x=" + x + "&y=" + y + states(e));
+
+ if (e.preventDefault) e.preventDefault();
+ if (e.stopPropogation) e.stopPropogation();
+ if (e.cancelBubble) e.cancelBubble();
+ return false;
+ };
+
+ canvas.onmousemove = function(e) {
+ var t = Date.now();
+ if (lastMove > t - 100) return true;
+ lastMove = t;
+
+ var box = canvas.getBoundingClientRect();
+ var x = e.clientX - box.left - 250;
+ var y = 250 - e.clientY + box.top;
+
+ fireEvent("&type=m&x=" + x + "&y=" + y);
+
+ if (e.preventDefault) e.preventDefault();
+ if (e.stopPropogation) e.stopPropogation();
+ if (e.cancelBubble) e.cancelBubble();
+ return false;
+ };
+
+ canvas.focus();
+ window.onfocus = function() { canvas.focus(); }
+ }
+ else
+ {
+ canvas.onmouseover = function() { tooltip.show(); };
+ canvas.onmouseout = function() { tooltip.hide(); };
+ canvas.onclick = function() { tooltip.pin(); };
+ }
+}
+
View
2  web/editor.tpl
@@ -1,7 +1,6 @@
<!DOCTYPE html>
<html>
<head>
- <script type="text/javascript" src="codemirror-compressed.js"></script>
<link rel="stylesheet" href="codemirror.css">
<link rel="stylesheet" href="theme/glossweb.css">
<style type="text/css">
@@ -23,6 +22,7 @@
max-height: 100%;
}
</style>
+ <script type="text/javascript" src="codemirror-compressed.js"></script>
<defaults/>
<script type="text/javascript">
var editor;
View
9 web/theme/elegant.css
@@ -1,9 +0,0 @@
-.cm-s-elegant span.cm-number, .cm-s-elegant span.cm-string, .cm-s-elegant span.cm-atom {color: #762;}
-.cm-s-elegant span.cm-comment {color: #262;font-style: italic;}
-.cm-s-elegant span.cm-meta {color: #555;font-style: italic;}
-.cm-s-elegant span.cm-variable {color: black;}
-.cm-s-elegant span.cm-variable-2 {color: #b11;}
-.cm-s-elegant span.cm-qualifier {color: #555;}
-.cm-s-elegant span.cm-keyword {color: #730;}
-.cm-s-elegant span.cm-builtin {color: #30a;}
-.cm-s-elegant span.cm-error {background-color: #fdd;}
View
6 web/theme/glossweb.css
@@ -19,11 +19,9 @@
.cm-s-glossweb span.cm-attribute {color: #00c;}
.CodeMirror-matchingbracket { color: inherit !important;
- border: solid #ccc 1px;
- margin: -1px }
+ outline: solid #ccc 1px }
.CodeMirror-nonmatchingbracket { color: inherit !important;
- border: solid #faa 1px;
- margin: -1px }
+ outline: solid #faa 1px }
.CodeMirror { font-family: 'Monaco', 'Menlo', 'Droid Sans Mono', 'Courier New', monospace;
font-size: 10pt }
View
8 web/theme/neat.css
@@ -1,8 +0,0 @@
-.cm-s-neat span.cm-comment { color: #a86; }
-.cm-s-neat span.cm-keyword { font-weight: bold; color: blue; }
-.cm-s-neat span.cm-string { color: #a22; }
-.cm-s-neat span.cm-builtin { font-weight: bold; color: #077; }
-.cm-s-neat span.cm-special { font-weight: bold; color: #0aa; }
-.cm-s-neat span.cm-variable { color: black; }
-.cm-s-neat span.cm-number, .cm-s-neat span.cm-atom { color: #3a3; }
-.cm-s-neat span.cm-meta {color: #555;}
View
20 web/theme/night.css
@@ -1,20 +0,0 @@
-/* Loosely based on the Midnight Textmate theme */
-
-.cm-s-night { background: #0a001f; color: #f8f8f8; }
-.cm-s-night span.CodeMirror-selected { background: #a8f !important; }
-.cm-s-night .CodeMirror-gutter { background: #0a001f; border-right: 1px solid #aaa; }
-.cm-s-night .CodeMirror-gutter-text { color: #f8f8f8; }
-.cm-s-night .CodeMirror-cursor { border-left: 1px solid white !important; }
-
-.cm-s-night span.cm-comment { color: #6900a1; }
-.cm-s-night span.cm-atom { color: #845dc4; }
-.cm-s-night span.cm-number, .cm-s-night span.cm-attribute { color: #ffd500; }
-.cm-s-night span.cm-keyword { color: #599eff; }
-.cm-s-night span.cm-string { color: #37f14a; }
-.cm-s-night span.cm-meta { color: #7678e2; }
-.cm-s-night span.cm-variable-2, .cm-s-night span.cm-tag { color: #99b2ff; }
-.cm-s-night span.cm-variable-3, .cm-s-night span.cm-def { color: white; }
-.cm-s-night span.cm-error { color: #9d1e15; }
-.cm-s-night span.cm-bracket { color: #8da6ce; }
-.cm-s-night span.cm-comment { color: #6900a1; }
-.cm-s-night span.cm-builtin, .cm-s-night span.cm-special { color: #ff9e59; }
View
126 web/tooltip.js
@@ -1,126 +0,0 @@
-/*
- * Tooltip function by Michael Leigeber, modified by Chris Smith
- */
-var tooltip = function() {
- var top = 3;
- var left = 0;
- var speed = 10;
- var timer = 20;
- var endalpha = 90;
- var alpha = 0;
- var tt = null;
- var c, h;
-
- return {
- show: function() {
- if(tt == null)
- {
- tt = document.createElement('div');
- tt.className = 'tt';
- c = document.createElement('div');
- c.className = 'ttcont';
- tt.appendChild(c);
- document.body.appendChild(tt);
- tt.style.opacity = 0;
- tt.style.filter = 'alpha(opacity=0)';
- document.onmousemove = this.pos;
- }
-
- tt.style.display = 'block';
- c.innerHTML = '()';
- tt.style.width = '90px';
- tt.style.whiteSpace = 'nowrap';
- h = parseInt(tt.offsetHeight) + top;
- clearInterval(tt.timer);
- tt.timer = setInterval(function() { tooltip.fade(1) }, timer);
- },
-
- pos: function(e) {
- if (tt == null)
- {
- tooltip.show();
- return;
- }
-
- var u,l;
-
- if (e.pageX)
- {
- var u = e.pageY;
- var l = e.pageX;
- }
- else
- {
- var u = event.clientY + document.documentElement.scrollTop;
- var l = event.clientX + document.documentElement.scrollLeft;
- }
-
- tt.style.top = (u - h) + 'px';
- tt.style.left = (l + left) + 'px';
- c.innerHTML = "(" + (l - 264) + "," + (252 - u) + ")";
- },
-
- fade: function(d) {
- if (tt == null) return;
-
- var a = alpha;
- if((a != endalpha && d == 1) || (a != 0 && d == -1))
- {
- var i = speed;
- if(endalpha - a < speed && d == 1)
- {
- i = endalpha - a;
- }
- else if(alpha < speed && d == -1)
- {
- i = a;
- }
- alpha = a + (i * d);
- tt.style.opacity = alpha * .01;
- tt.style.filter = 'alpha(opacity=' + alpha + ')';
- }
- else
- {
- clearInterval(tt.timer);
- if(d == -1){tt.style.display = 'none'}
- }
- },
-
- hide: function() {
- if (tt != null)
- {
- clearInterval(tt.timer);
- tt.timer = setInterval(function(){tooltip.fade(-1)},timer);
- }
- },
-
- pin: function() {
- if (tt == null || tt.style.display == 'none') return;
-
- var pinned = tt;
- tt = null;
- alpha = 0;
-
- var u,l;
- if (event.pageX)
- {
- u = event.pageY;
- l = event.pageX;
- }
- else
- {
- u = event.clientY + document.documentElement.scrollTop;
- l = event.clientX + document.documentElement.scrollLeft;
- }
-
- pinned.style.left = (l - 6) + 'px';
- pinned.style.top = (u - h + 1) + 'px';
- pinned.style.filter = 'alpha(opacity=' + endalpha + ')';
-
- pinned.onclick = function()
- {
- pinned.parentNode.removeChild(pinned);
- }
- }
- };
-}();
Please sign in to comment.
Something went wrong with that request. Please try again.