// Copyright (c) Jupyter Development Team. // Distributed under the terms of the Modified BSD License. define([ 'jquery', 'base/js/utils', ], function($, 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, comm_id, buffers) { /** * Create a new Comm, register it, and open its Kernel-side counterpart * Mimics the auto-registration in `Comm.__init__` in the Jupyter Comm. * * argument comm_id is optional */ var comm = new Comm(target_name, comm_id); this.register_comm(comm); comm.open(data, callbacks, metadata, buffers); 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; } var that = this; this.comms[content.comm_id] = this.comms[content.comm_id].then(function(comm) { that.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 }); return this.comms[content.comm_id]; }; 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) { return (Promise.resolve(comm.handle_msg(msg)) .catch(utils.reject('Exception handling comm message')) .then(function() {return comm;})); }); return this.comms[content.comm_id]; }; //----------------------------------------------------------------------- // 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, buffers) { 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, buffers); }; 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, buffers) { var content = { comm_id : this.comm_id, data : data || {}, }; return this.kernel.send_shell_message("comm_close", content, callbacks, metadata, buffers); }; // 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 { return callback(msg); } catch (e) { console.log("Exception in Comm callback", e, e.stack, msg); } } }; Comm.prototype.handle_msg = function (msg) { return this._callback('msg', msg); }; Comm.prototype.handle_close = function (msg) { this._callback('close', msg); }; return { 'CommManager': CommManager, 'Comm': Comm }; });