diff --git a/js/examples/dom-webworker/Makefile b/js/examples/dom-webworker/Makefile
new file mode 100644
index 0000000..34717b3
--- /dev/null
+++ b/js/examples/dom-webworker/Makefile
@@ -0,0 +1,7 @@
+all: index.expanded.js worker.expanded.js
+
+%.expanded.js: %.js
+ ../../bin/syndicatec $< > $@ || (rm -f $@; false)
+
+clean:
+ rm -f *.expanded.js
diff --git a/js/examples/dom-webworker/index.html b/js/examples/dom-webworker/index.html
new file mode 100644
index 0000000..6b5f389
--- /dev/null
+++ b/js/examples/dom-webworker/index.html
@@ -0,0 +1,15 @@
+
+
+
+ Syndicate: DOM WebWorker Example
+
+
+
+
+
+ DOM WebWorker example
+
+
+
+
+
diff --git a/js/examples/dom-webworker/index.js b/js/examples/dom-webworker/index.js
new file mode 100644
index 0000000..f5f396b
--- /dev/null
+++ b/js/examples/dom-webworker/index.js
@@ -0,0 +1,22 @@
+document.addEventListener('DOMContentLoaded', function () {
+ ground dataspace G {
+ Syndicate.UI.spawnUIDriver();
+
+ spawn {
+ var ui = new Syndicate.UI.Anchor();
+ during Syndicate.observe('bump_count') { // wait for the worker to boot and start listening
+ assert ui.html('#clicker-holder',
+ '');
+ }
+ on message Syndicate.UI.globalEvent('#clicker-holder > button', 'click', _) {
+ :: 'bump_count';
+ }
+ }
+
+ Syndicate.Dataspace.spawn(new Syndicate.Worker('worker.expanded.js'));
+ }
+
+ G.dataspace.setOnStateChange(function (mux, patch) {
+ document.getElementById('spy-holder').innerText = Syndicate.prettyTrie(mux.routingTable);
+ });
+});
diff --git a/js/examples/dom-webworker/worker.js b/js/examples/dom-webworker/worker.js
new file mode 100644
index 0000000..ffb8722
--- /dev/null
+++ b/js/examples/dom-webworker/worker.js
@@ -0,0 +1,19 @@
+importScripts("../../dist/syndicate.js");
+
+var G = new Syndicate.WorkerGround(function () {
+ spawn {
+ var ui = new Syndicate.UI.Anchor();
+ field this.counter = 0;
+
+ assert ui.html('#counter-holder', 'The current count is: '+this.counter+'
')
+ metalevel 1;
+
+ on message 'bump_count'
+ metalevel 1
+ {
+ this.counter++;
+ }
+ }
+});
+
+G.startStepping();
diff --git a/js/src/main.js b/js/src/main.js
index df6e1e8..1d363f7 100644
--- a/js/src/main.js
+++ b/js/src/main.js
@@ -48,6 +48,6 @@ module.exports.Actor = require("./actor.js");
// 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;
+var Worker = require("./worker.js");
+module.exports.Worker = Worker.Worker;
+module.exports.WorkerGround = Worker.WorkerGround;
diff --git a/js/src/randomid.js b/js/src/randomid.js
index 52473e3..9458679 100644
--- a/js/src/randomid.js
+++ b/js/src/randomid.js
@@ -1,11 +1,10 @@
var randomId;
-if ((typeof window !== 'undefined') &&
- (typeof window.crypto !== 'undefined') &&
- (typeof window.crypto.getRandomValues !== 'undefined')) {
+function browserCryptoObject(crypto) {
+ if (typeof crypto.getRandomValues === 'undefined') return false;
randomId = function (byteCount, hexOutput) {
var buf = new Uint8Array(byteCount);
- window.crypto.getRandomValues(buf);
+ crypto.getRandomValues(buf);
if (hexOutput) {
var encoded = [];
for (var i = 0; i < buf.length; i++) {
@@ -17,7 +16,24 @@ if ((typeof window !== 'undefined') &&
return btoa(String.fromCharCode.apply(null, buf)).replace(/=/g,'');
}
};
+ return true;
+}
+
+if ((typeof window !== 'undefined') &&
+ (typeof window.crypto !== 'undefined') &&
+ browserCryptoObject(window.crypto)) {
+ // We are in the main page, and window.crypto is available, and
+ // browserCryptoObject has installed a suitable randomId. Do
+ // nothing.
+} else if ((typeof self !== 'undefined') &&
+ (typeof self.crypto !== 'undefined') &&
+ browserCryptoObject(self.crypto)) {
+ // We are in a web worker, and self.crypto is available, and
+ // browserCryptoObject has installed a suitable randomId. Do
+ // nothing.
} else {
+ // See if we're in node.js.
+
var crypto;
try {
crypto = require('crypto');
diff --git a/js/src/worker.js b/js/src/worker.js
new file mode 100644
index 0000000..858b8f7
--- /dev/null
+++ b/js/src/worker.js
@@ -0,0 +1,52 @@
+/* Web Worker interface */
+var Ground = require("./ground.js").Ground;
+var Util = require("./util.js");
+var Codec = require("./codec.js");
+
+var Dataspace = require("./dataspace.js").Dataspace;
+
+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 = Dataspace.wrap(function (e) {
+ console.log("Received from worker", JSON.stringify(e.data));
+ Dataspace.enqueueAction(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.dataspace.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;