|
|
// Copyright (c) IPython Development Team.
|
|
|
// Distributed under the terms of the Modified BSD License.
|
|
|
|
|
|
define([
|
|
|
'base/js/namespace',
|
|
|
'jquery',
|
|
|
'base/js/utils',
|
|
|
], function(IPython, $, utils) {
|
|
|
"use strict";
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
// CommManager class
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
|
var CommManager = function (kernel) {
|
|
|
this.comms = {};
|
|
|
this.targets = {};
|
|
|
if (kernel !== undefined) {
|
|
|
this.init_kernel(kernel);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
CommManager.prototype.init_kernel = function (kernel) {
|
|
|
/**
|
|
|
* connect the kernel, and register message handlers
|
|
|
*/
|
|
|
this.kernel = kernel;
|
|
|
var msg_types = ['comm_open', 'comm_msg', 'comm_close'];
|
|
|
for (var i = 0; i < msg_types.length; i++) {
|
|
|
var msg_type = msg_types[i];
|
|
|
kernel.register_iopub_handler(msg_type, $.proxy(this[msg_type], this));
|
|
|
}
|
|
|
};
|
|
|
|
|
|
CommManager.prototype.new_comm = function (target_name, data, callbacks, metadata) {
|
|
|
/**
|
|
|
* Create a new Comm, register it, and open its Kernel-side counterpart
|
|
|
* Mimics the auto-registration in `Comm.__init__` in the IPython Comm
|
|
|
*/
|
|
|
var comm = new Comm(target_name);
|
|
|
this.register_comm(comm);
|
|
|
comm.open(data, callbacks, metadata);
|
|
|
return comm;
|
|
|
};
|
|
|
|
|
|
CommManager.prototype.register_target = function (target_name, f) {
|
|
|
/**
|
|
|
* Register a target function for a given target name
|
|
|
*/
|
|
|
this.targets[target_name] = f;
|
|
|
};
|
|
|
|
|
|
CommManager.prototype.unregister_target = function (target_name, f) {
|
|
|
/**
|
|
|
* Unregister a target function for a given target name
|
|
|
*/
|
|
|
delete this.targets[target_name];
|
|
|
};
|
|
|
|
|
|
CommManager.prototype.register_comm = function (comm) {
|
|
|
/**
|
|
|
* Register a comm in the mapping
|
|
|
*/
|
|
|
this.comms[comm.comm_id] = Promise.resolve(comm);
|
|
|
comm.kernel = this.kernel;
|
|
|
return comm.comm_id;
|
|
|
};
|
|
|
|
|
|
CommManager.prototype.unregister_comm = function (comm) {
|
|
|
/**
|
|
|
* Remove a comm from the mapping
|
|
|
*/
|
|
|
delete this.comms[comm.comm_id];
|
|
|
};
|
|
|
|
|
|
// comm message handlers
|
|
|
|
|
|
CommManager.prototype.comm_open = function (msg) {
|
|
|
var content = msg.content;
|
|
|
var that = this;
|
|
|
var comm_id = content.comm_id;
|
|
|
|
|
|
this.comms[comm_id] = utils.load_class(content.target_name, content.target_module,
|
|
|
this.targets).then(function(target) {
|
|
|
var comm = new Comm(content.target_name, comm_id);
|
|
|
comm.kernel = that.kernel;
|
|
|
try {
|
|
|
var response = target(comm, msg);
|
|
|
} catch (e) {
|
|
|
comm.close();
|
|
|
that.unregister_comm(comm);
|
|
|
var wrapped_error = new utils.WrappedError("Exception opening new comm", e);
|
|
|
console.error(wrapped_error);
|
|
|
return Promise.reject(wrapped_error);
|
|
|
}
|
|
|
// Regardless of the target return value, we need to
|
|
|
// then return the comm
|
|
|
return Promise.resolve(response).then(function() {return comm;});
|
|
|
}, utils.reject('Could not open comm', true));
|
|
|
return this.comms[comm_id];
|
|
|
};
|
|
|
|
|
|
CommManager.prototype.comm_close = function(msg) {
|
|
|
var content = msg.content;
|
|
|
if (this.comms[content.comm_id] === undefined) {
|
|
|
console.error('Comm promise not found for comm id ' + content.comm_id);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
|
|
|
this.unregister_comm(comm);
|
|
|
try {
|
|
|
comm.handle_close(msg);
|
|
|
} catch (e) {
|
|
|
console.log("Exception closing comm: ", e, e.stack, msg);
|
|
|
}
|
|
|
// don't return a comm, so that further .then() functions
|
|
|
// get an undefined comm input
|
|
|
});
|
|
|
};
|
|
|
|
|
|
CommManager.prototype.comm_msg = function(msg) {
|
|
|
var content = msg.content;
|
|
|
if (this.comms[content.comm_id] === undefined) {
|
|
|
console.error('Comm promise not found for comm id ' + content.comm_id);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) {
|
|
|
try {
|
|
|
comm.handle_msg(msg);
|
|
|
} catch (e) {
|
|
|
console.log("Exception handling comm msg: ", e, e.stack, msg);
|
|
|
}
|
|
|
return comm;
|
|
|
});
|
|
|
};
|
|
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
// Comm base class
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
|
|
var Comm = function (target_name, comm_id) {
|
|
|
this.target_name = target_name;
|
|
|
this.comm_id = comm_id || utils.uuid();
|
|
|
this._msg_callback = this._close_callback = null;
|
|
|
};
|
|
|
|
|
|
// methods for sending messages
|
|
|
Comm.prototype.open = function (data, callbacks, metadata) {
|
|
|
var content = {
|
|
|
comm_id : this.comm_id,
|
|
|
target_name : this.target_name,
|
|
|
data : data || {},
|
|
|
};
|
|
|
return this.kernel.send_shell_message("comm_open", content, callbacks, metadata);
|
|
|
};
|
|
|
|
|
|
Comm.prototype.send = function (data, callbacks, metadata, buffers) {
|
|
|
var content = {
|
|
|
comm_id : this.comm_id,
|
|
|
data : data || {},
|
|
|
};
|
|
|
return this.kernel.send_shell_message("comm_msg", content, callbacks, metadata, buffers);
|
|
|
};
|
|
|
|
|
|
Comm.prototype.close = function (data, callbacks, metadata) {
|
|
|
var content = {
|
|
|
comm_id : this.comm_id,
|
|
|
data : data || {},
|
|
|
};
|
|
|
return this.kernel.send_shell_message("comm_close", content, callbacks, metadata);
|
|
|
};
|
|
|
|
|
|
// methods for registering callbacks for incoming messages
|
|
|
Comm.prototype._register_callback = function (key, callback) {
|
|
|
this['_' + key + '_callback'] = callback;
|
|
|
};
|
|
|
|
|
|
Comm.prototype.on_msg = function (callback) {
|
|
|
this._register_callback('msg', callback);
|
|
|
};
|
|
|
|
|
|
Comm.prototype.on_close = function (callback) {
|
|
|
this._register_callback('close', callback);
|
|
|
};
|
|
|
|
|
|
// methods for handling incoming messages
|
|
|
|
|
|
Comm.prototype._callback = function (key, msg) {
|
|
|
var callback = this['_' + key + '_callback'];
|
|
|
if (callback) {
|
|
|
try {
|
|
|
callback(msg);
|
|
|
} catch (e) {
|
|
|
console.log("Exception in Comm callback", e, e.stack, msg);
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
Comm.prototype.handle_msg = function (msg) {
|
|
|
this._callback('msg', msg);
|
|
|
};
|
|
|
|
|
|
Comm.prototype.handle_close = function (msg) {
|
|
|
this._callback('close', msg);
|
|
|
};
|
|
|
|
|
|
// For backwards compatability.
|
|
|
IPython.CommManager = CommManager;
|
|
|
IPython.Comm = Comm;
|
|
|
|
|
|
return {
|
|
|
'CommManager': CommManager,
|
|
|
'Comm': Comm
|
|
|
};
|
|
|
});
|
|
|
|