on master help sign in
Begin migrating signals over to new module format and to use new ADT structure.
commit54ffaab4fa76ba4135ebceaf85d45d95806d7711 authorevancz <info@elm-lang.org> dateWed 20 Feb 2013, 23:36 (14 years ago) parent7b7b136
12 files changed+741
core/HTTP.elm+41 core/Native/HTTP.js+55 core/Native/Input.js+84 core/Native/Keyboard.js+123 core/Native/Mouse.js+106 core/Native/Random.js+29 core/Native/Time.js+112 core/Native/Touch.js+94 core/Native/WebSocket.js+33 core/Native/Window.js+35 core/Touch.elm+19 core/WebSocket.elm+10
core/HTTP.elmnew file+41
@@ -0,0 +1,41 @@
1+-- A library for asynchronous HTTP requests (AJAX). See the
2+-- [WebSocket](http://elm-lang.org/docs/WebSocket.elm) library if
3+-- you have very strict latency requirements.
4+
5+module HTTP where
6+
7+-- The datatype for responses. Success contains only the returned message.
8+-- Failures contain both an error code and an error message.
9+data Response a = Success a | Waiting | Failure Int String
10+
11+type Request a = {
12+ verb : String,
13+ url : String,
14+ body : a,
15+ headers : [(String,String)]
16+}
17+
18+-- Create a customized request. Arguments are request type (get, post, put,
19+-- delete, etc.), target url, data, and a list of additional headers.
20+request : String -> String -> String -> [(String,String)] -> Request String
21+request = Request
22+
23+-- Create a GET request to the given url.
24+get : String -> Request String
25+get url = Request "GET" url "" []
26+
27+-- Create a POST request to the given url, carrying the given data.
28+post : String -> String -> Request String
29+post url body = Request "POST" url body []
30+
31+
32+-- Performs an HTTP request with the given requests. Produces a signal
33+-- that carries the responses.
34+send : Signal (Request a) -> Signal (Response String)
35+send = Native.HTTP.send
36+
37+-- Performs an HTTP GET request with the given urls. Produces a signal
38+-- that carries the responses.
39+sendGet : Signal String -> Signal (Response String)
40+sendGet = send . lift get
41+
core/Native/HTTP.jsnew file+55
@@ -0,0 +1,55 @@
1+
2+function nativeHTTP(elm) {
3+ "use strict";
4+ var toElmString = Elm.JavaScript.castJSStringToString;
5+ var toJSString = Elm.JavaScript.castStringToJSString;
6+
7+ function registerReq(queue,responses) { return function(req) {
8+ if (req.url !== "") { sendReq(queue,responses,req); }
9+ };
10+ }
11+
12+ function updateQueue(queue,responses) {
13+ if (queue.length > 0) {
14+ Dispatcher.notify(responses.id, queue[0].value);
15+ if (queue[0].value.ctor !== Waiting) {
16+ queue.shift();
17+ setTimeout(function() { updateQueue(queue,responses); }, 0);
18+ }
19+ }
20+ }
21+
22+ function setHeader(pair) {
23+ request.setRequestHeader(toJSString(pair._0), toJSString(pair._1));
24+ }
25+
26+ function sendReq(queue,responses,req) {
27+ var response = { value: Elm.HTTP.Waiting };
28+ queue.push(response);
29+
30+ var request = null;
31+ if (window.ActiveXObject) { request = new ActiveXObject("Microsoft.XMLHTTP"); }
32+ if (window.XMLHttpRequest) { request = new XMLHttpRequest(); }
33+ request.onreadystatechange = function(e) {
34+ if (request.readyState === 4) {
35+ response.value = (request.status === 200
36+ ? Success(toElmString(request.responseText))
37+ : Failure(request.status)(toElmString(request.statusText)));
38+ setTimeout(function() { updateQueue(queue,responses); }, 0);
39+ }
40+ };
41+ request.open(toJSString(req.verb), toJSString(req.url), true);
42+ Elm.List.map(setHeader)(req.headers);
43+ request.send(toJSString(req.body));
44+ return null;
45+ }
46+
47+ function send(requests) {
48+ var responses = Elm.Signal.constant(Elm.HTTP.Waiting);
49+ var sender = Elm.Signal.lift(registerReq([],responses))(requests);
50+ function f(x) { return function(y) { return x; } }
51+ return Elm.Signal.lift2(f)(responses)(sender);
52+ }
53+
54+ elm.Native.HTTP = {send:send};
55+}
core/Native/Input.jsnew file+84
@@ -0,0 +1,84 @@
1+
2+Elm.Input = function() {
3+ var JS = Elm.JavaScript;
4+ var toElmString = Elm.JavaScript.castJSStringToString;
5+ var newTextInput = function(elem, ghostText) {
6+ elem.placeholder = JS.castStringToJSString(ghostText);
7+ var str = Elm.Signal.constant(["Nil"]);
8+ Value.addListener(elem, 'keyup', function(e) {
9+ Dispatcher.notify(str.id, toElmString(elem.value));
10+ elem.focus();
11+ });
12+ elem.style.padding = "1px";
13+ return Value.Tuple(Value.wrap(elem), str);
14+ };
15+ var newElement = function(name) {
16+ var e = document.createElement(name);
17+ e.style.padding = "0";
18+ e.style.margin = "0";
19+ return e;
20+ };
21+ var textArea = function(cols) { return function(rows) {
22+ var textarea = newElement('textarea');
23+ textarea.rows = rows;
24+ textarea.cols = cols;
25+ return newTextInput(textarea, "");
26+ };
27+ };
28+ var textField = function(ghostText) {
29+ var field = newElement('input');
30+ field.type = 'text';
31+ return newTextInput(field, ghostText);
32+ };
33+ var password = function(ghostText) {
34+ var field = newElement('input');
35+ field.type = 'password';
36+ return newTextInput(field, ghostText);
37+ };
38+ var checkbox = function(checked) {
39+ var box = newElement('input');
40+ box.type = 'checkbox';
41+ box.checked = checked;
42+ var status = Elm.Signal.constant(checked);
43+ Value.addListener(box, 'change', function(e) {
44+ Dispatcher.notify(status.id, box.checked);
45+ });
46+ return Value.Tuple(Value.wrap(box), status);
47+ };
48+ var dropDown = function(options) {
49+ var slct = newElement('select');
50+ var opts = [];
51+ while (options[0] === "Cons") {
52+ var opt = newElement('option');
53+ var str = Value.toText(options[1][1]);
54+ opt.value = str;
55+ opt.innerHTML = str;
56+ slct.appendChild(opt);
57+ opts.push(options[1][2]);
58+ options = options[2];
59+ }
60+ var status = Elm.Signal.constant(opts[0]);
61+ Value.addListener(slct, 'change', function(e) {
62+ Dispatcher.notify(status.id, opts[slct.selectedIndex]);
63+ });
64+ return Value.Tuple(Value.wrap(slct), status);
65+ };
66+ var stringDropDown = function(opts) {
67+ return dropDown(Elm.List.map (function(x) {return Value.Tuple(x,x);}) (opts));
68+ };
69+ var button = function(name) {
70+ var b = newElement('input');
71+ b.type = "button";
72+ b.value = JS.castStringToJSString(name);
73+ var press = Elm.Signal.constant(false);
74+ Value.addListener(b, 'click', function(e) {
75+ Dispatcher.notify(press.id, true);
76+ Dispatcher.notify(press.id, false);
77+ });
78+ return Value.Tuple(Value.wrap(b),press);
79+ };
80+ return {textArea:textArea, textField:textField,
81+ password:password, checkbox:checkbox,
82+ dropDown:dropDown, stringDropDown:stringDropDown,
83+ button:button};
84+}();
core/Native/Keyboard.jsnew file+123
@@ -0,0 +1,123 @@
1+
2+Elm.Keyboard = { Raw : function() {
3+ var keysDown = Elm.Signal.constant(["Nil"]);
4+ var charPressed = Elm.Signal.constant(["Nothing"]);
5+
6+ function remove(x,xs) {
7+ if (xs[0] === "Nil") return xs;
8+ if (xs[1] === x) return xs[2];
9+ return ["Cons", xs[1], remove(x,xs[2])];
10+ }
11+ function has(x,xs) {
12+ while (xs[0] !== "Nil") {
13+ if (xs[1] === x) return true;
14+ xs = xs[2];
15+ }
16+ return false;
17+ }
18+ Value.addListener(document, 'keydown', function(e) {
19+ if (has(e.keyCode, keysDown.value)) return;
20+ var hasListener = Dispatcher.notify(keysDown.id, ["Cons", e.keyCode, keysDown.value]);
21+ if (!hasListener)
22+ this.removeEventListener('keydown',arguments.callee,false);
23+ });
24+ Value.addListener(document, 'keyup', function(e) {
25+ var codes = remove(e.keyCode, keysDown.value);
26+ var hasListener = Dispatcher.notify(keysDown.id, codes);
27+ if (!hasListener)
28+ this.removeEventListener('keyup',arguments.callee,false);
29+ });
30+ Value.addListener(window, 'blur', function(e) {
31+ var hasListener = Dispatcher.notify(keysDown.id, ["Nil"]);
32+ if (!hasListener)
33+ this.removeEventListener('blur',arguments.callee,false);
34+ });
35+ Value.addListener(document, 'keypress', function(e) {
36+ var hasListener = Dispatcher.notify(charPressed.id, ["Just",e.charCode || e.keyCode]);
37+ Dispatcher.notify(charPressed.id, ["Nothing"]);
38+ if (!hasListener)
39+ this.removeEventListener('keypress',arguments.callee,false);
40+ });
41+ return {keysDown:keysDown,
42+ charPressed:charPressed};
43+ }()
44+};
45+
46+/*! Keyboard
47+These are nicely curated inputs from the keyboard. See the
48+[Keyboard.Raw library](/docs/Signal/KeyboardRaw.elm) for a
49+lower-level interface that will let you define more complicated behavior.
50+!*/
51+
52+(function() {
53+ function keySignal(f) {
54+ var signal = Elm.Signal.lift(f)(Elm.Keyboard.Raw.keysDown);
55+ Elm.Keyboard.Raw.keysDown.defaultNumberOfKids += 1;
56+ signal.defaultNumberOfKids = 0;
57+ return signal;
58+ }
59+
60+ function dir(left,right,up,down) {
61+ function f(ks) {
62+ var x = 0, y = 0;
63+ while (ks[0] == "Cons") {
64+ switch (ks[1]) {
65+ case left : --x; break;
66+ case right: ++x; break;
67+ case up : ++y; break;
68+ case down : --y; break;
69+ }
70+ ks = ks[2];
71+ }
72+ return { _:[true], x:[x], y:[y] };
73+ }
74+ return keySignal(f);
75+ }
76+
77+ function is(key) {
78+ function f(ks) {
79+ while (ks[0] == "Cons") {
80+ if (key == ks[1]) return true;
81+ ks = ks[2];
82+ }
83+ return false;
84+ }
85+ return keySignal(f);
86+ }
87+
88+ /*[Directions]*/
89+
90+ /** arrows : Signal { x:Int, y:Int }
91+ A signal of records indicating which arrow keys are pressed.
92+
93+ `{ x = 0, y = 0 }` when pressing no arrows.
94+ `{ x =-1, y = 0 }` when pressing the left arrow.
95+ `{ x = 1, y = 1 }` when pressing the up and right arrows.
96+ `{ x = 0, y =-1 }` when pressing the down, left, and right arrows.
97+ **/
98+ Elm.Keyboard.arrows = dir(37,39,38,40);
99+
100+ /** wasd : Signal { x:Int, y:Int }
101+ Just like the arrows signal, but this uses keys w, a, s, and d,
102+ which are common controls for many computer games.
103+ **/
104+ Elm.Keyboard.wasd = dir(65,68,87,83);
105+
106+ /*[Modifiers]*/
107+
108+ /** shift : Signal Bool
109+ Whether the shift key is pressed.
110+ **/
111+ Elm.Keyboard.shift = is(16);
112+
113+ /** ctrl : Signal Bool
114+ Whether the control key is pressed.
115+ **/
116+ Elm.Keyboard.ctrl = is(17);
117+
118+ /** space : Signal Bool
119+ Whether the space key is pressed.
120+ **/
121+ Elm.Keyboard.space = is(32);
122+
123+}());
core/Native/Mouse.jsnew file+106
@@ -0,0 +1,106 @@
1+/*! Mouse
2+ !*/
3+
4+Elm.Mouse = function() {
5+ /*[Position]*/
6+
7+ /** position : Signal (Int,Int)
8+ The current mouse position.
9+ **/
10+ var position = Elm.Signal.constant(Value.Tuple(0,0));
11+ position.defaultNumberOfKids = 2;
12+
13+ /** x : Signal Int
14+ The current x-coordinate of the mouse.
15+ **/
16+ var x = Elm.Signal.lift(function(p){return p[1];})(position);
17+ x.defaultNumberOfKids = 0;
18+
19+ /** y : Signal Int
20+ The current y-coordinate of the mouse.
21+ **/
22+ var y = Elm.Signal.lift(function(p){return p[2];})(position);
23+ y.defaultNumberOfKids = 0;
24+
25+ /*[Button Status]*/
26+
27+ /** isDown : Signal Bool
28+ The current state of the left mouse-button.
29+ True when the button is down, and false otherwise.
30+ **/
31+ var isDown = Elm.Signal.constant(false);
32+
33+ /** isClicked : Signal Bool
34+ True immediately after the left mouse-button has been clicked,
35+ and false otherwise.
36+ **/
37+ var isClicked = Elm.Signal.constant(false);
38+
39+ /** clicks : Signal ()
40+ Always equal to unit. Event triggers on every mouse click.
41+ **/
42+ var clicks = Elm.Signal.constant(Value.Tuple());
43+
44+ function getXY(e) {
45+ var posx = 0;
46+ var posy = 0;
47+ if (!e) e = window.event;
48+ if (e.pageX || e.pageY) {
49+ posx = e.pageX;
50+ posy = e.pageY;
51+ } else if (e.clientX || e.clientY) {
52+ posx = e.clientX + document.body.scrollLeft +
53+ document.documentElement.scrollLeft;
54+ posy = e.clientY + document.body.scrollTop +
55+ document.documentElement.scrollTop;
56+ }
57+ return Value.Tuple(posx, posy);
58+ }
59+
60+ Value.addListener(document, 'click', function(e) {
61+ var hasListener1 = Dispatcher.notify(isClicked.id, true);
62+ var hasListener2 = Dispatcher.notify(clicks.id, Value.Tuple());
63+ Dispatcher.notify(isClicked.id, false);
64+ if (!hasListener1 && !hasListener2)
65+ this.removeEventListener('click',arguments.callee,false);
66+ });
67+ Value.addListener(document, 'mousedown', function(e) {
68+ var hasListener = Dispatcher.notify(isDown.id, true);
69+ if (!hasListener)
70+ this.removeEventListener('mousedown',arguments.callee,false);
71+ });
72+ Value.addListener(document, 'mouseup', function(e) {
73+ var hasListener = Dispatcher.notify(isDown.id, false);
74+ if (!hasListener)
75+ this.removeEventListener('mouseup',arguments.callee,false);
76+ });
77+ Value.addListener(document, 'mousemove', function(e) {
78+ var hasListener = Dispatcher.notify(position.id, getXY(e));
79+ if (!hasListener)
80+ this.removeEventListener('mousemove',arguments.callee,false);
81+ });
82+
83+ /** isClickedOn : Element -> (Element, Signal Bool)
84+ Determine whether an element has been clicked. The resulting pair
85+ is a signal of booleans that is true when its paired element has
86+ been clicked. The signal is True immediately after the left
87+ mouse-button has been clicked, and false otherwise.
88+ **/
89+ var clickedOn = function(elem) {
90+ var node = Render.render(elem);
91+ var click = Elm.Signal.constant(false);
92+ Value.addListener(node, 'click', function(e) {
93+ Dispatcher.notify(click.id, true);
94+ Dispatcher.notify(click.id, false);
95+ });
96+ return Value.Tuple(Value.wrap(node), click);
97+ };
98+ return {position: position,
99+ x:x,
100+ y:y,
101+ isClicked: isClicked,
102+ isDown: isDown,
103+ clicks: clicks,
104+ isClickedOn: clickedOn
105+ };
106+ }();
core/Native/Random.jsnew file+29
@@ -0,0 +1,29 @@
1+/*! Random
2+ !*/
3+
4+Elm.Random = function() {
5+ /*[In a Range]*/
6+
7+ /** inRange : Int -> Int -> Signal Int
8+ Given a range from low to high, this produces a random number
9+ between 'low' and 'high' inclusive. The value in the signal does
10+ not change after the page has loaded.
11+ **/
12+ var inRange = function(min) { return function(max) {
13+ return Elm.Signal.constant(Math.floor(Math.random() * (max-min+1)) + min);
14+ };
15+ };
16+
17+ /** randomize : Int -> Int -> Signal a -> Signal Int
18+ Given a range from low to high and a signal of values, this produces
19+ a new signal that changes whenever the input signal changes. The new
20+ values are random number between 'low' and 'high' inclusive.
21+ **/
22+ var randomize = function(min) { return function(max) { return function(signal) {
23+ function f(x) { return Math.floor(Math.random() * (max-min+1)) + min; }
24+ return Elm.Signal.lift(f)(signal);
25+ };
26+ };
27+ };
28+ return { inRange:inRange, randomize:randomize };
29+}();
core/Native/Time.jsnew file+112
@@ -0,0 +1,112 @@
1+/*! Time
2+Library for working with time. Type `Time` represents some number of
3+milliseconds.
4+!*/
5+
6+Elm.Time = function() {
7+
8+ /*[Times]*/
9+
10+ /** hour, minute, second, ms : Time
11+ Units of time, making it easier to specify things like a
12+ half-second `(second / 2)`.
13+ **/
14+
15+ function timeNow() { return (new window.Date).getTime(); }
16+
17+ /*[Tickers]*/
18+
19+ /** fps : Number -> Signal Time
20+ Takes desired number of frames per second (fps). The resulting signal
21+ gives a sequence of time deltas as quickly as possible until it reaches
22+ the desired FPS. A time delta is the time between the last frame and the
23+ current frame.
24+ **/
25+
26+ /** fpsWhen : Number -> Signal Bool -> Signal Time
27+ Same as the fps function, but you can turn it on and off. Allows you
28+ to do brief animations based on user input without major ineffeciencies.
29+ The first time delta after a pause is always zero, no matter how long
30+ the pause was. This way summing the deltas will actually give the amount
31+ of time that the output signal has been running.
32+ **/
33+ function fpsWhen(desiredFPS) { return function (isOn) {
34+ var msPerFrame = 1000 / desiredFPS;
35+ var prev = timeNow(), curr = prev, diff = 0, wasOn = true;
36+ var ticker = Elm.Signal.constant(diff);
37+ function tick(zero) { return function() {
38+ curr = timeNow();
39+ diff = zero ? 0 : curr - prev;
40+ prev = curr;
41+ Dispatcher.notify(ticker.id, diff);
42+ };
43+ }
44+ var timeoutID = 0;
45+ function f(isOn) { return function(t) {
46+ if (isOn) {
47+ timeoutID = setTimeout(tick(!wasOn && isOn), msPerFrame);
48+ } else if (wasOn) {
49+ clearTimeout(timeoutID);
50+ }
51+ wasOn = isOn;
52+ return t;
53+ };
54+ }
55+ return Elm.Signal.lift2(f)(isOn)(ticker);
56+ };
57+ }
58+
59+ /** every : Time -> Signal Time
60+ Takes a time interval t. The resulting signal is the current time,
61+ updated every t.
62+ **/
63+ function everyWhen(isOn) { return function(t) {
64+ var clock = Elm.Signal.constant(timeNow());
65+ function tellTime() { Dispatcher.notify(clock.id, timeNow()); }
66+ setInterval(tellTime, t);
67+ return clock;
68+ };
69+ }
70+
71+ function since(t) { return function(s) {
72+ function cmp(a) { return function(b) { return !Value.eq(a,b); }; }
73+ var dcount = Elm.Signal.count(Elm.Signal.delay(t)(s));
74+ return Elm.Signal.lift2(cmp)(Elm.Signal.count(s))(dcount);
75+ };
76+ }
77+ function after(t) {
78+ t *= 1000;
79+ var thread = Elm.Signal.constant(false);
80+ setTimeout(function() { Dispatcher.notify(thread.id, true); }, t);
81+ return thread;
82+ }
83+ function before(t) {
84+ t *= 1000;
85+ var thread = Elm.Signal.constant(true);
86+ setTimeout(function() { Dispatcher.notify(thread.id, false); }, t);
87+ return thread;
88+ }
89+ function read(s) {
90+ var t = window.Date.parse(s);
91+ return isNaN(t) ? ["Nothing"] : ["Just",t];
92+ }
93+ return {fpsWhen : fpsWhen,
94+ fps : function(t) { return fpsWhen(t)(Elm.Signal.constant(true)); },
95+ every : everyWhen(Elm.Signal.constant(true)),
96+ delay : Elm.Signal.delay,
97+ since : since,
98+ after : after,
99+ before : before,
100+ hour : 3600000,
101+ minute : 60000,
102+ second : 1000,
103+ ms : 1,
104+ inHours : function(t) { return t / 3600000; },
105+ inMinutes : function(t) { return t / 60000; },
106+ inSeconds : function(t) { return t / 1000; },
107+ inMss : function(t) { return t; },
108+ toDate : function(t) { return new window.Date(t); },
109+ read : read
110+ };
111+
112+}();
core/Native/Touch.jsnew file+94
@@ -0,0 +1,94 @@
1+function nativeTouch(elm) {
2+ "use strict";
3+
4+ function Dict() {
5+ this.keys = [];
6+ this.values = [];
7+
8+ this.insert = function(key,value) {
9+ this.keys.push(key);
10+ this.values.push(value);
11+ };
12+ this.lookup = function(key) {
13+ var i = this.keys.indexOf(key)
14+ return i >= 0 ? this.values[i] : {x:0,y:0,t:0};
15+ };
16+ this.remove = function(key) {
17+ var i = this.keys.indexOf(key);
18+ if (i < 0) return;
19+ var t = this.values[i];
20+ this.keys.splice(i,1);
21+ this.values.splice(i,1);
22+ return t;
23+ };
24+ }
25+
26+ var root = Elm.Signal.constant([]),
27+ tapTime = 500,
28+ hasTap = false,
29+ tap = {_:{},x:0,y:0},
30+ dict = new Dict();
31+
32+ function touch(t) {
33+ var r = dict.lookup(t.identifier);
34+ return {_ : {},
35+ id: t.identifier,
36+ x : t.pageX,
37+ y : t.pageY,
38+ x0: r.x,
39+ y0: r.y,
40+ t0: r.t
41+ };
42+ }
43+
44+ function start(e) {
45+ dict.insert(e.identifier,{x:e.pageX,y:e.pageY,t:Date.now()});
46+ }
47+ function end(e) {
48+ var t = dict.remove(e.identifier);
49+ if (Date.now() - t.t < tapTime) {
50+ hasTap = true;
51+ tap = {_:{}, x:t.x, y:t.y};
52+ }
53+ }
54+
55+ function listen(name, f) {
56+ function update(e) {
57+ for (var i = e.changedTouches.length; i--; ) { f(e.changedTouches[i]); }
58+ var ts = new Array(e.touches.length);
59+ for (var i = e.touches.length; i--; ) { ts[i] = touch(e.touches[i]); }
60+ var hasListener = Dispatcher.notify(root.id, ts);
61+ if (!hasListener) return document.removeEventListener(name, update);
62+ e.preventDefault();
63+ }
64+ Value.addListener(document, name, update);
65+ }
66+
67+ listen("touchstart", start);
68+ listen("touchmove", function(_){});
69+ listen("touchend", end);
70+ listen("touchcancel", end);
71+ listen("touchleave", end);
72+
73+ function dependency(f) {
74+ var sig = Elm.Signal.lift(f)(root);
75+ root.defaultNumberOfKids += 1;
76+ sig.defaultNumberOfKids = 0;
77+ return sig;
78+ }
79+
80+ var touches = dependency(function(ts) {
81+ return Elm.JavaScript.castJSArrayToList(ts);
82+ });
83+
84+ var taps = function() {
85+ var sig = dependency(function(_) { return tap; });
86+ sig.defaultNumberOfKids = 1;
87+ function pred(_) { var b = hasTap; hasTap = false; return b; }
88+ var sig2 = Elm.Signal.keepIf(pred)({_:{},x:0,y:0})(sig);
89+ sig2.defaultNumberOfKids = 0;
90+ return sig2;
91+ }();
92+
93+ elm.Native.Touch = { touches: touches, taps: taps };
94+}
core/Native/WebSocket.jsnew file+33
@@ -0,0 +1,33 @@
1+
2+function nativeWebSocket(elm) {
3+ "use strict";
4+ var JS = Elm.JavaScript;
5+
6+ function open(url) { return function(outgoing) {
7+ var incoming = Elm.Signal.constant(Elm.Native.List.Nil);
8+ var ws = new window.WebSocket(JS.castStringToJSString(url));
9+
10+ var pending = [];
11+ var ready = false;
12+
13+ ws.onopen = function(e) {
14+ var len = pending.length;
15+ for (var i = 0; i < len; ++i) { ws.send(pending[i]); }
16+ ready = true;
17+ };
18+ ws.onmessage = function(event) {
19+ Dispatcher.notify(incoming.id, JS.castJSStringToString(event.data));
20+ };
21+
22+ function send(msg) {
23+ var s = JS.castStringToJSString(msg);
24+ ready ? ws.send(s) : pending.push(s);
25+ }
26+
27+ function take1(x) { return function(y) { return x; } }
28+ return Elm.Signal.lift2(take1)(incoming)(Elm.Signal.lift(send)(outgoing));
29+ };
30+ }
31+
32+ elm.Native.WebSocket = {open:open};
33+}
core/Native/Window.jsnew file+35
@@ -0,0 +1,35 @@
1+/*! Window !*/
2+
3+Elm.Window = function() {
4+
5+ /*[Dimensions]*/
6+
7+ /** dimensions : Signal (Int,Int)
8+ The current dimensions of the window (i.e. the area viewable to the
9+ user, not including scroll bars).
10+ **/
11+ var dimensions = Elm.Signal.constant(Value.Tuple(window.innerWidth,
12+ window.innerHeight));
13+ dimensions.defaultNumberOfKids = 2;
14+
15+ /** width : Signal Int
16+ The current width of the window.
17+ **/
18+ var width = Elm.Signal.lift(function(p){return p[1];})(dimensions);
19+ width.defaultNumberOfKids = 0;
20+
21+ /** height : Signal Int
22+ The current height of the window.
23+ **/
24+ var height = Elm.Signal.lift(function(p){return p[2];})(dimensions);
25+ height.defaultNumberOfKids = 0;
26+
27+ Value.addListener(window, 'resize', function(e) {
28+ var w = document.getElementById('widthChecker').offsetWidth;
29+ var hasListener = Dispatcher.notify(dimensions.id,
30+ Value.Tuple(w, window.innerHeight));
31+ if (!hasListener)
32+ this.removeEventListener('resize',arguments.callee,false);
33+ });
34+ return {dimensions:dimensions,width:width,height:height};
35+}();
core/Touch.elmnew file+19
@@ -0,0 +1,19 @@
1+-- This is an early version of the touch library. It will likely grow to
2+-- include gestures that would be useful for both games and web-pages.
3+
4+module Touch where
5+
6+type Touch = { x:Int, y:Int, id:Int, x0:Int, y0:Int, t0:Time }
7+
8+-- A list of touches. Each ongoing touch is represented by a set of
9+-- coordinates and an identifier id that allows you to distinguish
10+-- between different touches. Each touch also contains the coordinates and
11+-- time of the initial contact (x0, y0, and t0) which helps compute more
12+-- complicated gestures.
13+touches : Signal [Touch]
14+touches = Native.Touch.touches
15+
16+-- The last position that was tapped. Default value is `{x=0,y=0}`.
17+-- Updates whenever the user taps the screen.
18+taps : Signal { x:Int, y:Int }
19+taps = Native.Touch.taps
core/WebSocket.elmnew file+10
@@ -0,0 +1,10 @@
1+-- A library for low latency HTTP communication. See the HTTP library for standard
2+-- requests like GET, POST, etc.
3+
4+module WebSocket where
5+
6+-- Create a web-socket. The first argument is the URL of the desired
7+-- web-socket server. The input signal holds the outgoing messages,
8+-- and the resulting signal contains the incoming ones.
9+open : String -> Signal String -> Signal String
10+open = Native.WebSocket.open