This commit is contained in:
Tony Garnock-Jones 2016-08-25 13:13:44 +01:00
parent 70112027c6
commit 325655cd3b
20 changed files with 940 additions and 1003 deletions

14
dist/syndicate.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -2,14 +2,12 @@
new Syndicate.Ground(function () { new Syndicate.Ground(function () {
Syndicate.UI.spawnUIDriver(); Syndicate.UI.spawnUIDriver();
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.declareField(this, "counter", 0);
var ui = new Syndicate.UI.Anchor(); var ui = new Syndicate.UI.Anchor();
(function () { Syndicate.Actor.declareField(this, "counter", 0);
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(ui.html('#button-label', '' + this.counter), 0); }));
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(ui.html('#button-label', '' + this.counter), 0); })) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#counter', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#counter', 'click', _), metalevel: 0 }; }), (function() {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#counter', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#counter', 'click', _), metalevel: 0 }; }), (function() { this.counter++;
this.counter++; }));
})).completeBuild(); }).call(this); } }).bind(this)); });
});
}).startStepping(); }).startStepping();

View File

@ -2,13 +2,11 @@ ground dataspace {
Syndicate.UI.spawnUIDriver(); Syndicate.UI.spawnUIDriver();
actor { actor {
field this.counter = 0;
var ui = new Syndicate.UI.Anchor(); var ui = new Syndicate.UI.Anchor();
react { field this.counter = 0;
assert ui.html('#button-label', '' + this.counter); assert ui.html('#button-label', '' + this.counter);
on message Syndicate.UI.globalEvent('#counter', 'click', _) { on message Syndicate.UI.globalEvent('#counter', 'click', _) {
this.counter++; this.counter++;
}
} }
} }
} }

View File

