243 lines
No EOL
6.9 KiB
JavaScript
243 lines
No EOL
6.9 KiB
JavaScript
|
|
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;
|
|
|
|
|
|
function send(node, timestep, changed) {
|
|
var kids = node.kids;
|
|
for (var i = kids.length; i--; ) {
|
|
kids[i].recv(timestep, changed, node.id);
|
|
}
|
|
}
|
|
|
|
function Input(base) {
|
|
this.id = Utils.guid();
|
|
this.value = base;
|
|
this.kids = [];
|
|
this.defaultNumberOfKids = 0;
|
|
this.recv = function(timestep, eid, v) {
|
|
var changed = eid === this.id;
|
|
if (changed) { this.value = v; }
|
|
send(this, timestep, changed);
|
|
return changed;
|
|
};
|
|
elm.inputs.push(this);
|
|
}
|
|
|
|
function LiftN(update, args) {
|
|
this.id = Utils.guid();
|
|
this.value = update();
|
|
this.kids = [];
|
|
|
|
var n = args.length;
|
|
var count = 0;
|
|
var isChanged = false;
|
|
|
|
this.recv = function(timestep, changed, parentID) {
|
|
++count;
|
|
if (changed) { isChanged = true; }
|
|
if (count == n) {
|
|
if (isChanged) { this.value = update() }
|
|
send(this, timestep, isChanged);
|
|
isChanged = false;
|
|
count = 0;
|
|
}
|
|
};
|
|
for (var i = n; i--; ) { args[i].kids.push(this); }
|
|
}
|
|
|
|
function lift(func, a) {
|
|
function update() { return func(a.value) }
|
|
return new LiftN(update, [a]);
|
|
}
|
|
function lift2(func, a, b) {
|
|
function update() { return A2( func, a.value, b.value ) }
|
|
return new LiftN(update, [a,b]);
|
|
}
|
|
function lift3(func, a, b, c) {
|
|
function update() { return A3( func, a.value, b.value, c.value ) }
|
|
return new LiftN(update, [a,b,c]);
|
|
}
|
|
function lift4(func, a, b, c, d) {
|
|
function update() { return A4( func, a.value, b.value, c.value, d.value ) }
|
|
return new LiftN(update, [a,b,c,d]);
|
|
}
|
|
function lift5(func, a, b, c, d, e) {
|
|
function update() { return A5( func, a.value, b.value, c.value, d.value, e.value ) }
|
|
return new LiftN(update, [a,b,c,d,e]);
|
|
}
|
|
function lift6(func, a, b, c, d, e, f) {
|
|
function update() { return A6( func, a.value, b.value, c.value, d.value, e.value, f.value ) }
|
|
return new LiftN(update, [a,b,c,d,e,f]);
|
|
}
|
|
function lift7(func, a, b, c, d, e, f, g) {
|
|
function update() { return A7( func, a.value, b.value, c.value, d.value, e.value, f.value, g.value ) }
|
|
return new LiftN(update, [a,b,c,d,e,f,g]);
|
|
}
|
|
function lift8(func, a, b, c, d, e, f, g, h) {
|
|
function update() { return A8( func, a.value, b.value, c.value, d.value, e.value, f.value, g.value, h.value ) }
|
|
return new LiftN(update, [a,b,c,d,e,f,g,h]);
|
|
}
|
|
|
|
function foldp(func,state,input) {
|
|
function update() { state = A2(func, input.value, state); return state }
|
|
return new LiftN(update, [input]);
|
|
}
|
|
|
|
function dropIf(pred,base,input) {
|
|
this.id = Utils.guid();
|
|
this.value = pred(input.value) ? base : input.value;
|
|
this.kids = [];
|
|
this.recv = function(timestep, changed, parentID) {
|
|
var chng = changed && !pred(input.value);
|
|
if (chng) { this.value = input.value; }
|
|
send(this, timestep, chng);
|
|
};
|
|
input.kids.push(this);
|
|
}
|
|
|
|
function dropRepeats(input) {
|
|
this.id = Utils.guid();
|
|
this.value = input.value;
|
|
this.kids = [];
|
|
this.recv = function(timestep, changed, parentID) {
|
|
var chng = changed && !Utils.eq(this.value,input.value);
|
|
if (chng) { this.value = input.value; }
|
|
send(this, timestep, chng);
|
|
};
|
|
input.kids.push(this);
|
|
}
|
|
|
|
function dropWhen(s1,b,s2) {
|
|
var pairs = lift2( F2(function(x,y){return {x:x,y:y}}), s1, s2 );
|
|
var dropped = new dropIf(function(p){return p.x},{x:true,y:b},pairs);
|
|
return lift(function(p){return p.y}, dropped)
|
|
}
|
|
|
|
function timestamp(a) {
|
|
function update() { return Utils.Tuple2(Date.now(), a.value) }
|
|
return new LiftN(update, [a]);
|
|
}
|
|
|
|
function SampleOn(s1,s2) {
|
|
this.id = Utils.guid();
|
|
this.value = s2.value;
|
|
this.kids = [];
|
|
|
|
var count = 0;
|
|
var isChanged = false;
|
|
|
|
this.recv = function(timestep, changed, parentID) {
|
|
if (parentID === s1.id) isChanged = changed;
|
|
++count;
|
|
if (count == 2) {
|
|
if (isChanged) { this.value = s2.value; }
|
|
send(this, timestep, isChanged);
|
|
count = 0;
|
|
isChanged = false;
|
|
}
|
|
};
|
|
s1.kids.push(this);
|
|
s2.kids.push(this);
|
|
}
|
|
|
|
function sampleOn(s1,s2) { return new SampleOn(s1,s2) }
|
|
|
|
function delay(t,s) {
|
|
var delayed = new Input(s.value);
|
|
var firstEvent = true;
|
|
function update(v) {
|
|
if (firstEvent) { firstEvent = false; return; }
|
|
setTimeout(function() { elm.notify(delayed.id, v) }, t);
|
|
}
|
|
function first(a,b) { return a }
|
|
return new SampleOn(delayed, lift2(F2(first), delayed, lift(update,s)));
|
|
}
|
|
|
|
function Merge(s1,s2) {
|
|
this.id = Utils.guid();
|
|
this.value = s1.value;
|
|
this.kids = [];
|
|
|
|
var next = null;
|
|
var count = 0;
|
|
var isChanged = false;
|
|
|
|
this.recv = function(timestep, changed, parentID) {
|
|
++count;
|
|
if (changed) {
|
|
isChanged = true;
|
|
if (parentID == s2.id && next === null) { next = s2.value; }
|
|
if (parentID == s1.id) { next = s1.value; }
|
|
}
|
|
|
|
if (count == 2) {
|
|
if (isChanged) { this.value = next; next = null; }
|
|
send(this, timestep, isChanged);
|
|
isChanged = false;
|
|
count = 0;
|
|
}
|
|
};
|
|
s1.kids.push(this);
|
|
s2.kids.push(this);
|
|
}
|
|
|
|
function merge(s1,s2) { return new Merge(s1,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);
|
|
var i = sampleSize;
|
|
while (i--) { sample[i] = 0; }
|
|
i = 0;
|
|
var full = false;
|
|
var total = 0;
|
|
function f(n) {
|
|
total += n - sample[i];
|
|
sample[i] = n;
|
|
var avg = total / Math.max(1, full ? sampleSize : i);
|
|
if (++i == sampleSize) { full = true; i = 0; }
|
|
return avg;
|
|
}
|
|
return lift(f,s);pp
|
|
}
|
|
|
|
return elm.Native.Signal = {
|
|
constant : function(v) { return new Input(v); },
|
|
lift : F2(lift ),
|
|
lift2 : F3(lift2),
|
|
lift3 : F4(lift3),
|
|
lift4 : F5(lift4),
|
|
lift5 : F6(lift5),
|
|
lift6 : F7(lift6),
|
|
lift7 : F8(lift7),
|
|
lift8 : F9(lift8),
|
|
foldp : F3(foldp),
|
|
delay : delay,
|
|
merge : F2(merge),
|
|
merges : merges,
|
|
mergeEither : F2(mergeEither),
|
|
average : F2(average),
|
|
count : function(s) { return foldp(F2(function(x,y) { return x+y }), 0, s) },
|
|
countIf : F2(function(pred,s) {
|
|
return foldp(F2(function(x,c){return pred(x) ? c+1 : c}), 0, s)}),
|
|
keepIf : F3(function(pred,base,sig) {
|
|
return new dropIf(function(x) {return !pred(x)},base,sig) }),
|
|
dropIf : F3(function(pred,base,sig) { return new dropIf(pred,base,sig) }),
|
|
keepWhen : F3(function(s1,b,s2) {
|
|
return dropWhen(lift(function(b){return !b;},s1), b, s2) }),
|
|
dropWhen : F3(dropWhen),
|
|
dropRepeats : function(s) { return new dropRepeats(s);},
|
|
sampleOn : F2(sampleOn),
|
|
timestamp : timestamp
|
|
};
|
|
}; |