##// END OF EJS Templates
Wait for any promises returned by a view's render method before considering the view created...
Jason Grout -
Show More
@@ -1,240 +1,239
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 "underscore",
6 6 "backbone",
7 7 "jquery",
8 8 "base/js/utils",
9 9 "base/js/namespace",
10 10 ], function (_, Backbone, $, utils, IPython) {
11 11 "use strict";
12 12 //--------------------------------------------------------------------
13 13 // WidgetManager class
14 14 //--------------------------------------------------------------------
15 15 var WidgetManager = function (comm_manager, notebook) {
16 16 // Public constructor
17 17 WidgetManager._managers.push(this);
18 18
19 19 // Attach a comm manager to the
20 20 this.keyboard_manager = notebook.keyboard_manager;
21 21 this.notebook = notebook;
22 22 this.comm_manager = comm_manager;
23 23 this._models = {}; /* Dictionary of model ids and model instances */
24 24
25 25 // Register with the comm manager.
26 26 this.comm_manager.register_target('ipython.widget', $.proxy(this._handle_comm_open, this));
27 27 };
28 28
29 29 //--------------------------------------------------------------------
30 30 // Class level
31 31 //--------------------------------------------------------------------
32 32 WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
33 33 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
34 34 WidgetManager._managers = []; /* List of widget managers */
35 35
36 36 WidgetManager.register_widget_model = function (model_name, model_type) {
37 37 // Registers a widget model by name.
38 38 WidgetManager._model_types[model_name] = model_type;
39 39 };
40 40
41 41 WidgetManager.register_widget_view = function (view_name, view_type) {
42 42 // Registers a widget view by name.
43 43 WidgetManager._view_types[view_name] = view_type;
44 44 };
45 45
46 46 //--------------------------------------------------------------------
47 47 // Instance level
48 48 //--------------------------------------------------------------------
49 49 WidgetManager.prototype.display_view = function(msg, model) {
50 50 // Displays a view for a particular model.
51 51 var that = this;
52 52 var cell = this.get_msg_cell(msg.parent_header.msg_id);
53 53 if (cell === null) {
54 54 return Promise.reject(new Error("Could not determine where the display" +
55 55 " message was from. Widget will not be displayed"));
56 56 } else if (cell.widget_subarea) {
57 57 var dummy = $('<div />');
58 58 cell.widget_subarea.append(dummy);
59 59 return this.create_view(model, {cell: cell}).then(
60 60 function(view) {
61 61 that._handle_display_view(view);
62 62 dummy.replaceWith(view.$el);
63 63 view.trigger('displayed');
64 64 return view;
65 65 }).catch(utils.reject('Could not display view', true));
66 66 }
67 67 };
68 68
69 69 WidgetManager.prototype._handle_display_view = function (view) {
70 70 // Have the IPython keyboard manager disable its event
71 71 // handling so the widget can capture keyboard input.
72 72 // Note, this is only done on the outer most widgets.
73 73 if (this.keyboard_manager) {
74 74 this.keyboard_manager.register_events(view.$el);
75 75
76 76 if (view.additional_elements) {
77 77 for (var i = 0; i < view.additional_elements.length; i++) {
78 78 this.keyboard_manager.register_events(view.additional_elements[i]);
79 79 }
80 80 }
81 81 }
82 82 };
83 83
84 84 WidgetManager.prototype.create_view = function(model, options) {
85 85 // Creates a promise for a view of a given model
86 86
87 87 // Make sure the view creation is not out of order with
88 88 // any state updates.
89 89 model.state_change = model.state_change.then(function() {
90 90
91 91 return utils.load_class(model.get('_view_name'), model.get('_view_module'),
92 92 WidgetManager._view_types).then(function(ViewType) {
93 93
94 94 // If a view is passed into the method, use that view's cell as
95 95 // the cell for the view that is created.
96 96 options = options || {};
97 97 if (options.parent !== undefined) {
98 98 options.cell = options.parent.options.cell;
99 99 }
100 100 // Create and render the view...
101 101 var parameters = {model: model, options: options};
102 102 var view = new ViewType(parameters);
103 103 view.listenTo(model, 'destroy', view.remove);
104 view.render();
105 return view;
104 return Promise.resolve(view.render()).then(function() {return view;});
106 105 }).catch(utils.reject("Couldn't create a view for model id '" + String(model.id) + "'", true));
107 106 });
108 107 return model.state_change;
109 108 };
110 109
111 110 WidgetManager.prototype.get_msg_cell = function (msg_id) {
112 111 var cell = null;
113 112 // First, check to see if the msg was triggered by cell execution.
114 113 if (this.notebook) {
115 114 cell = this.notebook.get_msg_cell(msg_id);
116 115 }
117 116 if (cell !== null) {
118 117 return cell;
119 118 }
120 119 // Second, check to see if a get_cell callback was defined
121 120 // for the message. get_cell callbacks are registered for
122 121 // widget messages, so this block is actually checking to see if the
123 122 // message was triggered by a widget.
124 123 var kernel = this.comm_manager.kernel;
125 124 if (kernel) {
126 125 var callbacks = kernel.get_callbacks_for_msg(msg_id);
127 126 if (callbacks && callbacks.iopub &&
128 127 callbacks.iopub.get_cell !== undefined) {
129 128 return callbacks.iopub.get_cell();
130 129 }
131 130 }
132 131
133 132 // Not triggered by a cell or widget (no get_cell callback
134 133 // exists).
135 134 return null;
136 135 };
137 136
138 137 WidgetManager.prototype.callbacks = function (view) {
139 138 // callback handlers specific a view
140 139 var callbacks = {};
141 140 if (view && view.options.cell) {
142 141
143 142 // Try to get output handlers
144 143 var cell = view.options.cell;
145 144 var handle_output = null;
146 145 var handle_clear_output = null;
147 146 if (cell.output_area) {
148 147 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
149 148 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
150 149 }
151 150
152 151 // Create callback dictionary using what is known
153 152 var that = this;
154 153 callbacks = {
155 154 iopub : {
156 155 output : handle_output,
157 156 clear_output : handle_clear_output,
158 157
159 158 // Special function only registered by widget messages.
160 159 // Allows us to get the cell for a message so we know
161 160 // where to add widgets if the code requires it.
162 161 get_cell : function () {
163 162 return cell;
164 163 },
165 164 },
166 165 };
167 166 }
168 167 return callbacks;
169 168 };
170 169
171 170 WidgetManager.prototype.get_model = function (model_id) {
172 171 // Get a promise for a model by model id.
173 172 return this._models[model_id];
174 173 };
175 174
176 175 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
177 176 // Handle when a comm is opened.
178 177 return this.create_model({
179 178 model_name: msg.content.data.model_name,
180 179 model_module: msg.content.data.model_module,
181 180 comm: comm}).catch(utils.reject("Couldn't create a model.", true));
182 181 };
183 182
184 183 WidgetManager.prototype.create_model = function (options) {
185 184 // Create and return a promise for a new widget model
186 185 //
187 186 // Minimally, one must provide the model_name and widget_class
188 187 // parameters to create a model from Javascript.
189 188 //
190 189 // Example
191 190 // --------
192 191 // JS:
193 192 // IPython.notebook.kernel.widget_manager.create_model({
194 193 // model_name: 'WidgetModel',
195 194 // widget_class: 'IPython.html.widgets.widget_int.IntSlider'})
196 195 // .then(function(model) { console.log('Create success!', model); },
197 196 // $.proxy(console.error, console));
198 197 //
199 198 // Parameters
200 199 // ----------
201 200 // options: dictionary
202 201 // Dictionary of options with the following contents:
203 202 // model_name: string
204 203 // Target name of the widget model to create.
205 204 // model_module: (optional) string
206 205 // Module name of the widget model to create.
207 206 // widget_class: (optional) string
208 207 // Target name of the widget in the back-end.
209 208 // comm: (optional) Comm
210 209
211 210 // Create a comm if it wasn't provided.
212 211 var comm = options.comm;
213 212 if (!comm) {
214 213 comm = this.comm_manager.new_comm('ipython.widget', {'widget_class': options.widget_class});
215 214 }
216 215
217 216 var that = this;
218 217 var model_id = comm.comm_id;
219 218 var model_promise = utils.load_class(options.model_name, options.model_module, WidgetManager._model_types)
220 219 .then(function(ModelType) {
221 220 var widget_model = new ModelType(that, model_id, comm);
222 221 widget_model.once('comm:close', function () {
223 222 delete that._models[model_id];
224 223 });
225 224 return widget_model;
226 225
227 226 }, function(error) {
228 227 delete that._models[model_id];
229 228 var wrapped_error = new utils.WrappedError("Couldn't create model", error);
230 229 return Promise.reject(wrapped_error);
231 230 });
232 231 this._models[model_id] = model_promise;
233 232 return model_promise;
234 233 };
235 234
236 235 // Backwards compatibility.
237 236 IPython.WidgetManager = WidgetManager;
238 237
239 238 return {'WidgetManager': WidgetManager};
240 239 });
General Comments 0
You need to be logged in to leave comments. Login now