@ -19,37 +19,36 @@ function spawnChatApp() {
var ui = new Syndicate.UI.Anchor(); var ui = new Syndicate.UI.Anchor();
field this.nym; field this.nym;
field this.status; field this.status;
react {
on asserted inputValue('#nym', $v) { this.nym = v; }
on asserted inputValue('#status', $v) { this.status = v; }
on asserted brokerConnected($url) { outputState('connected to ' + url); } on asserted inputValue('#nym', $v) { this.nym = v; }
on retracted brokerConnected($url) { outputState('disconnected from ' + url); } on asserted inputValue('#status', $v) { this.status = v; }
during inputValue('#wsurl', $url) { on asserted brokerConnected($url) { outputState('connected to ' + url); }
assert brokerConnection(url); on retracted brokerConnected($url) { outputState('disconnected from ' + url); }
on message Syndicate.WakeDetector.wakeEvent() { during inputValue('#wsurl', $url) {
:: forceBrokerDisconnect(url); assert brokerConnection(url);
}
assert toBroker(url, present(this.nym, this.status)); on message Syndicate.WakeDetector.wakeEvent() {
during fromBroker(url, present($who, $status)) { :: forceBrokerDisconnect(url);
assert ui.context(who, status) }
.html('#nymlist',
Mustache.render($('#nym_template').html(), { who: who, status: status }));
}
on message Syndicate.UI.globalEvent('#send_chat', 'click', _) { assert toBroker(url, present(this.nym, this.status));
var inp = $("#chat_input"); during fromBroker(url, present($who, $status)) {
var utterance = inp.val(); assert ui.context(who, status)
inp.val(""); .html('#nymlist',
if (utterance) :: toBroker(url, says(this.nym, utterance)); Mustache.render($('#nym_template').html(), { who: who, status: status }));
} }
on message fromBroker(url, says($who, $what)) { on message Syndicate.UI.globalEvent('#send_chat', 'click', _) {
outputUtterance(who, what); var inp = $("#chat_input");
} var utterance = inp.val();
inp.val("");
if (utterance) :: toBroker(url, says(this.nym, utterance));
}
on message fromBroker(url, says($who, $what)) {
outputUtterance(who, what);
} }
} }
} }
@ -84,13 +83,11 @@ assertion type inputValue(selector, value);
function spawnInputChangeMonitor() { function spawnInputChangeMonitor() {
actor { actor {
react { during Syndicate.observe(inputValue($selector, _)) actor {
during Syndicate.observe(inputValue($selector, _)) actor { field this.value = $(selector).val();
field this.value = $(selector).val(); assert inputValue(selector, this.value);
assert inputValue(selector, this.value); on message Syndicate.UI.globalEvent(selector, 'change', $e) {
on message Syndicate.UI.globalEvent(selector, 'change', $e) { this.value = e.target.value;
this.value = e.target.value;
}
} }
} }
} }

View File

@ -11,10 +11,8 @@ assertion type componentPresent(name);
function spawnTV() { function spawnTV() {
actor { actor {
var ui = new Syndicate.UI.Anchor(); var ui = new Syndicate.UI.Anchor();
react { during tvAlert($text) {
during tvAlert($text) { assert ui.context(text).html('#tv', Mustache.render($('#alert_template').html(), { text: text }));
assert ui.context(text).html('#tv', Mustache.render($('#alert_template').html(), { text: text }));
}
} }
} }
} }
@ -24,11 +22,9 @@ function spawnTV() {
function spawnRemoteControl() { function spawnRemoteControl() {
actor { actor {
react { assert componentPresent('remote control');
assert componentPresent('remote control'); on message Syndicate.UI.globalEvent('#remote-control', 'click', _) {
on message Syndicate.UI.globalEvent('#remote-control', 'click', _) { :: remoteClick();
:: remoteClick();
}
} }
} }
} }
@ -41,15 +37,13 @@ function spawnRemoteListener() {
// state, if we've been clicked, turn it off. We don't do this // state, if we've been clicked, turn it off. We don't do this
// here, for simplicity. // here, for simplicity.
react { on asserted powerDraw($watts) {
on asserted powerDraw($watts) { this.stoveIsOn = watts > 0;
this.stoveIsOn = watts > 0; }
}
on message remoteClick() { on message remoteClick() {
if (this.stoveIsOn) { if (this.stoveIsOn) {
:: switchAction(false); :: switchAction(false);
}
} }
} }
} }
@ -62,24 +56,23 @@ function spawnStoveSwitch() {
actor { actor {
field this.powerOn = false; field this.powerOn = false;
this.ui = new Syndicate.UI.Anchor(); this.ui = new Syndicate.UI.Anchor();
react {
assert componentPresent('stove switch');
assert switchState(this.powerOn);
assert this.ui.html('#stove-switch', assert componentPresent('stove switch');
Mustache.render($('#stove_element_template').html(), assert switchState(this.powerOn);
{ imgurl: ("img/stove-coil-element-" +
(this.powerOn ? "hot" : "cold") + ".jpg") }));
on message Syndicate.UI.globalEvent('#stove-switch-on', 'click', _) { this.powerOn = true; } assert this.ui.html('#stove-switch',
on message Syndicate.UI.globalEvent('#stove-switch-off', 'click', _) { this.powerOn = false; } Mustache.render($('#stove_element_template').html(),
{ imgurl: ("img/stove-coil-element-" +
(this.powerOn ? "hot" : "cold") + ".jpg") }));
on message switchAction($newState) { on message Syndicate.UI.globalEvent('#stove-switch-on', 'click', _) { this.powerOn = true; }
this.powerOn = newState; on message Syndicate.UI.globalEvent('#stove-switch-off', 'click', _) { this.powerOn = false; }
}
} until { on message switchAction($newState) {
case message Syndicate.UI.globalEvent('#kill-stove-switch', 'click', _); this.powerOn = newState;
} }
stop on message Syndicate.UI.globalEvent('#kill-stove-switch', 'click', _);
} }
} }
@ -87,19 +80,18 @@ function spawnPowerDrawMonitor() {
actor { actor {
field this.watts = 0; field this.watts = 0;
this.ui = new Syndicate.UI.Anchor(); this.ui = new Syndicate.UI.Anchor();
react {
assert componentPresent('power draw monitor');
assert powerDraw(this.watts);
assert this.ui.html('#power-draw-meter', assert componentPresent('power draw monitor');
Mustache.render($('#power_draw_template').html(), { watts: this.watts })); assert powerDraw(this.watts);
on asserted switchState($on) { assert this.ui.html('#power-draw-meter',
this.watts = on ? 1500 : 0; Mustache.render($('#power_draw_template').html(), { watts: this.watts }));
}
} until { on asserted switchState($on) {
case message Syndicate.UI.globalEvent('#kill-power-draw-monitor', 'click', _); this.watts = on ? 1500 : 0;
} }
stop on message Syndicate.UI.globalEvent('#kill-power-draw-monitor', 'click', _);
} }
} }
@ -108,18 +100,16 @@ function spawnPowerDrawMonitor() {
function spawnTimeoutListener() { function spawnTimeoutListener() {
actor { actor {
react { during powerDraw($watts) {
during powerDraw($watts) { on start {
do { if (watts > 0) {
if (watts > 0) { var powerOnTime = Date.now();
var powerOnTime = Date.now(); react {
react { on asserted Syndicate.Timer.timeLaterThan(powerOnTime + 3000) {
on asserted Syndicate.Timer.timeLaterThan(powerOnTime + 3000) { react { assert tvAlert('Stove on too long?'); }
react { assert tvAlert('Stove on too long?'); } }
} on asserted Syndicate.Timer.timeLaterThan(powerOnTime + 10000) {
on asserted Syndicate.Timer.timeLaterThan(powerOnTime + 10000) { $("img.flames").show();
$("img.flames").show();
}
} }
} }
} }
@ -130,17 +120,14 @@ function spawnTimeoutListener() {
// function spawnTimeoutListener() { // function spawnTimeoutListener() {
// actor { // actor {
// react { // on asserted powerDraw($watts) {
// on asserted powerDraw($watts) { // if (watts > 0) {
// if (watts > 0) { // var powerOnTime = Date.now();
// var powerOnTime = Date.now(); // react {
// react { // on asserted Syndicate.Timer.timeLaterThan(powerOnTime + 3000) {
// on asserted Syndicate.Timer.timeLaterThan(powerOnTime + 3000) { // react { assert tvAlert('Stove on too long?'); }
// react { assert tvAlert('Stove on too long?'); }
// }
// } until {
// case asserted powerDraw(0); // alt: on retracted powerDraw(watts);
// } // }
// stop on asserted powerDraw(0); // alt: on retracted powerDraw(watts);
// } // }
// } // }
// } // }
@ -151,16 +138,14 @@ function spawnTimeoutListener() {
// actor { // actor {
// this.mostRecentTime = 0; // this.mostRecentTime = 0;
// this.powerOnTime = null; // this.powerOnTime = null;
// react { // on asserted powerDraw($watts) {
// on asserted powerDraw($watts) { // this.powerOnTime = (watts > 0) ? this.mostRecentTime : null;
// this.powerOnTime = (watts > 0) ? this.mostRecentTime : null;
// }
// on message Syndicate.Timer.periodicTick(200) {
// this.mostRecentTime = Date.now();
// }
// assert tvAlert('Stove on too long?')
// when (this.powerOnTime !== null && this.mostRecentTime - this.powerOnTime > 3000);
// } // }
// on message Syndicate.Timer.periodicTick(200) {
// this.mostRecentTime = Date.now();
// }
// assert tvAlert('Stove on too long?')
// when (this.powerOnTime !== null && this.mostRecentTime - this.powerOnTime > 3000);
// } // }
// } // }
@ -169,13 +154,10 @@ function spawnTimeoutListener() {
function spawnFailureMonitor() { function spawnFailureMonitor() {
actor { actor {
react { on retracted componentPresent($who) {
on retracted componentPresent($who) { react {
react { assert tvAlert('FAILURE: ' + who);
assert tvAlert('FAILURE: ' + who); stop on asserted componentPresent(who);
} until {
case asserted componentPresent(who);
}
} }
} }
} }
@ -185,7 +167,7 @@ function spawnFailureMonitor() {
// Chaos Monkey // Chaos Monkey
function spawnChaosMonkey() { function spawnChaosMonkey() {
actor { actor* {
monitorComponent('power draw monitor', monitorComponent('power draw monitor',
'#spawn-power-draw-monitor', '#spawn-power-draw-monitor',
'#kill-power-draw-monitor', '#kill-power-draw-monitor',
@ -201,11 +183,11 @@ function spawnChaosMonkey() {
var jKillButtons = $(killButtonSelector); var jKillButtons = $(killButtonSelector);
react { react {
during componentPresent(name) { during componentPresent(name) {
do { on start {
jSpawnButtons.prop('disabled', true); jSpawnButtons.prop('disabled', true);
jKillButtons.prop('disabled', false); jKillButtons.prop('disabled', false);
} }
finally { on stop {
jSpawnButtons.prop('disabled', false); jSpawnButtons.prop('disabled', false);
jKillButtons.prop('disabled', true); jKillButtons.prop('disabled', true);
} }

View File

@ -11,7 +11,7 @@ var G = new Syndicate.Ground(function () {
Syndicate.Timer.spawnTimerDriver(); Syndicate.Timer.spawnTimerDriver();
Syndicate.Broker.spawnBrokerClientDriver(); Syndicate.Broker.spawnBrokerClientDriver();
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
var id = Syndicate.RandomID.randomId(4, true); var id = Syndicate.RandomID.randomId(4, true);
var email_element = document.getElementById('my_email'); var email_element = document.getElementById('my_email');
@ -62,76 +62,89 @@ var G = new Syndicate.Ground(function () {
timeout: 15000 timeout: 15000
})); }));
(function () { Syndicate.Actor.declareField(this, "currentLocation", null); var selectedMarker = null; Syndicate.Actor.declareField(this, "currentLocation", null);
Syndicate.Actor.createFacet() var selectedMarker = null;
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(brokerConnection(this.wsurl), 0); }))
.addAssertion((function() { var _ = Syndicate.__; return (this.currentLocation) ? Syndicate.Patch.assert(toBroker(this.wsurl, this.currentLocation), 0) : Syndicate.Patch.emptyPatch; })) Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(brokerConnection(this.wsurl), 0); }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#my_email', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#my_email', 'change', _), metalevel: 0 }; }), (function() { Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return (this.currentLocation) ? Syndicate.Patch.assert(toBroker(this.wsurl, this.currentLocation), 0) : Syndicate.Patch.emptyPatch; }));
var v = email_element.value.trim();
if (this.currentLocation) this.currentLocation = this.currentLocation.set(1, v); Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#my_email', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#my_email', 'change', _), metalevel: 0 }; }), (function() {
localStorage.my_email = v; var v = email_element.value.trim();
})) if (this.currentLocation) this.currentLocation = this.currentLocation.set(1, v);
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#group', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#group', 'change', _), metalevel: 0 }; }), (function() { localStorage.my_email = v;
localStorage.group = group_element.value.trim(); }));
this.wsurl = wsurl_base + group_element.value.trim();
})) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#group', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#group', 'change', _), metalevel: 0 }; }), (function() {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#findMarker', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#findMarker', 'click', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) { localStorage.group = group_element.value.trim();
Syndicate.Dataspace.send(findMarker(document.getElementById('markerList').value)); this.wsurl = wsurl_base + group_element.value.trim();
})) }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#markerList', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#markerList', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
Syndicate.Dataspace.send(findMarker(document.getElementById('markerList').value)); Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#findMarker', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#findMarker', 'click', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
})) Syndicate.Dataspace.send(findMarker(document.getElementById('markerList').value));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub((locationRecord(_, _, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: ((Syndicate._$("loc",locationRecord(_, _, _, _, _)))), metalevel: 0 }; }), (function(loc) { }));
this.currentLocation = loc; Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('#markerList', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('#markerList', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
})) Syndicate.Dataspace.send(findMarker(document.getElementById('markerList').value));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(fromBroker(this.wsurl, locationRecord(_, _, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: fromBroker(this.wsurl, locationRecord((Syndicate._$("id")), (Syndicate._$("email")), _, _, _)), metalevel: 0 }; }), (function(id, email) { var ui = new Syndicate.UI.Anchor(); var marker = new google.maps.Marker({ }));
map: map,
clickable: true, Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub((locationRecord(_, _, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: ((Syndicate._$("loc",locationRecord(_, _, _, _, _)))), metalevel: 0 }; }), (function(loc) {
icon: 'https://www.gravatar.com/avatar/' + md5(email.trim().toLowerCase()) + '?s=32&d=retro' this.currentLocation = loc;
}); var latestTimestamp = null; var latestPosition = null; function selectMarker() { }));
selectedMarker = marker;
updateInfoWindow(); Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(fromBroker(this.wsurl, locationRecord(_, _, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: fromBroker(this.wsurl, locationRecord((Syndicate._$("id")), (Syndicate._$("email")), _, _, _)), metalevel: 0 }; }), (function(id, email) {
infoWindow.open(map, marker); var _cachedAssertion1472127196930_0 = (function() { var _ = Syndicate.__; return fromBroker(this.wsurl, locationRecord(id, email, _, _, _)); }).call(this);
} function updateInfoWindow() { { Syndicate.Actor.Facet.build((function () { {
if (selectedMarker === marker && latestPosition && latestTimestamp) { var ui = new Syndicate.UI.Anchor();
geocoder.geocode({'location': latestPosition}, function (results, status) { var marker = new google.maps.Marker({
if (status === google.maps.GeocoderStatus.OK && results[0]) { map: map,
infoWindow.setContent(Mustache.render(document.getElementById('info').innerHTML, { clickable: true,
email: email, icon: 'https://www.gravatar.com/avatar/' + md5(email.trim().toLowerCase()) + '?s=32&d=retro'
timestamp: latestTimestamp ? latestTimestamp.toString() : '', });
address: results[0].formatted_address var latestTimestamp = null;
})); var latestPosition = null;
} function selectMarker() {
}); selectedMarker = marker;
} updateInfoWindow();
infoWindow.open(map, marker);
}
function updateInfoWindow() {
if (selectedMarker === marker && latestPosition && latestTimestamp) {
geocoder.geocode({'location': latestPosition}, function (results, status) {
if (status === google.maps.GeocoderStatus.OK && results[0]) {
infoWindow.setContent(Mustache.render(document.getElementById('info').innerHTML, {
email: email,
timestamp: latestTimestamp ? latestTimestamp.toString() : '',
address: results[0].formatted_address
}));
}
});
} }
var _cachedAssertion1470624652650_0 = (function() { var _ = Syndicate.__; return fromBroker(this.wsurl, locationRecord(id, email, _, _, _)); }).call(this); }
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.addInitBlock((function() {
.addInitBlock((function() { marker.addListener('click', Syndicate.Dataspace.wrap(function () {
marker.addListener('click', Syndicate.Dataspace.wrap(function () {
selectMarker();
}));
}))
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(ui.html('#markerList',
Mustache.render(document.getElementById('markerList-option').innerHTML, {
id: id,
email: email
})), 0); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(findMarker(id), 0); }), (function() { var _ = Syndicate.__; return { assertion: findMarker(id), metalevel: 0 }; }), (function() {
selectMarker(); selectMarker();
if (latestPosition) map.panTo(latestPosition); }));
})) }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(fromBroker(this.wsurl, locationRecord(id, email, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: fromBroker(this.wsurl, locationRecord(id, email, (Syndicate._$("timestamp")), (Syndicate._$("lat")), (Syndicate._$("lng")))), metalevel: 0 }; }), (function(timestamp, lat, lng) { Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(ui.html('#markerList',
latestTimestamp = new Date(timestamp); Mustache.render(document.getElementById('markerList-option').innerHTML, {
latestPosition = {lat: lat, lng: lng}; id: id,
marker.setPosition(latestPosition); email: email
marker.setTitle(email + ' ' + latestTimestamp.toTimeString()); })), 0); }));
updateInfoWindow(); Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(findMarker(id), 0); }), (function() { var _ = Syndicate.__; return { assertion: findMarker(id), metalevel: 0 }; }), (function() {
})) selectMarker();
.addDoneBlock((function() { if (latestPosition) map.panTo(latestPosition);
marker.setMap(null); }));
if (selectedMarker === marker) selectedMarker = null; Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(fromBroker(this.wsurl, locationRecord(id, email, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: fromBroker(this.wsurl, locationRecord(id, email, (Syndicate._$("timestamp")), (Syndicate._$("lat")), (Syndicate._$("lng")))), metalevel: 0 }; }), (function(timestamp, lat, lng) {
})) latestTimestamp = new Date(timestamp);
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624652650_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624652650_0, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); }).call(this); latestPosition = {lat: lat, lng: lng};
}); marker.setPosition(latestPosition);
marker.setTitle(email + ' ' + latestTimestamp.toTimeString());
updateInfoWindow();
}));
Syndicate.Actor.Facet.current.addDoneBlock((function() {
marker.setMap(null);
if (selectedMarker === marker) selectedMarker = null;
}));
}
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127196930_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127196930_0, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
} }).bind(this)); });
}).startStepping(); }).startStepping();

View File

