diff --git a/IPython/html/static/widgets/js/manager.js b/IPython/html/static/widgets/js/manager.js index 60c0f44..07dc383 100644 --- a/IPython/html/static/widgets/js/manager.js +++ b/IPython/html/static/widgets/js/manager.js @@ -172,11 +172,19 @@ define([ WidgetManager.prototype.get_model = function (model_id) { // Look-up a model instance by its id. - var model = this._models[model_id]; - if (model !== undefined && model.id == model_id) { - return model; + var that = this; + var model = that._models[model_id]; + if (model !== undefined) { + return new Promise(function(resolve, reject){ + if (model instanceof Promise) { + model.then(resolve, reject); + } else { + resolve(model); + } + }); + } else { + return undefined; } - return null; }; WidgetManager.prototype._handle_comm_open = function (comm, msg) { @@ -213,30 +221,32 @@ define([ // widget_class: (optional) string // Target name of the widget in the back-end. // comm: (optional) Comm - return new Promise(function(resolve, reject) { + + // Create a comm if it wasn't provided. + var comm = options.comm; + if (!comm) { + comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class}); + } + + var that = this; + var model_id = comm.comm_id; + var promise = new Promise(function(resolve, reject) { // Get the model type using require or through the registry. var widget_type_name = options.model_name; var widget_module = options.model_module; - var that = this; utils.try_load(widget_type_name, widget_module, WidgetManager._model_types) .then(function(ModelType) { - - // Create a comm if it wasn't provided. - var comm = options.comm; - if (!comm) { - comm = that.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class}); - } - - var model_id = comm.comm_id; var widget_model = new ModelType(that, model_id, comm); widget_model.on('comm:close', function () { delete that._models[model_id]; }); that._models[model_id] = widget_model; - reolve(widget_model); + resolve(widget_model); }, reject); }); + this._models[model_id] = promise; + return promise; }; // Backwards compatibility. diff --git a/IPython/html/static/widgets/js/widget.js b/IPython/html/static/widgets/js/widget.js index 0cebd88..6e99190 100644 --- a/IPython/html/static/widgets/js/widget.js +++ b/IPython/html/static/widgets/js/widget.js @@ -92,11 +92,22 @@ define(["widgets/js/manager", // Handle when a widget is updated via the python side. this.state_lock = state; try { - var that = this; - WidgetModel.__super__.set.apply(this, [Object.keys(state).reduce(function(obj, key) { - obj[key] = that._unpack_models(state[key]); - return obj; - }, {})]); + var state_keys = []; + var state_values = []; + for (var state_key in Object.keys(state)) { + if (state.hasOwnProperty(state_key)) { + state_keys.push(state_key); + state_values.push(this._unpack_models(state[state_key])); + } + } + + Promise.all(state_values).then(function(promise_values){ + var unpacked_state = {}; + for (var i = 0; i < state_keys.length; i++) { + unpacked_state[state_keys[i]] = promise_values[i]; + } + WidgetModel.__super__.set.apply(this, [unpacked_state]); + }, console.error); } finally { this.state_lock = null; } @@ -254,30 +265,42 @@ define(["widgets/js/manager", // Replace model ids with models recursively. var that = this; var unpacked; - if ($.isArray(value)) { - unpacked = []; - _.each(value, function(sub_value, key) { - unpacked.push(that._unpack_models(sub_value)); - }); - return unpacked; - - } else if (value instanceof Object) { - unpacked = {}; - _.each(value, function(sub_value, key) { - unpacked[key] = that._unpack_models(sub_value); - }); - return unpacked; + return new Promise(function(resolve, reject) { - } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") { - var model = this.widget_manager.get_model(value.slice(10, value.length)); - if (model) { - return model; + if ($.isArray(value)) { + unpacked = []; + _.each(value, function(sub_value, key) { + unpacked.push(that._unpack_models(sub_value)); + }); + Promise.all(unpacked).then(resolve, reject); + + } else if (value instanceof Object) { + unpacked_values = []; + unpacked_keys = []; + _.each(value, function(sub_value, key) { + unpacked_keys.push(key); + unpacked_values.push(that._unpack_models(sub_value)); + }); + + Promise.all(unpacked_values).then(function(promise_values) { + unpacked = {}; + for (var i = 0; i < unpacked_keys.length; i++) { + unpacked[unpacked_keys[i]] = promise_values[i]; + } + resolve(unpacked); + }, reject); + + } else if (typeof value === 'string' && value.slice(0,10) === "IPY_MODEL_") { + var model = this.widget_manager.get_model(value.slice(10, value.length)); + if (model) { + model.then(resolve, reject); + } else { + resolve(value); + } } else { - return value; - } - } else { - return value; - } + resolve(value); + } + }); }, on_some_change: function(keys, callback, context) { @@ -322,11 +345,11 @@ define(["widgets/js/manager", // // -given a model and (optionally) a view name if the view name is // not given, it defaults to the model's default view attribute. + var that = this; return new Promise(function(resolve, reject) { // TODO: this is hacky, and makes the view depend on this cell attribute and widget manager behavior // it would be great to have the widget manager add the cell metadata // to the subview without having to add it here. - var that = this; options = $.extend({ parent: this }, options || {}); this.model.widget_manager.create_view(child_model, options).then(function(child_view) {