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