Reputation: 2745
consider the following code:
window.stage = bonsai.run(document.getElementById('stage'), {
code: function() {
var circle;
circle = new Circle(200, 200, 50);
circle.stroke('green', 2);
circle.addTo(stage);
circle.on('click', function(ev) {
stage.sendMessage('click', ev);
});
},
width: 500,
height: 500
});
stage.on('load', function() {
console.log('loaded');
stage.on('message:click', function(ev) {
console.log('click', ev);
});
});
So, clicking on the circle gives me the error: DATA_CLONE_ERR: DOM Exception 25
If I just send out properties like ev.x and ev.y, they pass out just fine. I can also reconstruct the object from its properties before sending and it passes fine.
How can I send the event object out intact to the parent context without deconstructing-> reconstructing it? And by the way, why does bonsai work this way?
Upvotes: 1
Views: 288
Reputation: 111
Great that you've asked why we do the separation that way. Just went through the BonsaiJS documentation and realized that we don't explicitely say a word about why we separate the rendering from the execution thread.
BonsaiJS code is mostly executed in a worker (falls back to iframe, if workers aren't available) and uses postMessage
to communicate with the context which created the worker. The DATA_CLONE_ERR: DOM Exception 25
is raised because the DOM event object can't be serialized by postMessage
. To solve your problem, you could create a simple function, which removes all nested objects / functions of the object, which should be passed:
window.stage = bonsai.run(document.getElementById('stage'), {
code: function() {
var circle;
var makeSerializable = function(obj) {
var ret = {}, val;
Object.keys(obj).forEach(function(key) {
val = obj[key];
if (typeof val != 'object' && typeof val != 'function') {
ret[key] = val;
};
});
return ret;
};
circle = new Circle(200, 200, 50);
circle.stroke('green', 2);
circle.addTo(stage);
circle.on('click', function(ev) {
stage.sendMessage('click', makeSerializable(ev));
});
},
width: 500,
height: 500
});
stage.on('load', function() {
console.log('loaded');
stage.on('message:click', function(ev) {
console.log('click', ev);
});
});
Or you can force BonsaiJS to be executed in an iFrame. Then you'll have access to the DOM and you can serialize any object (note: see below, why I wouldn't recommend that):
window.stage = bonsai.setup({
runnerContext: bonsai.IframeRunnerContext
}).run({...});
The main reason for putting the main code execution into a worker, is, that we don't want any computation to block the rendering "thread", so that we get more fluent animations (if the code is executed in an iFrame, rendering + code execution will happen in the same thread and it won't be as fluent as with the worker). Another advantage of executing JS code in a worker, is, that we don't rely on the DOM and can also take the same JS code and execute it in a different JS environment like Rhino or NodeJS (here is some example code, how you could execute BonsaiJS on node and send the rendering messages to the browser through SocketIO: https://github.com/uxebu/bonsai-server).
Upvotes: 2