@ -61,87 +61,85 @@ ground dataspace G {
timeout: 15000 timeout: 15000
})); }));
react { field this.currentLocation = null;
field this.currentLocation = null; var selectedMarker = null;
var selectedMarker = null;
assert brokerConnection(this.wsurl); assert brokerConnection(this.wsurl);
assert toBroker(this.wsurl, this.currentLocation) when (this.currentLocation); assert toBroker(this.wsurl, this.currentLocation) when (this.currentLocation);
on message Syndicate.UI.globalEvent('#my_email', 'change', _) { on message Syndicate.UI.globalEvent('#my_email', 'change', _) {
var v = email_element.value.trim(); var v = email_element.value.trim();
if (this.currentLocation) this.currentLocation = this.currentLocation.set(1, v); if (this.currentLocation) this.currentLocation = this.currentLocation.set(1, v);
localStorage.my_email = v; localStorage.my_email = v;
}
on message Syndicate.UI.globalEvent('#group', 'change', _) {
localStorage.group = group_element.value.trim();
this.wsurl = wsurl_base + group_element.value.trim();
}
on message Syndicate.UI.globalEvent('#findMarker', 'click', $e) {
:: findMarker(document.getElementById('markerList').value);
}
on message Syndicate.UI.globalEvent('#markerList', 'change', $e) {
:: findMarker(document.getElementById('markerList').value);
}
on message ($loc = locationRecord(_, _, _, _, _)) {
this.currentLocation = loc;
}
during fromBroker(this.wsurl, locationRecord($id, $email, _, _, _)) {
var ui = new Syndicate.UI.Anchor();
var marker = new google.maps.Marker({
map: map,
clickable: true,
icon: 'https://www.gravatar.com/avatar/' + md5(email.trim().toLowerCase()) + '?s=32&d=retro'
});
var latestTimestamp = null;
var latestPosition = null;
function selectMarker() {
selectedMarker = marker;
updateInfoWindow();
infoWindow.open(map, marker);
} }
function updateInfoWindow() {
on message Syndicate.UI.globalEvent('#group', 'change', _) { if (selectedMarker === marker && latestPosition && latestTimestamp) {
localStorage.group = group_element.value.trim(); geocoder.geocode({'location': latestPosition}, function (results, status) {
this.wsurl = wsurl_base + group_element.value.trim(); if (status === google.maps.GeocoderStatus.OK && results[0]) {
} infoWindow.setContent(Mustache.render(document.getElementById('info').innerHTML, {
email: email,
on message Syndicate.UI.globalEvent('#findMarker', 'click', $e) { timestamp: latestTimestamp ? latestTimestamp.toString() : '',
:: findMarker(document.getElementById('markerList').value); address: results[0].formatted_address
} }));
on message Syndicate.UI.globalEvent('#markerList', 'change', $e) { }
:: findMarker(document.getElementById('markerList').value); });
}
on message ($loc = locationRecord(_, _, _, _, _)) {
this.currentLocation = loc;
}
during fromBroker(this.wsurl, locationRecord($id, $email, _, _, _)) {
var ui = new Syndicate.UI.Anchor();
var marker = new google.maps.Marker({
map: map,
clickable: true,
icon: 'https://www.gravatar.com/avatar/' + md5(email.trim().toLowerCase()) + '?s=32&d=retro'
});
var latestTimestamp = null;
var latestPosition = null;
function selectMarker() {
selectedMarker = marker;
updateInfoWindow();
infoWindow.open(map, marker);
} }
function updateInfoWindow() { }
if (selectedMarker === marker && latestPosition && latestTimestamp) { on start {
geocoder.geocode({'location': latestPosition}, function (results, status) { marker.addListener('click', Syndicate.Dataspace.wrap(function () {
if (status === google.maps.GeocoderStatus.OK && results[0]) {
infoWindow.setContent(Mustache.render(document.getElementById('info').innerHTML, {
email: email,
timestamp: latestTimestamp ? latestTimestamp.toString() : '',
address: results[0].formatted_address
}));
}
});
}
}
do {
marker.addListener('click', Syndicate.Dataspace.wrap(function () {
selectMarker();
}));
}
assert ui.html('#markerList',
Mustache.render(document.getElementById('markerList-option').innerHTML, {
id: id,
email: email
}));
on message findMarker(id) {
selectMarker(); selectMarker();
if (latestPosition) map.panTo(latestPosition); }));
} }
on asserted fromBroker(this.wsurl, locationRecord(id, email, $timestamp, $lat, $lng)) { assert ui.html('#markerList',
latestTimestamp = new Date(timestamp); Mustache.render(document.getElementById('markerList-option').innerHTML, {
latestPosition = {lat: lat, lng: lng}; id: id,
marker.setPosition(latestPosition); email: email
marker.setTitle(email + ' ' + latestTimestamp.toTimeString()); }));
updateInfoWindow(); on message findMarker(id) {
} selectMarker();
finally { if (latestPosition) map.panTo(latestPosition);
marker.setMap(null); }
if (selectedMarker === marker) selectedMarker = null; on asserted fromBroker(this.wsurl, locationRecord(id, email, $timestamp, $lat, $lng)) {
} latestTimestamp = new Date(timestamp);
latestPosition = {lat: lat, lng: lng};
marker.setPosition(latestPosition);
marker.setTitle(email + ' ' + latestTimestamp.toTimeString());
updateInfoWindow();
}
on stop {
marker.setMap(null);
if (selectedMarker === marker) selectedMarker = null;
} }
} }
} }

View File

@ -23,32 +23,31 @@ ground dataspace G {
} }
var wsurl = 'wss://demo-broker.syndicate-lang.org:8443/'; var wsurl = 'wss://demo-broker.syndicate-lang.org:8443/';
react {
assert brokerConnection(wsurl);
assert Syndicate.UI.uiAttribute('rect#my_color', 'fill', color); assert brokerConnection(wsurl);
assert toBroker(wsurl, point(color, this.publishedX, this.publishedY)); assert Syndicate.UI.uiAttribute('rect#my_color', 'fill', color);
on message Syndicate.Timer.periodicTick(100) {
this.publishedX = x;
this.publishedY = y;
}
on message Syndicate.UI.windowEvent('deviceorientation', $e) { assert toBroker(wsurl, point(color, this.publishedX, this.publishedY));
var scale = 0.5; on message Syndicate.Timer.periodicTick(100) {
x = clamp(e.gamma * scale); this.publishedX = x;
y = clamp((e.beta - 40) * scale); this.publishedY = y;
} }
during fromBroker(wsurl, point($oc, $ox, $oy)) { on message Syndicate.UI.windowEvent('deviceorientation', $e) {
assert ui.context(oc) var scale = 0.5;
.html('#container', x = clamp(e.gamma * scale);
Mustache.render(document.getElementById('circle-template').innerHTML, { y = clamp((e.beta - 40) * scale);
color: oc, }
x: ox,
y: oy during fromBroker(wsurl, point($oc, $ox, $oy)) {
})); assert ui.context(oc)
} .html('#container',
Mustache.render(document.getElementById('circle-template').innerHTML, {
color: oc,
x: ox,
y: oy
}));
} }
} }
} }

View File

@ -4,28 +4,23 @@ ground dataspace {
console.log('starting ground boot'); console.log('starting ground boot');
actor { actor {
react until { stop on asserted Syndicate.observe(beep(_)) {
case asserted Syndicate.observe(beep(_)) { field this.counter = 0;
field this.counter = 0; react {
react { on start {
do { :: beep(this.counter++);
:: beep(this.counter++);
}
on message beep(_) {
:: beep(this.counter++);
}
} until {
case (this.counter > 10);
} }
on message beep(_) {
:: beep(this.counter++);
}
stop on (this.counter > 10);
} }
} }
} }
actor { actor {
react { on message beep($counter) {
on message beep($counter) { console.log("beep!", counter);
console.log("beep!", counter);
}
} }
} }
} }

View File

@ -8,19 +8,17 @@ ground dataspace G {
field this.handX; field this.handX;
field this.handY; field this.handY;
react { assert ui.html('#clock',
assert ui.html('#clock', '<svg width="300px" viewBox="0 0 100 100">'+
'<svg width="300px" viewBox="0 0 100 100">'+ '<circle fill="#0B79CE" r=45 cx=50 cy=50 />'+
'<circle fill="#0B79CE" r=45 cx=50 cy=50 />'+ '<line stroke="#023963" x1=50 y1=50 x2='+this.handX+' y2='+this.handY+' />'+
'<line stroke="#023963" x1=50 y1=50 x2='+this.handX+' y2='+this.handY+' />'+ '</svg>')
'</svg>')
when (typeof this.angle === 'number'); when (typeof this.angle === 'number');
on message Syndicate.Timer.periodicTick(1000) { on message Syndicate.Timer.periodicTick(1000) {
this.angle = ((((Date.now() / 1000) % 60) / 60) - 0.25) * 2 * Math.PI; this.angle = ((((Date.now() / 1000) % 60) / 60) - 0.25) * 2 * Math.PI;
this.handX = 50 + 40 * Math.cos(this.angle); this.handX = 50 + 40 * Math.cos(this.angle);
this.handY = 50 + 40 * Math.sin(this.angle); this.handY = 50 + 40 * Math.sin(this.angle);
}
} }
} }
} }

View File

@ -3,9 +3,7 @@ message type setSortColumn(number);
function newRow(id, firstName, lastName, address, age) { function newRow(id, firstName, lastName, address, age) {
actor named ('model' + id) { actor named ('model' + id) {
react { assert person(id, firstName, lastName, address, age);
assert person(id, firstName, lastName, address, age);
}
} }
} }
@ -27,25 +25,21 @@ function spawnView() {
return '<td>' + text + '</td>'; return '<td>' + text + '</td>';
} }
react { on message setSortColumn($c) { this.orderColumn = c; }
on message setSortColumn($c) { this.orderColumn = c; }
during person($id, $firstName, $lastName, $address, $age) { during person($id, $firstName, $lastName, $address, $age) {
assert ui.context(id) assert ui.context(id)
.html('table#the-table tbody', .html('table#the-table tbody',
'<tr>' + [id, firstName, lastName, address, age].map(cell).join('') + '</tr>', '<tr>' + [id, firstName, lastName, address, age].map(cell).join('') + '</tr>',
[id, firstName, lastName, address, age][this.orderColumn]); [id, firstName, lastName, address, age][this.orderColumn]);
}
} }
} }
} }
function spawnController() { function spawnController() {
actor named 'controller' { actor named 'controller' {
react { on message Syndicate.UI.globalEvent('table#the-table th', 'click', $e) {
on message Syndicate.UI.globalEvent('table#the-table th', 'click', $e) { :: setSortColumn(JSON.parse(e.target.dataset.column));
:: setSortColumn(JSON.parse(e.target.dataset.column));
}
} }
} }
} }

View File

