Compare commits
50 Commits
pre-packag
...
main
Author | SHA1 | Date |
---|---|---|
Tony Garnock-Jones | 382391b518 | |
Tony Garnock-Jones | 5621685052 | |
Tony Garnock-Jones | 85c6c228a3 | |
Tony Garnock-Jones | 18c4b184e5 | |
Tony Garnock-Jones | 9fdf90db68 | |
Tony Garnock-Jones | 26245951ad | |
Tony Garnock-Jones | 7cf9dabca4 | |
Tony Garnock-Jones | 0584b4d6d3 | |
Tony Garnock-Jones | d777418d5c | |
Tony Garnock-Jones | 160938cec8 | |
Tony Garnock-Jones | 1b3b355fea | |
Tony Garnock-Jones | 440c91b4d7 | |
Tony Garnock-Jones | 52bdc2eb3c | |
Tony Garnock-Jones | 10abaa1724 | |
Tony Garnock-Jones | 7bb57e2c39 | |
Tony Garnock-Jones | 66670e7f6f | |
Tony Garnock-Jones | 20d0f5d58d | |
Tony Garnock-Jones | f5e59bfcd8 | |
Tony Garnock-Jones | a0a6a3dbfe | |
Tony Garnock-Jones | d080494664 | |
Tony Garnock-Jones | c5f7e9db2a | |
Tony Garnock-Jones | a62f00b8a3 | |
Tony Garnock-Jones | e9697c8171 | |
Tony Garnock-Jones | d7baec744e | |
Tony Garnock-Jones | 216935d60f | |
Tony Garnock-Jones | 66709fe58e | |
Tony Garnock-Jones | 318770e301 | |
Tony Garnock-Jones | bd06a8a09e | |
Tony Garnock-Jones | 659ee24105 | |
Tony Garnock-Jones | 2bfacbfc7b | |
Tony Garnock-Jones | e78696c621 | |
Tony Garnock-Jones | f46fd52239 | |
Tony Garnock-Jones | e0def26f6c | |
Tony Garnock-Jones | 9965ce760e | |
Tony Garnock-Jones | 76044b539e | |
Tony Garnock-Jones | 55c9fa1d49 | |
Tony Garnock-Jones | 95a4bc3c93 | |
Tony Garnock-Jones | 9ffbec107f | |
Tony Garnock-Jones | fba2ee91a8 | |
Tony Garnock-Jones | 0b361137a2 | |
Tony Garnock-Jones | e0b476cc0a | |
Tony Garnock-Jones | cfeb46ef62 | |
Tony Garnock-Jones | 80befd60eb | |
Tony Garnock-Jones | 28405b544f | |
Tony Garnock-Jones | a605a438cf | |
Tony Garnock-Jones | 96d577b2d7 | |
Tony Garnock-Jones | f7baa65a2d | |
Tony Garnock-Jones | 8fde23d187 | |
Tony Garnock-Jones | 96d3d3c621 | |
Tony Garnock-Jones | 7bab9be492 |
|
@ -1,6 +1,5 @@
|
||||||
private-key.pem
|
private-key.pem
|
||||||
server-cert.pem
|
server-cert.pem
|
||||||
MarketplaceChat.app.zip
|
|
||||||
MarketplaceChat.app/
|
|
||||||
scratch/
|
scratch/
|
||||||
_site/
|
_site/
|
||||||
|
node_modules/
|
||||||
|
|
42
Makefile
42
Makefile
|
@ -1,21 +1,5 @@
|
||||||
APP_NAME=MarketplaceChat.app
|
all:
|
||||||
LIB_SOURCES=\
|
npm install .
|
||||||
route.js \
|
|
||||||
marketplace.js \
|
|
||||||
spy.js \
|
|
||||||
dom-driver.js \
|
|
||||||
jquery-driver.js \
|
|
||||||
routing-table-widget.js \
|
|
||||||
routing-table-widget.css \
|
|
||||||
wake-detector.js \
|
|
||||||
websocket-driver.js
|
|
||||||
APP_SOURCES=\
|
|
||||||
examples/chat/index.html \
|
|
||||||
examples/chat/index.js \
|
|
||||||
examples/chat/style.css
|
|
||||||
RESOURCES=$(wildcard examples/chat/app-resources/*)
|
|
||||||
|
|
||||||
all: $(APP_NAME).zip
|
|
||||||
|
|
||||||
keys: private-key.pem server-cert.pem
|
keys: private-key.pem server-cert.pem
|
||||||
|
|
||||||
|
@ -32,22 +16,8 @@ server-cert.pem: private-key.pem
|
||||||
clean-keys:
|
clean-keys:
|
||||||
rm -f private-key.pem server-cert.pem
|
rm -f private-key.pem server-cert.pem
|
||||||
|
|
||||||
$(APP_NAME).zip: $(APP_NAME)
|
|
||||||
zip -r $@ $<
|
|
||||||
|
|
||||||
$(APP_NAME): $(APP_SOURCES) $(LIB_SOURCES)
|
|
||||||
echo RESOURCES $(RESOURCES)
|
|
||||||
rm -rf $@
|
|
||||||
mkdir -p $@/Contents/MacOS
|
|
||||||
mkdir -p $@/Contents/Resources
|
|
||||||
cp examples/chat/app-resources/Info.plist $@/Contents
|
|
||||||
cp examples/chat/app-resources/boot.sh $@/Contents/MacOS
|
|
||||||
cp examples/chat/app-resources/app.icns $@/Contents/Resources
|
|
||||||
cp -r third-party $@/Contents/Resources
|
|
||||||
cp $(LIB_SOURCES) $@/Contents/Resources
|
|
||||||
mkdir -p $@/Contents/Resources/examples/chat
|
|
||||||
cp $(APP_SOURCES) $@/Contents/Resources/examples/chat
|
|
||||||
chmod a+x $@/Contents/MacOS/boot.sh
|
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf $(APP_NAME) $(APP_NAME).zip
|
rm -f dist/*.js
|
||||||
|
|
||||||
|
veryclean: clean
|
||||||
|
rm -rf node_modules/
|
||||||
|
|
|
@ -14,7 +14,7 @@ To install the Racket server:
|
||||||
|
|
||||||
To run the Racket server:
|
To run the Racket server:
|
||||||
|
|
||||||
- `racket server.rkt` from the base directory of this repository.
|
racket -l minimart/examples/broker
|
||||||
|
|
||||||
The Racket server listens for tunnelled Network Calculus events via
|
The Racket server listens for tunnelled Network Calculus events via
|
||||||
websocket on ports 8000 (HTTP) and 8443 (HTTPS, if you have a
|
websocket on ports 8000 (HTTP) and 8443 (HTTPS, if you have a
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Directory for build products, checked in to the repo for ease-of-use.
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,67 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>JS Marketplace: Chat Example</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||||
|
<link href="style.css" rel="stylesheet">
|
||||||
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="../../dist/minimart.js"></script>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<form class="form-horizontal" name="nym_form">
|
||||||
|
<fieldset>
|
||||||
|
<div class="control-group">
|
||||||
|
<label class="control-label" for="wsurl">Server:</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" id="wsurl" name="wsurl" value="ws://localhost:8000/">
|
||||||
|
</div>
|
||||||
|
<label class="control-label" for="nym">Nym:</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" id="nym" name="nym" value="">
|
||||||
|
</div>
|
||||||
|
<label class="control-label" for="status">Status:</label>
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" id="status" name="status" value="">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span9">
|
||||||
|
<h2>Messages</h2>
|
||||||
|
<div id="chat_output"></div>
|
||||||
|
</div>
|
||||||
|
<div class="span3">
|
||||||
|
<h2>Active Users</h2>
|
||||||
|
<div id="nymlist"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12">
|
||||||
|
<form class="form-horizontal" name="chat_form">
|
||||||
|
<fieldset>
|
||||||
|
<div class="control-group">
|
||||||
|
<div class="controls">
|
||||||
|
<input type="text" id="chat_input" name="chat_input" value="" autocomplete="off">
|
||||||
|
<button id="send_chat">Send</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12" id="spy-holder">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,162 @@
|
||||||
|
var Route = Minimart.Route;
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
function chatEvent(nym, status, utterance, stamp) {
|
||||||
|
return ["chatEvent", nym, status, utterance, stamp || +(new Date())];
|
||||||
|
}
|
||||||
|
function chatEventNym(c) { return c[1]; }
|
||||||
|
function chatEventStatus(c) { return c[2]; }
|
||||||
|
function chatEventUtterance(c) { return c[3]; }
|
||||||
|
function chatEventStamp(c) { return c[4]; }
|
||||||
|
|
||||||
|
function outputItem(item) {
|
||||||
|
var stamp = $("<span/>").text((new Date()).toGMTString()).addClass("timestamp");
|
||||||
|
var item = $("<div/>").append([stamp].concat(item));
|
||||||
|
var o = $("#chat_output");
|
||||||
|
o.append(item);
|
||||||
|
o[0].scrollTop = o[0].scrollHeight;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateNymList(g) {
|
||||||
|
var statuses = {};
|
||||||
|
var nymProj = ["broker", 0, ["chatEvent", _$, _$, __, __]];
|
||||||
|
var matchedNyms = Route.matcherKeys(g.project(Route.compileProjection(nymProj), true, 0, 0));
|
||||||
|
for (var i = 0; i < matchedNyms.length; i++) {
|
||||||
|
statuses[matchedNyms[i][0]] = matchedNyms[i][1];
|
||||||
|
}
|
||||||
|
var nyms = [];
|
||||||
|
for (var nym in statuses) { nyms.push(nym); }
|
||||||
|
nyms.sort();
|
||||||
|
|
||||||
|
var container = $("#nymlist");
|
||||||
|
container[0].innerHTML = ""; // remove all children
|
||||||
|
for (var i = 0; i < nyms.length; i++) {
|
||||||
|
var n = $("<span/>").text(nyms[i]).addClass("nym");
|
||||||
|
var s = statuses[nyms[i]];
|
||||||
|
if (s) {
|
||||||
|
container.append($("<div/>").append([n, $("<span/>").text(s).addClass("nym_status")]));
|
||||||
|
} else {
|
||||||
|
container.append($("<div/>").append(n));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function outputState(state) {
|
||||||
|
outputItem([$("<span/>").text(state).addClass(state).addClass("state")])
|
||||||
|
.addClass("state_" + state);
|
||||||
|
}
|
||||||
|
|
||||||
|
function outputUtterance(who, what) {
|
||||||
|
outputItem([$("<span/>").text(who).addClass("nym"),
|
||||||
|
$("<span/>").text(what).addClass("utterance")]).addClass("utterance");
|
||||||
|
}
|
||||||
|
|
||||||
|
var G;
|
||||||
|
$(document).ready(function () {
|
||||||
|
$("#chat_form").submit(function (e) { e.preventDefault(); return false; });
|
||||||
|
$("#nym_form").submit(function (e) { e.preventDefault(); return false; });
|
||||||
|
if (!($("#nym").val())) { $("#nym").val("nym" + Math.floor(Math.random() * 65536)); }
|
||||||
|
|
||||||
|
G = new Minimart.Ground(function () {
|
||||||
|
console.log('starting ground boot');
|
||||||
|
// World.spawn(new Spy());
|
||||||
|
Minimart.JQuery.spawnJQueryDriver();
|
||||||
|
Minimart.DOM.spawnDOMDriver();
|
||||||
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
|
||||||
|
|
||||||
|
World.spawn(new Minimart.WakeDetector());
|
||||||
|
var wsconn = new Minimart.WebSocket.WebSocketConnection("broker", $("#wsurl").val(), true);
|
||||||
|
World.spawn(wsconn);
|
||||||
|
World.spawn({
|
||||||
|
// Monitor connection, notifying connectivity changes
|
||||||
|
state: "crashed", // start with this to avoid spurious initial message print
|
||||||
|
boot: function () {
|
||||||
|
return [sub(["broker_state", __], 0, 1)];
|
||||||
|
},
|
||||||
|
handleEvent: function (e) {
|
||||||
|
if (e.type === "routes") {
|
||||||
|
var states =
|
||||||
|
Route.matcherKeys(e.gestalt.project(Route.compileProjection([__, _$]),
|
||||||
|
true, 0, 0));
|
||||||
|
var newState = states.length > 0 ? states[0][0] : "crashed";
|
||||||
|
if (this.state != newState) {
|
||||||
|
outputState(newState);
|
||||||
|
this.state = newState;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
World.spawn({
|
||||||
|
// Actual chat functionality
|
||||||
|
boot: function () {
|
||||||
|
return this.subscriptions();
|
||||||
|
},
|
||||||
|
nym: function () { return $("#nym").val(); },
|
||||||
|
currentStatus: function () { return $("#status").val(); },
|
||||||
|
subscriptions: function () {
|
||||||
|
return [sub("wake"),
|
||||||
|
sub(["jQuery", "#send_chat", "click", __]),
|
||||||
|
sub(["jQuery", "#nym", "change", __]),
|
||||||
|
sub(["jQuery", "#status", "change", __]),
|
||||||
|
sub(["jQuery", "#wsurl", "change", __]),
|
||||||
|
pub(["broker", 0, chatEvent(this.nym(), this.currentStatus(), __, __)]),
|
||||||
|
sub(["broker", 0, chatEvent(__, __, __, __)], 0, 1)];
|
||||||
|
},
|
||||||
|
handleEvent: function (e) {
|
||||||
|
var self = this;
|
||||||
|
switch (e.type) {
|
||||||
|
case "routes":
|
||||||
|
updateNymList(e.gestalt);
|
||||||
|
break;
|
||||||
|
case "message":
|
||||||
|
if (e.message === "wake") {
|
||||||
|
wsconn.forceclose();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
switch (e.message[0]) {
|
||||||
|
case "jQuery":
|
||||||
|
switch (e.message[1]) {
|
||||||
|
case "#send_chat":
|
||||||
|
var inp = $("#chat_input");
|
||||||
|
var utterance = inp.val();
|
||||||
|
inp.val("");
|
||||||
|
if (utterance) {
|
||||||
|
World.send(["broker", 0, chatEvent(this.nym(),
|
||||||
|
this.currentStatus(),
|
||||||
|
utterance)]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "#nym":
|
||||||
|
case "#status":
|
||||||
|
World.updateRoutes(this.subscriptions());
|
||||||
|
break;
|
||||||
|
case "#wsurl":
|
||||||
|
wsconn.forceclose();
|
||||||
|
wsconn.wsurl = $("#wsurl").val();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.log("Got jquery event from as-yet-unhandled subscription",
|
||||||
|
e.message[2], e.message[3]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "broker":
|
||||||
|
if (e.message[2][0] === "chatEvent") {
|
||||||
|
outputUtterance(chatEventNym(e.message[2]),
|
||||||
|
chatEventUtterance(e.message[2]));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
G.startStepping();
|
||||||
|
});
|
|
@ -0,0 +1,73 @@
|
||||||
|
span.timestamp {
|
||||||
|
color: #d0d0d0;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.timestamp:after {
|
||||||
|
content: " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
.utterance span.nym:after {
|
||||||
|
content: ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
span.arrived:after {
|
||||||
|
content: " arrived";
|
||||||
|
}
|
||||||
|
|
||||||
|
span.departed:after {
|
||||||
|
content: " departed";
|
||||||
|
}
|
||||||
|
|
||||||
|
div.notification {
|
||||||
|
background-color: #eeeeff;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.state.connected, span.arrived {
|
||||||
|
color: #00c000;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.state.disconnected, span.departed {
|
||||||
|
color: #c00000;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.state.crashed {
|
||||||
|
color: white;
|
||||||
|
background: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.state.crashed:after {
|
||||||
|
content: "; please reload the page";
|
||||||
|
}
|
||||||
|
|
||||||
|
div.state_disconnected {
|
||||||
|
background-color: #ffeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.state_connected {
|
||||||
|
background-color: #eeffee;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat_output {
|
||||||
|
height: 15em;
|
||||||
|
overflow-y: scroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
#chat_input {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nym {
|
||||||
|
color: #00c000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nym_status:before {
|
||||||
|
content: " (";
|
||||||
|
}
|
||||||
|
|
||||||
|
.nym_status:after {
|
||||||
|
content: ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
.nym_status {
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
|
@ -1,28 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>English</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>boot.sh</string>
|
|
||||||
<key>CFBundleIconFile</key>
|
|
||||||
<string>app.icns</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>com.leastfixedpoint.js-marketplace.chat-demo</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>chat-demo</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>APPL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>chat-demo 0.0.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>0.0.0</string>
|
|
||||||
<key>LSMinimumSystemVersion</key>
|
|
||||||
<string>10.6</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
Binary file not shown.
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
cd "$(dirname "$0")"/../Resources
|
|
||||||
open examples/chat/index.html
|
|
|
@ -3,33 +3,15 @@
|
||||||
<head>
|
<head>
|
||||||
<title>JS Marketplace: Chat Example</title>
|
<title>JS Marketplace: Chat Example</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
|
||||||
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
|
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||||
<link href="style.css" rel="stylesheet">
|
<link href="style.css" rel="stylesheet">
|
||||||
<!-- <script src="../../third-party/bootstrap/js/bootstrap.min.js"></script> -->
|
|
||||||
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="../../dist/minimart.js"></script>
|
||||||
<script src="../../route.js"></script>
|
|
||||||
<script src="../../marketplace.js"></script>
|
|
||||||
<script src="../../spy.js"></script>
|
|
||||||
<script src="../../dom-driver.js"></script>
|
|
||||||
|
|
||||||
<script src="../../routing-table-widget.js"></script>
|
|
||||||
<link href="../../routing-table-widget.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script src="../../jquery-driver.js"></script>
|
|
||||||
<script src="../../wake-detector.js"></script>
|
|
||||||
<script src="../../websocket-driver.js"></script>
|
|
||||||
<script src="index.js"></script>
|
<script src="index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<!-- <div class="row-fluid"> -->
|
|
||||||
<!-- <div class="span12"> -->
|
|
||||||
<!-- <h1>JS Marketplace</h1> -->
|
|
||||||
<!-- </div> -->
|
|
||||||
<!-- </div> -->
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span12">
|
<div class="span12">
|
||||||
<form class="form-horizontal" name="nym_form">
|
<form class="form-horizontal" name="nym_form">
|
||||||
|
|
|
@ -1,10 +1,14 @@
|
||||||
|
var Route = Minimart.Route;
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
function chatEvent(nym, status, utterance, stamp) {
|
function chatEvent(nym, status, utterance, stamp) {
|
||||||
return ["chatEvent", nym, status, utterance, stamp || +(new Date())];
|
return ["chatEvent", nym, status, utterance, stamp || +(new Date())];
|
||||||
}
|
}
|
||||||
function chatEventNym(c) { return c[1]; }
|
|
||||||
function chatEventStatus(c) { return c[2]; }
|
|
||||||
function chatEventUtterance(c) { return c[3]; }
|
|
||||||
function chatEventStamp(c) { return c[4]; }
|
|
||||||
|
|
||||||
function outputItem(item) {
|
function outputItem(item) {
|
||||||
var stamp = $("<span/>").text((new Date()).toGMTString()).addClass("timestamp");
|
var stamp = $("<span/>").text((new Date()).toGMTString()).addClass("timestamp");
|
||||||
|
@ -15,12 +19,10 @@ function outputItem(item) {
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNymList(g) {
|
function updateNymList(allStatuses) {
|
||||||
var statuses = {};
|
var statuses = {};
|
||||||
var nymProj = ["broker", 0, ["chatEvent", _$, _$, __, __]];
|
for (var i = 0; i < allStatuses.length; i++) {
|
||||||
var matchedNyms = route.matcherKeys(g.project(route.compileProjection(nymProj), true, 0, 0));
|
statuses[allStatuses[i].nym] = allStatuses[i].status;
|
||||||
for (var i = 0; i < matchedNyms.length; i++) {
|
|
||||||
statuses[matchedNyms[i][0]] = matchedNyms[i][1];
|
|
||||||
}
|
}
|
||||||
var nyms = [];
|
var nyms = [];
|
||||||
for (var nym in statuses) { nyms.push(nym); }
|
for (var nym in statuses) { nyms.push(nym); }
|
||||||
|
@ -55,101 +57,81 @@ $(document).ready(function () {
|
||||||
$("#nym_form").submit(function (e) { e.preventDefault(); return false; });
|
$("#nym_form").submit(function (e) { e.preventDefault(); return false; });
|
||||||
if (!($("#nym").val())) { $("#nym").val("nym" + Math.floor(Math.random() * 65536)); }
|
if (!($("#nym").val())) { $("#nym").val("nym" + Math.floor(Math.random() * 65536)); }
|
||||||
|
|
||||||
G = new Ground(function () {
|
G = new Minimart.Ground(function () {
|
||||||
console.log('starting ground boot');
|
console.log('starting ground boot');
|
||||||
// World.spawn(new Spy());
|
// World.spawn(new Spy());
|
||||||
spawnJQueryDriver();
|
Minimart.JQuery.spawnJQueryDriver();
|
||||||
spawnDOMDriver();
|
Minimart.DOM.spawnDOMDriver();
|
||||||
spawnRoutingTableWidget("#spy-holder", "spy");
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
|
||||||
|
|
||||||
World.spawn(new WakeDetector());
|
World.spawn(new Minimart.WakeDetector());
|
||||||
var wsconn = new WebSocketConnection("broker", $("#wsurl").val(), true);
|
var wsconn = new Minimart.WebSocket.WebSocketConnection("broker", $("#wsurl").val(), true);
|
||||||
World.spawn(wsconn);
|
World.spawn(wsconn);
|
||||||
World.spawn({
|
World.spawn(new Actor(function () {
|
||||||
// Monitor connection, notifying connectivity changes
|
// Monitor connection, notifying connectivity changes
|
||||||
state: "crashed", // start with this to avoid spurious initial message print
|
this.state = "crashed"; // start with this to avoid spurious initial message print
|
||||||
boot: function () {
|
|
||||||
World.updateRoutes([sub(["broker_state", __], 0, 1)]);
|
Actor.observeAdvertisers(
|
||||||
},
|
function () { return ["broker_state", _$("newState")]; },
|
||||||
handleEvent: function (e) {
|
{ name: "states" },
|
||||||
if (e.type === "routes") {
|
function () {
|
||||||
var states =
|
var newState = this.states.length > 0 ? this.states[0].newState : "crashed";
|
||||||
route.matcherKeys(e.gestalt.project(route.compileProjection([__, _$]),
|
|
||||||
true, 0, 0));
|
|
||||||
var newState = states.length > 0 ? states[0][0] : "crashed";
|
|
||||||
if (this.state != newState) {
|
if (this.state != newState) {
|
||||||
outputState(newState);
|
outputState(newState);
|
||||||
this.state = newState;
|
this.state = newState;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}));
|
||||||
});
|
World.spawn(new Actor(function () {
|
||||||
World.spawn({
|
|
||||||
// Actual chat functionality
|
// Actual chat functionality
|
||||||
boot: function () {
|
this.nym = function () { return $("#nym").val(); };
|
||||||
World.updateRoutes(this.subscriptions());
|
this.currentStatus = function () { return $("#status").val(); };
|
||||||
},
|
|
||||||
nym: function () { return $("#nym").val(); },
|
Actor.subscribe(
|
||||||
currentStatus: function () { return $("#status").val(); },
|
function () { return "wake"; },
|
||||||
subscriptions: function () {
|
function () { wsconn.forceclose(); });
|
||||||
return [sub("wake"),
|
|
||||||
sub(["jQuery", "#send_chat", "click", __]),
|
Actor.advertise(
|
||||||
sub(["jQuery", "#nym", "change", __]),
|
function () { return ["broker", 0,
|
||||||
sub(["jQuery", "#status", "change", __]),
|
chatEvent(this.nym(), this.currentStatus(), __, __)]; });
|
||||||
sub(["jQuery", "#wsurl", "change", __]),
|
Actor.observeAdvertisers(
|
||||||
pub(["broker", 0, chatEvent(this.nym(), this.currentStatus(), __, __)]),
|
function () { return ["broker", 0,
|
||||||
sub(["broker", 0, chatEvent(__, __, __, __)], 0, 1)];
|
chatEvent(_$("nym"), _$("status"), __, __)]; },
|
||||||
},
|
{ name: "allStatuses" },
|
||||||
handleEvent: function (e) {
|
function () { updateNymList(this.allStatuses); });
|
||||||
var self = this;
|
|
||||||
switch (e.type) {
|
Actor.subscribe(
|
||||||
case "routes":
|
function () { return ["jQuery", "#send_chat", "click", __]; },
|
||||||
updateNymList(e.gestalt);
|
function () {
|
||||||
break;
|
var inp = $("#chat_input");
|
||||||
case "message":
|
var utterance = inp.val();
|
||||||
if (e.message === "wake") {
|
inp.val("");
|
||||||
wsconn.forceclose();
|
if (utterance) {
|
||||||
return;
|
World.send(["broker", 0, chatEvent(this.nym(),
|
||||||
|
this.currentStatus(),
|
||||||
|
utterance)]);
|
||||||
}
|
}
|
||||||
switch (e.message[0]) {
|
});
|
||||||
case "jQuery":
|
|
||||||
switch (e.message[1]) {
|
Actor.subscribe(
|
||||||
case "#send_chat":
|
function () { return ["jQuery", "#nym", "change", __]; },
|
||||||
var inp = $("#chat_input");
|
function () { this.updateRoutes(); });
|
||||||
var utterance = inp.val();
|
|
||||||
inp.val("");
|
Actor.subscribe(
|
||||||
if (utterance) {
|
function () { return ["jQuery", "#status", "change", __]; },
|
||||||
World.send(["broker", 0, chatEvent(this.nym(),
|
function () { this.updateRoutes(); });
|
||||||
this.currentStatus(),
|
|
||||||
utterance)]);
|
Actor.subscribe(
|
||||||
}
|
function () { return ["jQuery", "#wsurl", "change", __]; },
|
||||||
break;
|
function () {
|
||||||
case "#nym":
|
wsconn.forceclose();
|
||||||
case "#status":
|
wsconn.wsurl = $("#wsurl").val();
|
||||||
World.updateRoutes(this.subscriptions());
|
});
|
||||||
break;
|
|
||||||
case "#wsurl":
|
Actor.subscribe(
|
||||||
wsconn.forceclose();
|
function () { return ["broker", 0, chatEvent(_$("who"), __, _$("what"), __)]; },
|
||||||
wsconn.wsurl = $("#wsurl").val();
|
function (who, what) { outputUtterance(who, what); });
|
||||||
break;
|
}));
|
||||||
default:
|
|
||||||
console.log("Got jquery event from as-yet-unhandled subscription",
|
|
||||||
e.message[2], e.message[3]);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "broker":
|
|
||||||
if (e.message[2][0] === "chatEvent") {
|
|
||||||
outputUtterance(chatEventNym(e.message[2]),
|
|
||||||
chatEventUtterance(e.message[2]));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
G.startStepping();
|
G.startStepping();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>JS Marketplace: DOM Example</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||||
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="../../dist/minimart.js"></script>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>DOM example</h1>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span3 well" id="counter-holder">
|
||||||
|
</div>
|
||||||
|
<div class="span9 well" id="clicker-holder">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12" id="spy-holder">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,49 @@
|
||||||
|
var G;
|
||||||
|
$(document).ready(function () {
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
G = new Minimart.Ground(function () {
|
||||||
|
console.log('starting ground boot');
|
||||||
|
// World.spawn(new Spy("GROUND", true));
|
||||||
|
Minimart.DOM.spawnDOMDriver();
|
||||||
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
|
||||||
|
|
||||||
|
World.spawn({
|
||||||
|
boot: function () {
|
||||||
|
return [pub(["DOM", "#clicker-holder", "clicker",
|
||||||
|
["button", ["span", [["style", "font-style: italic"]], "Click me!"]]]),
|
||||||
|
pub("bump_count"),
|
||||||
|
sub(["jQuery", "button.clicker", "click", __])];
|
||||||
|
},
|
||||||
|
handleEvent: function (e) {
|
||||||
|
if (e.type === "message" && e.message[0] === "jQuery") {
|
||||||
|
World.send("bump_count");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
World.spawn({
|
||||||
|
counter: 0,
|
||||||
|
boot: function () {
|
||||||
|
this.updateState();
|
||||||
|
},
|
||||||
|
updateState: function () {
|
||||||
|
World.updateRoutes([sub("bump_count"),
|
||||||
|
pub(["DOM", "#counter-holder", "counter",
|
||||||
|
["div",
|
||||||
|
["p", "The current count is: ", this.counter]]])]);
|
||||||
|
},
|
||||||
|
handleEvent: function (e) {
|
||||||
|
if (e.type === "message" && e.message === "bump_count") {
|
||||||
|
this.counter++;
|
||||||
|
this.updateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
G.startStepping();
|
||||||
|
});
|
|
@ -0,0 +1,27 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>JS Marketplace: DOM Example</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||||
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="../../dist/minimart.js"></script>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>DOM example</h1>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span3 well" id="counter-holder">
|
||||||
|
</div>
|
||||||
|
<div class="span9 well" id="clicker-holder">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12" id="spy-holder">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,35 @@
|
||||||
|
var G;
|
||||||
|
$(document).ready(function () {
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
G = new Minimart.Ground(function () {
|
||||||
|
console.log('starting ground boot');
|
||||||
|
// World.spawn(new Spy("GROUND", true));
|
||||||
|
Minimart.DOM.spawnDOMDriver();
|
||||||
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
|
||||||
|
|
||||||
|
World.spawn(new Actor(function () {
|
||||||
|
Actor.subscribe(
|
||||||
|
function () { return ["jQuery", "button.clicker", "click", __]; },
|
||||||
|
function () {
|
||||||
|
World.send("bump_count");
|
||||||
|
});
|
||||||
|
|
||||||
|
Actor.advertise(
|
||||||
|
function () { return "bump_count"; });
|
||||||
|
Actor.advertise(
|
||||||
|
function () {
|
||||||
|
return ["DOM", "#clicker-holder", "clicker",
|
||||||
|
["button", ["span", [["style", "font-style: italic"]], "Click me!"]]];
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
World.spawn(new Minimart.Worker("worker.js"));
|
||||||
|
});
|
||||||
|
G.startStepping();
|
||||||
|
});
|
|
@ -0,0 +1,31 @@
|
||||||
|
importScripts("../../dist/minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
new Minimart.WorkerGround(function () {
|
||||||
|
console.log('starting worker boot');
|
||||||
|
|
||||||
|
World.spawn(new Actor(function () {
|
||||||
|
this.counter = 0;
|
||||||
|
|
||||||
|
Actor.subscribe(
|
||||||
|
function () { return "bump_count"; },
|
||||||
|
{ metaLevel: 1},
|
||||||
|
function () {
|
||||||
|
this.counter++;
|
||||||
|
this.updateRoutes();
|
||||||
|
});
|
||||||
|
|
||||||
|
Actor.advertise(
|
||||||
|
function () {
|
||||||
|
return ["DOM", "#counter-holder", "counter",
|
||||||
|
["div",
|
||||||
|
["p", "The current count is: ", this.counter]]];
|
||||||
|
},
|
||||||
|
{ metaLevel: 1});
|
||||||
|
}));
|
||||||
|
}).startStepping();
|
|
@ -3,24 +3,10 @@
|
||||||
<head>
|
<head>
|
||||||
<title>JS Marketplace: DOM Example</title>
|
<title>JS Marketplace: DOM Example</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
|
|
||||||
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
|
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
|
||||||
<link href="style.css" rel="stylesheet">
|
|
||||||
<!-- <script src="../../third-party/bootstrap/js/bootstrap.min.js"></script> -->
|
|
||||||
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="../../dist/minimart.js"></script>
|
||||||
<script src="../../route.js"></script>
|
|
||||||
<script src="../../marketplace.js"></script>
|
|
||||||
<script src="../../spy.js"></script>
|
|
||||||
<script src="../../dom-driver.js"></script>
|
|
||||||
|
|
||||||
<script src="../../routing-table-widget.js"></script>
|
|
||||||
<link href="../../routing-table-widget.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script src="../../jquery-driver.js"></script>
|
|
||||||
<script src="../../wake-detector.js"></script>
|
|
||||||
<script src="../../websocket-driver.js"></script>
|
|
||||||
<script src="index.js"></script>
|
<script src="index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,40 +1,51 @@
|
||||||
var G;
|
var G;
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
G = new Ground(function () {
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
G = new Minimart.Ground(function () {
|
||||||
console.log('starting ground boot');
|
console.log('starting ground boot');
|
||||||
// World.spawn(new Spy("GROUND", true));
|
// World.spawn(new Spy("GROUND", true));
|
||||||
spawnDOMDriver();
|
Minimart.DOM.spawnDOMDriver();
|
||||||
spawnRoutingTableWidget("#spy-holder", "spy");
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
|
||||||
|
|
||||||
World.spawn({
|
World.spawn(new Actor(function () {
|
||||||
handleEvent: function (e) {
|
Actor.subscribe(
|
||||||
if (e.type === "message" && e.message[0] === "jQuery") {
|
function () { return ["jQuery", "button.clicker", "click", __]; },
|
||||||
|
function () {
|
||||||
World.send("bump_count");
|
World.send("bump_count");
|
||||||
}
|
});
|
||||||
}
|
|
||||||
}, [pub(["DOM", "#clicker-holder", "clicker",
|
|
||||||
["button", ["span", {"style": "font-style: italic"}, "Click me!"]]]),
|
|
||||||
pub("bump_count"),
|
|
||||||
sub(["jQuery", "button.clicker", "click", __])]);
|
|
||||||
|
|
||||||
World.spawn({
|
Actor.advertise(
|
||||||
counter: 0,
|
function () { return "bump_count"; });
|
||||||
boot: function () {
|
Actor.advertise(
|
||||||
this.updateState();
|
function () {
|
||||||
},
|
return ["DOM", "#clicker-holder", "clicker",
|
||||||
updateState: function () {
|
["button", ["span", [["style", "font-style: italic"]], "Click me!"]]];
|
||||||
World.updateRoutes([sub("bump_count"),
|
});
|
||||||
pub(["DOM", "#counter-holder", "counter",
|
}));
|
||||||
["div",
|
|
||||||
["p", "The current count is: ", this.counter]]])]);
|
World.spawn(new Actor(function () {
|
||||||
},
|
this.counter = 0;
|
||||||
handleEvent: function (e) {
|
|
||||||
if (e.type === "message" && e.message === "bump_count") {
|
Actor.subscribe(
|
||||||
|
function () { return "bump_count"; },
|
||||||
|
function () {
|
||||||
this.counter++;
|
this.counter++;
|
||||||
this.updateState();
|
this.updateRoutes();
|
||||||
}
|
});
|
||||||
}
|
|
||||||
});
|
Actor.advertise(
|
||||||
|
function () {
|
||||||
|
return ["DOM", "#counter-holder", "counter",
|
||||||
|
["div",
|
||||||
|
["p", "The current count is: ", this.counter]]];
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
G.startStepping();
|
G.startStepping();
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>JS Marketplace: Multi-user DOM Example</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="../../dist/minimart.js"></script>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>DOM example</h1>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span3 well" id="counter-holder">
|
||||||
|
</div>
|
||||||
|
<div class="span9 well" id="clicker-holder">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row-fluid">
|
||||||
|
<div class="span12" id="spy-holder">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,75 @@
|
||||||
|
var G;
|
||||||
|
$(document).ready(function () {
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
G = new Minimart.Ground(function () {
|
||||||
|
var localId = "instance-" + Math.floor(Math.random() * 65536);
|
||||||
|
|
||||||
|
function domWrap(selector, fragmentClass, fragmentSpec) {
|
||||||
|
return ["broker", 0, ["multidom", "DOM", selector, fragmentClass, fragmentSpec]];
|
||||||
|
}
|
||||||
|
|
||||||
|
function jQueryWrap(selector, eventName, eventValue) {
|
||||||
|
var v = eventValue instanceof Event || eventValue instanceof $.Event
|
||||||
|
? Minimart.JQuery.simplifyDOMEvent(eventValue)
|
||||||
|
: eventValue;
|
||||||
|
return ["broker", 0, ["multidom", "jQuery", selector, eventName, v]];
|
||||||
|
}
|
||||||
|
|
||||||
|
var wsconn = new Minimart.WebSocket.WebSocketConnection(
|
||||||
|
"broker", "ws://server.minimart.leastfixedpoint.com:8000/", true);
|
||||||
|
World.spawn(wsconn);
|
||||||
|
|
||||||
|
Minimart.DOM.spawnDOMDriver(domWrap, jQueryWrap); // remote
|
||||||
|
Minimart.DOM.spawnDOMDriver(); // local
|
||||||
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy"); // local
|
||||||
|
|
||||||
|
World.spawn({
|
||||||
|
boot: function () {
|
||||||
|
return [pub(domWrap("#clicker-holder", localId + "-clicker",
|
||||||
|
["button", ["span", [["style", "font-style: italic"]],
|
||||||
|
"Click me! (" + localId + ")"]])),
|
||||||
|
pub("bump_count"),
|
||||||
|
sub(jQueryWrap("button."+localId+"-clicker", "click", __))];
|
||||||
|
},
|
||||||
|
handleEvent: function (e) {
|
||||||
|
console.log(JSON.stringify(e));
|
||||||
|
if (e.type === "message"
|
||||||
|
&& e.message[0] === "broker"
|
||||||
|
&& Array.isArray(e.message[2])
|
||||||
|
&& e.message[2][0] === "multidom"
|
||||||
|
&& e.message[2][1] === "jQuery")
|
||||||
|
{
|
||||||
|
World.send("bump_count");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
World.spawn({
|
||||||
|
counter: 0,
|
||||||
|
boot: function () {
|
||||||
|
this.updateState();
|
||||||
|
},
|
||||||
|
updateState: function () {
|
||||||
|
World.updateRoutes([sub("bump_count"),
|
||||||
|
pub(domWrap("#counter-holder", localId + "-counter",
|
||||||
|
["div",
|
||||||
|
["p", "The current count for ",
|
||||||
|
localId,
|
||||||
|
" is: ",
|
||||||
|
this.counter]]))]);
|
||||||
|
},
|
||||||
|
handleEvent: function (e) {
|
||||||
|
if (e.type === "message" && e.message === "bump_count") {
|
||||||
|
this.counter++;
|
||||||
|
this.updateState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
G.startStepping();
|
||||||
|
});
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>JS Marketplace: Smoketest with Web Workers</title>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
|
<script src="../../dist/minimart.js"></script>
|
||||||
|
<script src="index.js"></script>
|
||||||
|
<style>
|
||||||
|
#gestalt-display {
|
||||||
|
white-space: pre;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Smoketest with Web Workers</h1>
|
||||||
|
<div id="gestalt-display"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,49 @@
|
||||||
|
var G;
|
||||||
|
$(document).ready(function () {
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
G = new Minimart.Ground(function () {
|
||||||
|
console.log('starting ground boot');
|
||||||
|
|
||||||
|
World.spawn({
|
||||||
|
name: 'GestaltDisplay',
|
||||||
|
boot: function () {
|
||||||
|
return [sub(__, 0, 10), pub(__, 0, 10)];
|
||||||
|
},
|
||||||
|
handleEvent: function (e) {
|
||||||
|
if (e.type === "routes") {
|
||||||
|
var gd = document.getElementById('gestalt-display');
|
||||||
|
var t = document.createTextNode(G.world.textProcessTree() + '\n' +
|
||||||
|
e.gestalt.pretty());
|
||||||
|
gd.innerHTML = '';
|
||||||
|
gd.appendChild(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
World.spawn(new Actor(function () {
|
||||||
|
this.counter = 0;
|
||||||
|
this.step = function () {
|
||||||
|
if (this.listenerExists && this.counter < 10) {
|
||||||
|
World.send(["beep", this.counter++]);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Actor.advertise(function () { return ["beep", __]; });
|
||||||
|
Actor.observeSubscribers(
|
||||||
|
function () { return ["beep", __]; },
|
||||||
|
{ presence: "listenerExists" });
|
||||||
|
}));
|
||||||
|
|
||||||
|
World.spawn(new Minimart.Worker("worker.js"));
|
||||||
|
});
|
||||||
|
G.startStepping();
|
||||||
|
});
|
|
@ -0,0 +1,19 @@
|
||||||
|
importScripts("../../dist/minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
new Minimart.WorkerGround(function () {
|
||||||
|
console.log('starting worker boot');
|
||||||
|
World.spawn(new Actor(function () {
|
||||||
|
Actor.subscribe(
|
||||||
|
function () { return ["beep", _$("counter")]; },
|
||||||
|
{ metaLevel: 1 },
|
||||||
|
function (counter) {
|
||||||
|
console.log("beep!", counter);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}).startStepping();
|
|
@ -4,9 +4,7 @@
|
||||||
<title>JS Marketplace: Smoketest</title>
|
<title>JS Marketplace: Smoketest</title>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
<script src="../../route.js"></script>
|
<script src="../../dist/minimart.js"></script>
|
||||||
<script src="../../marketplace.js"></script>
|
|
||||||
<script src="../../spy.js"></script>
|
|
||||||
<script src="index.js"></script>
|
<script src="index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,24 +1,33 @@
|
||||||
var G;
|
var G;
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
G = new Ground(function () {
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
G = new Minimart.Ground(function () {
|
||||||
console.log('starting ground boot');
|
console.log('starting ground boot');
|
||||||
World.spawn(new Spy("GROUND", true));
|
World.spawn(new Minimart.Spy("GROUND", true));
|
||||||
World.spawn({
|
|
||||||
counter: 0,
|
World.spawn(new Actor(function () {
|
||||||
handleEvent: function (e) {},
|
this.counter = 0;
|
||||||
step: function () {
|
this.step = function () {
|
||||||
World.send(["beep", this.counter++]);
|
World.send(["beep", this.counter++]);
|
||||||
return this.counter <= 10;
|
return this.counter <= 10;
|
||||||
}
|
};
|
||||||
}, [pub(["beep", __])]);
|
|
||||||
|
|
||||||
World.spawn({
|
Actor.advertise(function () { return ["beep", __]; });
|
||||||
handleEvent: function (e) {
|
}));
|
||||||
if (e.type === "message" && e.message[0] === "beep") {
|
|
||||||
console.log("beep!", e.message[1]);
|
World.spawn(new Actor(function () {
|
||||||
}
|
Actor.subscribe(
|
||||||
}
|
function () { return ["beep", _$("counter")]; },
|
||||||
}, [sub(["beep", __])]);
|
function (counter) {
|
||||||
|
console.log("beep!", counter);
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
G.startStepping();
|
G.startStepping();
|
||||||
});
|
});
|
||||||
|
|
|
@ -9,16 +9,7 @@
|
||||||
<link href="style.css" rel="stylesheet">
|
<link href="style.css" rel="stylesheet">
|
||||||
|
|
||||||
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
<script src="../../third-party/jquery-2.0.3.min.js"></script>
|
||||||
<script src="../../route.js"></script>
|
<script src="../../dist/minimart.js"></script>
|
||||||
<script src="../../marketplace.js"></script>
|
|
||||||
<script src="../../spy.js"></script>
|
|
||||||
<script src="../../dom-driver.js"></script>
|
|
||||||
|
|
||||||
<script src="../../routing-table-widget.js"></script>
|
|
||||||
<link href="../../routing-table-widget.css" rel="stylesheet">
|
|
||||||
|
|
||||||
<script src="../../jquery-driver.js"></script>
|
|
||||||
<script src="../../wake-detector.js"></script>
|
|
||||||
<script src="index.js"></script>
|
<script src="index.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// GUI
|
// GUI
|
||||||
|
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
function piece(text, pos, lo, hi, cls) {
|
function piece(text, pos, lo, hi, cls) {
|
||||||
return "<span class='"+cls+"'>"+
|
return "<span class='"+cls+"'>"+
|
||||||
((pos >= lo && pos < hi)
|
((pos >= lo && pos < hi)
|
||||||
|
@ -10,53 +17,47 @@ function piece(text, pos, lo, hi, cls) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function spawnGui() {
|
function spawnGui() {
|
||||||
World.spawn({
|
World.spawn(new Actor(function () {
|
||||||
boot: function () {
|
Actor.subscribe(
|
||||||
World.updateRoutes([sub(["jQuery", "#inputRow", "+keypress", __]),
|
function () { return ["jQuery", "#inputRow", "+keypress", _$("event")]; },
|
||||||
sub(["fieldContents", __, __], 0, 1),
|
function (event) {
|
||||||
sub(["highlight", __], 0, 1)]);
|
var keycode = event.keyCode;
|
||||||
},
|
var character = String.fromCharCode(event.charCode);
|
||||||
fieldContentsSpec: route.compileProjection(["fieldContents", _$, _$]),
|
if (keycode === 37 /* left */) {
|
||||||
highlightSpec: route.compileProjection(["highlight", _$]),
|
World.send(["fieldCommand", "cursorLeft"]);
|
||||||
handleEvent: function (e) {
|
} else if (keycode === 39 /* right */) {
|
||||||
switch (e.type) {
|
World.send(["fieldCommand", "cursorRight"]);
|
||||||
case "routes":
|
} else if (keycode === 9 /* tab */) {
|
||||||
var text = "", pos = 0, highlight = false;
|
// ignore
|
||||||
// BUG: escape text!
|
} else if (keycode === 8 /* backspace */) {
|
||||||
var fc = route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true));
|
World.send(["fieldCommand", "backspace"]);
|
||||||
if (fc.length > 0) {
|
} else if (character) {
|
||||||
text = fc[0][0];
|
World.send(["fieldCommand", ["insert", character]]);
|
||||||
pos = fc[0][1];
|
}
|
||||||
}
|
});
|
||||||
var hl = route.matcherKeys(e.gestalt.project(this.highlightSpec, true));
|
|
||||||
if (hl.length > 0) {
|
Actor.observeAdvertisers(
|
||||||
highlight = hl[0][0];
|
function () { return ["fieldContents", _$("text"), _$("pos")]; },
|
||||||
}
|
{ singleton: "field" },
|
||||||
$("#fieldContents")[0].innerHTML = highlight
|
updateDisplay);
|
||||||
? piece(text, pos, 0, highlight[0], "normal") +
|
|
||||||
piece(text, pos, highlight[0], highlight[1], "highlight") +
|
Actor.observeAdvertisers(
|
||||||
piece(text, pos, highlight[1], text.length + 1, "normal")
|
function () { return ["highlight", _$("state")]; },
|
||||||
: piece(text, pos, 0, text.length + 1, "normal");
|
{ singleton: "highlight" },
|
||||||
break;
|
updateDisplay);
|
||||||
case "message":
|
|
||||||
if (e.message[0] === "jQuery") { // it's a keypress event
|
function updateDisplay() {
|
||||||
var keycode = e.message[3].keyCode;
|
// BUG: escape text!
|
||||||
var character = String.fromCharCode(e.message[3].charCode);
|
var text = this.field ? this.field.text : "";
|
||||||
if (keycode === 37 /* left */) {
|
var pos = this.field ? this.field.pos : 0;
|
||||||
World.send(["fieldCommand", "cursorLeft"]);
|
var highlight = this.highlight ? this.highlight.state : false;
|
||||||
} else if (keycode === 39 /* right */) {
|
$("#fieldContents")[0].innerHTML = highlight
|
||||||
World.send(["fieldCommand", "cursorRight"]);
|
? piece(text, pos, 0, highlight[0], "normal") +
|
||||||
} else if (keycode === 9 /* tab */) {
|
piece(text, pos, highlight[0], highlight[1], "highlight") +
|
||||||
// ignore
|
piece(text, pos, highlight[1], text.length + 1, "normal")
|
||||||
} else if (keycode === 8 /* backspace */) {
|
: piece(text, pos, 0, text.length + 1, "normal");
|
||||||
World.send(["fieldCommand", "backspace"]);
|
}
|
||||||
} else if (character) {
|
}));
|
||||||
World.send(["fieldCommand", ["insert", character]]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -64,92 +65,80 @@ function spawnGui() {
|
||||||
|
|
||||||
function spawnModel() {
|
function spawnModel() {
|
||||||
var initialContents = "initial";
|
var initialContents = "initial";
|
||||||
World.spawn({
|
World.spawn(new Actor(function () {
|
||||||
fieldContents: initialContents,
|
this.fieldContents = initialContents;
|
||||||
cursorPos: initialContents.length, /* positions address gaps between characters */
|
this.cursorPos = initialContents.length; /* positions address gaps between characters */
|
||||||
boot: function () {
|
|
||||||
World.updateRoutes(this.subscriptions());
|
Actor.advertise(
|
||||||
},
|
function () { return ["fieldContents", this.fieldContents, this.cursorPos]; });
|
||||||
subscriptions: function () {
|
|
||||||
return [sub(["fieldCommand", __]),
|
Actor.subscribe(
|
||||||
pub(["fieldContents", this.fieldContents, this.cursorPos])];
|
function () { return ["fieldCommand", _$("command")]; },
|
||||||
},
|
function (command) {
|
||||||
handleEvent: function (e) {
|
if (command === "cursorLeft") {
|
||||||
switch (e.type) {
|
this.cursorPos--;
|
||||||
case "message":
|
if (this.cursorPos < 0)
|
||||||
if (e.message[1] === "cursorLeft") {
|
this.cursorPos = 0;
|
||||||
this.cursorPos--;
|
} else if (command === "cursorRight") {
|
||||||
if (this.cursorPos < 0)
|
this.cursorPos++;
|
||||||
this.cursorPos = 0;
|
if (this.cursorPos > this.fieldContents.length)
|
||||||
} else if (e.message[1] === "cursorRight") {
|
this.cursorPos = this.fieldContents.length;
|
||||||
this.cursorPos++;
|
} else if (command === "backspace" && this.cursorPos > 0) {
|
||||||
if (this.cursorPos > this.fieldContents.length)
|
this.fieldContents =
|
||||||
this.cursorPos = this.fieldContents.length;
|
this.fieldContents.substring(0, this.cursorPos - 1) +
|
||||||
} else if (e.message[1] === "backspace" && this.cursorPos > 0) {
|
this.fieldContents.substring(this.cursorPos);
|
||||||
this.fieldContents =
|
this.cursorPos--;
|
||||||
this.fieldContents.substring(0, this.cursorPos - 1) +
|
} else if (command.constructor === Array && command[0] === "insert") {
|
||||||
this.fieldContents.substring(this.cursorPos);
|
var newText = command[1];
|
||||||
this.cursorPos--;
|
this.fieldContents =
|
||||||
} else if (e.message[1].constructor === Array && e.message[1][0] === "insert") {
|
this.fieldContents.substring(0, this.cursorPos) +
|
||||||
var newText = e.message[1][1];
|
newText +
|
||||||
this.fieldContents =
|
this.fieldContents.substring(this.cursorPos);
|
||||||
this.fieldContents.substring(0, this.cursorPos) +
|
this.cursorPos += newText.length;
|
||||||
newText +
|
}
|
||||||
this.fieldContents.substring(this.cursorPos);
|
this.updateRoutes();
|
||||||
this.cursorPos += newText.length;
|
});
|
||||||
}
|
}));
|
||||||
World.updateRoutes(this.subscriptions());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Search engine
|
// Search engine
|
||||||
|
|
||||||
function spawnSearch() {
|
function spawnSearch() {
|
||||||
World.spawn({
|
World.spawn(new Actor(function () {
|
||||||
fieldContents: "",
|
var self = this;
|
||||||
highlight: false,
|
self.fieldContents = "";
|
||||||
fieldContentsSpec: route.compileProjection(["fieldContents", _$, _$]),
|
self.highlight = false;
|
||||||
boot: function () {
|
|
||||||
World.updateRoutes(this.subscriptions());
|
Actor.advertise(
|
||||||
},
|
function () { return ["highlight", self.highlight]; });
|
||||||
subscriptions: function () {
|
|
||||||
return [sub(["jQuery", "#searchBox", "input", __]),
|
Actor.subscribe(
|
||||||
sub(["fieldContents", __, __], 0, 1),
|
function () { return ["jQuery", "#searchBox", "input", _$("event")]; },
|
||||||
pub(["highlight", this.highlight])];
|
search);
|
||||||
},
|
|
||||||
search: function () {
|
Actor.observeAdvertisers(
|
||||||
var searchtext = $("#searchBox")[0].value;
|
function () { return ["fieldContents", _$("text"), _$("pos")]; },
|
||||||
var oldHighlight = this.highlight;
|
{ singleton: "field" },
|
||||||
if (searchtext) {
|
function () {
|
||||||
var pos = this.fieldContents.indexOf(searchtext);
|
self.fieldContents = self.field ? self.field.text : "";
|
||||||
this.highlight = (pos !== -1) && [pos, pos + searchtext.length];
|
search();
|
||||||
} else {
|
});
|
||||||
this.highlight = false;
|
|
||||||
}
|
function search() {
|
||||||
if (JSON.stringify(oldHighlight) !== JSON.stringify(this.highlight)) {
|
var searchtext = $("#searchBox")[0].value;
|
||||||
World.updateRoutes(this.subscriptions());
|
var oldHighlight = self.highlight;
|
||||||
}
|
if (searchtext) {
|
||||||
},
|
var pos = self.fieldContents.indexOf(searchtext);
|
||||||
handleEvent: function (e) {
|
self.highlight = (pos !== -1) && [pos, pos + searchtext.length];
|
||||||
switch (e.type) {
|
} else {
|
||||||
case "routes":
|
self.highlight = false;
|
||||||
var fc = route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true));
|
|
||||||
if (fc.length > 0) {
|
|
||||||
this.fieldContents = fc[0][0];
|
|
||||||
}
|
|
||||||
this.search();
|
|
||||||
break;
|
|
||||||
case "message":
|
|
||||||
if (e.message[0] === "jQuery") { // it's a search box input event
|
|
||||||
this.search();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
if (JSON.stringify(oldHighlight) !== JSON.stringify(self.highlight)) {
|
||||||
|
self.updateRoutes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -157,10 +146,10 @@ function spawnSearch() {
|
||||||
|
|
||||||
var G;
|
var G;
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
G = new Ground(function () {
|
G = new Minimart.Ground(function () {
|
||||||
spawnJQueryDriver();
|
Minimart.JQuery.spawnJQueryDriver();
|
||||||
spawnDOMDriver();
|
Minimart.DOM.spawnDOMDriver();
|
||||||
spawnRoutingTableWidget("#spy-holder", "spy");
|
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
|
||||||
|
|
||||||
spawnGui();
|
spawnGui();
|
||||||
spawnModel();
|
spawnModel();
|
||||||
|
|
|
@ -1,46 +0,0 @@
|
||||||
// JQuery event driver
|
|
||||||
|
|
||||||
function spawnJQueryDriver(baseSelector, metaLevel) {
|
|
||||||
metaLevel = metaLevel || 0;
|
|
||||||
var d = new DemandMatcher(["jQuery", _$, _$, __], metaLevel, {demandSideIsSubscription: true});
|
|
||||||
d.onDemandIncrease = function (captures) {
|
|
||||||
var selector = captures[0];
|
|
||||||
var eventName = captures[1];
|
|
||||||
World.spawn(new JQueryEventRouter(baseSelector, selector, eventName, metaLevel),
|
|
||||||
[pub(["jQuery", selector, eventName, __], metaLevel),
|
|
||||||
pub(["jQuery", selector, eventName, __], metaLevel, 1)]);
|
|
||||||
};
|
|
||||||
World.spawn(d);
|
|
||||||
}
|
|
||||||
|
|
||||||
function JQueryEventRouter(baseSelector, selector, eventName, metaLevel) {
|
|
||||||
var self = this;
|
|
||||||
this.baseSelector = baseSelector || null;
|
|
||||||
this.selector = selector;
|
|
||||||
this.eventName = eventName;
|
|
||||||
this.metaLevel = metaLevel || 0;
|
|
||||||
this.preventDefault = (this.eventName.charAt(0) !== "+");
|
|
||||||
this.handler =
|
|
||||||
World.wrap(function (e) {
|
|
||||||
World.send(["jQuery", self.selector, self.eventName, e], self.metaLevel);
|
|
||||||
if (self.preventDefault) e.preventDefault();
|
|
||||||
return !self.preventDefault;
|
|
||||||
});
|
|
||||||
this.computeNodes().on(this.preventDefault ? this.eventName : this.eventName.substring(1),
|
|
||||||
this.handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
JQueryEventRouter.prototype.handleEvent = function (e) {
|
|
||||||
if (e.type === "routes" && e.gestalt.isEmpty()) {
|
|
||||||
this.computeNodes().off(this.eventName, this.handler);
|
|
||||||
World.exit();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
JQueryEventRouter.prototype.computeNodes = function () {
|
|
||||||
if (this.baseSelector) {
|
|
||||||
return $(this.baseSelector).children(this.selector).addBack(this.selector);
|
|
||||||
} else {
|
|
||||||
return $(this.selector);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "js-marketplace",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Network Calculus in the browser",
|
||||||
|
"homepage": "https://github.com/tonyg/js-marketplace",
|
||||||
|
"main": "src/main.js",
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rm -f dist/*",
|
||||||
|
"build-debug": "browserify src/main.js -d -s Minimart -o dist/minimart.js",
|
||||||
|
"build-min": "browserify src/main.js -s Minimart -o dist/_minimart.js && uglifyjs dist/_minimart.js -o dist/minimart.min.js && rm dist/_minimart.js",
|
||||||
|
"build": "npm run build-debug && npm run build-min",
|
||||||
|
"watch": "watchify src/main.js -d -s Minimart -o dist/minimart.js",
|
||||||
|
"test": "mocha",
|
||||||
|
"prepublish": "npm run build"
|
||||||
|
},
|
||||||
|
"author": "Tony Garnock-Jones <tonyg@ccs.neu.edu>",
|
||||||
|
"devDependencies": {
|
||||||
|
"watchify": "^0.6.1",
|
||||||
|
"uglify-js": "^2.4.12",
|
||||||
|
"browserify": "^3.30.4",
|
||||||
|
"mocha": "^1.17.1",
|
||||||
|
"expect.js": "^0.3.1"
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,49 +0,0 @@
|
||||||
.routing-table li { list-style-type: none; }
|
|
||||||
|
|
||||||
.routing-table .sub .pattern { background-color: lightblue; }
|
|
||||||
.routing-table .sub .level { background-color: lightblue; }
|
|
||||||
.routing-table .pub .pattern { background-color: lightgreen; }
|
|
||||||
.routing-table .pub .level { background-color: lightgreen; }
|
|
||||||
|
|
||||||
.routing-table .route {
|
|
||||||
display: inline-block;
|
|
||||||
height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.routing-table .route .level { font-style: italic; line-height: 1em; padding: 0 0.5em; }
|
|
||||||
.routing-table .route .polarity { display: none; }
|
|
||||||
.routing-table .route .pattern { padding-right: 0.5em; }
|
|
||||||
|
|
||||||
.routing-table .route:before {
|
|
||||||
content: " ";
|
|
||||||
float: left;
|
|
||||||
}
|
|
||||||
|
|
||||||
.routing-table .pub:before {
|
|
||||||
border-right: 0.6em solid lightgreen;
|
|
||||||
border-top: 0.75em solid transparent;
|
|
||||||
border-bottom: 0.75em solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.routing-table .sub:before {
|
|
||||||
border-left: 0.6em solid transparent;
|
|
||||||
border-top: 0.75em solid lightblue;
|
|
||||||
border-bottom: 0.75em solid lightblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.routing-table .route:after {
|
|
||||||
content: " ";
|
|
||||||
float: right;
|
|
||||||
}
|
|
||||||
|
|
||||||
.routing-table .pub:after {
|
|
||||||
border-left: 0.6em solid lightgreen;
|
|
||||||
border-top: 0.75em solid transparent;
|
|
||||||
border-bottom: 0.75em solid transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.routing-table .sub:after {
|
|
||||||
border-right: 0.6em solid transparent;
|
|
||||||
border-top: 0.75em solid lightblue;
|
|
||||||
border-bottom: 0.75em solid lightblue;
|
|
||||||
}
|
|
|
@ -0,0 +1,268 @@
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var util = require("./util.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Route = Minimart.Route;
|
||||||
|
|
||||||
|
Actor._chunks = null;
|
||||||
|
|
||||||
|
function Actor(bootfn) {
|
||||||
|
return {
|
||||||
|
boot: function () {
|
||||||
|
delete this.boot;
|
||||||
|
var oldChunks = Actor._chunks;
|
||||||
|
try {
|
||||||
|
Actor._chunks = [];
|
||||||
|
bootfn.call(this);
|
||||||
|
var initialGestalt = finalizeActor(this, Actor._chunks);
|
||||||
|
Actor._chunks = oldChunks;
|
||||||
|
return [initialGestalt];
|
||||||
|
} catch (e) {
|
||||||
|
Actor._chunks = oldChunks;
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkChunks(type) {
|
||||||
|
if (!Actor._chunks) {
|
||||||
|
throw new Error("Call to Actor."+type+" outside of Actor constructor");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractChunk(type, kind, defaultOptions, args) {
|
||||||
|
var rawProjectionFn = args[0]
|
||||||
|
var options = null;
|
||||||
|
var handler = null;
|
||||||
|
if (typeof rawProjectionFn !== 'function') {
|
||||||
|
throw new Error("Actor."+type+" expects a function producing a pattern as first argument");
|
||||||
|
}
|
||||||
|
for (var i = 1; i < args.length; i++) { // NB: skip the first arg - it's rawProjectionFn
|
||||||
|
if (typeof args[i] === 'function') {
|
||||||
|
if (handler !== null) { throw new Error("Too many handler functions in Actor."+type); }
|
||||||
|
handler = args[i];
|
||||||
|
} else if (typeof args[i] === 'object') {
|
||||||
|
if (options !== null) { throw new Error("Too many options arguments in Actor."+type); }
|
||||||
|
options = args[i];
|
||||||
|
} else {
|
||||||
|
throw new Error("Unrecognised argument in Actor."+type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
options = options || {};
|
||||||
|
for (var k in options) {
|
||||||
|
if (!(k in defaultOptions)) {
|
||||||
|
throw new Error("Unrecognised option '"+k+"' in Actor."+type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var k in defaultOptions) {
|
||||||
|
if (!(k in options)) {
|
||||||
|
options[k] = defaultOptions[k];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type: type,
|
||||||
|
kind: kind,
|
||||||
|
rawProjectionFn: rawProjectionFn,
|
||||||
|
options: options,
|
||||||
|
handler: handler
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function recordChunk(chunk) {
|
||||||
|
Actor._chunks.push(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
|
function chunkExtractor(type, kind, defaultOptions) {
|
||||||
|
return function (/* ... */) {
|
||||||
|
checkChunks(type);
|
||||||
|
recordChunk(extractChunk(type,
|
||||||
|
kind,
|
||||||
|
defaultOptions,
|
||||||
|
Array.prototype.slice.call(arguments)));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var participantDefaults = {
|
||||||
|
metaLevel: 0,
|
||||||
|
when: function () { return true; }
|
||||||
|
};
|
||||||
|
|
||||||
|
var observerDefaults = {
|
||||||
|
metaLevel: 0,
|
||||||
|
level: 0,
|
||||||
|
when: function () { return true; },
|
||||||
|
presence: null,
|
||||||
|
name: null,
|
||||||
|
singleton: null,
|
||||||
|
set: null,
|
||||||
|
added: null,
|
||||||
|
removed: null
|
||||||
|
};
|
||||||
|
|
||||||
|
Actor.advertise = chunkExtractor('advertise', 'participant', participantDefaults);
|
||||||
|
Actor.subscribe = chunkExtractor('subscribe', 'participant', participantDefaults);
|
||||||
|
|
||||||
|
Actor.observeAdvertisers = chunkExtractor('observeAdvertisers', 'observer', observerDefaults);
|
||||||
|
Actor.observeSubscribers = chunkExtractor('observeSubscribers', 'observer', observerDefaults);
|
||||||
|
|
||||||
|
Actor.observeGestalt = function (gestaltFn, eventHandlerFn) {
|
||||||
|
checkChunks('observeGestalt');
|
||||||
|
recordChunk({
|
||||||
|
type: 'observeGestalt',
|
||||||
|
kind: 'raw',
|
||||||
|
gestaltFn: gestaltFn,
|
||||||
|
options: {
|
||||||
|
when: function () { return true; }
|
||||||
|
},
|
||||||
|
eventHandlerFn: eventHandlerFn
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function finalizeActor(behavior, chunks) {
|
||||||
|
var oldHandleEvent = behavior.handleEvent;
|
||||||
|
var projections = {};
|
||||||
|
var compiledProjections = {};
|
||||||
|
var previousObjs = {};
|
||||||
|
|
||||||
|
behavior._computeRoutes = function () {
|
||||||
|
var newRoutes = Route.emptyGestalt;
|
||||||
|
for (var i = 0; i < chunks.length; i++) {
|
||||||
|
var chunk = chunks[i];
|
||||||
|
if (chunk.options.when.call(this)) {
|
||||||
|
switch (chunk.kind) {
|
||||||
|
case 'raw':
|
||||||
|
newRoutes = newRoutes.union(chunk.gestaltFn.call(this));
|
||||||
|
break;
|
||||||
|
case 'participant':
|
||||||
|
var proj = chunk.rawProjectionFn.call(this);
|
||||||
|
projections[i] = proj;
|
||||||
|
var g = Route.simpleGestalt(chunk.type === 'advertise',
|
||||||
|
Route.projectionToPattern(proj),
|
||||||
|
chunk.options.metaLevel,
|
||||||
|
0);
|
||||||
|
newRoutes = newRoutes.union(g);
|
||||||
|
break;
|
||||||
|
case 'observer':
|
||||||
|
var proj = chunk.rawProjectionFn.call(this);
|
||||||
|
projections[i] = proj;
|
||||||
|
compiledProjections[i] = Route.compileProjection(proj);
|
||||||
|
var g = Route.simpleGestalt(chunk.type === 'observeSubscribers',
|
||||||
|
Route.projectionToPattern(proj),
|
||||||
|
chunk.options.metaLevel,
|
||||||
|
chunk.options.level + 1);
|
||||||
|
newRoutes = newRoutes.union(g);
|
||||||
|
if (chunk.options.added || chunk.options.removed) {
|
||||||
|
previousObjs[i] = Route.arrayToSet([]);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unsupported chunk type/kind: "+chunk.type+"/"+chunk.kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newRoutes;
|
||||||
|
};
|
||||||
|
|
||||||
|
behavior.updateRoutes = function () {
|
||||||
|
World.updateRoutes([this._computeRoutes()]);
|
||||||
|
};
|
||||||
|
|
||||||
|
behavior.handleEvent = function (e) {
|
||||||
|
if (oldHandleEvent) { oldHandleEvent.call(this, e); }
|
||||||
|
for (var i = 0; i < chunks.length; i++) {
|
||||||
|
var chunk = chunks[i];
|
||||||
|
switch (chunk.kind) {
|
||||||
|
case 'raw':
|
||||||
|
chunk.eventHandlerFn.call(this, e);
|
||||||
|
break;
|
||||||
|
case 'participant':
|
||||||
|
if (chunk.handler
|
||||||
|
&& (e.type === 'message')
|
||||||
|
&& (e.metaLevel === chunk.options.metaLevel)
|
||||||
|
&& (e.isFeedback === (chunk.type === 'advertise')))
|
||||||
|
{
|
||||||
|
var matchResult = Route.matchPattern(e.message, projections[i]);
|
||||||
|
if (matchResult) {
|
||||||
|
util.kwApply(chunk.handler, this, matchResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'observer':
|
||||||
|
if (e.type === 'routes') {
|
||||||
|
var projectionResult = e.gestalt.project(compiledProjections[i],
|
||||||
|
chunk.type !== 'observeSubscribers',
|
||||||
|
chunk.options.metaLevel,
|
||||||
|
chunk.options.level);
|
||||||
|
|
||||||
|
var isPresent = !Route.is_emptyMatcher(projectionResult);
|
||||||
|
if (chunk.options.presence) {
|
||||||
|
this[chunk.options.presence] = isPresent;
|
||||||
|
}
|
||||||
|
|
||||||
|
var objs = [];
|
||||||
|
if (isPresent) {
|
||||||
|
var keys = Route.matcherKeys(projectionResult);
|
||||||
|
if (keys === null) {
|
||||||
|
console.warn("Wildcard detected while projecting ("
|
||||||
|
+JSON.stringify(chunk.options)+")");
|
||||||
|
} else {
|
||||||
|
objs = Route.matcherKeysToObjects(keys, compiledProjections[i]);
|
||||||
|
if (chunk.options.set) {
|
||||||
|
for (var j = 0; j < objs.length; j++) {
|
||||||
|
objs[j] = chunk.options.set.call(this, objs[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (chunk.options.name) {
|
||||||
|
this[chunk.options.name] = objs;
|
||||||
|
}
|
||||||
|
if (chunk.options.singleton) {
|
||||||
|
this[chunk.options.singleton] = objs.length === 1 ? objs[0] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk.options.added || chunk.options.removed) {
|
||||||
|
var objSet = Route.arrayToSet(objs);
|
||||||
|
|
||||||
|
if (chunk.options.added) {
|
||||||
|
this[chunk.options.added] =
|
||||||
|
Route.setToArray(Route.setSubtract(objSet, previousObjs[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk.options.removed) {
|
||||||
|
this[chunk.options.removed] =
|
||||||
|
Route.setToArray(Route.setSubtract(previousObjs[i], objSet));
|
||||||
|
}
|
||||||
|
|
||||||
|
previousObjs[i] = objSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunk.handler) {
|
||||||
|
chunk.handler.call(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unsupported chunk type/kind: "+chunk.type+"/"+chunk.kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (behavior.boot) { behavior.boot(); }
|
||||||
|
for (var i = 0; i < chunks.length; i++) {
|
||||||
|
var chunk = chunks[i];
|
||||||
|
if (chunk.kind === 'observer') {
|
||||||
|
if (chunk.options.presence) { behavior[chunk.options.presence] = false; }
|
||||||
|
if (chunk.options.name) { behavior[chunk.options.name] = []; }
|
||||||
|
if (chunk.options.singleton) { behavior[chunk.options.singleton] = undefined; }
|
||||||
|
if (chunk.options.added) { behavior[chunk.options.added] = []; }
|
||||||
|
if (chunk.options.removed) { behavior[chunk.options.removed] = []; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return behavior._computeRoutes();
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module.exports.Actor = Actor;
|
|
@ -0,0 +1,33 @@
|
||||||
|
// Wire protocol representation of events and actions
|
||||||
|
|
||||||
|
var Route = require("./route.js");
|
||||||
|
|
||||||
|
function _encode(e) {
|
||||||
|
switch (e.type) {
|
||||||
|
case "routes":
|
||||||
|
return ["routes", e.gestalt.serialize(function (v) { return true; })];
|
||||||
|
case "message":
|
||||||
|
return ["message", e.message, e.metaLevel, e.isFeedback];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _decode(what) {
|
||||||
|
return function (j) {
|
||||||
|
switch (j[0]) {
|
||||||
|
case "routes":
|
||||||
|
return Minimart.updateRoutes([
|
||||||
|
Route.deserializeGestalt(j[1], function (v) { return true; })]);
|
||||||
|
case "message":
|
||||||
|
return Minimart.sendMessage(j[1], j[2], j[3]);
|
||||||
|
default:
|
||||||
|
throw { message: "Invalid JSON-encoded " + what + ": " + JSON.stringify(j) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module.exports.encodeEvent = _encode;
|
||||||
|
module.exports.decodeEvent = _decode("event");
|
||||||
|
module.exports.encodeAction = _encode;
|
||||||
|
module.exports.decodeAction = _decode("action");
|
|
@ -1,31 +1,51 @@
|
||||||
// DOM fragment display driver
|
// DOM fragment display driver
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
function spawnDOMDriver() {
|
function spawnDOMDriver(domWrapFunction, jQueryWrapFunction) {
|
||||||
var d = new DemandMatcher(["DOM", _$, _$, _$]);
|
domWrapFunction = domWrapFunction || defaultWrapFunction;
|
||||||
|
var d = new Minimart.DemandMatcher(domWrapFunction(_$, _$, _$));
|
||||||
d.onDemandIncrease = function (captures) {
|
d.onDemandIncrease = function (captures) {
|
||||||
var selector = captures[0];
|
var selector = captures[0];
|
||||||
var fragmentClass = captures[1];
|
var fragmentClass = captures[1];
|
||||||
var fragmentSpec = captures[2];
|
var fragmentSpec = captures[2];
|
||||||
World.spawn(new DOMFragment(selector, fragmentClass, fragmentSpec),
|
World.spawn(new DOMFragment(selector,
|
||||||
[sub(["DOM", selector, fragmentClass, fragmentSpec]),
|
fragmentClass,
|
||||||
sub(["DOM", selector, fragmentClass, fragmentSpec], 0, 1)]);
|
fragmentSpec,
|
||||||
|
domWrapFunction,
|
||||||
|
jQueryWrapFunction));
|
||||||
};
|
};
|
||||||
World.spawn(d);
|
World.spawn(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
function DOMFragment(selector, fragmentClass, fragmentSpec) {
|
function defaultWrapFunction(selector, fragmentClass, fragmentSpec) {
|
||||||
|
return ["DOM", selector, fragmentClass, fragmentSpec];
|
||||||
|
}
|
||||||
|
|
||||||
|
function DOMFragment(selector, fragmentClass, fragmentSpec, domWrapFunction, jQueryWrapFunction) {
|
||||||
this.selector = selector;
|
this.selector = selector;
|
||||||
this.fragmentClass = fragmentClass;
|
this.fragmentClass = fragmentClass;
|
||||||
this.fragmentSpec = fragmentSpec;
|
this.fragmentSpec = fragmentSpec;
|
||||||
|
this.domWrapFunction = domWrapFunction;
|
||||||
|
this.jQueryWrapFunction = jQueryWrapFunction;
|
||||||
this.nodes = this.buildNodes();
|
this.nodes = this.buildNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
DOMFragment.prototype.boot = function () {
|
DOMFragment.prototype.boot = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
var monitoring = sub(["DOM", self.selector, self.fragmentClass, self.fragmentSpec], 1, 2);
|
var monitoring =
|
||||||
|
sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec), 1, 2);
|
||||||
|
|
||||||
World.spawn(new World(function () {
|
World.spawn(new World(function () {
|
||||||
spawnJQueryDriver(self.selector+" > ."+self.fragmentClass, 1);
|
Minimart.JQuery.spawnJQueryDriver(self.selector+" > ."+self.fragmentClass,
|
||||||
|
1,
|
||||||
|
self.jQueryWrapFunction);
|
||||||
World.spawn({
|
World.spawn({
|
||||||
|
boot: function () { return [monitoring] },
|
||||||
handleEvent: function (e) {
|
handleEvent: function (e) {
|
||||||
if (e.type === "routes") {
|
if (e.type === "routes") {
|
||||||
var level = e.gestalt.getLevel(1, 0); // find participant peers
|
var level = e.gestalt.getLevel(1, 0); // find participant peers
|
||||||
|
@ -34,8 +54,11 @@ DOMFragment.prototype.boot = function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [monitoring]);
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
return [sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec)),
|
||||||
|
sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec), 0, 1)]
|
||||||
};
|
};
|
||||||
|
|
||||||
DOMFragment.prototype.handleEvent = function (e) {
|
DOMFragment.prototype.handleEvent = function (e) {
|
||||||
|
@ -48,30 +71,34 @@ DOMFragment.prototype.handleEvent = function (e) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function isAttributes(x) {
|
||||||
|
return Array.isArray(x) && ((x.length === 0) || Array.isArray(x[0]));
|
||||||
|
}
|
||||||
|
|
||||||
DOMFragment.prototype.interpretSpec = function (spec) {
|
DOMFragment.prototype.interpretSpec = function (spec) {
|
||||||
// Fragment specs are roughly JSON-equivalents of SXML.
|
// Fragment specs are roughly JSON-equivalents of SXML.
|
||||||
// spec ::== ["tag", {"attr": "value", ...}, spec, spec, ...]
|
// spec ::== ["tag", [["attr", "value"], ...], spec, spec, ...]
|
||||||
// | ["tag", spec, spec, ...]
|
// | ["tag", spec, spec, ...]
|
||||||
// | "cdata"
|
// | "cdata"
|
||||||
if (typeof(spec) === "string" || typeof(spec) === "number") {
|
if (typeof(spec) === "string" || typeof(spec) === "number") {
|
||||||
return document.createTextNode(spec);
|
return document.createTextNode(spec);
|
||||||
} else if ($.isArray(spec)) {
|
} else if ($.isArray(spec)) {
|
||||||
var tagName = spec[0];
|
var tagName = spec[0];
|
||||||
var hasAttrs = $.isPlainObject(spec[1]);
|
var hasAttrs = isAttributes(spec[1]);
|
||||||
var attrs = hasAttrs ? spec[1] : {};
|
var attrs = hasAttrs ? spec[1] : {};
|
||||||
var kidIndex = hasAttrs ? 2 : 1;
|
var kidIndex = hasAttrs ? 2 : 1;
|
||||||
|
|
||||||
// Wow! Such XSS! Many hacks! So vulnerability! Amaze!
|
// Wow! Such XSS! Many hacks! So vulnerability! Amaze!
|
||||||
var n = document.createElement(tagName);
|
var n = document.createElement(tagName);
|
||||||
for (var attr in attrs) {
|
for (var i = 0; i < attrs.length; i++) {
|
||||||
if (attrs.hasOwnProperty(attr)) {
|
n.setAttribute(attrs[i][0], attrs[i][1]);
|
||||||
n.setAttribute(attr, attrs[attr]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
for (var i = kidIndex; i < spec.length; i++) {
|
for (var i = kidIndex; i < spec.length; i++) {
|
||||||
n.appendChild(this.interpretSpec(spec[i]));
|
n.appendChild(this.interpretSpec(spec[i]));
|
||||||
}
|
}
|
||||||
return n;
|
return n;
|
||||||
|
} else {
|
||||||
|
throw new Error("Ill-formed DOM specification");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -86,3 +113,8 @@ DOMFragment.prototype.buildNodes = function () {
|
||||||
});
|
});
|
||||||
return nodes;
|
return nodes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module.exports.spawnDOMDriver = spawnDOMDriver;
|
||||||
|
module.exports.defaultWrapFunction = defaultWrapFunction;
|
|
@ -0,0 +1,61 @@
|
||||||
|
/* Ground interface */
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
|
||||||
|
function Ground(bootFn) {
|
||||||
|
var self = this;
|
||||||
|
this.stepperId = null;
|
||||||
|
World.withWorldStack([[this, -1]], function () {
|
||||||
|
self.world = new World(bootFn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ground.prototype.step = function () {
|
||||||
|
var self = this;
|
||||||
|
return World.withWorldStack([[this, -1]], function () {
|
||||||
|
return self.world.step();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Ground.prototype.checkPid = function (pid) {
|
||||||
|
if (pid !== -1) console.error("Weird pid in Ground markPidRunnable", pid);
|
||||||
|
};
|
||||||
|
|
||||||
|
Ground.prototype.markPidRunnable = function (pid) {
|
||||||
|
this.checkPid(pid);
|
||||||
|
this.startStepping();
|
||||||
|
};
|
||||||
|
|
||||||
|
Ground.prototype.startStepping = function () {
|
||||||
|
var self = this;
|
||||||
|
if (this.stepperId) return;
|
||||||
|
if (this.step()) {
|
||||||
|
this.stepperId = setTimeout(function () {
|
||||||
|
self.stepperId = null;
|
||||||
|
self.startStepping();
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ground.prototype.stopStepping = function () {
|
||||||
|
if (this.stepperId) {
|
||||||
|
clearTimeout(this.stepperId);
|
||||||
|
this.stepperId = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ground.prototype.enqueueAction = function (pid, action) {
|
||||||
|
this.checkPid(pid);
|
||||||
|
if (action.type === 'routes') {
|
||||||
|
if (!action.gestalt.isEmpty()) {
|
||||||
|
console.error("You have subscribed to a nonexistent event source.",
|
||||||
|
action.gestalt.pretty());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("You have sent a message into the outer void.", action);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module.exports.Ground = Ground;
|
|
@ -0,0 +1,88 @@
|
||||||
|
// JQuery event driver
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
function spawnJQueryDriver(baseSelector, metaLevel, wrapFunction) {
|
||||||
|
metaLevel = metaLevel || 0;
|
||||||
|
wrapFunction = wrapFunction || defaultWrapFunction;
|
||||||
|
var d = new Minimart.DemandMatcher(wrapFunction(_$, _$, __), metaLevel,
|
||||||
|
{demandSideIsSubscription: true});
|
||||||
|
d.onDemandIncrease = function (captures) {
|
||||||
|
var selector = captures[0];
|
||||||
|
var eventName = captures[1];
|
||||||
|
World.spawn(new JQueryEventRouter(baseSelector,
|
||||||
|
selector,
|
||||||
|
eventName,
|
||||||
|
metaLevel,
|
||||||
|
wrapFunction));
|
||||||
|
};
|
||||||
|
World.spawn(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
function defaultWrapFunction(selector, eventName, eventValue) {
|
||||||
|
return ["jQuery", selector, eventName, eventValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
function JQueryEventRouter(baseSelector, selector, eventName, metaLevel, wrapFunction) {
|
||||||
|
var self = this;
|
||||||
|
this.baseSelector = baseSelector || null;
|
||||||
|
this.selector = selector;
|
||||||
|
this.eventName = eventName;
|
||||||
|
this.metaLevel = metaLevel || 0;
|
||||||
|
this.wrapFunction = wrapFunction || defaultWrapFunction;
|
||||||
|
this.preventDefault = (this.eventName.charAt(0) !== "+");
|
||||||
|
this.handler =
|
||||||
|
World.wrap(function (e) {
|
||||||
|
World.send(self.wrapFunction(self.selector, self.eventName, e), self.metaLevel);
|
||||||
|
if (self.preventDefault) e.preventDefault();
|
||||||
|
return !self.preventDefault;
|
||||||
|
});
|
||||||
|
this.computeNodes().on(this.preventDefault ? this.eventName : this.eventName.substring(1),
|
||||||
|
this.handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
JQueryEventRouter.prototype.boot = function () {
|
||||||
|
return [pub(this.wrapFunction(this.selector, this.eventName, __), this.metaLevel),
|
||||||
|
pub(this.wrapFunction(this.selector, this.eventName, __), this.metaLevel, 1)];
|
||||||
|
};
|
||||||
|
|
||||||
|
JQueryEventRouter.prototype.handleEvent = function (e) {
|
||||||
|
if (e.type === "routes" && e.gestalt.isEmpty()) {
|
||||||
|
this.computeNodes().off(this.eventName, this.handler);
|
||||||
|
World.exit();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
JQueryEventRouter.prototype.computeNodes = function () {
|
||||||
|
if (this.baseSelector) {
|
||||||
|
return $(this.baseSelector).children(this.selector).addBack(this.selector);
|
||||||
|
} else {
|
||||||
|
return $(this.selector);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function simplifyDOMEvent(e) {
|
||||||
|
var keys = [];
|
||||||
|
for (var k in e) {
|
||||||
|
var v = e[k];
|
||||||
|
if (typeof v === 'object') continue;
|
||||||
|
if (typeof v === 'function') continue;
|
||||||
|
keys.push(k);
|
||||||
|
}
|
||||||
|
keys.sort();
|
||||||
|
var simplified = [];
|
||||||
|
for (var i = 0; i < keys.length; i++) {
|
||||||
|
simplified.push([keys[i], e[keys[i]]]);
|
||||||
|
}
|
||||||
|
return simplified;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module.exports.spawnJQueryDriver = spawnJQueryDriver;
|
||||||
|
module.exports.simplifyDOMEvent = simplifyDOMEvent;
|
||||||
|
module.exports.defaultWrapFunction = defaultWrapFunction;
|
|
@ -0,0 +1,16 @@
|
||||||
|
module.exports = require("./minimart.js");
|
||||||
|
|
||||||
|
module.exports.DOM = require("./dom-driver.js");
|
||||||
|
module.exports.JQuery = require("./jquery-driver.js");
|
||||||
|
module.exports.RoutingTableWidget = require("./routing-table-widget.js");
|
||||||
|
module.exports.WebSocket = require("./websocket-driver.js");
|
||||||
|
module.exports.Reflect = require("./reflect.js");
|
||||||
|
|
||||||
|
module.exports.Ground = require("./ground.js").Ground;
|
||||||
|
module.exports.Actor = require("./actor.js").Actor;
|
||||||
|
module.exports.Spy = require("./spy.js").Spy;
|
||||||
|
module.exports.WakeDetector = require("./wake-detector.js").WakeDetector;
|
||||||
|
|
||||||
|
var Worker = require("./worker.js");
|
||||||
|
module.exports.Worker = Worker.Worker;
|
||||||
|
module.exports.WorkerGround = Worker.WorkerGround;
|
|
@ -1,27 +1,30 @@
|
||||||
|
var Route = require("./route.js");
|
||||||
|
var Util = require("./util.js");
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// TODO: trigger-guards as per minimart
|
// TODO: trigger-guards as per minimart
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
/*---------------------------------------------------------------------------*/
|
||||||
/* Events and Actions */
|
/* Events and Actions */
|
||||||
|
|
||||||
var __ = route.__;
|
var __ = Route.__;
|
||||||
var _$ = route._$;
|
var _$ = Route._$;
|
||||||
|
|
||||||
function sub(pattern, metaLevel, level) {
|
function sub(pattern, metaLevel, level) {
|
||||||
return route.simpleGestalt(false, pattern, metaLevel, level);
|
return Route.simpleGestalt(false, pattern, metaLevel, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pub(pattern, metaLevel, level) {
|
function pub(pattern, metaLevel, level) {
|
||||||
return route.simpleGestalt(true, pattern, metaLevel, level);
|
return Route.simpleGestalt(true, pattern, metaLevel, level);
|
||||||
}
|
}
|
||||||
|
|
||||||
function spawn(behavior, initialGestalts) {
|
function spawn(behavior) {
|
||||||
return { type: "spawn",
|
return { type: "spawn", behavior: behavior };
|
||||||
behavior: behavior,
|
|
||||||
initialGestalt: route.gestaltUnion(initialGestalts || []) };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateRoutes(gestalts) {
|
function updateRoutes(gestalts) {
|
||||||
return { type: "routes", gestalt: route.gestaltUnion(gestalts) };
|
return { type: "routes", gestalt: Route.gestaltUnion(gestalts) };
|
||||||
}
|
}
|
||||||
|
|
||||||
function pendingRoutingUpdate(aggregate, affectedSubgestalt, knownTarget) {
|
function pendingRoutingUpdate(aggregate, affectedSubgestalt, knownTarget) {
|
||||||
|
@ -49,11 +52,11 @@ function World(bootFn) {
|
||||||
this.alive = true;
|
this.alive = true;
|
||||||
this.eventQueue = [];
|
this.eventQueue = [];
|
||||||
this.runnablePids = {};
|
this.runnablePids = {};
|
||||||
this.partialGestalt = route.emptyGestalt; // Only gestalt from local processes
|
this.partialGestalt = Route.emptyGestalt; // Only gestalt from local processes
|
||||||
this.fullGestalt = route.emptyGestalt ;; // partialGestalt unioned with downwardGestalt
|
this.fullGestalt = Route.emptyGestalt ;; // partialGestalt unioned with downwardGestalt
|
||||||
this.processTable = {};
|
this.processTable = {};
|
||||||
this.tombstones = {};
|
this.tombstones = {};
|
||||||
this.downwardGestalt = route.emptyGestalt;
|
this.downwardGestalt = Route.emptyGestalt;
|
||||||
this.processActions = [];
|
this.processActions = [];
|
||||||
this.asChild(-1, bootFn, true);
|
this.asChild(-1, bootFn, true);
|
||||||
}
|
}
|
||||||
|
@ -80,8 +83,8 @@ World.updateRoutes = function (gestalts) {
|
||||||
World.current().enqueueAction(World.activePid(), updateRoutes(gestalts));
|
World.current().enqueueAction(World.activePid(), updateRoutes(gestalts));
|
||||||
};
|
};
|
||||||
|
|
||||||
World.spawn = function (behavior, initialGestalts) {
|
World.spawn = function (behavior) {
|
||||||
World.current().enqueueAction(World.activePid(), spawn(behavior, initialGestalts));
|
World.current().enqueueAction(World.activePid(), spawn(behavior));
|
||||||
};
|
};
|
||||||
|
|
||||||
World.exit = function (exn) {
|
World.exit = function (exn) {
|
||||||
|
@ -134,7 +137,7 @@ World.prototype.enqueueAction = function (pid, action) {
|
||||||
World.prototype.isInert = function () {
|
World.prototype.isInert = function () {
|
||||||
return this.eventQueue.length === 0
|
return this.eventQueue.length === 0
|
||||||
&& this.processActions.length === 0
|
&& this.processActions.length === 0
|
||||||
&& route.is_emptySet(this.runnablePids);
|
&& Route.is_emptySet(this.runnablePids);
|
||||||
};
|
};
|
||||||
|
|
||||||
World.prototype.markPidRunnable = function (pid) {
|
World.prototype.markPidRunnable = function (pid) {
|
||||||
|
@ -174,16 +177,16 @@ World.prototype.kill = function (pid, exn) {
|
||||||
console.log("Process exited", pid, exn);
|
console.log("Process exited", pid, exn);
|
||||||
}
|
}
|
||||||
var p = this.processTable[pid];
|
var p = this.processTable[pid];
|
||||||
if (p && p.behavior.trapexit) {
|
|
||||||
this.asChild(pid, function () { return p.behavior.trapexit(exn); });
|
|
||||||
}
|
|
||||||
delete this.processTable[pid];
|
delete this.processTable[pid];
|
||||||
if (p) {
|
if (p) {
|
||||||
|
if (p.behavior.trapexit) {
|
||||||
|
this.asChild(pid, function () { return p.behavior.trapexit(exn); }, true);
|
||||||
|
}
|
||||||
if (exn) {
|
if (exn) {
|
||||||
p.exitReason = exn;
|
p.exitReason = exn;
|
||||||
this.tombstones[pid] = p;
|
this.tombstones[pid] = p;
|
||||||
}
|
}
|
||||||
this.applyAndIssueRoutingUpdate(p.gestalt, route.emptyGestalt);
|
this.applyAndIssueRoutingUpdate(p.gestalt, Route.emptyGestalt);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -221,13 +224,18 @@ World.prototype.performAction = function (pid, action) {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case "spawn":
|
case "spawn":
|
||||||
var pid = World.nextPid++;
|
var pid = World.nextPid++;
|
||||||
var newGestalt = action.initialGestalt.label(pid);
|
var entry = { gestalt: Route.emptyGestalt, behavior: action.behavior };
|
||||||
this.processTable[pid] = { gestalt: newGestalt, behavior: action.behavior };
|
this.processTable[pid] = entry;
|
||||||
if (action.behavior.boot) {
|
if (entry.behavior.boot) {
|
||||||
this.asChild(pid, function () { action.behavior.boot() });
|
var initialGestalts = this.asChild(pid, function () { return entry.behavior.boot() });
|
||||||
|
if (initialGestalts) {
|
||||||
|
entry.gestalt = Route.gestaltUnion(initialGestalts).label(pid);
|
||||||
|
}
|
||||||
this.markPidRunnable(pid);
|
this.markPidRunnable(pid);
|
||||||
}
|
}
|
||||||
this.applyAndIssueRoutingUpdate(route.emptyGestalt, newGestalt, pid);
|
if (!Route.emptyGestalt.equals(entry.gestalt)) {
|
||||||
|
this.applyAndIssueRoutingUpdate(Route.emptyGestalt, entry.gestalt, pid);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "routes":
|
case "routes":
|
||||||
if (pid in this.processTable) {
|
if (pid in this.processTable) {
|
||||||
|
@ -310,6 +318,11 @@ World.prototype.dispatchEvent = function (e) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
World.prototype.boot = function () {
|
||||||
|
// Needed in order for the new World to be marked as "runnable", so
|
||||||
|
// its initial actions get performed.
|
||||||
|
};
|
||||||
|
|
||||||
World.prototype.handleEvent = function (e) {
|
World.prototype.handleEvent = function (e) {
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case "routes":
|
case "routes":
|
||||||
|
@ -343,7 +356,7 @@ World.prototype.processTree = function () {
|
||||||
for (var pid in this.tombstones) {
|
for (var pid in this.tombstones) {
|
||||||
kids.push([pid, this.tombstones[pid]]);
|
kids.push([pid, this.tombstones[pid]]);
|
||||||
}
|
}
|
||||||
kids.sort();
|
kids.sort(function (a, b) { return a[0] - b[0] });
|
||||||
return kids;
|
return kids;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -351,7 +364,7 @@ World.prototype.textProcessTree = function (ownPid) {
|
||||||
var lines = [];
|
var lines = [];
|
||||||
|
|
||||||
function dumpProcess(prefix, pid, p) {
|
function dumpProcess(prefix, pid, p) {
|
||||||
if (p instanceof Array) {
|
if (Array.isArray(p)) {
|
||||||
lines.push(prefix + '--+ ' + pid);
|
lines.push(prefix + '--+ ' + pid);
|
||||||
for (var i = 0; i < p.length; i++) {
|
for (var i = 0; i < p.length; i++) {
|
||||||
dumpProcess(prefix + ' |', p[i][0], p[i][1]);
|
dumpProcess(prefix + ' |', p[i][0], p[i][1]);
|
||||||
|
@ -360,11 +373,16 @@ World.prototype.textProcessTree = function (ownPid) {
|
||||||
} else {
|
} else {
|
||||||
var label = p.behavior.name || p.behavior.constructor.name || '';
|
var label = p.behavior.name || p.behavior.constructor.name || '';
|
||||||
var tombstoneString = p.exitReason ? ' (EXITED: ' + p.exitReason + ') ' : '';
|
var tombstoneString = p.exitReason ? ' (EXITED: ' + p.exitReason + ') ' : '';
|
||||||
lines.push(prefix + '-- ' + pid + ': ' + label +
|
var stringifiedState;
|
||||||
tombstoneString +
|
try {
|
||||||
JSON.stringify(p.behavior, function (k, v) {
|
var rawState = p.behavior.debugState ? p.behavior.debugState() : p.behavior;
|
||||||
return k === 'name' ? undefined : v;
|
stringifiedState = JSON.stringify(rawState, function (k, v) {
|
||||||
}));
|
return (k === 'name') ? undefined : v;
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
stringifiedState = "(cannot convert process state to JSON)";
|
||||||
|
}
|
||||||
|
lines.push(prefix + '-- ' + pid + ': ' + label + tombstoneString + stringifiedState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -386,13 +404,16 @@ World.prototype.clearTombstones = function () {
|
||||||
/* Utilities: matching demand for some service */
|
/* Utilities: matching demand for some service */
|
||||||
|
|
||||||
function DemandMatcher(projection, metaLevel, options) {
|
function DemandMatcher(projection, metaLevel, options) {
|
||||||
options = $.extend({
|
options = Util.extend({
|
||||||
demandLevel: 0,
|
demandLevel: 0,
|
||||||
supplyLevel: 0,
|
supplyLevel: 0,
|
||||||
demandSideIsSubscription: false
|
demandSideIsSubscription: false,
|
||||||
|
supplyProjection: projection
|
||||||
}, options);
|
}, options);
|
||||||
this.pattern = route.projectionToPattern(projection);
|
this.demandPattern = Route.projectionToPattern(projection);
|
||||||
this.projectionSpec = route.compileProjection(projection);
|
this.supplyPattern = Route.projectionToPattern(options.supplyProjection);
|
||||||
|
this.demandProjectionSpec = Route.compileProjection(projection);
|
||||||
|
this.supplyProjectionSpec = Route.compileProjection(options.supplyProjection);
|
||||||
this.metaLevel = metaLevel | 0;
|
this.metaLevel = metaLevel | 0;
|
||||||
this.demandLevel = options.demandLevel;
|
this.demandLevel = options.demandLevel;
|
||||||
this.supplyLevel = options.supplyLevel;
|
this.supplyLevel = options.supplyLevel;
|
||||||
|
@ -407,10 +428,24 @@ function DemandMatcher(projection, metaLevel, options) {
|
||||||
this.currentSupply = {};
|
this.currentSupply = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DemandMatcher.prototype.debugState = function () {
|
||||||
|
return {
|
||||||
|
demandPattern: this.demandPattern,
|
||||||
|
supplyPattern: this.supplyPattern,
|
||||||
|
metaLevel: this.metaLevel,
|
||||||
|
demandLevel: this.demandLevel,
|
||||||
|
supplyLevel: this.supplyLevel,
|
||||||
|
demandSideIsSubscription: this.demandSideIsSubscription
|
||||||
|
|
||||||
|
// , currentDemand: this.currentDemand
|
||||||
|
// , currentSupply: this.currentSupply
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
DemandMatcher.prototype.boot = function () {
|
DemandMatcher.prototype.boot = function () {
|
||||||
var observerLevel = 1 + Math.max(this.demandLevel, this.supplyLevel);
|
var observerLevel = 1 + Math.max(this.demandLevel, this.supplyLevel);
|
||||||
World.updateRoutes([sub(this.pattern, this.metaLevel, observerLevel),
|
return [sub(this.demandPattern, this.metaLevel, observerLevel),
|
||||||
pub(this.pattern, this.metaLevel, observerLevel)]);
|
pub(this.supplyPattern, this.metaLevel, observerLevel)];
|
||||||
};
|
};
|
||||||
|
|
||||||
DemandMatcher.prototype.handleEvent = function (e) {
|
DemandMatcher.prototype.handleEvent = function (e) {
|
||||||
|
@ -420,20 +455,20 @@ DemandMatcher.prototype.handleEvent = function (e) {
|
||||||
};
|
};
|
||||||
|
|
||||||
DemandMatcher.prototype.handleGestalt = function (gestalt) {
|
DemandMatcher.prototype.handleGestalt = function (gestalt) {
|
||||||
var newDemandMatcher = gestalt.project(this.projectionSpec,
|
var newDemandMatcher = gestalt.project(this.demandProjectionSpec,
|
||||||
!this.demandSideIsSubscription,
|
!this.demandSideIsSubscription,
|
||||||
this.metaLevel,
|
this.metaLevel,
|
||||||
this.demandLevel);
|
this.demandLevel);
|
||||||
var newSupplyMatcher = gestalt.project(this.projectionSpec,
|
var newSupplyMatcher = gestalt.project(this.supplyProjectionSpec,
|
||||||
this.demandSideIsSubscription,
|
this.demandSideIsSubscription,
|
||||||
this.metaLevel,
|
this.metaLevel,
|
||||||
this.supplyLevel);
|
this.supplyLevel);
|
||||||
var newDemand = route.arrayToSet(route.matcherKeys(newDemandMatcher));
|
var newDemand = Route.arrayToSet(Route.matcherKeys(newDemandMatcher));
|
||||||
var newSupply = route.arrayToSet(route.matcherKeys(newSupplyMatcher));
|
var newSupply = Route.arrayToSet(Route.matcherKeys(newSupplyMatcher));
|
||||||
var demandDelta = route.setSubtract(newDemand, this.currentDemand);
|
var demandDelta = Route.setSubtract(newDemand, this.currentDemand);
|
||||||
var supplyDelta = route.setSubtract(this.currentSupply, newSupply);
|
var supplyDelta = Route.setSubtract(this.currentSupply, newSupply);
|
||||||
var demandIncr = route.setSubtract(demandDelta, newSupply);
|
var demandIncr = Route.setSubtract(demandDelta, newSupply);
|
||||||
var supplyDecr = route.setIntersect(supplyDelta, newDemand);
|
var supplyDecr = Route.setIntersect(supplyDelta, newDemand);
|
||||||
this.currentDemand = newDemand;
|
this.currentDemand = newDemand;
|
||||||
this.currentSupply = newSupply;
|
this.currentSupply = newSupply;
|
||||||
for (var k in demandIncr) this.onDemandIncrease(demandIncr[k]);
|
for (var k in demandIncr) this.onDemandIncrease(demandIncr[k]);
|
||||||
|
@ -477,59 +512,19 @@ Deduplicator.prototype.expireMessages = function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/*---------------------------------------------------------------------------*/
|
///////////////////////////////////////////////////////////////////////////
|
||||||
/* Ground interface */
|
|
||||||
|
|
||||||
function Ground(bootFn) {
|
module.exports.__ = __;
|
||||||
var self = this;
|
module.exports._$ = _$;
|
||||||
this.stepperId = null;
|
|
||||||
World.withWorldStack([[this, -1]], function () {
|
|
||||||
self.world = new World(bootFn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Ground.prototype.step = function () {
|
module.exports.sub = sub;
|
||||||
var self = this;
|
module.exports.pub = pub;
|
||||||
return World.withWorldStack([[this, -1]], function () {
|
module.exports.spawn = spawn;
|
||||||
return self.world.step();
|
module.exports.updateRoutes = updateRoutes;
|
||||||
});
|
module.exports.sendMessage = sendMessage;
|
||||||
};
|
module.exports.shutdownWorld = shutdownWorld;
|
||||||
|
|
||||||
Ground.prototype.checkPid = function (pid) {
|
module.exports.World = World;
|
||||||
if (pid !== -1) console.error("Weird pid in Ground markPidRunnable", pid);
|
module.exports.DemandMatcher = DemandMatcher;
|
||||||
};
|
module.exports.Deduplicator = Deduplicator;
|
||||||
|
module.exports.Route = Route;
|
||||||
Ground.prototype.markPidRunnable = function (pid) {
|
|
||||||
this.checkPid(pid);
|
|
||||||
this.startStepping();
|
|
||||||
};
|
|
||||||
|
|
||||||
Ground.prototype.startStepping = function () {
|
|
||||||
var self = this;
|
|
||||||
if (this.stepperId) return;
|
|
||||||
if (this.step()) {
|
|
||||||
this.stepperId = setTimeout(function () {
|
|
||||||
self.stepperId = null;
|
|
||||||
self.startStepping();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ground.prototype.stopStepping = function () {
|
|
||||||
if (this.stepperId) {
|
|
||||||
clearTimeout(this.stepperId);
|
|
||||||
this.stepperId = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Ground.prototype.enqueueAction = function (pid, action) {
|
|
||||||
this.checkPid(pid);
|
|
||||||
if (action.type === 'routes') {
|
|
||||||
if (!action.gestalt.isEmpty()) {
|
|
||||||
console.error("You have subscribed to a nonexistent event source.",
|
|
||||||
action.gestalt.pretty());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error("You have sent a message into the outer void.", action);
|
|
||||||
}
|
|
||||||
};
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
// Reflection on function formal parameter lists.
|
||||||
|
// This module is based on Angular's "injector" code,
|
||||||
|
// https://github.com/angular/angular.js/blob/master/src/auto/injector.js,
|
||||||
|
// MIT licensed, and hence:
|
||||||
|
// Copyright (c) 2010-2014 Google, Inc. http://angularjs.org
|
||||||
|
// Copyright (c) 2014 Tony Garnock-Jones
|
||||||
|
|
||||||
|
var FN_ARGS = /^function\s*[^\(]*\(\s*([^\)]*)\)/m;
|
||||||
|
var FN_ARG_SPLIT = /,/;
|
||||||
|
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||||
|
|
||||||
|
function formalParameters(fn) {
|
||||||
|
var result = [];
|
||||||
|
|
||||||
|
var fnText = fn.toString().replace(STRIP_COMMENTS, '');
|
||||||
|
var argDecl = fnText.match(FN_ARGS);
|
||||||
|
var args = argDecl[1].split(FN_ARG_SPLIT);
|
||||||
|
for (var i = 0; i < args.length; i++) {
|
||||||
|
var trimmed = args[i].trim();
|
||||||
|
if (trimmed) { result.push(trimmed); }
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.formalParameters = formalParameters;
|
File diff suppressed because it is too large
Load Diff
|
@ -1,16 +1,24 @@
|
||||||
function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
|
var Minimart = require("./minimart.js");
|
||||||
|
var Route = Minimart.Route;
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
|
||||||
|
function spawnRoutingTableWidget(selector, fragmentClass, domWrap, observationLevel) {
|
||||||
observationLevel = observationLevel || 10;
|
observationLevel = observationLevel || 10;
|
||||||
// ^ arbitrary: should be Infinity, when route.js supports it. TODO
|
// ^ arbitrary: should be Infinity, when route.js supports it. TODO
|
||||||
|
domWrap = domWrap || Minimart.DOM.defaultWrapFunction;
|
||||||
|
|
||||||
World.spawn({
|
World.spawn({
|
||||||
boot: function () { this.updateState(); },
|
boot: function () { this.updateState(); },
|
||||||
|
|
||||||
state: route.emptyGestalt.serialize(),
|
state: Route.emptyGestalt.serialize(),
|
||||||
nextState: route.emptyGestalt.serialize(),
|
nextState: Route.emptyGestalt.serialize(),
|
||||||
timer: false,
|
timer: false,
|
||||||
|
|
||||||
localGestalt: (sub( ["DOM", selector, fragmentClass, __], 0, 2)
|
localGestalt: (sub( domWrap(selector, fragmentClass, __), 0, 2)
|
||||||
.union(pub(["DOM", selector, fragmentClass, __], 0, 2))
|
.union(pub(domWrap(selector, fragmentClass, __), 0, 2))
|
||||||
.telescoped()),
|
.telescoped()),
|
||||||
|
|
||||||
digestGestalt: function (g) {
|
digestGestalt: function (g) {
|
||||||
|
@ -18,11 +26,10 @@ function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
|
||||||
},
|
},
|
||||||
|
|
||||||
updateState: function () {
|
updateState: function () {
|
||||||
var elts = ["ul", {"class": "routing-table"},
|
var elts = ["pre", Route.deserializeGestalt(this.state).pretty()];
|
||||||
["li", ["pre", route.deserializeGestalt(this.state).pretty()]]];
|
|
||||||
World.updateRoutes([sub(__, 0, observationLevel),
|
World.updateRoutes([sub(__, 0, observationLevel),
|
||||||
pub(__, 0, observationLevel),
|
pub(__, 0, observationLevel),
|
||||||
pub(["DOM", selector, fragmentClass, elts])]);
|
pub(domWrap(selector, fragmentClass, elts))]);
|
||||||
},
|
},
|
||||||
|
|
||||||
handleEvent: function (e) {
|
handleEvent: function (e) {
|
||||||
|
@ -45,3 +52,5 @@ function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
module.exports.spawnRoutingTableWidget = spawnRoutingTableWidget;
|
|
@ -1,4 +1,9 @@
|
||||||
// Generic Spy
|
// Generic Spy
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
|
||||||
function Spy(label, useJson, observationLevel) {
|
function Spy(label, useJson, observationLevel) {
|
||||||
this.label = label || "SPY";
|
this.label = label || "SPY";
|
||||||
|
@ -7,7 +12,7 @@ function Spy(label, useJson, observationLevel) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Spy.prototype.boot = function () {
|
Spy.prototype.boot = function () {
|
||||||
World.updateRoutes([sub(__, 0, this.observationLevel), pub(__, 0, this.observationLevel)]);
|
return [sub(__, 0, this.observationLevel), pub(__, 0, this.observationLevel)];
|
||||||
};
|
};
|
||||||
|
|
||||||
Spy.prototype.handleEvent = function (e) {
|
Spy.prototype.handleEvent = function (e) {
|
||||||
|
@ -29,3 +34,5 @@ Spy.prototype.handleEvent = function (e) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.Spy = Spy;
|
|
@ -0,0 +1,23 @@
|
||||||
|
var Reflect = require("./reflect.js");
|
||||||
|
|
||||||
|
module.exports.extend = function (what, _with) {
|
||||||
|
for (var prop in _with) {
|
||||||
|
if (_with.hasOwnProperty(prop)) {
|
||||||
|
what[prop] = _with[prop];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return what;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports.kwApply = function (f, thisArg, args) {
|
||||||
|
var formals = Reflect.formalParameters(f);
|
||||||
|
var actuals = []
|
||||||
|
for (var i = 0; i < formals.length; i++) {
|
||||||
|
var formal = formals[i];
|
||||||
|
if (!(formal in args)) {
|
||||||
|
throw new Error("Function parameter '"+formal+"' not present in args");
|
||||||
|
}
|
||||||
|
actuals.push(args[formal]);
|
||||||
|
}
|
||||||
|
return f.apply(thisArg, actuals);
|
||||||
|
};
|
|
@ -2,6 +2,11 @@
|
||||||
// suspension/sleeping!) has caused periodic activities to be
|
// suspension/sleeping!) has caused periodic activities to be
|
||||||
// interrupted, and warns others about it
|
// interrupted, and warns others about it
|
||||||
// Inspired by http://blog.alexmaccaw.com/javascript-wake-event
|
// Inspired by http://blog.alexmaccaw.com/javascript-wake-event
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
|
||||||
function WakeDetector(period) {
|
function WakeDetector(period) {
|
||||||
this.message = "wake";
|
this.message = "wake";
|
||||||
|
@ -12,8 +17,8 @@ function WakeDetector(period) {
|
||||||
|
|
||||||
WakeDetector.prototype.boot = function () {
|
WakeDetector.prototype.boot = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
World.updateRoutes([pub(this.message)]);
|
|
||||||
this.timerId = setInterval(World.wrap(function () { self.trigger(); }), this.period);
|
this.timerId = setInterval(World.wrap(function () { self.trigger(); }), this.period);
|
||||||
|
return [pub(this.message)];
|
||||||
};
|
};
|
||||||
|
|
||||||
WakeDetector.prototype.handleEvent = function (e) {};
|
WakeDetector.prototype.handleEvent = function (e) {};
|
||||||
|
@ -25,3 +30,5 @@ WakeDetector.prototype.trigger = function () {
|
||||||
}
|
}
|
||||||
this.mostRecentTrigger = now;
|
this.mostRecentTrigger = now;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.WakeDetector = WakeDetector;
|
|
@ -1,3 +1,12 @@
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var Codec = require("./codec.js");
|
||||||
|
var Route = Minimart.Route;
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// WebSocket client driver
|
// WebSocket client driver
|
||||||
|
|
||||||
|
@ -15,11 +24,11 @@ function WebSocketConnection(label, wsurl, shouldReconnect) {
|
||||||
this.wsurl = wsurl;
|
this.wsurl = wsurl;
|
||||||
this.shouldReconnect = shouldReconnect ? true : false;
|
this.shouldReconnect = shouldReconnect ? true : false;
|
||||||
this.reconnectDelay = DEFAULT_RECONNECT_DELAY;
|
this.reconnectDelay = DEFAULT_RECONNECT_DELAY;
|
||||||
this.localGestalt = route.emptyGestalt;
|
this.localGestalt = Route.emptyGestalt;
|
||||||
this.peerGestalt = route.emptyGestalt;
|
this.peerGestalt = Route.emptyGestalt;
|
||||||
this.prevLocalRoutesMessage = null;
|
this.prevLocalRoutesMessage = null;
|
||||||
this.prevPeerRoutesMessage = null;
|
this.prevPeerRoutesMessage = null;
|
||||||
this.deduplicator = new Deduplicator();
|
this.deduplicator = new Minimart.Deduplicator();
|
||||||
this.connectionCount = 0;
|
this.connectionCount = 0;
|
||||||
|
|
||||||
this.activityTimestamp = 0;
|
this.activityTimestamp = 0;
|
||||||
|
@ -29,6 +38,20 @@ function WebSocketConnection(label, wsurl, shouldReconnect) {
|
||||||
this.pingTimer = null;
|
this.pingTimer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebSocketConnection.prototype.debugState = function () {
|
||||||
|
return {
|
||||||
|
label: this.label,
|
||||||
|
sendsAttempted: this.sendsAttempted,
|
||||||
|
sendsTransmitted: this.sendsTransmitted,
|
||||||
|
receiveCount: this.receiveCount,
|
||||||
|
wsurl: this.wsurl,
|
||||||
|
shouldReconnect: this.shouldReconnect,
|
||||||
|
reconnectDelay: this.reconnectDelay,
|
||||||
|
connectionCount: this.connectionCount,
|
||||||
|
activityTimestamp: this.activityTimestamp
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
WebSocketConnection.prototype.clearHeartbeatTimers = function () {
|
WebSocketConnection.prototype.clearHeartbeatTimers = function () {
|
||||||
if (this.idleTimer) { clearTimeout(this.idleTimer); this.idleTimer = null; }
|
if (this.idleTimer) { clearTimeout(this.idleTimer); this.idleTimer = null; }
|
||||||
if (this.pingTimer) { clearTimeout(this.pingTimer); this.pingTimer = null; }
|
if (this.pingTimer) { clearTimeout(this.pingTimer); this.pingTimer = null; }
|
||||||
|
@ -58,8 +81,8 @@ WebSocketConnection.prototype.relayGestalt = function () {
|
||||||
WebSocketConnection.prototype.aggregateGestalt = function () {
|
WebSocketConnection.prototype.aggregateGestalt = function () {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.peerGestalt.transform(function (m, metaLevel) {
|
return this.peerGestalt.transform(function (m, metaLevel) {
|
||||||
return route.compilePattern(true,
|
return Route.compilePattern(true,
|
||||||
[self.label, metaLevel, route.embeddedMatcher(m)]);
|
[self.label, metaLevel, Route.embeddedMatcher(m)]);
|
||||||
}).union(this.relayGestalt());
|
}).union(this.relayGestalt());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -88,7 +111,8 @@ WebSocketConnection.prototype.safeSend = function (m) {
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketConnection.prototype.sendLocalRoutes = function () {
|
WebSocketConnection.prototype.sendLocalRoutes = function () {
|
||||||
var newLocalRoutesMessage = JSON.stringify(encodeEvent(updateRoutes([this.localGestalt])));
|
var newLocalRoutesMessage =
|
||||||
|
JSON.stringify(Codec.encodeEvent(Minimart.updateRoutes([this.localGestalt])));
|
||||||
if (this.prevLocalRoutesMessage !== newLocalRoutesMessage) {
|
if (this.prevLocalRoutesMessage !== newLocalRoutesMessage) {
|
||||||
this.prevLocalRoutesMessage = newLocalRoutesMessage;
|
this.prevLocalRoutesMessage = newLocalRoutesMessage;
|
||||||
this.safeSend(newLocalRoutesMessage);
|
this.safeSend(newLocalRoutesMessage);
|
||||||
|
@ -96,14 +120,14 @@ WebSocketConnection.prototype.sendLocalRoutes = function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
WebSocketConnection.prototype.collectMatchers = function (getAdvertisements, level, g) {
|
WebSocketConnection.prototype.collectMatchers = function (getAdvertisements, level, g) {
|
||||||
var extractMetaLevels = route.compileProjection([this.label, _$, __]);
|
var extractMetaLevels = Route.compileProjection([this.label, _$, __]);
|
||||||
var mls = route.matcherKeys(g.project(extractMetaLevels, getAdvertisements, 0, level));
|
var mls = Route.matcherKeys(g.project(extractMetaLevels, getAdvertisements, 0, level));
|
||||||
for (var i = 0; i < mls.length; i++) {
|
for (var i = 0; i < mls.length; i++) {
|
||||||
var metaLevel = mls[i][0]; // only one capture in the projection
|
var metaLevel = mls[i][0]; // only one capture in the projection
|
||||||
var extractMatchers = route.compileProjection([this.label, metaLevel, _$]);
|
var extractMatchers = Route.compileProjection([this.label, metaLevel, _$]);
|
||||||
var m = g.project(extractMatchers, getAdvertisements, 0, level);
|
var m = g.project(extractMatchers, getAdvertisements, 0, level);
|
||||||
this.localGestalt = this.localGestalt.union(route.simpleGestalt(getAdvertisements,
|
this.localGestalt = this.localGestalt.union(Route.simpleGestalt(getAdvertisements,
|
||||||
route.embeddedMatcher(m),
|
Route.embeddedMatcher(m),
|
||||||
metaLevel,
|
metaLevel,
|
||||||
level));
|
level));
|
||||||
}
|
}
|
||||||
|
@ -115,9 +139,9 @@ WebSocketConnection.prototype.handleEvent = function (e) {
|
||||||
case "routes":
|
case "routes":
|
||||||
// TODO: GROSS - erasing by pid!
|
// TODO: GROSS - erasing by pid!
|
||||||
var nLevels = e.gestalt.levelCount(0);
|
var nLevels = e.gestalt.levelCount(0);
|
||||||
var relayGestalt = route.fullGestalt(1, nLevels).label(World.activePid());
|
var relayGestalt = Route.fullGestalt(1, nLevels).label(World.activePid());
|
||||||
var g = e.gestalt.erasePath(relayGestalt);
|
var g = e.gestalt.erasePath(relayGestalt);
|
||||||
this.localGestalt = route.emptyGestalt;
|
this.localGestalt = Route.emptyGestalt;
|
||||||
for (var level = 0; level < nLevels; level++) {
|
for (var level = 0; level < nLevels; level++) {
|
||||||
this.collectMatchers(false, level, g);
|
this.collectMatchers(false, level, g);
|
||||||
this.collectMatchers(true, level, g);
|
this.collectMatchers(true, level, g);
|
||||||
|
@ -129,7 +153,8 @@ WebSocketConnection.prototype.handleEvent = function (e) {
|
||||||
var m = e.message;
|
var m = e.message;
|
||||||
if (m.length && m.length === 3 && m[0] === this.label)
|
if (m.length && m.length === 3 && m[0] === this.label)
|
||||||
{
|
{
|
||||||
var encoded = JSON.stringify(encodeEvent(sendMessage(m[2], m[1], e.isFeedback)));
|
var encoded = JSON.stringify(Codec.encodeEvent(
|
||||||
|
Minimart.sendMessage(m[2], m[1], e.isFeedback)));
|
||||||
if (this.deduplicator.accept(encoded)) {
|
if (this.deduplicator.accept(encoded)) {
|
||||||
this.safeSend(encoded);
|
this.safeSend(encoded);
|
||||||
}
|
}
|
||||||
|
@ -182,7 +207,7 @@ WebSocketConnection.prototype.onmessage = function (wse) {
|
||||||
return; // recordActivity already took care of our timers
|
return; // recordActivity already took care of our timers
|
||||||
}
|
}
|
||||||
|
|
||||||
var e = decodeAction(j);
|
var e = Codec.decodeAction(j);
|
||||||
switch (e.type) {
|
switch (e.type) {
|
||||||
case "routes":
|
case "routes":
|
||||||
if (this.prevPeerRoutesMessage !== wse.data) {
|
if (this.prevPeerRoutesMessage !== wse.data) {
|
||||||
|
@ -218,24 +243,5 @@ WebSocketConnection.prototype.onclose = function (e) {
|
||||||
};
|
};
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Wire protocol representation of events and actions
|
|
||||||
|
|
||||||
function encodeEvent(e) {
|
module.exports.WebSocketConnection = WebSocketConnection;
|
||||||
switch (e.type) {
|
|
||||||
case "routes":
|
|
||||||
return ["routes", e.gestalt.serialize(function (v) { return true; })];
|
|
||||||
case "message":
|
|
||||||
return ["message", e.message, e.metaLevel, e.isFeedback];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeAction(j) {
|
|
||||||
switch (j[0]) {
|
|
||||||
case "routes":
|
|
||||||
return updateRoutes([route.deserializeGestalt(j[1], function (v) { return true; })]);
|
|
||||||
case "message":
|
|
||||||
return sendMessage(j[1], j[2], j[3]);
|
|
||||||
default:
|
|
||||||
throw { message: "Invalid JSON-encoded action: " + JSON.stringify(j) };
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/* Web Worker interface */
|
||||||
|
var Ground = require("./ground.js").Ground;
|
||||||
|
var Util = require("./util.js");
|
||||||
|
var Codec = require("./codec.js");
|
||||||
|
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
|
||||||
|
var BuiltinWorker = typeof window !== 'undefined' && window.Worker;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function Worker(scriptUrl) {
|
||||||
|
this.scriptUrl = scriptUrl;
|
||||||
|
this.w = new BuiltinWorker(scriptUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
Worker.prototype.boot = function () {
|
||||||
|
this.w.onmessage = World.wrap(function (e) {
|
||||||
|
console.log("Received from worker", JSON.stringify(e.data));
|
||||||
|
World.current().enqueueAction(World.activePid(), Codec.decodeAction(e.data));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Worker.prototype.handleEvent = function (e) {
|
||||||
|
console.log("Sending to worker", JSON.stringify(Codec.encodeEvent(e)));
|
||||||
|
this.w.postMessage(Codec.encodeEvent(e));
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function WorkerGround(bootFn) {
|
||||||
|
var self = this;
|
||||||
|
Ground.call(this, bootFn);
|
||||||
|
onmessage = function (e) {
|
||||||
|
console.log("Received from main page", JSON.stringify(e.data));
|
||||||
|
self.world.handleEvent(Codec.decodeEvent(e.data));
|
||||||
|
self.startStepping();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkerGround.prototype = Util.extend({}, Ground.prototype);
|
||||||
|
|
||||||
|
WorkerGround.prototype.enqueueAction = function (pid, action) {
|
||||||
|
console.log("Sending to main page", JSON.stringify(Codec.encodeAction(action)));
|
||||||
|
postMessage(Codec.encodeAction(action));
|
||||||
|
console.log("Sent to main page");
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module.exports.Worker = Worker;
|
||||||
|
module.exports.WorkerGround = WorkerGround;
|
|
@ -0,0 +1,99 @@
|
||||||
|
var expect = require('expect.js');
|
||||||
|
|
||||||
|
var Minimart = require('../src/main.js');
|
||||||
|
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Actor = Minimart.Actor;
|
||||||
|
var sub = Minimart.sub;
|
||||||
|
var pub = Minimart.pub;
|
||||||
|
var __ = Minimart.__;
|
||||||
|
var _$ = Minimart._$;
|
||||||
|
|
||||||
|
function configurationTrace(bootConfiguration) {
|
||||||
|
var eventLog = [];
|
||||||
|
function trace(item) {
|
||||||
|
eventLog.push(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
var G = new Minimart.Ground(function () {
|
||||||
|
bootConfiguration(trace);
|
||||||
|
});
|
||||||
|
|
||||||
|
while (G.step()) {
|
||||||
|
// do nothing until G becomes inert
|
||||||
|
}
|
||||||
|
|
||||||
|
return eventLog;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkTrace(bootConfiguration, expected) {
|
||||||
|
expect(configurationTrace(bootConfiguration)).to.eql(expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("configurationTrace", function() {
|
||||||
|
describe("with an inert configuration", function () {
|
||||||
|
it("should yield an empty trace", function () {
|
||||||
|
checkTrace(function (trace) {}, []);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("with a single trace in an inert configuration", function () {
|
||||||
|
it("should yield that trace", function () {
|
||||||
|
checkTrace(function (trace) { trace(1) }, [1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("with some traced communication", function () {
|
||||||
|
it("should yield an appropriate trace", function () {
|
||||||
|
checkTrace(function (trace) {
|
||||||
|
World.spawn({
|
||||||
|
boot: function () { return [sub(__)] },
|
||||||
|
handleEvent: function (e) {
|
||||||
|
trace(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
World.send(123);
|
||||||
|
World.send(234);
|
||||||
|
}, [Minimart.updateRoutes([]),
|
||||||
|
Minimart.sendMessage(123),
|
||||||
|
Minimart.sendMessage(234)]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("nonempty initial routes", function () {
|
||||||
|
it("should be immediately signalled to the process", function () {
|
||||||
|
// Specifically, no Minimart.updateRoutes([]) first.
|
||||||
|
checkTrace(function (trace) {
|
||||||
|
World.spawn({
|
||||||
|
boot: function () { return [pub(["A", __])] },
|
||||||
|
handleEvent: function (e) {
|
||||||
|
World.spawn({
|
||||||
|
boot: function () { return [sub(["A", __], 0, 1)] },
|
||||||
|
handleEvent: trace
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [Minimart.updateRoutes([pub(["A", __]).label(1)])]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("actor with nonempty initial routes", function () {
|
||||||
|
it("shouldn't see initial empty conversational context", function () {
|
||||||
|
checkTrace(function (trace) {
|
||||||
|
World.spawn({
|
||||||
|
boot: function () { return [pub(["A", __])] },
|
||||||
|
handleEvent: function (e) {
|
||||||
|
World.spawn(new Actor(function () {
|
||||||
|
Actor.observeAdvertisers(
|
||||||
|
function () { return ["A", __] },
|
||||||
|
{ presence: "isPresent" },
|
||||||
|
function () {
|
||||||
|
trace(["isPresent", this.isPresent]);
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [["isPresent", true]]);
|
||||||
|
});
|
||||||
|
});
|
|
@ -0,0 +1,544 @@
|
||||||
|
var expect = require('expect.js');
|
||||||
|
var util = require('util');
|
||||||
|
var r = require("../src/route.js");
|
||||||
|
|
||||||
|
function checkPrettyMatcher(m, expected) {
|
||||||
|
expect(r.prettyMatcher(m)).to.equal(expected.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkPrettyGestalt(g, expected) {
|
||||||
|
expect(g.pretty()).to.equal(expected.join('\n') + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("basic pattern compilation", function () {
|
||||||
|
var sAny = r.arrayToSet(['mAny']);
|
||||||
|
var sAAny = r.arrayToSet(['mAAny']);
|
||||||
|
var mAny = r.compilePattern(sAny, r.__);
|
||||||
|
var mAAny = r.compilePattern(sAAny, ['A', r.__]);
|
||||||
|
|
||||||
|
it("should print as expected", function () {
|
||||||
|
checkPrettyMatcher(mAny, [' ★ >{["mAny"]}']);
|
||||||
|
checkPrettyMatcher(mAAny, [' < "A" ★ > >{["mAAny"]}']);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("of wildcard", function () {
|
||||||
|
it("should match anything", function () {
|
||||||
|
expect(r.matchValue(mAny, 'hi')).to.eql(sAny);
|
||||||
|
expect(r.matchValue(mAny, ['A', 'hi'])).to.eql(sAny);
|
||||||
|
expect(r.matchValue(mAny, ['B', 'hi'])).to.eql(sAny);
|
||||||
|
expect(r.matchValue(mAny, ['A', [['hi']]])).to.eql(sAny);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("of A followed by wildcard", function () {
|
||||||
|
it("should match A followed by anything", function () {
|
||||||
|
expect(r.matchValue(mAAny, 'hi')).to.be(null);
|
||||||
|
expect(r.matchValue(mAAny, ['A', 'hi'])).to.eql(sAAny);
|
||||||
|
expect(r.matchValue(mAAny, ['B', 'hi'])).to.be(null);
|
||||||
|
expect(r.matchValue(mAAny, ['A', [['hi']]])).to.eql(sAAny);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should observe basic (in)equivalences", function () {
|
||||||
|
expect(r.matcherEquals(mAny, mAAny)).to.be(false);
|
||||||
|
expect(r.matcherEquals(mAny, mAny)).to.be(true);
|
||||||
|
expect(r.matcherEquals(mAAny, mAAny)).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("unions", function () {
|
||||||
|
it("should collapse common prefix wildcard", function () {
|
||||||
|
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
||||||
|
[' < ★ "A" > >{["A"]}',
|
||||||
|
' "B" > >{["B"]}']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should unroll wildcard unioned with nonwildcard", function () {
|
||||||
|
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['W']), r.__)),
|
||||||
|
[' ★ >{["W"]}',
|
||||||
|
' < ★ "A" ★...> >{["W"]}',
|
||||||
|
' > >{["A","W"]}',
|
||||||
|
' ★...> >{["W"]}',
|
||||||
|
' > >{["W"]}',
|
||||||
|
' > >{["W"]}']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should properly multiply out", function () {
|
||||||
|
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
||||||
|
[' < 1 2 > >{["A"]}',
|
||||||
|
' 3 > >{["C"]}',
|
||||||
|
' 3 2 > >{["A"]}',
|
||||||
|
' 4 > >{["B"]}',
|
||||||
|
' ★ 2 > >{["A"]}']);
|
||||||
|
|
||||||
|
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
||||||
|
[' < 1 3 > >{["C"]}',
|
||||||
|
' 3 4 > >{["B"]}']);
|
||||||
|
|
||||||
|
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3])),
|
||||||
|
[' < 1 2 > >{["A"]}',
|
||||||
|
' 3 > >{["C"]}',
|
||||||
|
' ★ 2 > >{["A"]}']);
|
||||||
|
|
||||||
|
checkPrettyMatcher(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
||||||
|
[' < 3 2 > >{["A"]}',
|
||||||
|
' 4 > >{["B"]}',
|
||||||
|
' ★ 2 > >{["A"]}']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should correctly construct intermediate values", function () {
|
||||||
|
var MU = r.emptyMatcher;
|
||||||
|
MU = r.union(MU, r.compilePattern(r.arrayToSet(['A']), [r.__, 2]));
|
||||||
|
checkPrettyMatcher(MU, [' < ★ 2 > >{["A"]}']);
|
||||||
|
MU = r.union(MU, r.compilePattern(r.arrayToSet(['C']), [1, 3]));
|
||||||
|
checkPrettyMatcher(MU, [' < 1 2 > >{["A"]}',
|
||||||
|
' 3 > >{["C"]}',
|
||||||
|
' ★ 2 > >{["A"]}']);
|
||||||
|
MU = r.union(MU, r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
checkPrettyMatcher(MU, [' < 1 2 > >{["A"]}',
|
||||||
|
' 3 > >{["C"]}',
|
||||||
|
' 3 2 > >{["A"]}',
|
||||||
|
' 4 > >{["B"]}',
|
||||||
|
' ★ 2 > >{["A"]}']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should handle identical patterns with different pids", function () {
|
||||||
|
var m = r.union(r.compilePattern(r.arrayToSet('B'), [2]),
|
||||||
|
r.compilePattern(r.arrayToSet('C'), [3]));
|
||||||
|
checkPrettyMatcher(m, [' < 2 > >{["B"]}',
|
||||||
|
' 3 > >{["C"]}']);
|
||||||
|
m = r.union(r.compilePattern(r.arrayToSet('A'), [2]), m);
|
||||||
|
checkPrettyMatcher(m, [' < 2 > >{["A","B"]}',
|
||||||
|
' 3 > >{["C"]}']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("projections", function () {
|
||||||
|
describe("with picky structure", function () {
|
||||||
|
var proj = r.compileProjection(r._$("v", [[r.__]]));
|
||||||
|
|
||||||
|
it("should include things that match as well as wildcards", function () {
|
||||||
|
checkPrettyMatcher(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), r.__),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [['b']])),
|
||||||
|
proj),
|
||||||
|
[' < < "b" > > >{["B","A"]}',
|
||||||
|
' ★ > > >{["A"]}']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should exclude things that lack the required structure", function () {
|
||||||
|
checkPrettyMatcher(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), r.__),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), ['b'])),
|
||||||
|
proj),
|
||||||
|
[' < < ★ > > >{["A"]}']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("simple positional", function () {
|
||||||
|
var proj = r.compileProjection([r._$, r._$]);
|
||||||
|
|
||||||
|
it("should collapse common prefixes", function () {
|
||||||
|
checkPrettyMatcher(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
||||||
|
proj),
|
||||||
|
[' 1 2 >{["A"]}',
|
||||||
|
' 3 >{["C"]}',
|
||||||
|
' 3 4 >{["B"]}']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should yield a correct set of results", function () {
|
||||||
|
expect(r.matcherKeys(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
||||||
|
proj))).to.eql([[1, 2], [1, 3], [3, 4]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("erasePath after union", function () {
|
||||||
|
var R1 = r.compilePattern(r.arrayToSet(['A']), [r.__, "B"]);
|
||||||
|
var R2 = r.compilePattern(r.arrayToSet(['B']), ["A", r.__]);
|
||||||
|
var R12 = r.union(R1, R2);
|
||||||
|
|
||||||
|
it("should have sane preconditions", function () { // Am I doing this right?
|
||||||
|
checkPrettyMatcher(R1, [' < ★ "B" > >{["A"]}']);
|
||||||
|
checkPrettyMatcher(R2, [' < "A" ★ > >{["B"]}']);
|
||||||
|
checkPrettyMatcher(R12, [' < "A" "B" > >{["B","A"]}',
|
||||||
|
' ★ > >{["B"]}',
|
||||||
|
' ★ "B" > >{["A"]}']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should yield the remaining ingredients of the union", function () {
|
||||||
|
expect(r.matcherEquals(r.erasePath(R12, R1), R2)).to.be(true);
|
||||||
|
expect(r.matcherEquals(r.erasePath(R12, R2), R1)).to.be(true);
|
||||||
|
expect(r.matcherEquals(r.erasePath(R12, R1), R1)).to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("basic gestalt construction", function () {
|
||||||
|
it("should print as expected", function () {
|
||||||
|
checkPrettyGestalt(r.simpleGestalt(false, "A", 0, 0),
|
||||||
|
['GESTALT metalevel 0 level 0:',
|
||||||
|
' - subs: "A" >{true}']);
|
||||||
|
checkPrettyGestalt(r.simpleGestalt(true, "B", 0, 0),
|
||||||
|
['GESTALT metalevel 0 level 0:',
|
||||||
|
' - advs: "B" >{true}']);
|
||||||
|
checkPrettyGestalt(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0)),
|
||||||
|
['GESTALT metalevel 0 level 0:',
|
||||||
|
' - subs: "A" >{true}',
|
||||||
|
' - advs: "B" >{true}']);
|
||||||
|
|
||||||
|
checkPrettyGestalt(r.simpleGestalt(false, "A", 2, 2),
|
||||||
|
['GESTALT metalevel 2 level 2:',
|
||||||
|
' - subs: "A" >{true}']);
|
||||||
|
checkPrettyGestalt(r.simpleGestalt(true, "B", 2, 2),
|
||||||
|
['GESTALT metalevel 2 level 2:',
|
||||||
|
' - advs: "B" >{true}']);
|
||||||
|
checkPrettyGestalt(r.simpleGestalt(false, "A", 2, 2).union(r.simpleGestalt(true, "B", 2, 2)),
|
||||||
|
['GESTALT metalevel 2 level 2:',
|
||||||
|
' - subs: "A" >{true}',
|
||||||
|
' - advs: "B" >{true}']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("matching", function () {
|
||||||
|
function check1(gMetalevel, level, mMetalevel) {
|
||||||
|
var g = r.simpleGestalt(false, "A", gMetalevel, level).label(123);
|
||||||
|
var result = g.matchValue("A", mMetalevel, false);
|
||||||
|
if (gMetalevel === mMetalevel) {
|
||||||
|
it("should match at level "+level, function () {
|
||||||
|
expect(result).to.eql([123]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
it("should not match at level "+level, function () {
|
||||||
|
expect(result).to.eql([]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function gMetaLevelCheck(gMetalevel, mMetalevel) {
|
||||||
|
describe("at gestalt metalevel "+gMetalevel+", message metalevel "+mMetalevel, function () {
|
||||||
|
check1(gMetalevel, 0, mMetalevel);
|
||||||
|
check1(gMetalevel, 1, mMetalevel);
|
||||||
|
check1(gMetalevel, 2, mMetalevel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function mMetaLevelCheck(mMetalevel) {
|
||||||
|
gMetaLevelCheck(0, mMetalevel);
|
||||||
|
gMetaLevelCheck(2, mMetalevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
mMetaLevelCheck(0);
|
||||||
|
mMetaLevelCheck(1);
|
||||||
|
mMetaLevelCheck(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("gestalt filtering", function () {
|
||||||
|
function check1(metalevel, observedLevel, observerLevel) {
|
||||||
|
var observer = r.simpleGestalt(true, r.__, metalevel, observerLevel).label("observer");
|
||||||
|
var observed = r.simpleGestalt(false, "A", metalevel, observedLevel).label("observed");
|
||||||
|
var resultM = observed.filter(observer);
|
||||||
|
var resultL = observed.match(observer);
|
||||||
|
if (observedLevel < observerLevel) {
|
||||||
|
it("should be able to see gestalt at level "+observedLevel, function () {
|
||||||
|
expect(resultM.isEmpty()).to.be(false);
|
||||||
|
expect(resultL).to.eql(["observer"]);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
it("should not be able to see gestalt at level "+observedLevel, function () {
|
||||||
|
expect(resultM.isEmpty()).to.be(true);
|
||||||
|
expect(resultL).to.eql([]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function metalevelCheck(metalevel, observerLevel) {
|
||||||
|
describe("observer at level "+observerLevel+" in metalevel "+metalevel, function () {
|
||||||
|
check1(metalevel, 0, observerLevel);
|
||||||
|
check1(metalevel, 1, observerLevel);
|
||||||
|
check1(metalevel, 2, observerLevel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function levelCheck(observerLevel) {
|
||||||
|
metalevelCheck(0, observerLevel);
|
||||||
|
metalevelCheck(2, observerLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
levelCheck(0);
|
||||||
|
levelCheck(1);
|
||||||
|
levelCheck(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("matcher equality", function () {
|
||||||
|
it("should not rely on object identity", function () {
|
||||||
|
expect(r.matcherEquals(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
||||||
|
r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B']))))
|
||||||
|
.to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should respect commutativity of union", function () {
|
||||||
|
expect(r.matcherEquals(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
||||||
|
r.union(r.compilePattern(r.arrayToSet(['B']), [r.__, 'B']),
|
||||||
|
r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']))))
|
||||||
|
.to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("gestalt equality", function () {
|
||||||
|
it("should distinguish emptyGestalt reliably", function () {
|
||||||
|
expect(r.simpleGestalt(false, r.__, 0, 10)
|
||||||
|
.union(r.simpleGestalt(true, r.__, 0, 10))
|
||||||
|
.equals(r.emptyGestalt))
|
||||||
|
.to.be(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should not rely on object identity", function () {
|
||||||
|
expect(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
||||||
|
.equals(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))))
|
||||||
|
.to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should respect commutativity of union", function () {
|
||||||
|
expect(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
||||||
|
.equals(r.simpleGestalt(true, "B", 0, 0).union(r.simpleGestalt(false, "A", 0, 0))))
|
||||||
|
.to.be(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should discriminate between advs and subs", function () {
|
||||||
|
expect(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
||||||
|
.equals(r.simpleGestalt(false, "B", 0, 0).union(r.simpleGestalt(true, "A", 0, 0))))
|
||||||
|
.to.be(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("matcherKeys on wild matchers", function () {
|
||||||
|
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
|
||||||
|
it("should yield null to signal an infinite result", function () {
|
||||||
|
expect(r.matcherKeys(r.project(M, r.compileProjection([r._$, r._$])))).to.be(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should extract just the second array element successfully", function () {
|
||||||
|
expect(r.matcherKeys(r.project(M, r.compileProjection([r.__, r._$])))).to.eql([[2],[3],[4]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
var M2 = r.project(M, r.compileProjection([r._$, r._$]));
|
||||||
|
|
||||||
|
it("should survive double-projection", function () {
|
||||||
|
expect(r.matcherKeys(r.project(M2, r.compileProjection(r.__, r._$)))).to.eql([[2],[3],[4]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should survive embedding and reprojection", function () {
|
||||||
|
expect(r.matcherKeys(r.project(r.compilePattern(true, [r.embeddedMatcher(M2)]),
|
||||||
|
r.compileProjection([r.__, r._$])))).to.eql([[2],[3],[4]]);
|
||||||
|
expect(r.matcherKeys(r.project(r.compilePattern(true, [[r.embeddedMatcher(M2)]]),
|
||||||
|
r.compileProjection([[r.__, r._$]])))).to.eql([[2],[3],[4]]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("matcherKeys using multiple-values in projections", function () {
|
||||||
|
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
var proj = r.compileProjection([r._$, r._$]);
|
||||||
|
var M2 = r.project(M, proj);
|
||||||
|
|
||||||
|
it("should be able to extract ordinary values", function () {
|
||||||
|
expect(r.matcherKeys(M2))
|
||||||
|
.to.eql([[1,2],[1,3],[3,4]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be able to be reprojected as a sequence of more than one value", function () {
|
||||||
|
expect(r.matcherKeys(r.project(M2, r.compileProjection(r._$, r._$))))
|
||||||
|
.to.eql([[1,2],[1,3],[3,4]]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should be convertible into objects with $-indexed fields", function () {
|
||||||
|
expect(r.matcherKeysToObjects(r.matcherKeys(M2), proj))
|
||||||
|
.to.eql([{'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}, {'$0': 3, '$1': 4}]);
|
||||||
|
expect(r.projectObjects(M, proj))
|
||||||
|
.to.eql([{'$0': 1, '$1': 2}, {'$0': 1, '$1': 3}, {'$0': 3, '$1': 4}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("matcherKeys using multiple-values in projections, with names", function () {
|
||||||
|
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
|
||||||
|
it("should yield named fields", function () {
|
||||||
|
expect(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$("snd")])))
|
||||||
|
.to.eql([{'fst': 1, 'snd': 2}, {'fst': 1, 'snd': 3}, {'fst': 3, 'snd': 4}]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should yield numbered fields where names are missing", function () {
|
||||||
|
expect(r.projectObjects(M, r.compileProjection([r._$, r._$("snd")])))
|
||||||
|
.to.eql([{'$0': 1, 'snd': 2}, {'$0': 1, 'snd': 3}, {'$0': 3, 'snd': 4}]);
|
||||||
|
expect(r.projectObjects(M, r.compileProjection([r._$("fst"), r._$])))
|
||||||
|
.to.eql([{'fst': 1, '$1': 2}, {'fst': 1, '$1': 3}, {'fst': 3, '$1': 4}]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("serializeMatcher", function () {
|
||||||
|
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
||||||
|
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['D']), [r.__, 3]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
||||||
|
var S = r.serializeMatcher(M, r.setToArray);
|
||||||
|
|
||||||
|
it("should basically work", function () {
|
||||||
|
expect(S).to.eql(
|
||||||
|
[ [ [ '(' ],
|
||||||
|
[ [ 1,
|
||||||
|
[ [ 2, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'A' ] ] ] ] ] ] ],
|
||||||
|
[ 3, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'C', 'D' ] ] ] ] ] ] ] ] ],
|
||||||
|
[ 3,
|
||||||
|
[ [ 2, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'A' ] ] ] ] ] ] ],
|
||||||
|
[ 3, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'D' ] ] ] ] ] ] ],
|
||||||
|
[ 4, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'B' ] ] ] ] ] ] ] ] ],
|
||||||
|
[ [ '__' ],
|
||||||
|
[ [ 2, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'A' ] ] ] ] ] ] ],
|
||||||
|
[ 3, [ [ [ ')' ], [ [ [ ')' ], [ '', [ 'D' ] ] ] ] ] ] ] ] ] ] ] ]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deserialize to something equivalent", function () {
|
||||||
|
expect(r.matcherEquals(M, r.deserializeMatcher(S, r.arrayToSet))).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("serialize Gestalts", function () {
|
||||||
|
var G = r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 2, 2));
|
||||||
|
var S = G.serialize();
|
||||||
|
|
||||||
|
it("should basically work", function () {
|
||||||
|
expect(S).to.eql(
|
||||||
|
[ 'gestalt',
|
||||||
|
[ [ [ [ [ 'A', [ [ [ ')' ], [ '', true ] ] ] ] ], [] ] ],
|
||||||
|
[],
|
||||||
|
[ [ [], [] ],
|
||||||
|
[ [], [] ],
|
||||||
|
[ [], [ [ 'B', [ [ [ ')' ], [ '', true ] ] ] ] ] ] ] ] ]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should deserialize to something equivalent", function () {
|
||||||
|
expect(G.equals(r.deserializeGestalt(S))).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("complex erasure", function () {
|
||||||
|
var A = r.compilePattern(r.arrayToSet(['A']), r.__);
|
||||||
|
var B = r.union(r.compilePattern(r.arrayToSet(['B']), [[[["foo"]]]]),
|
||||||
|
r.compilePattern(r.arrayToSet(['B']), [[[["bar"]]]]));
|
||||||
|
describe("after a union", function () {
|
||||||
|
var R0 = r.union(A, B);
|
||||||
|
var R1a = r.erasePath(R0, B);
|
||||||
|
var R1b = r.erasePath(R0, A);
|
||||||
|
|
||||||
|
it("should yield the other parts of the union", function () {
|
||||||
|
expect(r.matcherEquals(R1a, A)).to.be(true);
|
||||||
|
expect(r.matcherEquals(R1b, B)).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("embedding matchers in patterns", function () {
|
||||||
|
var M1a =
|
||||||
|
r.compilePattern(r.arrayToSet(['A']),
|
||||||
|
[1, r.embeddedMatcher(r.compilePattern(r.arrayToSet(['B']), [2, 3])), 4]);
|
||||||
|
var M1b =
|
||||||
|
r.compilePattern(r.arrayToSet(['A']), [1, [2, 3], 4]);
|
||||||
|
var M2a =
|
||||||
|
r.compilePattern(r.arrayToSet(['A']),
|
||||||
|
[r.embeddedMatcher(r.compilePattern(r.arrayToSet(['B']), [1, 2])),
|
||||||
|
r.embeddedMatcher(r.compilePattern(r.arrayToSet(['C']), [3, 4]))]);
|
||||||
|
var M2b =
|
||||||
|
r.compilePattern(r.arrayToSet(['A']), [[1, 2], [3, 4]]);
|
||||||
|
|
||||||
|
it("should yield matchers equivalent to the original patterns", function () {
|
||||||
|
expect(r.matcherEquals(M1a, M1b)).to.be(true);
|
||||||
|
expect(r.matcherEquals(M2a, M2b)).to.be(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("calls to matchPattern", function () {
|
||||||
|
it("should yield appropriately-named/-numbered fields", function () {
|
||||||
|
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$])).to.eql({'$0': 3, 'length': 1});
|
||||||
|
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three")])).to.eql({'three': 3, 'length': 1});
|
||||||
|
expect(r.matchPattern([1, 2, 3], [r._$, 2, r._$("three")]))
|
||||||
|
.to.eql({'$0': 1, 'three': 3, 'length': 2});
|
||||||
|
expect(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$]))
|
||||||
|
.to.eql({'one': 1, '$1': 3, 'length': 2});
|
||||||
|
expect(r.matchPattern([1, 2, 3], [r._$("one"), 2, r._$("three")]))
|
||||||
|
.to.eql({'one': 1, 'three': 3, 'length': 2});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail on value mismatch", function () {
|
||||||
|
expect(r.matchPattern([1, 2, 3], [r.__, 999, r._$("three")])).to.be(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should fail on array length mismatch", function () {
|
||||||
|
expect(r.matchPattern([1, 2, 3], [r.__, 2, r._$("three"), 4])).to.be(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("matches substructure", function () {
|
||||||
|
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]))
|
||||||
|
.to.eql({ one: 1, '$1': [ 2, 999 ], three: 3, length: 3 });
|
||||||
|
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]))
|
||||||
|
.to.eql({ one: 1, two: [ 2, 999 ], three: 3, length: 3 });
|
||||||
|
expect(r.matchPattern([1, [999, 2], 3], [r._$("one"), r._$(null, [2, r.__]), r._$("three")]))
|
||||||
|
.to.be(null);
|
||||||
|
expect(r.matchPattern([1, [999, 2], 3], [r._$("one"), r._$("two", [2, r.__]), r._$("three")]))
|
||||||
|
.to.be(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("matches nested captures", function () {
|
||||||
|
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$(null, [2, r._$]), r._$("three")]))
|
||||||
|
.to.eql({ one: 1, '$2': 999, '$1': [ 2, 999 ], three: 3, length: 4 });
|
||||||
|
expect(r.matchPattern([1, [2, 999], 3], [r._$("one"), r._$("two", [2, r._$]), r._$("three")]))
|
||||||
|
.to.eql({ one: 1, '$2': 999, two: [ 2, 999 ], three: 3, length: 4 });
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Projection with no captures", function () {
|
||||||
|
it("should yield the empty sequence when there's a match", function () {
|
||||||
|
var emptySequence = [' >{["A"]}'];
|
||||||
|
|
||||||
|
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection(r.__)),
|
||||||
|
emptySequence);
|
||||||
|
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection([r.__, r.__])),
|
||||||
|
emptySequence);
|
||||||
|
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection(["X", r.__])),
|
||||||
|
emptySequence);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should yield null when there's no match", function () {
|
||||||
|
expect(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection(["Y", r.__]))).to.be(null);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should yield nonempty sequences when there are captures after all", function () {
|
||||||
|
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection([r.__, r._$])),
|
||||||
|
[' ★ >{["A"]}']);
|
||||||
|
checkPrettyMatcher(r.project(r.compilePattern(r.arrayToSet(['A']), ["X", r.__]),
|
||||||
|
r.compileProjection([r._$, r._$])),
|
||||||
|
[' "X" ★ >{["A"]}']);
|
||||||
|
});
|
||||||
|
});
|
264
tr.js
264
tr.js
|
@ -1,264 +0,0 @@
|
||||||
var util = require('util');
|
|
||||||
var r = require("./route.js");
|
|
||||||
|
|
||||||
function dump(x) {
|
|
||||||
console.log(util.inspect(x, { depth: null }));
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
function dumpM(m) {
|
|
||||||
console.log(r.prettyMatcher(m));
|
|
||||||
console.log();
|
|
||||||
return m;
|
|
||||||
}
|
|
||||||
|
|
||||||
function dumpG(g) {
|
|
||||||
console.log(g.pretty());
|
|
||||||
console.log();
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
|
|
||||||
mAny = r.compilePattern(r.arrayToSet(['mAny']), r.__);
|
|
||||||
mAAny = r.compilePattern(r.arrayToSet(['mAAny']), ['A', r.__]);
|
|
||||||
dumpM(mAny);
|
|
||||||
dumpM(mAAny);
|
|
||||||
|
|
||||||
dump("mAny:");
|
|
||||||
dump(r.matchValue(mAny, 'hi'));
|
|
||||||
dump(r.matchValue(mAny, ['A', 'hi']));
|
|
||||||
dump(r.matchValue(mAny, ['B', 'hi']));
|
|
||||||
dump(r.matchValue(mAny, ['A', [['hi']]]));
|
|
||||||
|
|
||||||
dump("mAAny:");
|
|
||||||
dump(r.matchValue(mAAny, 'hi'));
|
|
||||||
dump(r.matchValue(mAAny, ['A', 'hi']));
|
|
||||||
dump(r.matchValue(mAAny, ['B', 'hi']));
|
|
||||||
dump(r.matchValue(mAAny, ['A', [['hi']]]));
|
|
||||||
|
|
||||||
console.log("unions");
|
|
||||||
|
|
||||||
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])));
|
|
||||||
|
|
||||||
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['W']), r.__)));
|
|
||||||
|
|
||||||
console.log("projections");
|
|
||||||
|
|
||||||
dumpM(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), r.__),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), ['b'])),
|
|
||||||
r.compileProjection(r._$([[r.__]]))));
|
|
||||||
|
|
||||||
dumpM(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
|
||||||
r.compileProjection([r._$(), r._$()])));
|
|
||||||
|
|
||||||
dump(r.matcherKeys(r.project(r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])),
|
|
||||||
r.compileProjection([r._$(), r._$()]))));
|
|
||||||
|
|
||||||
var R1 = r.compilePattern(r.arrayToSet(['A']), [r.__, "B"]);
|
|
||||||
var R2 = r.compilePattern(r.arrayToSet(['B']), ["A", r.__]);
|
|
||||||
var R12 = r.union(R1, R2);
|
|
||||||
dumpM(R1);
|
|
||||||
dumpM(R2);
|
|
||||||
dumpM(R12);
|
|
||||||
dumpM(r.erasePath(R12, R1));
|
|
||||||
dumpM(r.erasePath(R12, R2));
|
|
||||||
|
|
||||||
console.log();
|
|
||||||
dumpG(r.simpleGestalt(false, "A", 0, 0));
|
|
||||||
dumpG(r.simpleGestalt(true, "B", 0, 0));
|
|
||||||
dumpG(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0)));
|
|
||||||
|
|
||||||
console.log();
|
|
||||||
dumpG(r.simpleGestalt(false, "A", 2, 2));
|
|
||||||
dumpG(r.simpleGestalt(true, "B", 2, 2));
|
|
||||||
dumpG(r.simpleGestalt(false, "A", 2, 2).union(r.simpleGestalt(true, "B", 2, 2)));
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
function check1(i, j, n) {
|
|
||||||
var result = r.simpleGestalt(false, "A", i, j).label(123).matchValue("A", n, false);
|
|
||||||
dump([i === n ? result.length === 1 && result[0] === 123 : result.length === 0,
|
|
||||||
i, j, n, result]);
|
|
||||||
}
|
|
||||||
function metaLevelCheck(n) {
|
|
||||||
console.log("Checking message matching at metaLevel " + n);
|
|
||||||
check1(0, 0, n);
|
|
||||||
check1(0, 1, n);
|
|
||||||
check1(0, 2, n);
|
|
||||||
check1(2, 0, n);
|
|
||||||
check1(2, 1, n);
|
|
||||||
check1(2, 2, n);
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
metaLevelCheck(0);
|
|
||||||
metaLevelCheck(1);
|
|
||||||
metaLevelCheck(2);
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
function check1(i, j, n) {
|
|
||||||
var observer = r.simpleGestalt(true, r.__, i, n).label("observer");
|
|
||||||
var observed = r.simpleGestalt(false, "A", i, j).label("observed");
|
|
||||||
var resultM = observed.filter(observer);
|
|
||||||
var resultL = observed.match(observer);
|
|
||||||
dump([ (j < n
|
|
||||||
? !resultM.isEmpty() && resultL.length === 1 && resultL[0] === "observer"
|
|
||||||
: resultM.isEmpty() && resultL.length === 0),
|
|
||||||
i, j, n, resultL]);
|
|
||||||
}
|
|
||||||
function levelCheck(n) {
|
|
||||||
console.log("Checking gestalt filtering at level " + n);
|
|
||||||
check1(0, 0, n);
|
|
||||||
check1(0, 1, n);
|
|
||||||
check1(0, 2, n);
|
|
||||||
check1(2, 0, n);
|
|
||||||
check1(2, 1, n);
|
|
||||||
check1(2, 2, n);
|
|
||||||
console.log();
|
|
||||||
}
|
|
||||||
levelCheck(0);
|
|
||||||
levelCheck(1);
|
|
||||||
levelCheck(2);
|
|
||||||
})();
|
|
||||||
|
|
||||||
console.log("Checking matcher equality");
|
|
||||||
dump(r.matcherEquals(mAny, mAAny) === false);
|
|
||||||
dump(r.matcherEquals(mAny, mAny) === true);
|
|
||||||
dump(r.matcherEquals(mAAny, mAAny) === true);
|
|
||||||
dump(r.matcherEquals(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
|
||||||
r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])))
|
|
||||||
=== true);
|
|
||||||
dump(r.matcherEquals(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 'A']),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [r.__, 'B'])),
|
|
||||||
r.union(r.compilePattern(r.arrayToSet(['B']), [r.__, 'B']),
|
|
||||||
r.compilePattern(r.arrayToSet(['A']), [r.__, 'A'])))
|
|
||||||
=== true);
|
|
||||||
|
|
||||||
dump(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
|
||||||
.equals(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0)))
|
|
||||||
=== true);
|
|
||||||
dump(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
|
||||||
.equals(r.simpleGestalt(true, "B", 0, 0).union(r.simpleGestalt(false, "A", 0, 0)))
|
|
||||||
=== true);
|
|
||||||
|
|
||||||
dump(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
|
|
||||||
.equals(r.simpleGestalt(false, "B", 0, 0).union(r.simpleGestalt(true, "A", 0, 0)))
|
|
||||||
=== false);
|
|
||||||
|
|
||||||
|
|
||||||
console.log("debugging unions (1)");
|
|
||||||
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])));
|
|
||||||
|
|
||||||
dumpM(r.union(r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])));
|
|
||||||
|
|
||||||
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3])));
|
|
||||||
|
|
||||||
dumpM(r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4])));
|
|
||||||
|
|
||||||
console.log("debugging unions (2)");
|
|
||||||
var MU = r.emptyMatcher;
|
|
||||||
MU = r.union(MU, r.compilePattern(r.arrayToSet(['A']), [r.__, 2]));
|
|
||||||
dumpM(MU);
|
|
||||||
MU = r.union(MU, r.compilePattern(r.arrayToSet(['C']), [1, 3]));
|
|
||||||
dumpM(MU);
|
|
||||||
MU = r.union(MU, r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
dumpM(MU);
|
|
||||||
|
|
||||||
console.log("debugging unions (3)");
|
|
||||||
dumpM(r.union(r.compilePattern(r.arrayToSet('A'), [2]),
|
|
||||||
dumpM(r.union(r.compilePattern(r.arrayToSet('B'), [2]),
|
|
||||||
r.compilePattern(r.arrayToSet('C'), [3])))));
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
console.log("matcherKeys on wild matchers");
|
|
||||||
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
dump(r.matcherKeys(r.project(M, r.compileProjection([r._$(), r._$()]))));
|
|
||||||
dump(r.matcherKeys(r.project(M, r.compileProjection([r.__, r._$]))));
|
|
||||||
var M2 = r.project(M, r.compileProjection([r._$(), r._$()]));
|
|
||||||
dump(r.matcherKeys(r.project(M2,
|
|
||||||
r.compileProjection(r.__, r._$))));
|
|
||||||
dump(r.matcherKeys(r.project(r.compilePattern(true, [r.embeddedMatcher(M2)]),
|
|
||||||
r.compileProjection([r.__, r._$]))));
|
|
||||||
dump(r.matcherKeys(r.project(r.compilePattern(true, [[r.embeddedMatcher(M2)]]),
|
|
||||||
r.compileProjection([[r.__, r._$]]))));
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
console.log("matcherKeys using multiple-values in projections");
|
|
||||||
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [1, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
var M2 = r.project(M, r.compileProjection([r._$(), r._$()]));
|
|
||||||
dump(r.matcherKeys(M2));
|
|
||||||
dump(r.matcherKeys(r.project(M2, r.compileProjection(r._$(), r._$()))));
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
console.log("serializeMatcher");
|
|
||||||
var M = r.union(r.compilePattern(r.arrayToSet(['A']), [r.__, 2]),
|
|
||||||
r.compilePattern(r.arrayToSet(['C']), [1, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['D']), [r.__, 3]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [3, 4]));
|
|
||||||
var S = r.serializeMatcher(M, r.setToArray);
|
|
||||||
dump(S);
|
|
||||||
console.log(JSON.stringify(S));
|
|
||||||
dumpM(r.deserializeMatcher(S, r.arrayToSet));
|
|
||||||
dump(r.matcherEquals(M, r.deserializeMatcher(S, r.arrayToSet)) === true);
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
console.log("serialize Gestalts");
|
|
||||||
var G = dumpG(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 2, 2)));
|
|
||||||
var S = G.serialize();
|
|
||||||
dump(S);
|
|
||||||
console.log(JSON.stringify(S));
|
|
||||||
dumpG(r.deserializeGestalt(S));
|
|
||||||
dump(G.equals(r.deserializeGestalt(S)) === true);
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
console.log("complex erasure");
|
|
||||||
var A = r.compilePattern(r.arrayToSet(['A']), r.__);
|
|
||||||
var B = r.union(r.compilePattern(r.arrayToSet(['B']), [[[["foo"]]]]),
|
|
||||||
r.compilePattern(r.arrayToSet(['B']), [[[["bar"]]]]));
|
|
||||||
var R0 = r.union(A, B);
|
|
||||||
var R1a = r.erasePath(R0, B);
|
|
||||||
var R1b = r.erasePath(R0, A);
|
|
||||||
dumpM(R0);
|
|
||||||
dumpM(R1a);
|
|
||||||
dumpM(R1b);
|
|
||||||
dump(r.matcherEquals(R1a, A) === true);
|
|
||||||
dump(r.matcherEquals(R1b, B) === true);
|
|
||||||
})();
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
console.log("Embedding matchers in patterns");
|
|
||||||
var M1a =
|
|
||||||
r.compilePattern(r.arrayToSet(['A']),
|
|
||||||
[1, r.embeddedMatcher(r.compilePattern(r.arrayToSet(['B']), [2, 3])), 4]);
|
|
||||||
var M1b =
|
|
||||||
r.compilePattern(r.arrayToSet(['A']), [1, [2, 3], 4]);
|
|
||||||
var M2a =
|
|
||||||
r.compilePattern(r.arrayToSet(['A']),
|
|
||||||
[r.embeddedMatcher(r.compilePattern(r.arrayToSet(['B']), [1, 2])),
|
|
||||||
r.embeddedMatcher(r.compilePattern(r.arrayToSet(['C']), [3, 4]))]);
|
|
||||||
var M2b =
|
|
||||||
r.compilePattern(r.arrayToSet(['A']), [[1, 2], [3, 4]]);
|
|
||||||
dumpM(M1a);
|
|
||||||
dumpM(M2a);
|
|
||||||
dump(r.matcherEquals(M1a, M1b) === true);
|
|
||||||
dump(r.matcherEquals(M2a, M2b) === true);
|
|
||||||
})();
|
|
Loading…
Reference in New Issue