diff --git a/src/codec.js b/src/codec.js new file mode 100644 index 0000000..d15cfdd --- /dev/null +++ b/src/codec.js @@ -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"); diff --git a/src/ground.js b/src/ground.js new file mode 100644 index 0000000..c1839d9 --- /dev/null +++ b/src/ground.js @@ -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; diff --git a/src/main.js b/src/main.js index 06c1468..f957905 100644 --- a/src/main.js +++ b/src/main.js @@ -6,6 +6,7 @@ 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; -module.exports.Actor = require("./actor.js").Actor; diff --git a/src/minimart.js b/src/minimart.js index dd6c9fc..c7cad5f 100644 --- a/src/minimart.js +++ b/src/minimart.js @@ -1,4 +1,5 @@ var Route = require("./route.js"); +var Util = require("./util.js"); /////////////////////////////////////////////////////////////////////////// @@ -390,7 +391,7 @@ World.prototype.clearTombstones = function () { /* Utilities: matching demand for some service */ function DemandMatcher(projection, metaLevel, options) { - options = $.extend({ + options = Util.extend({ demandLevel: 0, supplyLevel: 0, demandSideIsSubscription: false @@ -481,63 +482,6 @@ Deduplicator.prototype.expireMessages = function () { } }; -/*---------------------------------------------------------------------------*/ -/* Ground interface */ - -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.__ = __; @@ -553,5 +497,4 @@ module.exports.shutdownWorld = shutdownWorld; module.exports.World = World; module.exports.DemandMatcher = DemandMatcher; module.exports.Deduplicator = Deduplicator; -module.exports.Ground = Ground; module.exports.Route = Route; diff --git a/src/util.js b/src/util.js new file mode 100644 index 0000000..3b20f3b --- /dev/null +++ b/src/util.js @@ -0,0 +1,13 @@ +// Minimal jQueryish utilities. Reimplemented because jQuery needs +// window to exist, and we want to run in Web Worker context as well. + +function extend(what, _with) { + for (var prop in _with) { + if (_with.hasOwnProperty(prop)) { + what[prop] = _with[prop]; + } + } + return what; +} + +module.exports.extend = extend; diff --git a/src/websocket-driver.js b/src/websocket-driver.js index 765c9f4..1120d72 100644 --- a/src/websocket-driver.js +++ b/src/websocket-driver.js @@ -1,4 +1,5 @@ var Minimart = require("./minimart.js"); +var Codec = require("./codec.js"); var Route = Minimart.Route; var World = Minimart.World; var sub = Minimart.sub; @@ -97,7 +98,7 @@ WebSocketConnection.prototype.safeSend = function (m) { WebSocketConnection.prototype.sendLocalRoutes = function () { var newLocalRoutesMessage = - JSON.stringify(encodeEvent(Minimart.updateRoutes([this.localGestalt]))); + JSON.stringify(Codec.encodeEvent(Minimart.updateRoutes([this.localGestalt]))); if (this.prevLocalRoutesMessage !== newLocalRoutesMessage) { this.prevLocalRoutesMessage = newLocalRoutesMessage; this.safeSend(newLocalRoutesMessage); @@ -138,7 +139,7 @@ WebSocketConnection.prototype.handleEvent = function (e) { var m = e.message; if (m.length && m.length === 3 && m[0] === this.label) { - var encoded = JSON.stringify(encodeEvent( + var encoded = JSON.stringify(Codec.encodeEvent( Minimart.sendMessage(m[2], m[1], e.isFeedback))); if (this.deduplicator.accept(encoded)) { this.safeSend(encoded); @@ -227,32 +228,6 @@ WebSocketConnection.prototype.onclose = function (e) { } }; -/////////////////////////////////////////////////////////////////////////// -// Wire protocol representation of events and actions - -function encodeEvent(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 decodeAction(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 action: " + JSON.stringify(j) }; - } -} - /////////////////////////////////////////////////////////////////////////// module.exports.WebSocketConnection = WebSocketConnection; -module.exports.encodeEvent = encodeEvent; -module.exports.decodeAction = decodeAction;