@ -26,52 +26,50 @@ function piece(text, pos, lo, hi, cls) {
function spawnGui() { function spawnGui() {
actor { actor {
react { field this.text = '';
field this.text = ''; field this.pos = 0;
field this.pos = 0; field this.highlightState = false;
field this.highlightState = false;
dataflow { dataflow {
var text = this.text; var text = this.text;
var pos = this.pos; var pos = this.pos;
var highlight = this.highlightState; var highlight = this.highlightState;
var hLeft = highlight ? highlight[0] : 0; var hLeft = highlight ? highlight[0] : 0;
var hRight = highlight ? highlight[1] : 0; var hRight = highlight ? highlight[1] : 0;
document.getElementById("fieldContents").innerHTML = highlight document.getElementById("fieldContents").innerHTML = highlight
? piece(text, pos, 0, hLeft, "normal") + ? piece(text, pos, 0, hLeft, "normal") +
piece(text, pos, hLeft, hRight, "highlight") + piece(text, pos, hLeft, hRight, "highlight") +
piece(text, pos, hRight, text.length + 1, "normal") piece(text, pos, hRight, text.length + 1, "normal")
: piece(text, pos, 0, text.length + 1, "normal"); : piece(text, pos, 0, text.length + 1, "normal");
} }
on message globalEvent("#inputRow", "+keydown", $event) { on message globalEvent("#inputRow", "+keydown", $event) {
switch (event.keyCode) { switch (event.keyCode) {
case 37 /* left */: :: fieldCommand("cursorLeft"); break; case 37 /* left */: :: fieldCommand("cursorLeft"); break;
case 39 /* right */: :: fieldCommand("cursorRight"); break; case 39 /* right */: :: fieldCommand("cursorRight"); break;
case 9 /* tab */: /* ignore */ break; case 9 /* tab */: /* ignore */ break;
case 8 /* backspace */: case 8 /* backspace */:
event.preventDefault(); // that this works here is a minor miracle event.preventDefault(); // that this works here is a minor miracle
:: fieldCommand("backspace"); :: fieldCommand("backspace");
break; break;
default: break; default: break;
}
} }
}
on message globalEvent("#inputRow", "keypress", $event) { on message globalEvent("#inputRow", "keypress", $event) {
var character = String.fromCharCode(event.charCode); var character = String.fromCharCode(event.charCode);
if (event.charCode && character) { if (event.charCode && character) {
:: fieldCommand(["insert", character]); :: fieldCommand(["insert", character]);
}
} }
}
on asserted fieldContents($text, $pos) { on asserted fieldContents($text, $pos) {
this.text = text; this.text = text;
this.pos = pos; this.pos = pos;
} }
on asserted highlight($state) { on asserted highlight($state) {
this.highlightState = state; this.highlightState = state;
}
} }
} }
} }
@ -81,40 +79,38 @@ function spawnGui() {
function spawnModel() { function spawnModel() {
actor { actor {
react { field this.fieldValue = "initial";
field this.fieldValue = "initial"; field this.cursorPos = this.fieldValue.length; /* positions address gaps between characters */
field this.cursorPos = this.fieldValue.length; /* positions address gaps between characters */
assert fieldContents(this.fieldValue, this.cursorPos); assert fieldContents(this.fieldValue, this.cursorPos);
on message fieldCommand("cursorLeft") { on message fieldCommand("cursorLeft") {
this.cursorPos--; this.cursorPos--;
if (this.cursorPos < 0) if (this.cursorPos < 0)
this.cursorPos = 0; this.cursorPos = 0;
}
on message fieldCommand("cursorRight") {
this.cursorPos++;
if (this.cursorPos > this.fieldValue.length)
this.cursorPos = this.fieldValue.length;
}
on message fieldCommand("backspace") {
if (this.cursorPos > 0) {
this.fieldValue =
this.fieldValue.substring(0, this.cursorPos - 1) +
this.fieldValue.substring(this.cursorPos);
this.cursorPos--;
} }
}
on message fieldCommand("cursorRight") { on message fieldCommand(["insert", $newText]) {
this.cursorPos++; this.fieldValue =
if (this.cursorPos > this.fieldValue.length) this.fieldValue.substring(0, this.cursorPos) +
this.cursorPos = this.fieldValue.length; newText +
} this.fieldValue.substring(this.cursorPos);
this.cursorPos += newText.length;
on message fieldCommand("backspace") {
if (this.cursorPos > 0) {
this.fieldValue =
this.fieldValue.substring(0, this.cursorPos - 1) +
this.fieldValue.substring(this.cursorPos);
this.cursorPos--;
}
}
on message fieldCommand(["insert", $newText]) {
this.fieldValue =
this.fieldValue.substring(0, this.cursorPos) +
newText +
this.fieldValue.substring(this.cursorPos);
this.cursorPos += newText.length;
}
} }
} }
} }
@ -124,29 +120,27 @@ function spawnModel() {
function spawnSearch() { function spawnSearch() {
actor { actor {
react { field this.searchtext = document.getElementById("searchBox").value;
field this.searchtext = document.getElementById("searchBox").value; field this.fieldValue = "";
field this.fieldValue = ""; field this.highlight = false;
field this.highlight = false;
assert highlight(this.highlight); assert highlight(this.highlight);
dataflow { dataflow {
if (this.searchtext) { if (this.searchtext) {
var pos = this.fieldValue.indexOf(this.searchtext); var pos = this.fieldValue.indexOf(this.searchtext);
this.highlight = (pos !== -1) && [pos, pos + this.searchtext.length]; this.highlight = (pos !== -1) && [pos, pos + this.searchtext.length];
} else { } else {
this.highlight = false; this.highlight = false;
}
} }
}
on message globalEvent("#searchBox", "input", $event) { on message globalEvent("#searchBox", "input", $event) {
this.searchtext = document.getElementById("searchBox").value; this.searchtext = document.getElementById("searchBox").value;
} }
on asserted fieldContents($text, _) { on asserted fieldContents($text, _) {
this.fieldValue = text; this.fieldValue = text;
}
} }
} }
} }

View File

@ -20,18 +20,24 @@ var show = Syndicate.Struct.makeConstructor("show", ["completed"]);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
function todoListItemModel(initialId, initialTitle, initialCompleted) { function todoListItemModel(initialId, initialTitle, initialCompleted) {
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
(function () { Syndicate.Actor.declareField(this, "id", initialId); Syndicate.Actor.declareField(this, "title", initialTitle); Syndicate.Actor.declareField(this, "completed", initialCompleted); Syndicate.Actor.declareField(this, "id", initialId);
Syndicate.Actor.createFacet() Syndicate.Actor.declareField(this, "title", initialTitle);
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(todo(this.id, this.title, this.completed), 0); })) Syndicate.Actor.declareField(this, "completed", initialCompleted);
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(setCompleted(this.id, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: setCompleted(this.id, (Syndicate._$("v"))), metalevel: 0 }; }), (function(v) { this.completed = v; }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(setAllCompleted(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: setAllCompleted((Syndicate._$("v"))), metalevel: 0 }; }), (function(v) { this.completed = v; })) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(deleteTodo(this.id), 0); }), (function() { var _ = Syndicate.__; return { assertion: deleteTodo(this.id), metalevel: 0 }; }), (function() {}));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(setTitle(this.id, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: setTitle(this.id, (Syndicate._$("v"))), metalevel: 0 }; }), (function(v) { this.title = v; }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(clearCompletedTodos(), 0); }), (function() { var _ = Syndicate.__; return { assertion: clearCompletedTodos(), metalevel: 0 }; }), (function() { Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(todo(this.id, this.title, this.completed), 0); }));
if (this.completed) Syndicate.Dataspace.send(deleteTodo(this.id));
})) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(setCompleted(this.id, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: setCompleted(this.id, (Syndicate._$("v"))), metalevel: 0 }; }), (function(v) { this.completed = v; }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(deleteTodo(this.id), 0); }), (function() { var _ = Syndicate.__; return { assertion: deleteTodo(this.id), metalevel: 0 }; }), (function() {})).completeBuild(); }).call(this); Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(setAllCompleted(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: setAllCompleted((Syndicate._$("v"))), metalevel: 0 }; }), (function(v) { this.completed = v; }));
});
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(setTitle(this.id, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: setTitle(this.id, (Syndicate._$("v"))), metalevel: 0 }; }), (function(v) { this.title = v; }));
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(clearCompletedTodos(), 0); }), (function() { var _ = Syndicate.__; return { assertion: clearCompletedTodos(), metalevel: 0 }; }), (function() {
if (this.completed) Syndicate.Dataspace.send(deleteTodo(this.id));
}));
} }).bind(this)); });
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -44,53 +50,60 @@ function getTemplate(id) {
} }
function todoListItemView(id) { function todoListItemView(id) {
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(id, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo(id, _, _), metalevel: 0 }; }), (function() {}));
this.ui = new Syndicate.UI.Anchor(); this.ui = new Syndicate.UI.Anchor();
(function () { Syndicate.Actor.declareField(this, "editing", false); Syndicate.Actor.declareField(this, "editing", false);
Syndicate.Actor.createFacet()
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(id, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo(id, (Syndicate._$("title")), (Syndicate._$("completed"))), metalevel: 0 }; }), (function(title, completed) { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(id, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo(id, (Syndicate._$("title")), (Syndicate._$("completed"))), metalevel: 0 }; }), (function(title, completed) {
var _cachedAssertion1470624655068_0 = (function() { var _ = Syndicate.__; return todo(id, title, completed); }).call(this); var _cachedAssertion1472127199197_0 = (function() { var _ = Syndicate.__; return todo(id, title, completed); }).call(this);
Syndicate.Actor.createFacet() { Syndicate.Actor.Facet.build((function () { {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(show(completed), 0); }), (function() { var _ = Syndicate.__; return { assertion: show(completed), metalevel: 0 }; }), (function() { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(show(completed), 0); }), (function() { var _ = Syndicate.__; return { assertion: show(completed), metalevel: 0 }; }), (function() {
var _cachedAssertion1470624655068_1 = (function() { var _ = Syndicate.__; return show(completed); }).call(this); var _cachedAssertion1472127199197_1 = (function() { var _ = Syndicate.__; return show(completed); }).call(this);
Syndicate.Actor.createFacet() { Syndicate.Actor.Facet.build((function () { {
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(this.ui.html('.todo-list', Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(this.ui.html('.todo-list',
Mustache.render(getTemplate(this.editing Mustache.render(getTemplate(this.editing
? 'todo-list-item-edit-template' ? 'todo-list-item-edit-template'
: 'todo-list-item-view-template'), : 'todo-list-item-view-template'),
{ {
id: id, id: id,
title: title, title: title,
completed_class: completed ? "completed" : "", completed_class: completed ? "completed" : "",
checked: completed ? "checked" : "", checked: completed ? "checked" : "",
}), }),
id), 0); })) id), 0); }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_1, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_1, metalevel: 0 }; }), (function() {})).completeBuild(); })) }
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_0, metalevel: 0 }; }), (function() {})).completeBuild(); })) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_1, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_1, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('.toggle', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('.toggle', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) { }
Syndicate.Dataspace.send(setCompleted(id, e.target.checked)); Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_0, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
}))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('.destroy', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('.destroy', 'click', _), metalevel: 0 }; }), (function() { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('.toggle', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('.toggle', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
Syndicate.Dataspace.send(deleteTodo(id)); Syndicate.Dataspace.send(setCompleted(id, e.target.checked));
})) }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('label', 'dblclick', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('label', 'dblclick', _), metalevel: 0 }; }), (function() {
this.editing = true; Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('.destroy', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('.destroy', 'click', _), metalevel: 0 }; }), (function() {
})) Syndicate.Dataspace.send(deleteTodo(id));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('input.edit', 'keyup', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('input.edit', 'keyup', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) { }));
if (e.keyCode === ESCAPE_KEY_CODE || e.keyCode === ENTER_KEY_CODE) {
this.editing = false; Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('label', 'dblclick', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('label', 'dblclick', _), metalevel: 0 }; }), (function() {
} this.editing = true;
})) }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('input.edit', 'blur', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('input.edit', 'blur', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('input.edit', 'keyup', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('input.edit', 'keyup', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
if (e.keyCode === ESCAPE_KEY_CODE || e.keyCode === ENTER_KEY_CODE) {
this.editing = false; this.editing = false;
})) }
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('input.edit', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('input.edit', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) { }));
var newTitle = e.target.value.trim(); Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('input.edit', 'blur', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('input.edit', 'blur', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
Syndicate.Dataspace.send((newTitle ? setTitle(id, newTitle) : deleteTodo(id))); this.editing = false;
this.editing = false; }));
})) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('input.edit', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('input.edit', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(id, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo(id, _, _), metalevel: 0 }; }), (function() {})).completeBuild(); }).call(this); var newTitle = e.target.value.trim();
}); Syndicate.Dataspace.send((newTitle ? setTitle(id, newTitle) : deleteTodo(id)));
this.editing = false;
}));
} }).bind(this)); });
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -98,94 +111,101 @@ Syndicate.Actor.createFacet()
var G = new Syndicate.Ground(function () { var G = new Syndicate.Ground(function () {
Syndicate.UI.spawnUIDriver(); Syndicate.UI.spawnUIDriver();
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
(function () { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('.new-todo', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('.new-todo', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
Syndicate.Actor.createFacet() var newTitle = e.target.value.trim();
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('.new-todo', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('.new-todo', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) { if (newTitle) Syndicate.Dataspace.send(createTodo(newTitle));
var newTitle = e.target.value.trim(); e.target.value = "";
if (newTitle) Syndicate.Dataspace.send(createTodo(newTitle)); }));
e.target.value = ""; } }).bind(this)); });
})).completeBuild(); }).call(this);
});
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
this.ui = new Syndicate.UI.Anchor(); this.ui = new Syndicate.UI.Anchor();
(function () { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(activeTodoCount(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: activeTodoCount((Syndicate._$("count"))), metalevel: 0 }; }), (function(count) {
Syndicate.Actor.createFacet() var _cachedAssertion1472127199197_2 = (function() { var _ = Syndicate.__; return activeTodoCount(count); }).call(this);
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(activeTodoCount(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: activeTodoCount((Syndicate._$("count"))), metalevel: 0 }; }), (function(count) { { Syndicate.Actor.Facet.build((function () { {
var _cachedAssertion1470624655068_2 = (function() { var _ = Syndicate.__; return activeTodoCount(count); }).call(this); Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(this.ui.context('count').html('.todo-count strong', '' + count), 0); }));
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return (count !== 1) ? Syndicate.Patch.assert(this.ui.context('plural').html('.todo-count span.s', 's'), 0) : Syndicate.Patch.emptyPatch; }));
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(this.ui.context('count').html('.todo-count strong', '' + count), 0); })) }
.addAssertion((function() { var _ = Syndicate.__; return (count !== 1) ? Syndicate.Patch.assert(this.ui.context('plural').html('.todo-count span.s', 's'), 0) : Syndicate.Patch.emptyPatch; })) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_2, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_2, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_2, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_2, metalevel: 0 }; }), (function() {})).completeBuild(); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(totalTodoCount(0), 0); }), (function() { var _ = Syndicate.__; return { assertion: totalTodoCount(0), metalevel: 0 }; }), (function() {
var _cachedAssertion1470624655068_3 = (function() { var _ = Syndicate.__; return totalTodoCount(0); }).call(this);
Syndicate.Actor.createFacet()
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('section.main', 'class', 'hidden'), 0); }))
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('footer.footer', 'class', 'hidden'), 0); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_3, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_3, metalevel: 0 }; }), (function() {})).completeBuild(); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(completedTodoCount(0), 0); }), (function() { var _ = Syndicate.__; return { assertion: completedTodoCount(0), metalevel: 0 }; }), (function() {
var _cachedAssertion1470624655068_4 = (function() { var _ = Syndicate.__; return completedTodoCount(0); }).call(this);
Syndicate.Actor.createFacet()
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('button.clear-completed', 'class', 'hidden'), 0); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_4, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_4, metalevel: 0 }; }), (function() {})).completeBuild(); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('button.clear-completed', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('button.clear-completed', 'click', _), metalevel: 0 }; }), (function() {
Syndicate.Dataspace.send(clearCompletedTodos());
}))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(allCompleted(), 0); }), (function() { var _ = Syndicate.__; return { assertion: allCompleted(), metalevel: 0 }; }), (function() {
var _cachedAssertion1470624655068_5 = (function() { var _ = Syndicate.__; return allCompleted(); }).call(this);
Syndicate.Actor.createFacet()
.addInitBlock((function() { Syndicate.Dataspace.send(Syndicate.UI.setProperty('.toggle-all', 'checked', true)); }))
.addDoneBlock((function() { Syndicate.Dataspace.send(Syndicate.UI.setProperty('.toggle-all', 'checked', false)); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_5, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_5, metalevel: 0 }; }), (function() {})).completeBuild(); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('.toggle-all', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('.toggle-all', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
Syndicate.Dataspace.send(setAllCompleted(e.target.checked));
}))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, _), metalevel: 0 }; }), (function(id) {
todoListItemView(id);
})).completeBuild(); }).call(this);
});
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(totalTodoCount(0), 0); }), (function() { var _ = Syndicate.__; return { assertion: totalTodoCount(0), metalevel: 0 }; }), (function() {
(function () { Syndicate.Actor.declareField(this, "completedCount", 0); Syndicate.Actor.declareField(this, "activeCount", 0); var _cachedAssertion1472127199197_3 = (function() { var _ = Syndicate.__; return totalTodoCount(0); }).call(this);
Syndicate.Actor.createFacet() { Syndicate.Actor.Facet.build((function () { {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, (Syndicate._$("c"))), metalevel: 0 }; }), (function(id, c) { if (c) this.completedCount++; else this.activeCount++; })) Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('section.main', 'class', 'hidden'), 0); }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, (Syndicate._$("c"))), metalevel: 0 }; }), (function(id, c) { if (c) this.completedCount--; else this.activeCount--; })) Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('footer.footer', 'class', 'hidden'), 0); }));
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(activeTodoCount(this.activeCount), 0); })) }
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(completedTodoCount(this.completedCount), 0); })) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_3, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_3, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(totalTodoCount(this.activeCount + this.completedCount), 0); }))
.addAssertion((function() { var _ = Syndicate.__; return (this.completedCount > 0 && this.activeCount === 0) ? Syndicate.Patch.assert(allCompleted(), 0) : Syndicate.Patch.emptyPatch; })).completeBuild(); }).call(this);
});
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(completedTodoCount(0), 0); }), (function() { var _ = Syndicate.__; return { assertion: completedTodoCount(0), metalevel: 0 }; }), (function() {
(function () { var _cachedAssertion1472127199197_4 = (function() { var _ = Syndicate.__; return completedTodoCount(0); }).call(this);
Syndicate.Actor.createFacet() { Syndicate.Actor.Facet.build((function () { {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash((Syndicate._$("hash"))), metalevel: 0 }; }), (function(hash) { Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('button.clear-completed', 'class', 'hidden'), 0); }));
var _cachedAssertion1470624655068_6 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash(hash); }).call(this); }
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_4, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_4, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('ul.filters > li > a[href="#'+hash+'"]', Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('button.clear-completed', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('button.clear-completed', 'click', _), metalevel: 0 }; }), (function() {
'class', 'selected'), 0); })) Syndicate.Dataspace.send(clearCompletedTodos());
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_6, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_6, metalevel: 0 }; }), (function() {})).completeBuild(); })) }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/'), metalevel: 0 }; }), (function() {
var _cachedAssertion1470624655068_7 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/'); }).call(this);
Syndicate.Actor.createFacet()
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(true), 0); }))
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(false), 0); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_7, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_7, metalevel: 0 }; }), (function() {})).completeBuild(); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/active'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/active'), metalevel: 0 }; }), (function() {
var _cachedAssertion1470624655068_8 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/active'); }).call(this);
Syndicate.Actor.createFacet()
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(false), 0); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_8, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_8, metalevel: 0 }; }), (function() {})).completeBuild(); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/completed'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/completed'), metalevel: 0 }; }), (function() {
var _cachedAssertion1470624655068_9 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/completed'); }).call(this);
Syndicate.Actor.createFacet()
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(true), 0); }))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_9, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_9, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); }).call(this);
});
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(allCompleted(), 0); }), (function() { var _ = Syndicate.__; return { assertion: allCompleted(), metalevel: 0 }; }), (function() {
var _cachedAssertion1472127199197_5 = (function() { var _ = Syndicate.__; return allCompleted(); }).call(this);
{ Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.Facet.current.addInitBlock((function() { Syndicate.Dataspace.send(Syndicate.UI.setProperty('.toggle-all', 'checked', true)); }));
Syndicate.Actor.Facet.current.addDoneBlock((function() { Syndicate.Dataspace.send(Syndicate.UI.setProperty('.toggle-all', 'checked', false)); }));
}
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_5, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_5, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('.toggle-all', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('.toggle-all', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) {
Syndicate.Dataspace.send(setAllCompleted(e.target.checked));
}));
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, _), metalevel: 0 }; }), (function(id) {
todoListItemView(id);
}));
} }).bind(this)); });
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.declareField(this, "completedCount", 0);
Syndicate.Actor.declareField(this, "activeCount", 0);
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, (Syndicate._$("c"))), metalevel: 0 }; }), (function(id, c) { if (c) this.completedCount++; else this.activeCount++; }));
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, (Syndicate._$("c"))), metalevel: 0 }; }), (function(id, c) { if (c) this.completedCount--; else this.activeCount--; }));
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(activeTodoCount(this.activeCount), 0); }));
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(completedTodoCount(this.completedCount), 0); }));
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(totalTodoCount(this.activeCount + this.completedCount), 0); }));
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return (this.completedCount > 0 && this.activeCount === 0) ? Syndicate.Patch.assert(allCompleted(), 0) : Syndicate.Patch.emptyPatch; }));
} }).bind(this)); });
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash((Syndicate._$("hash"))), metalevel: 0 }; }), (function(hash) {
var _cachedAssertion1472127199197_6 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash(hash); }).call(this);
{ Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('ul.filters > li > a[href="#'+hash+'"]',
'class', 'selected'), 0); }));
}
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_6, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_6, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/'), metalevel: 0 }; }), (function() {
var _cachedAssertion1472127199197_7 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/'); }).call(this);
{ Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(true), 0); }));
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(false), 0); }));
}
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_7, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_7, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/active'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/active'), metalevel: 0 }; }), (function() {
var _cachedAssertion1472127199197_8 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/active'); }).call(this);
{ Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(false), 0); }));
}
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_8, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_8, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/completed'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/completed'), metalevel: 0 }; }), (function() {
var _cachedAssertion1472127199197_9 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/completed'); }).call(this);
{ Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(true), 0); }));
}
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_9, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_9, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
} }).bind(this)); });
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
var db; var db;
if ('todos-syndicate' in localStorage) { if ('todos-syndicate' in localStorage) {
@ -196,35 +216,38 @@ Syndicate.Actor.createFacet()
} }
} else { } else {
db = {nextId: 0, todos: {}}; db = {nextId: 0, todos: {}};
(function () { Syndicate.Actor.Facet.current.addInitBlock((function() {
Syndicate.Actor.createFacet() (function () { Syndicate.Actor.Facet.build((function () { {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(createTodo(_)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(createTodo(_)), metalevel: 0 }; }), (function() { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(createTodo(_)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(createTodo(_)), metalevel: 0 }; }), (function() {
Syndicate.Dataspace.send(createTodo('Buy milk')); Syndicate.Dataspace.send(createTodo('Buy milk'));
Syndicate.Dataspace.send(createTodo('Buy bread')); Syndicate.Dataspace.send(createTodo('Buy bread'));
Syndicate.Dataspace.send(createTodo('Finish PhD')); Syndicate.Dataspace.send(createTodo('Finish PhD'));
})).completeBuild(); }).call(this); }));
} }).bind(this)); }).call(this);
}));
} }
(function () { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(createTodo(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: createTodo((Syndicate._$("title"))), metalevel: 0 }; }), (function(title) {
Syndicate.Actor.createFacet() todoListItemModel(db.nextId++, title, false);
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(createTodo(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: createTodo((Syndicate._$("title"))), metalevel: 0 }; }), (function(title) { }));
todoListItemModel(db.nextId++, title, false);
})) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, _), metalevel: 0 }; }), (function(id) {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, _), metalevel: 0 }; }), (function(id) { var _cachedAssertion1472127199197_10 = (function() { var _ = Syndicate.__; return todo(id, _, _); }).call(this);
var _cachedAssertion1470624655068_10 = (function() { var _ = Syndicate.__; return todo(id, _, _); }).call(this); { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(id, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo(id, (Syndicate._$("title")), (Syndicate._$("completed"))), metalevel: 0 }; }), (function(title, completed) {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(id, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo(id, (Syndicate._$("title")), (Syndicate._$("completed"))), metalevel: 0 }; }), (function(title, completed) { var _cachedAssertion1472127199197_11 = (function() { var _ = Syndicate.__; return todo(id, title, completed); }).call(this);
var _cachedAssertion1470624655068_11 = (function() { var _ = Syndicate.__; return todo(id, title, completed); }).call(this); { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.addInitBlock((function() {
.addInitBlock((function() { db.todos[id] = {id: id, title: title, completed: completed};
db.todos[id] = {id: id, title: title, completed: completed};
localStorage['todos-syndicate'] = JSON.stringify(db);
}))
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_11, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_11, metalevel: 0 }; }), (function() {})).completeBuild(); }))
.addDoneBlock((function() {
delete db.todos[id];
localStorage['todos-syndicate'] = JSON.stringify(db); localStorage['todos-syndicate'] = JSON.stringify(db);
})) }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624655068_10, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624655068_10, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); }).call(this); }
}); Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_11, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_11, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
Syndicate.Actor.Facet.current.addDoneBlock((function() {
delete db.todos[id];
localStorage['todos-syndicate'] = JSON.stringify(db);
}));
}
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127199197_10, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127199197_10, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
} }).bind(this)); });
}).startStepping(); }).startStepping();

