Some modularity; general cleanup of codebase

This commit is contained in:
Tony Garnock-Jones 2014-07-23 17:21:51 -07:00
parent 4239c1ea33
commit 7bab9be492
27 changed files with 4390 additions and 1732 deletions

3
.gitignore vendored
View File

@ -1,6 +1,5 @@
private-key.pem
server-cert.pem
MarketplaceChat.app.zip
MarketplaceChat.app/
scratch/
_site/
node_modules/

View File

@ -1,21 +1,8 @@
APP_NAME=MarketplaceChat.app
LIB_SOURCES=\
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:
npm run build
all: $(APP_NAME).zip
deps:
npm install .
keys: private-key.pem server-cert.pem
@ -31,23 +18,3 @@ server-cert.pem: private-key.pem
clean-keys:
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:
rm -rf $(APP_NAME) $(APP_NAME).zip

View File

@ -14,7 +14,7 @@ To install 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
websocket on ports 8000 (HTTP) and 8443 (HTTPS, if you have a

1
dist/README.md vendored Normal file
View File

@ -0,0 +1 @@
Directory for build products, checked in to the repo for ease-of-use.

2650
dist/minimart.js vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/minimart.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,33 +3,15 @@
<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/bootstrap/js/bootstrap.min.js"></script> -->
<script src="../../third-party/jquery-2.0.3.min.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="../../dist/minimart.js"></script>
<script src="index.js"></script>
</head>
<body>
<div class="container-fluid">
<!-- <div class="row-fluid"> -->
<!-- <div class="span12"> -->
<!-- <h1>JS Marketplace</h1> -->
<!-- </div> -->
<!-- </div> -->
<div class="row-fluid">
<div class="span12">
<form class="form-horizontal" name="nym_form">

View File

@ -1,3 +1,10 @@
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())];
}
@ -18,7 +25,7 @@ function outputItem(item) {
function updateNymList(g) {
var statuses = {};
var nymProj = ["broker", 0, ["chatEvent", _$, _$, __, __]];
var matchedNyms = route.matcherKeys(g.project(route.compileProjection(nymProj), true, 0, 0));
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];
}
@ -55,15 +62,15 @@ $(document).ready(function () {
$("#nym_form").submit(function (e) { e.preventDefault(); return false; });
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');
// World.spawn(new Spy());
spawnJQueryDriver();
spawnDOMDriver();
spawnRoutingTableWidget("#spy-holder", "spy");
Minimart.JQuery.spawnJQueryDriver();
Minimart.DOM.spawnDOMDriver();
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
World.spawn(new WakeDetector());
var wsconn = new WebSocketConnection("broker", $("#wsurl").val(), true);
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
@ -74,7 +81,7 @@ $(document).ready(function () {
handleEvent: function (e) {
if (e.type === "routes") {
var states =
route.matcherKeys(e.gestalt.project(route.compileProjection([__, _$]),
Route.matcherKeys(e.gestalt.project(Route.compileProjection([__, _$]),
true, 0, 0));
var newState = states.length > 0 ? states[0][0] : "crashed";
if (this.state != newState) {

View File

@ -3,24 +3,11 @@
<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">
<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="../../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="../../dist/minimart.js"></script>
<script src="index.js"></script>
</head>
<body>

View File

@ -1,10 +1,16 @@
var G;
$(document).ready(function () {
G = new Ground(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));
spawnDOMDriver();
spawnRoutingTableWidget("#spy-holder", "spy");
Minimart.DOM.spawnDOMDriver();
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
World.spawn({
handleEvent: function (e) {

View File

@ -4,9 +4,7 @@
<title>JS Marketplace: Smoketest</title>
<meta charset="utf-8">
<script src="../../third-party/jquery-2.0.3.min.js"></script>
<script src="../../route.js"></script>
<script src="../../marketplace.js"></script>
<script src="../../spy.js"></script>
<script src="../../dist/minimart.js"></script>
<script src="index.js"></script>
</head>
<body>

View File

@ -1,8 +1,14 @@
var G;
$(document).ready(function () {
G = new Ground(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));
World.spawn(new Minimart.Spy("GROUND", true));
World.spawn({
counter: 0,
handleEvent: function (e) {},

View File

@ -9,16 +9,7 @@
<link href="style.css" rel="stylesheet">
<script src="../../third-party/jquery-2.0.3.min.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="../../dist/minimart.js"></script>
<script src="index.js"></script>
</head>
<body>

View File

@ -1,6 +1,13 @@
///////////////////////////////////////////////////////////////////////////
// GUI
var Route = Minimart.Route;
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function piece(text, pos, lo, hi, cls) {
return "<span class='"+cls+"'>"+
((pos >= lo && pos < hi)
@ -16,19 +23,19 @@ function spawnGui() {
sub(["fieldContents", __, __], 0, 1),
sub(["highlight", __], 0, 1)]);
},
fieldContentsSpec: route.compileProjection(["fieldContents", _$, _$]),
highlightSpec: route.compileProjection(["highlight", _$]),
fieldContentsSpec: Route.compileProjection(["fieldContents", _$, _$]),
highlightSpec: Route.compileProjection(["highlight", _$]),
handleEvent: function (e) {
switch (e.type) {
case "routes":
var text = "", pos = 0, highlight = false;
// BUG: escape text!
var fc = route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true));
var fc = Route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true));
if (fc.length > 0) {
text = fc[0][0];
pos = fc[0][1];
}
var hl = route.matcherKeys(e.gestalt.project(this.highlightSpec, true));
var hl = Route.matcherKeys(e.gestalt.project(this.highlightSpec, true));
if (hl.length > 0) {
highlight = hl[0][0];
}
@ -112,7 +119,7 @@ function spawnSearch() {
World.spawn({
fieldContents: "",
highlight: false,
fieldContentsSpec: route.compileProjection(["fieldContents", _$, _$]),
fieldContentsSpec: Route.compileProjection(["fieldContents", _$, _$]),
boot: function () {
World.updateRoutes(this.subscriptions());
},
@ -137,7 +144,7 @@ function spawnSearch() {
handleEvent: function (e) {
switch (e.type) {
case "routes":
var fc = route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true));
var fc = Route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true));
if (fc.length > 0) {
this.fieldContents = fc[0][0];
}
@ -157,10 +164,10 @@ function spawnSearch() {
var G;
$(document).ready(function () {
G = new Ground(function () {
spawnJQueryDriver();
spawnDOMDriver();
spawnRoutingTableWidget("#spy-holder", "spy");
G = new Minimart.Ground(function () {
Minimart.JQuery.spawnJQueryDriver();
Minimart.DOM.spawnDOMDriver();
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
spawnGui();
spawnModel();

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"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",
"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"
}
}

1531
route.js

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -1,7 +1,13 @@
// 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() {
var d = new DemandMatcher(["DOM", _$, _$, _$]);
var d = new Minimart.DemandMatcher(["DOM", _$, _$, _$]);
d.onDemandIncrease = function (captures) {
var selector = captures[0];
var fragmentClass = captures[1];
@ -24,7 +30,7 @@ DOMFragment.prototype.boot = function () {
var self = this;
var monitoring = sub(["DOM", self.selector, self.fragmentClass, self.fragmentSpec], 1, 2);
World.spawn(new World(function () {
spawnJQueryDriver(self.selector+" > ."+self.fragmentClass, 1);
Minimart.JQuery.spawnJQueryDriver(self.selector+" > ."+self.fragmentClass, 1);
World.spawn({
handleEvent: function (e) {
if (e.type === "routes") {
@ -86,3 +92,7 @@ DOMFragment.prototype.buildNodes = function () {
});
return nodes;
};
///////////////////////////////////////////////////////////////////////////
module.exports.spawnDOMDriver = spawnDOMDriver;

View File

@ -1,8 +1,15 @@
// 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) {
metaLevel = metaLevel || 0;
var d = new DemandMatcher(["jQuery", _$, _$, __], metaLevel, {demandSideIsSubscription: true});
var d = new Minimart.DemandMatcher(["jQuery", _$, _$, __], metaLevel,
{demandSideIsSubscription: true});
d.onDemandIncrease = function (captures) {
var selector = captures[0];
var eventName = captures[1];
@ -44,3 +51,7 @@ JQueryEventRouter.prototype.computeNodes = function () {
return $(this.selector);
}
};
///////////////////////////////////////////////////////////////////////////
module.exports.spawnJQueryDriver = spawnJQueryDriver;

9
src/main.js Normal file
View File

@ -0,0 +1,9 @@
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.Spy = require("./spy.js").Spy;
module.exports.WakeDetector = require("./wake-detector.js").WakeDetector;

View File

@ -1,27 +1,31 @@
var Route = require("./route.js");
///////////////////////////////////////////////////////////////////////////
// TODO: trigger-guards as per minimart
/*---------------------------------------------------------------------------*/
/* Events and Actions */
var __ = route.__;
var _$ = route._$;
var __ = Route.__;
var _$ = Route._$;
function sub(pattern, metaLevel, level) {
return route.simpleGestalt(false, pattern, metaLevel, level);
return Route.simpleGestalt(false, 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) {
return { type: "spawn",
behavior: behavior,
initialGestalt: route.gestaltUnion(initialGestalts || []) };
initialGestalt: Route.gestaltUnion(initialGestalts || []) };
}
function updateRoutes(gestalts) {
return { type: "routes", gestalt: route.gestaltUnion(gestalts) };
return { type: "routes", gestalt: Route.gestaltUnion(gestalts) };
}
function pendingRoutingUpdate(aggregate, affectedSubgestalt, knownTarget) {
@ -49,11 +53,11 @@ function World(bootFn) {
this.alive = true;
this.eventQueue = [];
this.runnablePids = {};
this.partialGestalt = route.emptyGestalt; // Only gestalt from local processes
this.fullGestalt = route.emptyGestalt ;; // partialGestalt unioned with downwardGestalt
this.partialGestalt = Route.emptyGestalt; // Only gestalt from local processes
this.fullGestalt = Route.emptyGestalt ;; // partialGestalt unioned with downwardGestalt
this.processTable = {};
this.tombstones = {};
this.downwardGestalt = route.emptyGestalt;
this.downwardGestalt = Route.emptyGestalt;
this.processActions = [];
this.asChild(-1, bootFn, true);
}
@ -134,7 +138,7 @@ World.prototype.enqueueAction = function (pid, action) {
World.prototype.isInert = function () {
return this.eventQueue.length === 0
&& this.processActions.length === 0
&& route.is_emptySet(this.runnablePids);
&& Route.is_emptySet(this.runnablePids);
};
World.prototype.markPidRunnable = function (pid) {
@ -183,7 +187,7 @@ World.prototype.kill = function (pid, exn) {
p.exitReason = exn;
this.tombstones[pid] = p;
}
this.applyAndIssueRoutingUpdate(p.gestalt, route.emptyGestalt);
this.applyAndIssueRoutingUpdate(p.gestalt, Route.emptyGestalt);
}
};
@ -227,7 +231,7 @@ World.prototype.performAction = function (pid, action) {
this.asChild(pid, function () { action.behavior.boot() });
this.markPidRunnable(pid);
}
this.applyAndIssueRoutingUpdate(route.emptyGestalt, newGestalt, pid);
this.applyAndIssueRoutingUpdate(Route.emptyGestalt, newGestalt, pid);
break;
case "routes":
if (pid in this.processTable) {
@ -391,8 +395,8 @@ function DemandMatcher(projection, metaLevel, options) {
supplyLevel: 0,
demandSideIsSubscription: false
}, options);
this.pattern = route.projectionToPattern(projection);
this.projectionSpec = route.compileProjection(projection);
this.pattern = Route.projectionToPattern(projection);
this.projectionSpec = Route.compileProjection(projection);
this.metaLevel = metaLevel | 0;
this.demandLevel = options.demandLevel;
this.supplyLevel = options.supplyLevel;
@ -428,12 +432,12 @@ DemandMatcher.prototype.handleGestalt = function (gestalt) {
this.demandSideIsSubscription,
this.metaLevel,
this.supplyLevel);
var newDemand = route.arrayToSet(route.matcherKeys(newDemandMatcher));
var newSupply = route.arrayToSet(route.matcherKeys(newSupplyMatcher));
var demandDelta = route.setSubtract(newDemand, this.currentDemand);
var supplyDelta = route.setSubtract(this.currentSupply, newSupply);
var demandIncr = route.setSubtract(demandDelta, newSupply);
var supplyDecr = route.setIntersect(supplyDelta, newDemand);
var newDemand = Route.arrayToSet(Route.matcherKeys(newDemandMatcher));
var newSupply = Route.arrayToSet(Route.matcherKeys(newSupplyMatcher));
var demandDelta = Route.setSubtract(newDemand, this.currentDemand);
var supplyDelta = Route.setSubtract(this.currentSupply, newSupply);
var demandIncr = Route.setSubtract(demandDelta, newSupply);
var supplyDecr = Route.setIntersect(supplyDelta, newDemand);
this.currentDemand = newDemand;
this.currentSupply = newSupply;
for (var k in demandIncr) this.onDemandIncrease(demandIncr[k]);
@ -533,3 +537,21 @@ Ground.prototype.enqueueAction = function (pid, action) {
console.error("You have sent a message into the outer void.", action);
}
};
///////////////////////////////////////////////////////////////////////////
module.exports.__ = __;
module.exports._$ = _$;
module.exports.sub = sub;
module.exports.pub = pub;
module.exports.spawn = spawn;
module.exports.updateRoutes = updateRoutes;
module.exports.sendMessage = sendMessage;
module.exports.shutdownWorld = shutdownWorld;
module.exports.World = World;
module.exports.DemandMatcher = DemandMatcher;
module.exports.Deduplicator = Deduplicator;
module.exports.Ground = Ground;
module.exports.Route = Route;

1519
src/route.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,11 @@
var Minimart = require("./minimart.js");
var Route = Minimart.Route;
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
observationLevel = observationLevel || 10;
// ^ arbitrary: should be Infinity, when route.js supports it. TODO
@ -5,8 +13,8 @@ function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
World.spawn({
boot: function () { this.updateState(); },
state: route.emptyGestalt.serialize(),
nextState: route.emptyGestalt.serialize(),
state: Route.emptyGestalt.serialize(),
nextState: Route.emptyGestalt.serialize(),
timer: false,
localGestalt: (sub( ["DOM", selector, fragmentClass, __], 0, 2)
@ -18,8 +26,7 @@ function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
},
updateState: function () {
var elts = ["ul", {"class": "routing-table"},
["li", ["pre", route.deserializeGestalt(this.state).pretty()]]];
var elts = ["pre", Route.deserializeGestalt(this.state).pretty()];
World.updateRoutes([sub(__, 0, observationLevel),
pub(__, 0, observationLevel),
pub(["DOM", selector, fragmentClass, elts])]);
@ -45,3 +52,5 @@ function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
});
}
module.exports.spawnRoutingTableWidget = spawnRoutingTableWidget;

View File

@ -1,4 +1,10 @@
// Generic Spy
var Minimart = require("./minimart.js");
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function Spy(label, useJson, observationLevel) {
this.label = label || "SPY";
@ -29,3 +35,5 @@ Spy.prototype.handleEvent = function (e) {
break;
}
};
module.exports.Spy = Spy;

View File

@ -2,6 +2,12 @@
// suspension/sleeping!) has caused periodic activities to be
// interrupted, and warns others about it
// 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.__;
var _$ = Minimart._$;
function WakeDetector(period) {
this.message = "wake";
@ -25,3 +31,5 @@ WakeDetector.prototype.trigger = function () {
}
this.mostRecentTrigger = now;
};
module.exports.WakeDetector = WakeDetector;

View File

@ -1,3 +1,11 @@
var Minimart = require("./minimart.js");
var Route = Minimart.Route;
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
///////////////////////////////////////////////////////////////////////////
// WebSocket client driver
@ -15,11 +23,11 @@ function WebSocketConnection(label, wsurl, shouldReconnect) {
this.wsurl = wsurl;
this.shouldReconnect = shouldReconnect ? true : false;
this.reconnectDelay = DEFAULT_RECONNECT_DELAY;
this.localGestalt = route.emptyGestalt;
this.peerGestalt = route.emptyGestalt;
this.localGestalt = Route.emptyGestalt;
this.peerGestalt = Route.emptyGestalt;
this.prevLocalRoutesMessage = null;
this.prevPeerRoutesMessage = null;
this.deduplicator = new Deduplicator();
this.deduplicator = new Minimart.Deduplicator();
this.connectionCount = 0;
this.activityTimestamp = 0;
@ -58,8 +66,8 @@ WebSocketConnection.prototype.relayGestalt = function () {
WebSocketConnection.prototype.aggregateGestalt = function () {
var self = this;
return this.peerGestalt.transform(function (m, metaLevel) {
return route.compilePattern(true,
[self.label, metaLevel, route.embeddedMatcher(m)]);
return Route.compilePattern(true,
[self.label, metaLevel, Route.embeddedMatcher(m)]);
}).union(this.relayGestalt());
};
@ -88,7 +96,8 @@ WebSocketConnection.prototype.safeSend = function (m) {
};
WebSocketConnection.prototype.sendLocalRoutes = function () {
var newLocalRoutesMessage = JSON.stringify(encodeEvent(updateRoutes([this.localGestalt])));
var newLocalRoutesMessage =
JSON.stringify(encodeEvent(Minimart.updateRoutes([this.localGestalt])));
if (this.prevLocalRoutesMessage !== newLocalRoutesMessage) {
this.prevLocalRoutesMessage = newLocalRoutesMessage;
this.safeSend(newLocalRoutesMessage);
@ -96,14 +105,14 @@ WebSocketConnection.prototype.sendLocalRoutes = function () {
};
WebSocketConnection.prototype.collectMatchers = function (getAdvertisements, level, g) {
var extractMetaLevels = route.compileProjection([this.label, _$, __]);
var mls = route.matcherKeys(g.project(extractMetaLevels, getAdvertisements, 0, level));
var extractMetaLevels = Route.compileProjection([this.label, _$, __]);
var mls = Route.matcherKeys(g.project(extractMetaLevels, getAdvertisements, 0, level));
for (var i = 0; i < mls.length; i++) {
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);
this.localGestalt = this.localGestalt.union(route.simpleGestalt(getAdvertisements,
route.embeddedMatcher(m),
this.localGestalt = this.localGestalt.union(Route.simpleGestalt(getAdvertisements,
Route.embeddedMatcher(m),
metaLevel,
level));
}
@ -115,9 +124,9 @@ WebSocketConnection.prototype.handleEvent = function (e) {
case "routes":
// TODO: GROSS - erasing by pid!
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);
this.localGestalt = route.emptyGestalt;
this.localGestalt = Route.emptyGestalt;
for (var level = 0; level < nLevels; level++) {
this.collectMatchers(false, level, g);
this.collectMatchers(true, level, g);
@ -129,7 +138,8 @@ WebSocketConnection.prototype.handleEvent = function (e) {
var m = e.message;
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(encodeEvent(
Minimart.sendMessage(m[2], m[1], e.isFeedback)));
if (this.deduplicator.accept(encoded)) {
this.safeSend(encoded);
}
@ -232,10 +242,17 @@ function encodeEvent(e) {
function decodeAction(j) {
switch (j[0]) {
case "routes":
return updateRoutes([route.deserializeGestalt(j[1], function (v) { return true; })]);
return Minimart.updateRoutes([
Route.deserializeGestalt(j[1], function (v) { return true; })]);
case "message":
return sendMessage(j[1], j[2], j[3]);
return Minimart.sendMessage(j[1], j[2], j[3]);
default:
throw { message: "Invalid JSON-encoded action: " + JSON.stringify(j) };
}
}
///////////////////////////////////////////////////////////////////////////
module.exports.WebSocketConnection = WebSocketConnection;
module.exports.encodeEvent = encodeEvent;
module.exports.decodeAction = decodeAction;

View File