on master help sign in
Create the Utils module which contains any utilities that can be created before all other modules. Switch to using Utils in existing modules.

Add the Error module for reporting the rare runtime errors that are possible.

commit851d2ff22166f2d6c526b8531c1a6b62ef681271 authorevancz <info@elm-lang.org> dateSat 9 Mar 2013, 19:02 (14 years ago) parent05631f2
9 files changed+183−76
libraries/Native/Error.js+10−3 libraries/Native/List.js+3−1 libraries/Native/Misc.js+18−20 libraries/Native/Keyboard/Raw.js → libraries/Native/Signal/KeyboardRaw.js+8−13 libraries/Native/Signal/Mouse.js+14−14 libraries/Native/Signal/Signal.js+16−13 libraries/Native/Signal/Touch.js+3−4 libraries/Native/Signal/Window.js+10−8 libraries/Native/Utils.js+101
libraries/Native/Error.js+10−3
@@ -1,11 +1,18 @@
11
2−Elm.Error = {
3− Case : function(span) {
2+Elm.Native.Error = function(elm) {
3+ 'use strict';
4+ elm.Native = elm.Native || {};
5+ if (elm.Native.Error) return elm.Native.Error;
6+
7+ function Case(span) {
48 var msg = 'Non-exhaustive pattern match in case expression'
59 throw new Error(msg + " (" + span + ")")
610 }
7− If : function(span) {
11+
12+ function If(span) {
813 var msg = 'Non-exhaustive pattern match in multi-way-if expression'
914 throw new Error(msg + " (" + span + ")")
1015 }
16+
17+ return elm.Native.Error = { Case: Case, If: If };
1118};
libraries/Native/List.js+3−1
@@ -5,6 +5,8 @@ Elm.Native.List = function(elm) {
55 elm.Native = elm.Native || {};
66 if (elm.Native.List) return elm.Native.List;
77
8+ var eq = elm.Native.Utils(elm).eq;
9+
810 var Nil = { ctor:'Nil' };
911 function Cons(hd,tl) { return { ctor:"Cons", _0:hd, _1:tl }; }
1012
@@ -141,7 +143,7 @@ Elm.Native.List = function(elm) {
141143 function member(x, xs) {
142144 var out = 0;
143145 while (xs.ctor === "Cons") {
144− if (Misc.eq(x,xs._0)) return true;
146+ if (eq(x,xs._0)) return true;
145147 xs = xs._1;
146148 }
147149 return false;
libraries/Native/Misc.js+18−20
@@ -2,6 +2,24 @@
22Elm.Native.Misc = function(elm) {
33 'use strict';
44
5+ elm.node.addEventListener('elm_log', function(e) { console.log(e.value); });
6+ elm.node.addEventListener('elm_title', function(e) {document.title = e.value;});
7+ elm.node.addEventListener('elm_redirect', function(e) {
8+ if (e.value.length > 0) { window.location = e.value; }
9+ });
10+ elm.node.addEventListener('elm_viewport', function(e) {
11+ var node = document.getElementById('elm_viewport');
12+ if (!node) {
13+ node = document.createElement('meta');
14+ node.id = 'elm_viewport';
15+ node.name = 'viewport';
16+ document.head.appendChild(node);
17+ }
18+ node.content = e.value;
19+ Dispatcher.notify(elm.Window.dimensions.id,
20+ Tuple2(window.innerWidth, window.innerHeight));
21+ });
22+
523 var NativeList = Elm.Native.List(elm);
624 var List = Elm.List(elm);
725 var Maybe = Elm.Maybe(elm);
@@ -9,26 +27,6 @@ Elm.Native.Misc = function(elm) {
927 var Dict = Elm.Dict(elm);
1028 var Json = Elm.Json(elm);
1129
12− function eq(x,y) {
13− if (x === y) return true;
14− if (typeof x === "object") {
15− if ('_' in x) {
16− for (var i in x) { if (!eq(x[i],y[i])) return false; }
17− for (var i in y) { if (!(i in x)) return false; }
18− return true;
19− }
20− if (x.ctor !== y.ctor) return false;
21− for (var i = x.length; i--; ) {
22− if (!eq(x[i],y[i])) return false;
23− }
24− return true;
25− }
26− return x === y;
27− }
28−
29− var Tuple0 = { ctor: "Tuple0" }
30− function Tuple2(x,y) { return { ctor = "Tuple2", _0:x, _1:y } }
31−
3230 function getTextSize(w,h,txt) {
3331 var t = document.createElement('div');
3432 t.innerHTML = txt;
libraries/Native/Keyboard/Raw.js → libraries/Native/Signal/KeyboardRaw.jsrenamed+8−13
@@ -6,7 +6,6 @@ Elm.Native.Keyboard.Raw = function(elm) {
66 var NList = Elm.Native.List(elm);
77 var List = Elm.List(elm);
88 var Maybe = Elm.Maybe(elm);
9− var Misc = Elm.Native.Misc(elm);
109
1110 var keysDown = Signal.constant(NList.Nil);
1211 var charPressed = Signal.constant(Maybe.Nothing);
@@ -15,33 +14,29 @@ Elm.Native.Keyboard.Raw = function(elm) {
1514 if (List.member(e.keyCode)(keysDown.value)) return;
1615 var list = NList.Cons(e.keyCode, keysDown.value);
1716 var hasListener = elm.notify(keysDown.id, list);
18− if (!hasListener)
19− this.removeEventListener('keydown',arguments.callee,false);
17+ if (!hasListener) elm.node.removeEventListener('keydown', down);
2018 }
2119 function up(e) {
2220 function notEq(kc) { return kc !== e.keyCode; }
2321 var codes = List.filter(notEq)(keysDown.value);
2422 var hasListener = elm.notify(keysDown.id, codes);
25− if (!hasListener)
26− this.removeEventListener('keyup',arguments.callee,false);
23+ if (!hasListener) elm.node.removeEventListener('keyup', up);
2724 }
2825 function blur(e) {
2926 var hasListener = elm.notify(keysDown.id, NList.Nil);
30− if (!hasListener)
31− this.removeEventListener('blur',arguments.callee,false);
27+ if (!hasListener) elm.node.removeEventListener('blur', blur);
3228 }
3329 function press(e) {
3430 var next = Maybe.Just(e.charCode || e.keyCode);
3531 var hasListener = elm.notify(charPressed.id, next);
3632 elm.notify(charPressed.id, Maybe.Nothing);
37− if (!hasListener)
38− this.removeEventListener('keypress',arguments.callee,false);
33+ if (!hasListener) elm.node.removeEventListener('keypress', press);
3934 }
4035
41− Misc.addListener(document, 'keydown' , down );
42− Misc.addListener(document, 'keyup' , up );
43− Misc.addListener(window , 'blur' , blur );
44− Misc.addListener(document, 'keypress', press);
36+ elm.node.addEventListener('keydown' , down );
37+ elm.node.addEventListener('keyup' , up );
38+ elm.node.addEventListener('blur' , blur );
39+ elm.node.addEventListener('keypress', press);
4540
4641 return elm.Native.Keyboard.Raw = {
4742 keysDown:keysDown,
libraries/Native/Signal/Mouse.js+14−14
@@ -5,12 +5,12 @@ Elm.Native.Mouse = function(elm) {
55 if (elm.Native.Mouse) return elm.Native.Mouse;
66
77 var Signal = Elm.Signal(elm);
8− var Misc = Elm.Native.Misc(elm);
8+ var Utils = Elm.Native.Utils(elm);
99
10− var position = Signal.constant(Misc.Tuple(0,0));
10+ var position = Signal.constant(Utils.Tuple2(0,0));
1111 position.defaultNumberOfKids = 2;
1212
13− // do not get rid of x and y. By setting their default number
13+ // do not move x and y into Elm. By setting their default number
1414 // of kids, it is possible to detatch the mouse listeners if
1515 // they are not needed.
1616 var x = A2( Signal.lift, function(p){return p._0}, position);
@@ -20,7 +20,7 @@ Elm.Native.Mouse = function(elm) {
2020
2121 var isDown = Signal.constant(false);
2222 var isClicked = Signal.constant(false);
23− var clicks = Signal.constant(Misc.Tuple0);
23+ var clicks = Signal.constant(Utils.Tuple0);
2424
2525 function getXY(e) {
2626 var posx = 0;
@@ -35,36 +35,36 @@ Elm.Native.Mouse = function(elm) {
3535 posy = e.clientY + document.body.scrollTop +
3636 document.documentElement.scrollTop;
3737 }
38− return Misc.Tuple2(posx, posy);
38+ return Utils.Tuple2(posx, posy);
3939 }
4040
4141 function click(e) {
4242 var hasListener1 = elm.notify(isClicked.id, true);
43− var hasListener2 = elm.notify(clicks.id, Misc.Tuple0);
43+ var hasListener2 = elm.notify(clicks.id, Utils.Tuple0);
4444 elm.notify(isClicked.id, false);
4545 if (!hasListener1 && !hasListener2)
46− this.removeEventListener('click',arguments.callee,false);
46+ elm.node.removeEventListener('click', click);
4747 }
4848
4949 function down(e) {
5050 var hasListener = elm.notify(isDown.id, true);
51− if (!hasListener) this.removeEventListener('mousedown',arguments.callee,false);
51+ if (!hasListener) elm.node.removeEventListener('mousedown', down);
5252 }
5353
5454 function up(e) {
5555 var hasListener = elm.notify(isDown.id, false);
56− if (!hasListener) this.removeEventListener('mouseup',arguments.callee,false);
56+ if (!hasListener) elm.node.removeEventListener('mouseup', up);
5757 }
5858
5959 function move(e) {
6060 var hasListener = elm.notify(position.id, getXY(e));
61− if (!hasListener) this.removeEventListener('mousemove',arguments.callee,false);
61+ if (!hasListener) elm.node.removeEventListener('mousemove', move);
6262 }
6363
64− Misc.addListener(elm.node, 'click' , click);
65− Misc.addListener(elm.node, 'mousedown', down);
66− Misc.addListener(elm.node, 'mouseup' , up);
67− Misc.addListener(elm.node, 'mousemove', move);
64+ elm.node.addEventListener('click' , click);
65+ elm.node.addEventListener('mousedown', down);
66+ elm.node.addEventListener('mouseup' , up);
67+ elm.node.addEventListener('mousemove', move);
6868
6969 return elm.Native.Mouse = {
7070 position: position,
libraries/Native/Signal/Signal.js+16−13
@@ -1,12 +1,15 @@
11
2−/**
3−module Native.Signal where
2+Elm.Native.Signal = function(elm) {
3+ 'use strict';
4+
5+ elm.Native = elm.Native || {};
6+ if (elm.Native.Signal) return elm.Native.Signal;
7+
8+ var Utils = Elm.Native.Utils(elm);
9+ var Either = Elm.Either(elm);
10+ var foldl1 = Elm.List(elm).foldl1;
411
5−import Either
6−import List
7−**/
812
9−(function() {
1013 function send(node, timestep, changed) {
1114 var kids = node.kids;
1215 for (var i = kids.length; i--; ) {
@@ -105,7 +108,7 @@ import List
105108 this.value = input.value;
106109 this.kids = [];
107110 this.recv = function(timestep, changed, parentID) {
108− var chng = changed && !Elm.Misc.eq(this.value,input.value);
111+ var chng = changed && !Utils.eq(this.value,input.value);
109112 if (chng) { this.value = input.value; }
110113 send(this, timestep, chng);
111114 };
@@ -119,7 +122,7 @@ import List
119122 }
120123
121124 function timestamp(a) {
122− function update() { return Elm.Misc.Tuple2(Date.now(), a.value) }
125+ function update() { return Utils.Tuple2(Date.now(), a.value) }
123126 return new LiftN(update, [a]);
124127 }
125128
@@ -187,9 +190,9 @@ import List
187190 }
188191
189192 function merge(s1,s2) { return new Merge(s1,s2) }
190− function merges(ss) { return A2(Elm.List.foldl1, F2(merge), ss) }
191− function mergeEither(s1,s2) {return new Merge(A2(lift, Elm.Either.Left , s1),
192− A2(lift, Elm.Either.Right, s2))}
193+ function merges(ss) { return A2(foldl1, F2(merge), ss) }
194+ function mergeEither(s1,s2) {return new Merge(A2(lift, Either.Left , s1),
195+ A2(lift, Either.Right, s2))}
193196
194197 function average(sampleSize, s) {
195198 var sample = new Array(sampleSize);
@@ -208,7 +211,7 @@ import List
208211 return lift(f,s);pp
209212 }
210213
211− Elm.Native.Signal = {
214+ return elm.Native.Signal = {
212215 constant : function(v) { return new Input(v); },
213216 lift : F2(lift ),
214217 lift2 : F3(lift2),
@@ -237,4 +240,4 @@ import List
237240 sampleOn : F2(sampleOn),
238241 timestamp : timestamp
239242 };
240−}())
243+};
libraries/Native/Signal/Touch.js+3−4
@@ -7,7 +7,6 @@ Elm.Native.Touch = function(elm) {
77
88 var Signal = Elm.Signal(elm);
99 var JS = Elm.JavaScript(elm);
10− var Misc = Elm.Native.Misc(elm);
1110
1211 function Dict() {
1312 this.keys = [];
@@ -65,11 +64,11 @@ Elm.Native.Touch = function(elm) {
6564 for (var i = e.changedTouches.length; i--; ) { f(e.changedTouches[i]); }
6665 var ts = new Array(e.touches.length);
6766 for (var i = e.touches.length; i--; ) { ts[i] = touch(e.touches[i]); }
68− var hasListener = Dispatcher.notify(root.id, ts);
69− if (!hasListener) return document.removeEventListener(name, update);
67+ var hasListener = elm.notify(root.id, ts);
68+ if (!hasListener) return elm.node.removeEventListener(name, update);
7069 e.preventDefault();
7170 }
72− Misc.addListener(document, name, update);
71+ elm.node.addEventListener(name, update);
7372 }
7473
7574 listen("touchstart", start);
libraries/Native/Signal/Window.js+10−8
@@ -6,11 +6,14 @@ Elm.Native.Window = function(elm) {
66 if (elm.Native.Window) return elm.Native.Window;
77
88 var Signal = Elm.Signal(elm);
9− var Misc = Elm.Native.Misc(elm);
9+ var Tuple2 = Elm.Native.Utils(elm).Tuple2;
1010
11− var dimensions = Signal.constant(Misc.Tuple2(window.innerWidth,window.innerHeight));
11+ var dimensions = Signal.constant(Tuple2(elm.node.clientWidth,
12+ elm.node.clientHeight));
1213 dimensions.defaultNumberOfKids = 2;
1314
15+ // Do not move width and height into Elm. By setting the default number of kids,
16+ // the resize listener can be detached.
1417 var width = A2(Signal.lift, function(p){return p._0}, dimensions);
1518 width.defaultNumberOfKids = 0;
1619
@@ -18,13 +21,12 @@ Elm.Native.Window = function(elm) {
1821 height.defaultNumberOfKids = 0;
1922
2023 function resize(e) {
21− var w = elm.node.getElementById('widthChecker').offsetWidth;
22− var hasListener = elm.notify(dimensions.id,
23− Misc.Tuple2(w, window.innerHeight));
24− if (!hasListener)
25− this.removeEventListener('resize',arguments.callee,false);
24+ console.log('use the base node (should happen after resize)');
25+ var hasListener = elm.notify(dimensions.id, Tuple2(elm.node.clientWidth,
26+ elm.node.clientHeight));
27+ if (!hasListener) window.removeEventListener('resize', resize);
2628 }
27− Misc.addListener(window, 'resize', resize);
29+ window.addEventListener('resize', resize);
2830
2931 return elm.Native.Window = {
3032 dimensions:dimensions,
libraries/Native/Utils.jsnew file+101
@@ -0,0 +1,101 @@
1+
2+Elm.Native.Utils = function(elm) {
3+ 'use strict';
4+
5+ function eq(x,y) {
6+ if (x === y) return true;
7+ if (typeof x === "object") {
8+ var c = 0;
9+ for (var i in x) { ++c; if (!eq(x[i],y[i])) return false; }
10+ return c === Object.keys(y).length;
11+ }
12+ if (typeof x === 'function') {
13+ throw new Error('Equality error: general function equality is ' +
14+ 'undecidable, and therefore, unsupported');
15+ }
16+ return x === y;
17+ }
18+
19+ var EQ = 0, LT = 1, GT = 2, ord = ['EQ','LT','GT'];
20+ function compare(x,y) { return { ctor: ord[cmp(x,y)] } }
21+ function cmp(x,y) {
22+ if (typeof x !== 'object') return x === y ? EQ : x < y ? LT : GT;
23+
24+ if (x.ctor === "Cons" || x.ctor === "Nil") {
25+ while (true) {
26+ if (x.ctor === "Nil" && y.ctor === "Nil") return EQ;
27+ if (x.ctor !== y.ctor) return {ctor: x.ctor==='Nil'?LT:GT};
28+ var ord = cmp(x._0, y._0);
29+ if (ord !== EQ) return ord;
30+ x = x._1;
31+ y = y._1;
32+ }
33+ }
34+ if (x.ctor.slice(0,5) === 'Tuple') {
35+ var n = x.ctor.slice(5) - 0;
36+ var err = 'cannot compare tuples with more than 6 elements.';
37+ if (n === 0) return EQ;
38+ if (n >= 1) { ord = cmp(x._0, y._0); if (ord !== EQ) return ord;
39+ if (n >= 2) { ord = cmp(x._1, y._1); if (ord !== EQ) return ord;
40+ if (n >= 3) { ord = cmp(x._2, y._2); if (ord !== EQ) return ord;
41+ if (n >= 4) { ord = cmp(x._3, y._3); if (ord !== EQ) return ord;
42+ if (n >= 5) { ord = cmp(x._4, y._4); if (ord !== EQ) return ord;
43+ if (n >= 6) { ord = cmp(x._5, y._5); if (ord !== EQ) return ord;
44+ if (n >= 7) throw new Error('Comparison error: ' + err); } } } } } }
45+ return EQ;
46+ }
47+ throw new Error('Comparison error: comparison is only defined on ints, ' +
48+ 'floats, times, chars, strings, lists of comparable values, ' +
49+ 'and tuples of comparable values.')
50+ }
51+
52+
53+ var Tuple0 = { ctor: "Tuple0" }
54+ function Tuple2(x,y) { return { ctor = "Tuple2", _0:x, _1:y } }
55+
56+
57+ function copy(r) {
58+ var o = {};
59+ for (var i in r) { o[i] = r[i]; }
60+ return o;
61+ }
62+
63+ function remove(x,r) {
64+ var o = copy(r);
65+ if (x in o._) {
66+ o[x] = o._[x][0];
67+ o._[x] = o._[x].slice(1);
68+ if (o._[x].length === 0) { delete o._[x]; }
69+ } else {
70+ delete o[x];
71+ }
72+ return o;
73+ }
74+
75+ function replace(kvs,r) {
76+ var o = copy(r);
77+ for (var i = kvs.length; i--; ) {
78+ kvsi = kvs[i];
79+ o[kvsi[0]] = kvsi[1];
80+ }
81+ return o;
82+ }
83+
84+ function insert(x,v,r) {
85+ var o = copy(r);
86+ if (x in o) o._[x] = [o[x]].concat(x in o._ ? o._[x].slice(0) : []);
87+ o[x] = v;
88+ return o;
89+ }
90+
91+ return elm.Native.Utils = {
92+ eq:eq,
93+ compare:compare,
94+ Tuple0:Tuple0,
95+ Tuple2:Tuple2,
96+ copy: copy,
97+ remove: remove,
98+ replace: replace,
99+ insert: insert
100+ };
101+};