View File

@ -25,23 +25,21 @@ assertion type show(completed);
function todoListItemModel(initialId, initialTitle, initialCompleted) { function todoListItemModel(initialId, initialTitle, initialCompleted) {
actor { actor {
react { field this.id = initialId;
field this.id = initialId; field this.title = initialTitle;
field this.title = initialTitle; field this.completed = initialCompleted;
field this.completed = initialCompleted;
assert todo(this.id, this.title, this.completed); stop on message deleteTodo(this.id);
on message setCompleted(this.id, $v) { this.completed = v; } assert todo(this.id, this.title, this.completed);
on message setAllCompleted($v) { this.completed = v; }
on message setTitle(this.id, $v) { this.title = v; } on message setCompleted(this.id, $v) { this.completed = v; }
on message setAllCompleted($v) { this.completed = v; }
on message clearCompletedTodos() { on message setTitle(this.id, $v) { this.title = v; }
if (this.completed) :: deleteTodo(this.id);
} on message clearCompletedTodos() {
} until { if (this.completed) :: deleteTodo(this.id);
case message deleteTodo(this.id);
} }
} }
} }
@ -57,53 +55,51 @@ function getTemplate(id) {
function todoListItemView(id) { function todoListItemView(id) {
actor { actor {
stop on retracted todo(id, _, _);
this.ui = new Syndicate.UI.Anchor(); this.ui = new Syndicate.UI.Anchor();
react { field this.editing = false;
field this.editing = false;
during todo(id, $title, $completed) { during todo(id, $title, $completed) {
during show(completed) { during show(completed) {
assert this.ui.html('.todo-list', assert this.ui.html('.todo-list',
Mustache.render(getTemplate(this.editing Mustache.render(getTemplate(this.editing
? 'todo-list-item-edit-template' ? 'todo-list-item-edit-template'
: 'todo-list-item-view-template'), : 'todo-list-item-view-template'),
{ {
id: id, id: id,
title: title, title: title,
completed_class: completed ? "completed" : "", completed_class: completed ? "completed" : "",
checked: completed ? "checked" : "", checked: completed ? "checked" : "",
}), }),
id); id);
}
} }
}
on message this.ui.event('.toggle', 'change', $e) { on message this.ui.event('.toggle', 'change', $e) {
:: setCompleted(id, e.target.checked); :: setCompleted(id, e.target.checked);
} }
on message this.ui.event('.destroy', 'click', _) { on message this.ui.event('.destroy', 'click', _) {
:: deleteTodo(id); :: deleteTodo(id);
} }
on message this.ui.event('label', 'dblclick', _) { on message this.ui.event('label', 'dblclick', _) {
this.editing = true; this.editing = true;
} }
on message this.ui.event('input.edit', 'keyup', $e) { on message this.ui.event('input.edit', 'keyup', $e) {
if (e.keyCode === ESCAPE_KEY_CODE || e.keyCode === ENTER_KEY_CODE) { if (e.keyCode === ESCAPE_KEY_CODE || e.keyCode === ENTER_KEY_CODE) {
this.editing = false;
}
}
on message this.ui.event('input.edit', 'blur', $e) {
this.editing = false; this.editing = false;
} }
on message this.ui.event('input.edit', 'change', $e) { }
var newTitle = e.target.value.trim(); on message this.ui.event('input.edit', 'blur', $e) {
:: (newTitle ? setTitle(id, newTitle) : deleteTodo(id)); this.editing = false;
this.editing = false; }
} on message this.ui.event('input.edit', 'change', $e) {
} until { var newTitle = e.target.value.trim();
case retracted todo(id, _, _); :: (newTitle ? setTitle(id, newTitle) : deleteTodo(id));
this.editing = false;
} }
} }
} }
@ -114,80 +110,72 @@ ground dataspace G {
Syndicate.UI.spawnUIDriver(); Syndicate.UI.spawnUIDriver();
actor { actor {
react { on message Syndicate.UI.globalEvent('.new-todo', 'change', $e) {
on message Syndicate.UI.globalEvent('.new-todo', 'change', $e) { var newTitle = e.target.value.trim();
var newTitle = e.target.value.trim(); if (newTitle) :: createTodo(newTitle);
if (newTitle) :: createTodo(newTitle); e.target.value = "";
e.target.value = "";
}
} }
} }
actor { actor {
this.ui = new Syndicate.UI.Anchor(); this.ui = new Syndicate.UI.Anchor();
react { during activeTodoCount($count) {
during activeTodoCount($count) { assert this.ui.context('count').html('.todo-count strong', '' + count);
assert this.ui.context('count').html('.todo-count strong', '' + count); assert this.ui.context('plural').html('.todo-count span.s', 's') when (count !== 1);
assert this.ui.context('plural').html('.todo-count span.s', 's') when (count !== 1); }
}
during totalTodoCount(0) { during totalTodoCount(0) {
assert Syndicate.UI.uiAttribute('section.main', 'class', 'hidden'); assert Syndicate.UI.uiAttribute('section.main', 'class', 'hidden');
assert Syndicate.UI.uiAttribute('footer.footer', 'class', 'hidden'); assert Syndicate.UI.uiAttribute('footer.footer', 'class', 'hidden');
} }
during completedTodoCount(0) { during completedTodoCount(0) {
assert Syndicate.UI.uiAttribute('button.clear-completed', 'class', 'hidden'); assert Syndicate.UI.uiAttribute('button.clear-completed', 'class', 'hidden');
} }
on message Syndicate.UI.globalEvent('button.clear-completed', 'click', _) { on message Syndicate.UI.globalEvent('button.clear-completed', 'click', _) {
:: clearCompletedTodos(); :: clearCompletedTodos();
} }
during allCompleted() { during allCompleted() {
do { :: Syndicate.UI.setProperty('.toggle-all', 'checked', true); } on start { :: Syndicate.UI.setProperty('.toggle-all', 'checked', true); }
finally { :: Syndicate.UI.setProperty('.toggle-all', 'checked', false); } on stop { :: Syndicate.UI.setProperty('.toggle-all', 'checked', false); }
} }
on message Syndicate.UI.globalEvent('.toggle-all', 'change', $e) { on message Syndicate.UI.globalEvent('.toggle-all', 'change', $e) {
:: setAllCompleted(e.target.checked); :: setAllCompleted(e.target.checked);
} }
on asserted todo($id, _, _) { on asserted todo($id, _, _) {
todoListItemView(id); todoListItemView(id);
}
} }
} }
actor { actor {
react { field this.completedCount = 0;
field this.completedCount = 0; field this.activeCount = 0;
field this.activeCount = 0; on asserted todo($id, _, $c) { if (c) this.completedCount++; else this.activeCount++; }
on asserted todo($id, _, $c) { if (c) this.completedCount++; else this.activeCount++; } on retracted todo($id, _, $c) { if (c) this.completedCount--; else this.activeCount--; }
on retracted todo($id, _, $c) { if (c) this.completedCount--; else this.activeCount--; } assert activeTodoCount(this.activeCount);
assert activeTodoCount(this.activeCount); assert completedTodoCount(this.completedCount);
assert completedTodoCount(this.completedCount); assert totalTodoCount(this.activeCount + this.completedCount);
assert totalTodoCount(this.activeCount + this.completedCount); assert allCompleted() when (this.completedCount > 0 && this.activeCount === 0);
assert allCompleted() when (this.completedCount > 0 && this.activeCount === 0);
}
} }
actor { actor {
react { during Syndicate.UI.locationHash($hash) {
during Syndicate.UI.locationHash($hash) { assert Syndicate.UI.uiAttribute('ul.filters > li > a[href="#'+hash+'"]',
assert Syndicate.UI.uiAttribute('ul.filters > li > a[href="#'+hash+'"]', 'class', 'selected');
'class', 'selected'); }
}
during Syndicate.UI.locationHash('/') { during Syndicate.UI.locationHash('/') {
assert show(true); assert show(true);
assert show(false); assert show(false);
} }
during Syndicate.UI.locationHash('/active') { during Syndicate.UI.locationHash('/active') {
assert show(false); assert show(false);
} }
during Syndicate.UI.locationHash('/completed') { during Syndicate.UI.locationHash('/completed') {
assert show(true); assert show(true);
}
} }
} }
@ -202,32 +190,32 @@ ground dataspace G {
} }
} else { } else {
db = {nextId: 0, todos: {}}; db = {nextId: 0, todos: {}};
react until { on start {
case asserted Syndicate.observe(createTodo(_)) { react {
:: createTodo('Buy milk'); stop on asserted Syndicate.observe(createTodo(_)) {
:: createTodo('Buy bread'); :: createTodo('Buy milk');
:: createTodo('Finish PhD'); :: createTodo('Buy bread');
:: createTodo('Finish PhD');
}
} }
} }
} }
react { on message createTodo($title) {
on message createTodo($title) { todoListItemModel(db.nextId++, title, false);
todoListItemModel(db.nextId++, title, false); }
}
during todo($id, _, _) { during todo($id, _, _) {
during todo(id, $title, $completed) { during todo(id, $title, $completed) {
do { on start {
db.todos[id] = {id: id, title: title, completed: completed}; db.todos[id] = {id: id, title: title, completed: completed};
localStorage['todos-syndicate'] = JSON.stringify(db);
}
}
finally {
delete db.todos[id];
localStorage['todos-syndicate'] = JSON.stringify(db); localStorage['todos-syndicate'] = JSON.stringify(db);
} }
} }
on stop {
delete db.todos[id];
localStorage['todos-syndicate'] = JSON.stringify(db);
}
} }
} }
} }

