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.
This commit is contained in:
evancz 2013-03-09 19:02:07 -08:00
parent 05631f2b37
commit 851d2ff221
9 changed files with 183 additions and 76 deletions

View file

@ -1,11 +1,18 @@
Elm.Error = {
Case : function(span) {
Elm.Native.Error = function(elm) {
'use strict';
elm.Native = elm.Native || {};
if (elm.Native.Error) return elm.Native.Error;
function Case(span) {
var msg = 'Non-exhaustive pattern match in case expression'
throw new Error(msg + " (" + span + ")")
}
If : function(span) {
function If(span) {
var msg = 'Non-exhaustive pattern match in multi-way-if expression'
throw new Error(msg + " (" + span + ")")
}
return elm.Native.Error = { Case: Case, If: If };
};

View file

@ -5,6 +5,8 @@ Elm.Native.List = function(elm) {
elm.Native = elm.Native || {};
if (elm.Native.List) return elm.Native.List;
var eq = elm.Native.Utils(elm).eq;
var Nil = { ctor:'Nil' };
function Cons(hd,tl) { return { ctor:"Cons", _0:hd, _1:tl }; }
@ -141,7 +143,7 @@ Elm.Native.List = function(elm) {
function member(x, xs) {
var out = 0;
while (xs.ctor === "Cons") {
if (Misc.eq(x,xs._0)) return true;
if (eq(x,xs._0)) return true;
xs = xs._1;
}
return false;

View file

@ -2,6 +2,24 @@
Elm.Native.Misc = function(elm) {
'use strict';
elm.node.addEventListener('elm_log', function(e) { console.log(e.value); });
elm.node.addEventListener('elm_title', function(e) {document.title = e.value;});
elm.node.addEventListener('elm_redirect', function(e) {
if (e.value.length > 0) { window.location = e.value; }
});
elm.node.addEventListener('elm_viewport', function(e) {
var node = document.getElementById('elm_viewport');
if (!node) {
node = document.createElement('meta');
node.id = 'elm_viewport';
node.name = 'viewport';
document.head.appendChild(node);
}
node.content = e.value;
Dispatcher.notify(elm.Window.dimensions.id,
Tuple2(window.innerWidth, window.innerHeight));
});
var NativeList = Elm.Native.List(elm);
var List = Elm.List(elm);
var Maybe = Elm.Maybe(elm);
@ -9,26 +27,6 @@ Elm.Native.Misc = function(elm) {
var Dict = Elm.Dict(elm);
var Json = Elm.Json(elm);
function eq(x,y) {
if (x === y) return true;
if (typeof x === "object") {
if ('_' in x) {
for (var i in x) { if (!eq(x[i],y[i])) return false; }
for (var i in y) { if (!(i in x)) return false; }
return true;
}
if (x.ctor !== y.ctor) return false;
for (var i = x.length; i--; ) {
if (!eq(x[i],y[i])) return false;
}
return true;
}
return x === y;
}
var Tuple0 = { ctor: "Tuple0" }
function Tuple2(x,y) { return { ctor = "Tuple2", _0:x, _1:y } }
function getTextSize(w,h,txt) {
var t = document.createElement('div');
t.innerHTML = txt;

View file

@ -6,7 +6,6 @@ Elm.Native.Keyboard.Raw = function(elm) {
var NList = Elm.Native.List(elm);
var List = Elm.List(elm);
var Maybe = Elm.Maybe(elm);
var Misc = Elm.Native.Misc(elm);
var keysDown = Signal.constant(NList.Nil);
var charPressed = Signal.constant(Maybe.Nothing);
@ -15,33 +14,29 @@ Elm.Native.Keyboard.Raw = function(elm) {
if (List.member(e.keyCode)(keysDown.value)) return;
var list = NList.Cons(e.keyCode, keysDown.value);
var hasListener = elm.notify(keysDown.id, list);
if (!hasListener)
this.removeEventListener('keydown',arguments.callee,false);
if (!hasListener) elm.node.removeEventListener('keydown', down);
}
function up(e) {
function notEq(kc) { return kc !== e.keyCode; }
var codes = List.filter(notEq)(keysDown.value);
var hasListener = elm.notify(keysDown.id, codes);
if (!hasListener)
this.removeEventListener('keyup',arguments.callee,false);
if (!hasListener) elm.node.removeEventListener('keyup', up);
}
function blur(e) {
var hasListener = elm.notify(keysDown.id, NList.Nil);
if (!hasListener)
this.removeEventListener('blur',arguments.callee,false);
if (!hasListener) elm.node.removeEventListener('blur', blur);
}
function press(e) {
var next = Maybe.Just(e.charCode || e.keyCode);
var hasListener = elm.notify(charPressed.id, next);
elm.notify(charPressed.id, Maybe.Nothing);
if (!hasListener)
this.removeEventListener('keypress',arguments.callee,false);
if (!hasListener) elm.node.removeEventListener('keypress', press);
}
Misc.addListener(document, 'keydown' , down );
Misc.addListener(document, 'keyup' , up );
Misc.addListener(window , 'blur' , blur );
Misc.addListener(document, 'keypress', press);
elm.node.addEventListener('keydown' , down );
elm.node.addEventListener('keyup' , up );
elm.node.addEventListener('blur' , blur );
elm.node.addEventListener('keypress', press);
return elm.Native.Keyboard.Raw = {
keysDown:keysDown,

View file

@ -5,12 +5,12 @@ Elm.Native.Mouse = function(elm) {
if (elm.Native.Mouse) return elm.Native.Mouse;
var Signal = Elm.Signal(elm);
var Misc = Elm.Native.Misc(elm);
var Utils = Elm.Native.Utils(elm);
var position = Signal.constant(Misc.Tuple(0,0));
var position = Signal.constant(Utils.Tuple2(0,0));
position.defaultNumberOfKids = 2;
// do not get rid of x and y. By setting their default number
// do not move x and y into Elm. By setting their default number
// of kids, it is possible to detatch the mouse listeners if
// they are not needed.
var x = A2( Signal.lift, function(p){return p._0}, position);
@ -20,7 +20,7 @@ Elm.Native.Mouse = function(elm) {
var isDown = Signal.constant(false);
var isClicked = Signal.constant(false);
var clicks = Signal.constant(Misc.Tuple0);
var clicks = Signal.constant(Utils.Tuple0);
function getXY(e) {
var posx = 0;
@ -35,36 +35,36 @@ Elm.Native.Mouse = function(elm) {
posy = e.clientY + document.body.scrollTop +
document.documentElement.scrollTop;
}
return Misc.Tuple2(posx, posy);
return Utils.Tuple2(posx, posy);
}
function click(e) {
var hasListener1 = elm.notify(isClicked.id, true);
var hasListener2 = elm.notify(clicks.id, Misc.Tuple0);
var hasListener2 = elm.notify(clicks.id, Utils.Tuple0);
elm.notify(isClicked.id, false);
if (!hasListener1 && !hasListener2)
this.removeEventListener('click',arguments.callee,false);
elm.node.removeEventListener('click', click);
}
function down(e) {
var hasListener = elm.notify(isDown.id, true);
if (!hasListener) this.removeEventListener('mousedown',arguments.callee,false);
if (!hasListener) elm.node.removeEventListener('mousedown', down);
}
function up(e) {
var hasListener = elm.notify(isDown.id, false);
if (!hasListener) this.removeEventListener('mouseup',arguments.callee,false);
if (!hasListener) elm.node.removeEventListener('mouseup', up);
}
function move(e) {
var hasListener = elm.notify(position.id, getXY(e));
if (!hasListener) this.removeEventListener('mousemove',arguments.callee,false);
if (!hasListener) elm.node.removeEventListener('mousemove', move);
}
Misc.addListener(elm.node, 'click' , click);
Misc.addListener(elm.node, 'mousedown', down);
Misc.addListener(elm.node, 'mouseup' , up);
Misc.addListener(elm.node, 'mousemove', move);
elm.node.addEventListener('click' , click);
elm.node.addEventListener('mousedown', down);
elm.node.addEventListener('mouseup' , up);
elm.node.addEventListener('mousemove', move);
return elm.Native.Mouse = {
position: position,

View file

@ -1,12 +1,15 @@
/**
module Native.Signal where
Elm.Native.Signal = function(elm) {
'use strict';
elm.Native = elm.Native || {};
if (elm.Native.Signal) return elm.Native.Signal;
var Utils = Elm.Native.Utils(elm);
var Either = Elm.Either(elm);
var foldl1 = Elm.List(elm).foldl1;
import Either
import List
**/
(function() {
function send(node, timestep, changed) {
var kids = node.kids;
for (var i = kids.length; i--; ) {
@ -105,7 +108,7 @@ import List
this.value = input.value;
this.kids = [];
this.recv = function(timestep, changed, parentID) {
var chng = changed && !Elm.Misc.eq(this.value,input.value);
var chng = changed && !Utils.eq(this.value,input.value);
if (chng) { this.value = input.value; }
send(this, timestep, chng);
};
@ -119,7 +122,7 @@ import List
}
function timestamp(a) {
function update() { return Elm.Misc.Tuple2(Date.now(), a.value) }
function update() { return Utils.Tuple2(Date.now(), a.value) }
return new LiftN(update, [a]);
}
@ -187,9 +190,9 @@ import List
}
function merge(s1,s2) { return new Merge(s1,s2) }
function merges(ss) { return A2(Elm.List.foldl1, F2(merge), ss) }
function mergeEither(s1,s2) {return new Merge(A2(lift, Elm.Either.Left , s1),
A2(lift, Elm.Either.Right, s2))}
function merges(ss) { return A2(foldl1, F2(merge), ss) }
function mergeEither(s1,s2) {return new Merge(A2(lift, Either.Left , s1),
A2(lift, Either.Right, s2))}
function average(sampleSize, s) {
var sample = new Array(sampleSize);
@ -208,7 +211,7 @@ import List
return lift(f,s);pp
}
Elm.Native.Signal = {
return elm.Native.Signal = {
constant : function(v) { return new Input(v); },
lift : F2(lift ),
lift2 : F3(lift2),
@ -237,4 +240,4 @@ import List
sampleOn : F2(sampleOn),
timestamp : timestamp
};
}())
};

View file

@ -7,7 +7,6 @@ Elm.Native.Touch = function(elm) {
var Signal = Elm.Signal(elm);
var JS = Elm.JavaScript(elm);
var Misc = Elm.Native.Misc(elm);
function Dict() {
this.keys = [];
@ -65,11 +64,11 @@ Elm.Native.Touch = function(elm) {
for (var i = e.changedTouches.length; i--; ) { f(e.changedTouches[i]); }
var ts = new Array(e.touches.length);
for (var i = e.touches.length; i--; ) { ts[i] = touch(e.touches[i]); }
var hasListener = Dispatcher.notify(root.id, ts);
if (!hasListener) return document.removeEventListener(name, update);
var hasListener = elm.notify(root.id, ts);
if (!hasListener) return elm.node.removeEventListener(name, update);
e.preventDefault();
}
Misc.addListener(document, name, update);
elm.node.addEventListener(name, update);
}
listen("touchstart", start);

View file

@ -6,11 +6,14 @@ Elm.Native.Window = function(elm) {
if (elm.Native.Window) return elm.Native.Window;
var Signal = Elm.Signal(elm);
var Misc = Elm.Native.Misc(elm);
var Tuple2 = Elm.Native.Utils(elm).Tuple2;
var dimensions = Signal.constant(Misc.Tuple2(window.innerWidth,window.innerHeight));
var dimensions = Signal.constant(Tuple2(elm.node.clientWidth,
elm.node.clientHeight));
dimensions.defaultNumberOfKids = 2;
// Do not move width and height into Elm. By setting the default number of kids,
// the resize listener can be detached.
var width = A2(Signal.lift, function(p){return p._0}, dimensions);
width.defaultNumberOfKids = 0;
@ -18,13 +21,12 @@ Elm.Native.Window = function(elm) {
height.defaultNumberOfKids = 0;
function resize(e) {
var w = elm.node.getElementById('widthChecker').offsetWidth;
var hasListener = elm.notify(dimensions.id,
Misc.Tuple2(w, window.innerHeight));
if (!hasListener)
this.removeEventListener('resize',arguments.callee,false);
console.log('use the base node (should happen after resize)');
var hasListener = elm.notify(dimensions.id, Tuple2(elm.node.clientWidth,
elm.node.clientHeight));
if (!hasListener) window.removeEventListener('resize', resize);
}
Misc.addListener(window, 'resize', resize);
window.addEventListener('resize', resize);
return elm.Native.Window = {
dimensions:dimensions,

101
libraries/Native/Utils.js Normal file
View file

@ -0,0 +1,101 @@
Elm.Native.Utils = function(elm) {
'use strict';
function eq(x,y) {
if (x === y) return true;
if (typeof x === "object") {
var c = 0;
for (var i in x) { ++c; if (!eq(x[i],y[i])) return false; }
return c === Object.keys(y).length;
}
if (typeof x === 'function') {
throw new Error('Equality error: general function equality is ' +
'undecidable, and therefore, unsupported');
}
return x === y;
}
var EQ = 0, LT = 1, GT = 2, ord = ['EQ','LT','GT'];
function compare(x,y) { return { ctor: ord[cmp(x,y)] } }
function cmp(x,y) {
if (typeof x !== 'object') return x === y ? EQ : x < y ? LT : GT;
if (x.ctor === "Cons" || x.ctor === "Nil") {
while (true) {
if (x.ctor === "Nil" && y.ctor === "Nil") return EQ;
if (x.ctor !== y.ctor) return {ctor: x.ctor==='Nil'?LT:GT};
var ord = cmp(x._0, y._0);
if (ord !== EQ) return ord;
x = x._1;
y = y._1;
}
}
if (x.ctor.slice(0,5) === 'Tuple') {
var n = x.ctor.slice(5) - 0;
var err = 'cannot compare tuples with more than 6 elements.';
if (n === 0) return EQ;
if (n >= 1) { ord = cmp(x._0, y._0); if (ord !== EQ) return ord;
if (n >= 2) { ord = cmp(x._1, y._1); if (ord !== EQ) return ord;
if (n >= 3) { ord = cmp(x._2, y._2); if (ord !== EQ) return ord;
if (n >= 4) { ord = cmp(x._3, y._3); if (ord !== EQ) return ord;
if (n >= 5) { ord = cmp(x._4, y._4); if (ord !== EQ) return ord;
if (n >= 6) { ord = cmp(x._5, y._5); if (ord !== EQ) return ord;
if (n >= 7) throw new Error('Comparison error: ' + err); } } } } } }
return EQ;
}
throw new Error('Comparison error: comparison is only defined on ints, ' +
'floats, times, chars, strings, lists of comparable values, ' +
'and tuples of comparable values.')
}
var Tuple0 = { ctor: "Tuple0" }
function Tuple2(x,y) { return { ctor = "Tuple2", _0:x, _1:y } }
function copy(r) {
var o = {};
for (var i in r) { o[i] = r[i]; }
return o;
}
function remove(x,r) {
var o = copy(r);
if (x in o._) {
o[x] = o._[x][0];
o._[x] = o._[x].slice(1);
if (o._[x].length === 0) { delete o._[x]; }
} else {
delete o[x];
}
return o;
}
function replace(kvs,r) {
var o = copy(r);
for (var i = kvs.length; i--; ) {
kvsi = kvs[i];
o[kvsi[0]] = kvsi[1];
}
return o;
}
function insert(x,v,r) {
var o = copy(r);
if (x in o) o._[x] = [o[x]].concat(x in o._ ? o._[x].slice(0) : []);
o[x] = v;
return o;
}
return elm.Native.Utils = {
eq:eq,
compare:compare,
Tuple0:Tuple0,
Tuple2:Tuple2,
copy: copy,
remove: remove,
replace: replace,
insert: insert
};
};