View File

@ -72,16 +72,16 @@ var splitProposal = Syndicate.Struct.makeConstructor("splitProposal", ["title","
/// core library. /// core library.
/// ///
function whileRelevantAssert(P) { function whileRelevantAssert(P) {
(function () { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(P, 0); }));
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(P, 0); })) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(P), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(P), metalevel: 0 }; }), (function() {}));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(P), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(P), metalevel: 0 }; }), (function() {})).completeBuild(); }).call(this); } }).bind(this)); });
} }
/// ### Implementation: SELLER /// ### Implementation: SELLER
function seller() { function seller() {
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
/// We give our actor two state variables: a dictionary recording our /// We give our actor two state variables: a dictionary recording our
/// inventory of books (mapping title to price), and a counter /// inventory of books (mapping title to price), and a counter
@ -112,42 +112,37 @@ function seller() {
/// The seller responds to interest in bookQuotes by asserting a /// The seller responds to interest in bookQuotes by asserting a
/// responsive record, if one exists. /// responsive record, if one exists.
(function () { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(bookQuote(_, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(bookQuote((Syndicate._$("title")), _)), metalevel: 0 }; }), (function(title) {
Syndicate.Actor.createFacet() var _cachedAssertion1472127201683_0 = (function() { var _ = Syndicate.__; return Syndicate.observe(bookQuote(title, _)); }).call(this);
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(bookQuote(_, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(bookQuote((Syndicate._$("title")), _)), metalevel: 0 }; }), (function(title) { { Syndicate.Actor.Facet.build((function () { {
var _cachedAssertion1470624657591_0 = (function() { var _ = Syndicate.__; return Syndicate.observe(bookQuote(title, _)); }).call(this); Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(bookQuote(title, this.priceOf(title)), 0); }));
Syndicate.Actor.createFacet() }
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(bookQuote(title, this.priceOf(title)), 0); })) Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1472127201683_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1472127201683_0, metalevel: 0 }; }), (function() {})); }).bind(this)); }}));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1470624657591_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1470624657591_0, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); }).call(this);
/// It also responds to order requests. /// It also responds to order requests.
(function () { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(order(_, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(order((Syndicate._$("title")), (Syndicate._$("offerPrice")), _, _)), metalevel: 0 }; }), (function(title, offerPrice) {
Syndicate.Actor.createFacet()
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(order(_, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(order((Syndicate._$("title")), (Syndicate._$("offerPrice")), _, _)), metalevel: 0 }; }), (function(title, offerPrice) {
/// We cannot sell a book we do not have, and we will not sell for /// We cannot sell a book we do not have, and we will not sell for
/// less than our asking price. /// less than our asking price.
var askingPrice = this.priceOf(title); var askingPrice = this.priceOf(title);
if ((askingPrice === false) || (offerPrice < askingPrice)) { if ((askingPrice === false) || (offerPrice < askingPrice)) {
whileRelevantAssert( whileRelevantAssert(
order(title, offerPrice, false, false)); order(title, offerPrice, false, false));
} else { } else {
/// But if we can sell it, we do so by allocating an order ID and /// But if we can sell it, we do so by allocating an order ID and
/// replying to the orderer. /// replying to the orderer.
var orderId = this.nextOrderId++; var orderId = this.nextOrderId++;
Syndicate.Actor.deleteField(this.books, title); Syndicate.Actor.deleteField(this.books, title);
Syndicate.Actor.spawnActor(function() { whileRelevantAssert(
whileRelevantAssert( order(title, offerPrice, orderId, "March 9th"));
order(title, offerPrice, orderId, "March 9th")); }
}); }));
} } }).bind(this)); });
})).completeBuild(); }).call(this);
});
} }
/// ### Implementation: SPLIT-PROPOSER and book-quote-requestor /// ### Implementation: SPLIT-PROPOSER and book-quote-requestor
@ -180,9 +175,8 @@ function buyerA() {
/// First, retrieve a quote for the title, and analyze the result. /// First, retrieve a quote for the title, and analyze the result.
(function () { (function () { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(bookQuote(title, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: bookQuote(title, (Syndicate._$("price"))), metalevel: 0 }; }), (function(price) {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(bookQuote(title, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: bookQuote(title, (Syndicate._$("price"))), metalevel: 0 }; }), (function(price) {
if (price === false) { if (price === false) {
console.log("A learns that "+title+" is out-of-stock."); console.log("A learns that "+title+" is out-of-stock.");
buyBooks(); buyBooks();
@ -197,7 +191,8 @@ Syndicate.Actor.createFacet()
trySplit(title, price, price / 2); trySplit(title, price, price / 2);
} }
})).completeBuild(); }).call(this); }));
} }).bind(this)); }).call(this);
} }
function trySplit(title, price, contribution) { function trySplit(title, price, contribution) {
@ -216,20 +211,21 @@ Syndicate.Actor.createFacet()
/// Make our proposal, and wait for a response. /// Make our proposal, and wait for a response.
(function () { (function () { Syndicate.Actor.Facet.build((function () { {
Syndicate.Actor.createFacet() Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(splitProposal(title, price, contribution, true), 0); }), (function() { var _ = Syndicate.__; return { assertion: splitProposal(title, price, contribution, true), metalevel: 0 }; }), (function() {
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(splitProposal(title, price, contribution, true), 0); }), (function() { var _ = Syndicate.__; return { assertion: splitProposal(title, price, contribution, true), metalevel: 0 }; }), (function() {
console.log("A learns that the split-proposal for "+ console.log("A learns that the split-proposal for "+
title+" was accepted"); title+" was accepted");
buyBooks(); buyBooks();
})) }));
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(splitProposal(title, price, contribution, false), 0); }), (function() { var _ = Syndicate.__; return { assertion: splitProposal(title, price, contribution, false), metalevel: 0 }; }), (function() {
Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(splitProposal(title, price, contribution, false), 0); }), (function() { var _ = Syndicate.__; return { assertion: splitProposal(title, price, contribution, false), metalevel: 0 }; }), (function() {
console.log("A learns that the split-proposal for "+ console.log("A learns that the split-proposal for "+
title+" was rejected"); title+" was rejected");
trySplit(title, trySplit(title,
price, price,
contribution + ((price - contribution) / 2)); contribution + ((price - contribution) / 2));
})).completeBuild(); }).call(this); }));
} }).bind(this)); }).call(this);
} }
} }
}); });
@ -238,7 +234,7 @@ Syndicate.Actor.createFacet()
/// ### Implementation: SPLIT-DISPOSER and BUYER /// ### Implementation: SPLIT-DISPOSER and BUYER
function buyerB() { function buyerB() {
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
/// This actor maintains a record of the amount of money it has left /// This actor maintains a record of the amount of money it has left
/// to spend. /// to spend.
@ -248,52 +244,55 @@ function buyerB() {
/// It spends its time waiting for a SPLIT-PROPOSER to offer a /// It spends its time waiting for a SPLIT-PROPOSER to offer a
/// `splitProposal`. /// `splitProposal`.
(function () { Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(splitProposal(_,
Syndicate.Actor.createFacet() _,
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(splitProposal(_, _,
_, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(splitProposal((Syndicate._$("title")),
_, (Syndicate._$("price")),
_)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(splitProposal((Syndicate._$("title")), (Syndicate._$("theirContribution")),
(Syndicate._$("price")), _)), metalevel: 0 }; }), (function(title, price, theirContribution) {
(Syndicate._$("theirContribution")), var myContribution = price - theirContribution;
_)), metalevel: 0 }; }), (function(title, price, theirContribution) { console.log("B is being asked to contribute "+myContribution+
var myContribution = price - theirContribution; " toward "+title+" at price "+price);
console.log("B is being asked to contribute "+myContribution+
" toward "+title+" at price "+price);
/// We may not be able to afford contributing this much. /// We may not be able to afford contributing this much.
if (myContribution > this.funds) { if (myContribution > this.funds) {
console.log("B hasn't enough funds ("+this.funds+ console.log("B hasn't enough funds ("+this.funds+
" remaining)"); " remaining)");
whileRelevantAssert( whileRelevantAssert(
splitProposal(title, price, theirContribution, false)); splitProposal(title, price, theirContribution, false));
} else { } else {
/// But if we *can* afford it, update our remaining funds and spawn a /// But if we *can* afford it, update our remaining funds and spawn a
/// small actor to handle the actual purchase now that we have agreed /// small actor to handle the actual purchase now that we have agreed
/// on a split. /// on a split.
var remainingFunds = this.funds - myContribution; var remainingFunds = this.funds - myContribution;
console.log("B accepts the offer, leaving them with "+ console.log("B accepts the offer, leaving them with "+
remainingFunds+" remaining funds"); remainingFunds+" remaining funds");
this.funds = remainingFunds; this.funds = remainingFunds;
Syndicate.Actor.spawnActor(function() { Syndicate.Actor.spawnActor(function() { Syndicate.Actor.Facet.build((function () { {
(function () {
Syndicate.Actor.createFacet() /// While waiting for order confirmation, take the opportunity to
.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(splitProposal(title, /// signal to our SPLIT-PROPOSER that we accepted their proposal.
price,
theirContribution, Syndicate.Actor.Facet.current.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(splitProposal(title,
true), 0); })) price,
.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(order(title, price, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: order(title, price, (Syndicate._$("id")), (Syndicate._$("date"))), metalevel: 0 }; }), (function(id, date) { theirContribution,
console.log("The order for "+title+" has id "+id+ true), 0); }));
", and will be delivered on "+date);
})).completeBuild(); }).call(this); /// When order confirmation arrives, this purchase is completed.
});
} Syndicate.Actor.Facet.current.onEvent(Syndicate.Actor.PRIORITY_NORMAL, true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(order(title, price, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: order(title, price, (Syndicate._$("id")), (Syndicate._$("date"))), metalevel: 0 }; }), (function(id, date) {
})).completeBuild(); }).call(this); console.log("The order for "+title+" has id "+id+
}); ", and will be delivered on "+date);
}));
} }).bind(this)); });
}
}));
} }).bind(this)); });
} }
/// ### Starting Configuration /// ### Starting Configuration

View File

@ -76,10 +76,9 @@ assertion type splitProposal(title, price, contribution, accepted);
/// core library. /// core library.
/// ///
function whileRelevantAssert(P) { function whileRelevantAssert(P) {
react { actor {
assert P; assert P;
} until { stop on retracted Syndicate.observe(P);
case retracted Syndicate.observe(P);
} }
} }
@ -117,38 +116,32 @@ function seller() {
/// The seller responds to interest in bookQuotes by asserting a /// The seller responds to interest in bookQuotes by asserting a
/// responsive record, if one exists. /// responsive record, if one exists.
react { during Syndicate.observe(bookQuote($title, _)) {
during Syndicate.observe(bookQuote($title, _)) { assert bookQuote(title, this.priceOf(title));
assert bookQuote(title, this.priceOf(title));
}
} }
/// It also responds to order requests. /// It also responds to order requests.
react { on asserted
on asserted Syndicate.observe(order($title, $offerPrice, _, _)) {
Syndicate.observe(order($title, $offerPrice, _, _)) {
/// We cannot sell a book we do not have, and we will not sell for /// We cannot sell a book we do not have, and we will not sell for
/// less than our asking price. /// less than our asking price.
var askingPrice = this.priceOf(title); var askingPrice = this.priceOf(title);
if ((askingPrice === false) || (offerPrice < askingPrice)) { if ((askingPrice === false) || (offerPrice < askingPrice)) {
whileRelevantAssert( whileRelevantAssert(
order(title, offerPrice, false, false)); order(title, offerPrice, false, false));
} else { } else {
/// But if we can sell it, we do so by allocating an order ID and /// But if we can sell it, we do so by allocating an order ID and
/// replying to the orderer. /// replying to the orderer.
var orderId = this.nextOrderId++; var orderId = this.nextOrderId++;
delete field this.books[title]; delete field this.books[title];
actor { whileRelevantAssert(
whileRelevantAssert( order(title, offerPrice, orderId, "March 9th"));
order(title, offerPrice, orderId, "March 9th"));
}
}
} }
} }
} }
@ -157,7 +150,7 @@ function seller() {
/// ### Implementation: SPLIT-PROPOSER and book-quote-requestor /// ### Implementation: SPLIT-PROPOSER and book-quote-requestor
function buyerA() { function buyerA() {
actor { actor* {
var self = this; var self = this;
/// Our actor remembers which books remain on its shopping list, and /// Our actor remembers which books remain on its shopping list, and
@ -184,8 +177,8 @@ function buyerA() {
/// First, retrieve a quote for the title, and analyze the result. /// First, retrieve a quote for the title, and analyze the result.
react until { react {
case asserted bookQuote(title, $price) { stop on asserted bookQuote(title, $price) {
if (price === false) { if (price === false) {
console.log("A learns that "+title+" is out-of-stock."); console.log("A learns that "+title+" is out-of-stock.");
buyBooks(); buyBooks();
@ -220,15 +213,15 @@ function buyerA() {
/// Make our proposal, and wait for a response. /// Make our proposal, and wait for a response.
react until { react {
case asserted stop on asserted
splitProposal(title, price, contribution, true) { splitProposal(title, price, contribution, true) {
console.log("A learns that the split-proposal for "+ console.log("A learns that the split-proposal for "+
title+" was accepted"); title+" was accepted");
buyBooks(); buyBooks();
} }
case asserted stop on asserted
splitProposal(title, price, contribution, false) { splitProposal(title, price, contribution, false) {
console.log("A learns that the split-proposal for "+ console.log("A learns that the split-proposal for "+
title+" was rejected"); title+" was rejected");
@ -255,55 +248,50 @@ function buyerB() {
/// It spends its time waiting for a SPLIT-PROPOSER to offer a /// It spends its time waiting for a SPLIT-PROPOSER to offer a
/// `splitProposal`. /// `splitProposal`.
react { on asserted
on asserted Syndicate.observe(splitProposal($title,
Syndicate.observe(splitProposal($title, $price,
$price, $theirContribution,
$theirContribution, _))
_)) {
{ var myContribution = price - theirContribution;
var myContribution = price - theirContribution; console.log("B is being asked to contribute "+myContribution+
console.log("B is being asked to contribute "+myContribution+ " toward "+title+" at price "+price);
" toward "+title+" at price "+price);
/// We may not be able to afford contributing this much. /// We may not be able to afford contributing this much.
if (myContribution > this.funds) { if (myContribution > this.funds) {
console.log("B hasn't enough funds ("+this.funds+ console.log("B hasn't enough funds ("+this.funds+
" remaining)"); " remaining)");
whileRelevantAssert( whileRelevantAssert(
splitProposal(title, price, theirContribution, false)); splitProposal(title, price, theirContribution, false));
} else { } else {
/// But if we *can* afford it, update our remaining funds and spawn a /// But if we *can* afford it, update our remaining funds and spawn a
/// small actor to handle the actual purchase now that we have agreed /// small actor to handle the actual purchase now that we have agreed
/// on a split. /// on a split.
var remainingFunds = this.funds - myContribution; var remainingFunds = this.funds - myContribution;
console.log("B accepts the offer, leaving them with "+ console.log("B accepts the offer, leaving them with "+
remainingFunds+" remaining funds"); remainingFunds+" remaining funds");
this.funds = remainingFunds; this.funds = remainingFunds;
actor { actor {
react {
/// While waiting for order confirmation, take the opportunity to /// While waiting for order confirmation, take the opportunity to
/// signal to our SPLIT-PROPOSER that we accepted their proposal. /// signal to our SPLIT-PROPOSER that we accepted their proposal.
assert splitProposal(title, assert splitProposal(title,
price, price,
theirContribution, theirContribution,
true); true);
/// When order confirmation arrives, this purchase is completed. /// When order confirmation arrives, this purchase is completed.
} until { stop on asserted order(title, price, $id, $date) {
case asserted order(title, price, $id, $date) { console.log("The order for "+title+" has id "+id+
console.log("The order for "+title+" has id "+id+ ", and will be delivered on "+date);
", and will be delivered on "+date);
}
} }
}
} }
} }
} }

View File

@ -76,10 +76,9 @@ This routine is under consideration for possible addition to the
core library. core library.
function whileRelevantAssert(P) { function whileRelevantAssert(P) {
react { actor {
assert P; assert P;
} until { stop on retracted Syndicate.observe(P);
case retracted Syndicate.observe(P);
} }
} }
@ -117,38 +116,32 @@ inventory.
The seller responds to interest in bookQuotes by asserting a The seller responds to interest in bookQuotes by asserting a
responsive record, if one exists. responsive record, if one exists.
react { during Syndicate.observe(bookQuote($title, _)) {
during Syndicate.observe(bookQuote($title, _)) { assert bookQuote(title, this.priceOf(title));
assert bookQuote(title, this.priceOf(title));
}
} }
It also responds to order requests. It also responds to order requests.
react { on asserted
on asserted Syndicate.observe(order($title, $offerPrice, _, _)) {
Syndicate.observe(order($title, $offerPrice, _, _)) {
We cannot sell a book we do not have, and we will not sell for We cannot sell a book we do not have, and we will not sell for
less than our asking price. less than our asking price.
var askingPrice = this.priceOf(title); var askingPrice = this.priceOf(title);
if ((askingPrice === false) || (offerPrice < askingPrice)) { if ((askingPrice === false) || (offerPrice < askingPrice)) {
whileRelevantAssert( whileRelevantAssert(
order(title, offerPrice, false, false)); order(title, offerPrice, false, false));
} else { } else {
But if we can sell it, we do so by allocating an order ID and But if we can sell it, we do so by allocating an order ID and
replying to the orderer. replying to the orderer.
var orderId = this.nextOrderId++; var orderId = this.nextOrderId++;
delete field this.books[title]; delete field this.books[title];
actor { whileRelevantAssert(
whileRelevantAssert( order(title, offerPrice, orderId, "March 9th"));
order(title, offerPrice, orderId, "March 9th"));
}
}
} }
} }
} }
@ -157,7 +150,7 @@ replying to the orderer.
### Implementation: SPLIT-PROPOSER and book-quote-requestor ### Implementation: SPLIT-PROPOSER and book-quote-requestor
function buyerA() { function buyerA() {
actor { actor* {
var self = this; var self = this;
Our actor remembers which books remain on its shopping list, and Our actor remembers which books remain on its shopping list, and
@ -184,8 +177,8 @@ loop in almost a tail-recursive style, using helper functions
First, retrieve a quote for the title, and analyze the result. First, retrieve a quote for the title, and analyze the result.
react until { react {
case asserted bookQuote(title, $price) { stop on asserted bookQuote(title, $price) {
if (price === false) { if (price === false) {
console.log("A learns that "+title+" is out-of-stock."); console.log("A learns that "+title+" is out-of-stock.");
buyBooks(); buyBooks();
@ -220,15 +213,15 @@ perform the BUYER role here.
Make our proposal, and wait for a response. Make our proposal, and wait for a response.
react until { react {
case asserted stop on asserted
splitProposal(title, price, contribution, true) { splitProposal(title, price, contribution, true) {
console.log("A learns that the split-proposal for "+ console.log("A learns that the split-proposal for "+
title+" was accepted"); title+" was accepted");
buyBooks(); buyBooks();
} }
case asserted stop on asserted
splitProposal(title, price, contribution, false) { splitProposal(title, price, contribution, false) {
console.log("A learns that the split-proposal for "+ console.log("A learns that the split-proposal for "+
title+" was rejected"); title+" was rejected");
@ -255,55 +248,50 @@ to spend.
It spends its time waiting for a SPLIT-PROPOSER to offer a It spends its time waiting for a SPLIT-PROPOSER to offer a
`splitProposal`. `splitProposal`.
react { on asserted
on asserted Syndicate.observe(splitProposal($title,
Syndicate.observe(splitProposal($title, $price,
$price, $theirContribution,
$theirContribution, _))
_)) {
{ var myContribution = price - theirContribution;
var myContribution = price - theirContribution; console.log("B is being asked to contribute "+myContribution+
console.log("B is being asked to contribute "+myContribution+ " toward "+title+" at price "+price);
" toward "+title+" at price "+price);
We may not be able to afford contributing this much. We may not be able to afford contributing this much.
if (myContribution > this.funds) { if (myContribution > this.funds) {
console.log("B hasn't enough funds ("+this.funds+ console.log("B hasn't enough funds ("+this.funds+
" remaining)"); " remaining)");
whileRelevantAssert( whileRelevantAssert(
splitProposal(title, price, theirContribution, false)); splitProposal(title, price, theirContribution, false));
} else { } else {
But if we *can* afford it, update our remaining funds and spawn a But if we *can* afford it, update our remaining funds and spawn a
small actor to handle the actual purchase now that we have agreed small actor to handle the actual purchase now that we have agreed
on a split. on a split.
var remainingFunds = this.funds - myContribution; var remainingFunds = this.funds - myContribution;
console.log("B accepts the offer, leaving them with "+ console.log("B accepts the offer, leaving them with "+
remainingFunds+" remaining funds"); remainingFunds+" remaining funds");
this.funds = remainingFunds; this.funds = remainingFunds;
actor { actor {
react {
While waiting for order confirmation, take the opportunity to While waiting for order confirmation, take the opportunity to
signal to our SPLIT-PROPOSER that we accepted their proposal. signal to our SPLIT-PROPOSER that we accepted their proposal.
assert splitProposal(title, assert splitProposal(title,
price, price,
theirContribution, theirContribution,
true); true);
When order confirmation arrives, this purchase is completed. When order confirmation arrives, this purchase is completed.
} until { stop on asserted order(title, price, $id, $date) {
case asserted order(title, price, $id, $date) { console.log("The order for "+title+" has id "+id+
console.log("The order for "+title+" has id "+id+ ", and will be delivered on "+date);
", and will be delivered on "+date);
}
} }
}
} }
} }
} }