##// END OF EJS Templates
jshint widget.js
Jonathan Frederic -
Show More
This diff has been collapsed as it changes many lines, (1025 lines changed) Show them Hide them
@@ -1,619 +1,622 b''
1 //----------------------------------------------------------------------------
1 //----------------------------------------------------------------------------
2 // Copyright (C) 2013 The IPython Development Team
2 // Copyright (C) 2013 The IPython Development Team
3 //
3 //
4 // Distributed under the terms of the BSD License. The full license is in
4 // Distributed under the terms of the BSD License. The full license is in
5 // the file COPYING, distributed as part of this software.
5 // the file COPYING, distributed as part of this software.
6 //----------------------------------------------------------------------------
6 //----------------------------------------------------------------------------
7
7
8 //============================================================================
8 //============================================================================
9 // WidgetModel, WidgetView, and WidgetManager
9 // WidgetModel, WidgetView, and WidgetManager
10 //============================================================================
10 //============================================================================
11 /**
11 /**
12 * Base Widget classes
12 * Base Widget classes
13 * @module IPython
13 * @module IPython
14 * @namespace IPython
14 * @namespace IPython
15 * @submodule widget
15 * @submodule widget
16 */
16 */
17
17
18 "use strict";
18 (function () {
19 "use strict";
20
21 // Use require.js 'define' method so that require.js is intelligent enough to
22 // syncronously load everything within this file when it is being 'required'
23 // elsewhere.
24 define(["components/underscore/underscore-min",
25 "components/backbone/backbone-min",
26 ], function(underscore, backbone){
27
28
29 //--------------------------------------------------------------------
30 // WidgetModel class
31 //--------------------------------------------------------------------
32 var WidgetModel = Backbone.Model.extend({
33 constructor: function(comm_manager, comm, widget_manager) {
34 this.comm_manager = comm_manager;
35 this.widget_manager = widget_manager;
36 this.pending_msgs = 0;
37 this.msg_throttle = 3;
38 this.msg_buffer = null;
39 this.views = [];
40 this._custom_msg_callbacks = [];
41
42 // Remember comm associated with the model.
43 this.comm = comm;
44 comm.model = this;
45
46 // Hook comm messages up to model.
47 comm.on_close($.proxy(this._handle_comm_closed, this));
48 comm.on_msg($.proxy(this._handle_comm_msg, this));
49
50 return Backbone.Model.apply(this);
51 },
52
53
54 update_other_views: function(caller) {
55 this.last_modified_view = caller;
56 this.save(this.changedAttributes(), {patch: true});
57
58 for (var view_index in this.views) {
59 var view = this.views[view_index];
60 if (view !== caller) {
61 view.update();
62 }
63 }
64 },
65
19
66
20 // Use require.js 'define' method so that require.js is intelligent enough to
67 send: function(content, cell) {
21 // syncronously load everything within this file when it is being 'required'
22 // elsewhere.
23 define(["components/underscore/underscore-min",
24 "components/backbone/backbone-min",
25 ], function(underscore, backbone){
26
68
69 // Used the last modified view as the sender of the message. This
70 // will insure that any python code triggered by the sent message
71 // can create and display widgets and output.
72 if (cell === undefined) {
73 if (this.last_modified_view !== undefined &&
74 this.last_modified_view.cell !== undefined) {
75 cell = this.last_modified_view.cell;
76 }
77 }
78 var callbacks = this._make_callbacks(cell);
79 var data = {method: 'custom', custom_content: content};
80 this.comm.send(data, callbacks);
81 },
27
82
28 //--------------------------------------------------------------------
29 // WidgetModel class
30 //--------------------------------------------------------------------
31 var WidgetModel = Backbone.Model.extend({
32 constructor: function(comm_manager, comm, widget_manager) {
33 this.comm_manager = comm_manager;
34 this.widget_manager = widget_manager;
35 this.pending_msgs = 0;
36 this.msg_throttle = 3;
37 this.msg_buffer = null;
38 this.views = [];
39 this._custom_msg_callbacks = [];
40
83
41 // Remember comm associated with the model.
84 on_view_displayed: function (callback) {
42 this.comm = comm;
85 this._view_displayed_callback = callback;
43 comm.model = this;
86 },
44
87
45 // Hook comm messages up to model.
46 comm.on_close($.proxy(this._handle_comm_closed, this));
47 comm.on_msg($.proxy(this._handle_comm_msg, this));
48
88
49 return Backbone.Model.apply(this);
89 on_close: function (callback) {
50 },
90 this._close_callback = callback;
91 },
51
92
52
93
53 update_other_views: function(caller) {
94 on_msg: function (callback, remove) {
54 this.last_modified_view = caller;
95 if (remove) {
55 this.save(this.changedAttributes(), {patch: true});
96 var found_index = -1;
97 for (var index in this._custom_msg_callbacks) {
98 if (callback === this._custom_msg_callbacks[index]) {
99 found_index = index;
100 break;
101 }
102 }
56
103
57 for (var view_index in this.views) {
104 if (found_index >= 0) {
58 var view = this.views[view_index];
105 this._custom_msg_callbacks.splice(found_index, 1);
59 if (view !== caller) {
106 }
60 view.update();
107 } else {
61 }
108 this._custom_msg_callbacks.push(callback);
62 }
63 },
64
65
66 send: function(content, cell) {
67
68 // Used the last modified view as the sender of the message. This
69 // will insure that any python code triggered by the sent message
70 // can create and display widgets and output.
71 if (cell === undefined) {
72 if (this.last_modified_view != undefined &&
73 this.last_modified_view.cell != undefined) {
74 cell = this.last_modified_view.cell;
75 }
109 }
76 }
110 },
77 var callbacks = this._make_callbacks(cell);
78 var data = {method: 'custom', custom_content: content};
79 this.comm.send(data, callbacks);
80 },
81
82
111
83 on_view_displayed: function (callback) {
84 this._view_displayed_callback = callback;
85 },
86
112
87
113 _handle_custom_msg: function (content) {
88 on_close: function (callback) {
89 this._close_callback = callback;
90 },
91
92
93 on_msg: function (callback, remove) {
94 if (remove) {
95 var found_index = -1;
96 for (var index in this._custom_msg_callbacks) {
114 for (var index in this._custom_msg_callbacks) {
97 if (callback === this._custom_msg_callbacks[index]) {
115 try {
98 found_index = index;
116 this._custom_msg_callbacks[index](content);
99 break;
117 } catch (e) {
118 console.log("Exception in widget model msg callback", e, content);
100 }
119 }
101 }
120 }
121 },
102
122
103 if (found_index >= 0) {
104 this._custom_msg_callbacks.splice(found_index, 1);
105 }
106 } else {
107 this._custom_msg_callbacks.push(callback)
108 }
109 },
110
123
111
124 // Handle when a widget is closed.
112 _handle_custom_msg: function (content) {
125 _handle_comm_closed: function (msg) {
113 for (var index in this._custom_msg_callbacks) {
126 this._execute_views_method('remove');
114 try {
127 delete this.comm.model; // Delete ref so GC will collect widget model.
115 this._custom_msg_callbacks[index](content);
128 },
116 } catch (e) {
117 console.log("Exception in widget model msg callback", e, content);
118 }
119 }
120 },
121
129
122
130
123 // Handle when a widget is closed.
131 // Handle incomming comm msg.
124 _handle_comm_closed: function (msg) {
132 _handle_comm_msg: function (msg) {
125 this._execute_views_method('remove');
133 var method = msg.content.data.method;
126 delete this.comm.model; // Delete ref so GC will collect widget model.
134 switch (method){
127 },
135 case 'display':
128
136
137 // Try to get the cell.
138 var cell = this._get_msg_cell(msg.parent_header.msg_id);
139 if (cell === null) {
140 console.log("Could not determine where the display" +
141 " message was from. Widget will not be displayed");
142 } else {
143 this._display_view(msg.content.data.view_name,
144 msg.content.data.parent,
145 cell);
146 }
147 break;
148 case 'update':
149 this._handle_update(msg.content.data.state);
150 break;
151 case 'add_class':
152 case 'remove_class':
153 var selector = msg.content.data.selector;
154 if (selector === undefined) {
155 selector = '';
156 }
129
157
130 // Handle incomming comm msg.
158 var class_list = msg.content.data.class_list;
131 _handle_comm_msg: function (msg) {
159 this._execute_views_method(method, selector, class_list);
132 var method = msg.content.data.method;
160 break;
133 switch (method){
161 case 'custom':
134 case 'display':
162 this._handle_custom_msg(msg.content.data.custom_content);
163 break;
164 }
165 },
135
166
136 // Try to get the cell.
137 var cell = this._get_msg_cell(msg.parent_header.msg_id);
138 if (cell == null) {
139 console.log("Could not determine where the display" +
140 " message was from. Widget will not be displayed")
141 } else {
142 this._display_view(msg.content.data.view_name,
143 msg.content.data.parent,
144 cell);
145 }
146 break;
147 case 'update':
148 this._handle_update(msg.content.data.state);
149 break;
150 case 'add_class':
151 case 'remove_class':
152 var selector = msg.content.data.selector;
153 if (selector === undefined) {
154 selector = '';
155 }
156
167
157 var class_list = msg.content.data.class_list;
168 // Handle when a widget is updated via the python side.
158 this._execute_views_method(method, selector, class_list);
169 _handle_update: function (state) {
159 break;
170 this.updating = true;
160 case 'custom':
171 try {
161 this._handle_custom_msg(msg.content.data.custom_content);
172 for (var key in state) {
162 break;
173 if (state.hasOwnProperty(key)) {
163 }
174 if (key == "_css"){
164 },
175
165
176 // Set the css value of the model as an attribute
166
177 // instead of a backbone trait because we are only
167 // Handle when a widget is updated via the python side.
178 // interested in backend css -> frontend css. In
168 _handle_update: function (state) {
179 // other words, if the css dict changes in the
169 this.updating = true;
180 // frontend, we don't need to push the changes to
170 try {
181 // the backend.
171 for (var key in state) {
182 this.css = state[key];
172 if (state.hasOwnProperty(key)) {
183 } else {
173 if (key == "_css"){
184 this.set(key, state[key]);
174
185 }
175 // Set the css value of the model as an attribute
176 // instead of a backbone trait because we are only
177 // interested in backend css -> frontend css. In
178 // other words, if the css dict changes in the
179 // frontend, we don't need to push the changes to
180 // the backend.
181 this.css = state[key];
182 } else {
183 this.set(key, state[key]);
184 }
186 }
185 }
187 }
188 this.id = this.comm.comm_id;
189 this.save();
190 } finally {
191 this.updating = false;
186 }
192 }
187 this.id = this.comm.comm_id;
193 },
188 this.save();
189 } finally {
190 this.updating = false;
191 }
192 },
193
194
194
195
195 _handle_status: function (cell, msg) {
196 _handle_status: function (cell, msg) {
196 //execution_state : ('busy', 'idle', 'starting')
197 //execution_state : ('busy', 'idle', 'starting')
197 if (msg.content.execution_state=='idle') {
198 if (msg.content.execution_state=='idle') {
198
199
199 // Send buffer if this message caused another message to be
200 // Send buffer if this message caused another message to be
200 // throttled.
201 // throttled.
201 if (this.msg_buffer != null &&
202 if (this.msg_buffer !== null &&
202 this.msg_throttle == this.pending_msgs) {
203 this.msg_throttle == this.pending_msgs) {
203
204
204 var cell = this._get_msg_cell(msg.parent_header.msg_id);
205 var callbacks = this._make_callbacks(cell);
205 var callbacks = this._make_callbacks(cell);
206 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
206 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
207 this.comm.send(data, callbacks);
207 this.comm.send(data, callbacks);
208 this.msg_buffer = null;
208 this.msg_buffer = null;
209 } else {
209 } else {
210
210
211 // Only decrease the pending message count if the buffer
211 // Only decrease the pending message count if the buffer
212 // doesn't get flushed (sent).
212 // doesn't get flushed (sent).
213 --this.pending_msgs;
213 --this.pending_msgs;
214 }
214 }
215 }
215 }
216 },
216 },
217
217
218
218
219 // Custom syncronization logic.
219 // Custom syncronization logic.
220 _handle_sync: function (method, options) {
220 _handle_sync: function (method, options) {
221 var model_json = this.toJSON();
221 var model_json = this.toJSON();
222 var attr;
222
223
223 // Only send updated state if the state hasn't been changed
224 // Only send updated state if the state hasn't been changed
224 // during an update.
225 // during an update.
225 if (!this.updating) {
226 if (!this.updating) {
226 if (this.pending_msgs >= this.msg_throttle) {
227 if (this.pending_msgs >= this.msg_throttle) {
227 // The throttle has been exceeded, buffer the current msg so
228 // The throttle has been exceeded, buffer the current msg so
228 // it can be sent once the kernel has finished processing
229 // it can be sent once the kernel has finished processing
229 // some of the existing messages.
230 // some of the existing messages.
230 if (method=='patch') {
231 if (method=='patch') {
231 if (this.msg_buffer == null) {
232 if (this.msg_buffer === null) {
233 this.msg_buffer = $.extend({}, model_json); // Copy
234 }
235 for (attr in options.attrs) {
236 this.msg_buffer[attr] = options.attrs[attr];
237 }
238 } else {
232 this.msg_buffer = $.extend({}, model_json); // Copy
239 this.msg_buffer = $.extend({}, model_json); // Copy
233 }
240 }
234 for (var attr in options.attrs) {
235 this.msg_buffer[attr] = options.attrs[attr];
236 }
237 } else {
238 this.msg_buffer = $.extend({}, model_json); // Copy
239 }
240
241
241 } else {
242 } else {
242 // We haven't exceeded the throttle, send the message like
243 // We haven't exceeded the throttle, send the message like
243 // normal. If this is a patch operation, just send the
244 // normal. If this is a patch operation, just send the
244 // changes.
245 // changes.
245 var send_json = model_json;
246 var send_json = model_json;
246 if (method=='patch') {
247 if (method=='patch') {
247 send_json = {};
248 send_json = {};
248 for (var attr in options.attrs) {
249 for (attr in options.attrs) {
249 send_json[attr] = options.attrs[attr];
250 send_json[attr] = options.attrs[attr];
251 }
250 }
252 }
251 }
252
253
253 var data = {method: 'backbone', sync_method: method, sync_data: send_json};
254 var data = {method: 'backbone', sync_method: method, sync_data: send_json};
254
255
255 var cell = null;
256 var cell = null;
256 if (this.last_modified_view != undefined && this.last_modified_view != null) {
257 if (this.last_modified_view !== undefined && this.last_modified_view !== null) {
257 cell = this.last_modified_view.cell;
258 cell = this.last_modified_view.cell;
259 }
260
261 var callbacks = this._make_callbacks(cell);
262 this.comm.send(data, callbacks);
263 this.pending_msgs++;
258 }
264 }
259
260 var callbacks = this._make_callbacks(cell);
261 this.comm.send(data, callbacks);
262 this.pending_msgs++;
263 }
265 }
264 }
266
265
267 // Since the comm is a one-way communication, assume the message
266 // Since the comm is a one-way communication, assume the message
268 // arrived.
267 // arrived.
269 return model_json;
268 return model_json;
270 },
269 },
271
270
272
271
273 _handle_view_displayed: function(view) {
272 _handle_view_displayed: function(view) {
274 if (this._view_displayed_callback) {
273 if (this._view_displayed_callback) {
275 try {
274 try {
276 this._view_displayed_callback(view);
275 this._view_displayed_callback(view)
277 } catch (e) {
276 } catch (e) {
278 console.log("Exception in widget model view displayed callback", e, view, this);
277 console.log("Exception in widget model view displayed callback", e, view, this);
279 }
278 }
280 }
279 }
281 },
280 },
281
282
282
283
283 _execute_views_method: function (/* method_name, [argument0], [argument1], [...] */) {
284 _execute_views_method: function (/* method_name, [argument0], [argument1], [...] */) {
284 var method_name = arguments[0];
285 var method_name = arguments[0];
285 var args = null;
286 var args = null;
286 if (arguments.length > 1) {
287 if (arguments.length > 1) {
287 args = [].splice.call(arguments,1);
288 args = [].splice.call(arguments,1);
288 }
289 }
289
290
290 for (var view_index in this.views) {
291 for (var view_index in this.views) {
291 var view = this.views[view_index];
292 var view = this.views[view_index];
292 var method = view[method_name];
293 var method = view[method_name];
293 if (args === null) {
294 if (args === null) {
294 method.apply(view);
295 method.apply(view);
295 } else {
296 } else {
296 method.apply(view, args);
297 method.apply(view, args);
298 }
297 }
299 }
298 }
300 },
299 },
301
300
302
301
303 // Create view that represents the model.
302 // Create view that represents the model.
304 _display_view: function (view_name, parent_comm_id, cell) {
303 _display_view: function (view_name, parent_comm_id, cell) {
305 var new_views = [];
304 var new_views = [];
306 var view;
305
307
306 // Try creating and adding the view to it's parent.
308 // Try creating and adding the view to it's parent.
307 var displayed = false;
309 var displayed = false;
308 if (parent_comm_id != undefined) {
310 if (parent_comm_id !== undefined) {
309 var parent_comm = this.comm_manager.comms[parent_comm_id];
311 var parent_comm = this.comm_manager.comms[parent_comm_id];
310 var parent_model = parent_comm.model;
312 var parent_model = parent_comm.model;
311 var parent_views = parent_model.views;
313 var parent_views = parent_model.views;
312 for (var parent_view_index in parent_views) {
314 for (var parent_view_index in parent_views) {
313 var parent_view = parent_views[parent_view_index];
315 var parent_view = parent_views[parent_view_index];
314 if (parent_view.cell === cell) {
316 if (parent_view.cell === cell) {
315 if (parent_view.display_child != undefined) {
317 if (parent_view.display_child !== undefined) {
316 var view = this._create_view(view_name, cell);
318 view = this._create_view(view_name, cell);
317 if (view != null) {
319 if (view !== null) {
318 new_views.push(view);
320 new_views.push(view);
319 parent_view.display_child(view);
321 parent_view.display_child(view);
320 displayed = true;
322 displayed = true;
321 this._handle_view_displayed(view);
323 this._handle_view_displayed(view);
322 }
324 }
323 }
325 }
326 }
324 }
327 }
325 }
328 }
326 }
327
329
328 // If no parent view is defined or exists. Add the view's
330 // If no parent view is defined or exists. Add the view's
329 // element to cell's widget div.
331 // element to cell's widget div.
330 if (!displayed) {
332 if (!displayed) {
331 var view = this._create_view(view_name, cell);
333 view = this._create_view(view_name, cell);
332 if (view != null) {
334 if (view !== null) {
333 new_views.push(view);
335 new_views.push(view);
334
336
335 if (cell.widget_subarea != undefined && cell.widget_subarea != null) {
337 if (cell.widget_subarea !== undefined && cell.widget_subarea !== null) {
336 cell.widget_area.show();
338 cell.widget_area.show();
337 cell.widget_subarea.append(view.$el);
339 cell.widget_subarea.append(view.$el);
338 this._handle_view_displayed(view);
340 this._handle_view_displayed(view);
341 }
339 }
342 }
340 }
343 }
341 }
342
344
343 // Force the new view(s) to update their selves
345 // Force the new view(s) to update their selves
344 for (var view_index in new_views) {
346 for (var view_index in new_views) {
345 var view = new_views[view_index];
347 view = new_views[view_index];
346 view.update();
348 view.update();
347 }
349 }
348 },
350 },
349
351
350
352
351 // Create a view
353 // Create a view
352 _create_view: function (view_name, cell) {
354 _create_view: function (view_name, cell) {
353 var view_type = this.widget_manager.widget_view_types[view_name];
355 var ViewType = this.widget_manager.widget_view_types[view_name];
354 if (view_type != undefined && view_type != null) {
356 if (ViewType !== undefined && ViewType !== null) {
355 var view = new view_type({model: this});
357 var view = new ViewType({model: this});
356 view.render();
358 view.render();
357 this.views.push(view);
359 this.views.push(view);
358 view.cell = cell;
360 view.cell = cell;
359
361
360 // Handle when the view element is remove from the page.
362 // Handle when the view element is remove from the page.
361 var that = this;
363 var that = this;
362 view.$el.on("remove", function(){
364 view.$el.on("remove", function(){
363 var index = that.views.indexOf(view);
365 var index = that.views.indexOf(view);
364 if (index > -1) {
366 if (index > -1) {
365 that.views.splice(index, 1);
367 that.views.splice(index, 1);
366 }
368 }
367 view.remove(); // Clean-up view
369 view.remove(); // Clean-up view
368
370
369 // Close the comm if there are no views left.
371 // Close the comm if there are no views left.
370 if (that.views.length()==0) {
372 if (that.views.length() === 0) {
371 if (that._close_callback) {
373 if (that._close_callback) {
372 try {
374 try {
373 that._close_callback(that)
375 that._close_callback(that);
374 } catch (e) {
376 } catch (e) {
375 console.log("Exception in widget model close callback", e, that);
377 console.log("Exception in widget model close callback", e, that);
378 }
376 }
379 }
380 that.comm.close();
381 delete that.comm.model; // Delete ref so GC will collect widget model.
377 }
382 }
378 that.comm.close();
383 });
379 delete that.comm.model; // Delete ref so GC will collect widget model.
384 return view;
380 }
381 });
382 return view;
383 }
384 return null;
385 },
386
387
388 // Build a callback dict.
389 _make_callbacks: function (cell) {
390 var callbacks = {};
391 if (cell != null) {
392
393 // Try to get output handlers
394 var handle_output = null;
395 var handle_clear_output = null;
396 if (cell.output_area != undefined && cell.output_area != null) {
397 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
398 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
399 }
385 }
386 return null;
387 },
400
388
401 // Create callback dict usign what is known
402 var that = this;
403 callbacks = {
404 iopub : {
405 output : handle_output,
406 clear_output : handle_clear_output,
407
389
408 status : function(msg){
390 // Build a callback dict.
409 that._handle_status(cell, msg);
391 _make_callbacks: function (cell) {
410 },
392 var callbacks = {};
393 if (cell !== null) {
394
395 // Try to get output handlers
396 var handle_output = null;
397 var handle_clear_output = null;
398 if (cell.output_area !== undefined && cell.output_area !== null) {
399 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
400 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
401 }
411
402
412 // Special function only registered by widget messages.
403 // Create callback dict usign what is known
413 // Allows us to get the cell for a message so we know
404 var that = this;
414 // where to add widgets if the code requires it.
405 callbacks = {
415 get_cell : function() {
406 iopub : {
416 return cell;
407 output : handle_output,
408 clear_output : handle_clear_output,
409
410 status : function(msg){
411 that._handle_status(cell, msg);
412 },
413
414 // Special function only registered by widget messages.
415 // Allows us to get the cell for a message so we know
416 // where to add widgets if the code requires it.
417 get_cell : function() {
418 return cell;
419 },
417 },
420 },
418 },
421 };
419 };
422 }
420 }
423 return callbacks;
421 return callbacks;
424 },
422 },
423
425
424
426
425 // Get the output area corresponding to the msg_id.
427 // Get the output area corresponding to the msg_id.
426 // cell is an instance of IPython.Cell
428 // cell is an instance of IPython.Cell
427 _get_msg_cell: function (msg_id) {
429 _get_msg_cell: function (msg_id) {
428
430
429 // First, check to see if the msg was triggered by cell execution.
431 // First, check to see if the msg was triggered by cell execution.
430 var cell = this.widget_manager.get_msg_cell(msg_id);
432 var cell = this.widget_manager.get_msg_cell(msg_id);
431 if (cell != null) {
433 if (cell !== null) {
432 return cell;
434 return cell;
433 }
435 }
434
436
435 // Second, check to see if a get_cell callback was defined
437 // Second, check to see if a get_cell callback was defined
436 // for the message. get_cell callbacks are registered for
438 // for the message. get_cell callbacks are registered for
437 // widget messages, so this block is actually checking to see if the
439 // widget messages, so this block is actually checking to see if the
438 // message was triggered by a widget.
440 // message was triggered by a widget.
439 var kernel = this.comm_manager.kernel;
441 var kernel = this.comm_manager.kernel;
440 var callbacks = kernel.get_callbacks_for_msg(msg_id);
442 var callbacks = kernel.get_callbacks_for_msg(msg_id);
441 if (callbacks != undefined &&
443 if (callbacks !== undefined &&
442 callbacks.iopub != undefined &&
444 callbacks.iopub !== undefined &&
443 callbacks.iopub.get_cell != undefined) {
445 callbacks.iopub.get_cell !== undefined) {
444
446
445 return callbacks.iopub.get_cell();
447 return callbacks.iopub.get_cell();
446 }
448 }
447
449
448 // Not triggered by a cell or widget (no get_cell callback
450 // Not triggered by a cell or widget (no get_cell callback
449 // exists).
451 // exists).
450 return null;
452 return null;
451 },
453 },
452
454
453 });
455 });
454
456
455
457
456 //--------------------------------------------------------------------
458 //--------------------------------------------------------------------
457 // WidgetView class
459 // WidgetView class
458 //--------------------------------------------------------------------
460 //--------------------------------------------------------------------
459 var WidgetView = Backbone.View.extend({
461 var WidgetView = Backbone.View.extend({
460
462
461 initialize: function() {
463 initialize: function() {
462 this.visible = true;
464 this.visible = true;
463 this.model.on('sync',this.update,this);
465 this.model.on('sync',this.update,this);
464 },
466 },
467
468 add_class: function(selector, class_list){
469 var elements = this._get_selector_element(selector);
470 if (elements.length > 0) {
471 elements.addClass(class_list);
472 }
473 },
474
475 remove_class: function(selector, class_list){
476 var elements = this._get_selector_element(selector);
477 if (elements.length > 0) {
478 elements.removeClass(class_list);
479 }
480 },
465
481
466 add_class: function(selector, class_list){
467 var elements = this._get_selector_element(selector);
468 if (elements.length > 0) {
469 elements.addClass(class_list);
470 }
471 },
472
482
473 remove_class: function(selector, class_list){
483 send: function(content) {
474 var elements = this._get_selector_element(selector);
484 this.model.send(content, this.cell);
475 if (elements.length > 0) {
485 },
476 elements.removeClass(class_list);
486
477 }
487 update: function() {
478 },
488 if (this.model.get('visible') !== undefined) {
479
489 if (this.visible != this.model.get('visible')) {
480
490 this.visible = this.model.get('visible');
481 send: function(content) {
491 if (this.visible) {
482 this.model.send(content, this.cell);
492 this.$el.show();
483 },
493 } else {
484
494 this.$el.hide();
485 update: function() {
495 }
486 if (this.model.get('visible') != undefined) {
487 if (this.visible != this.model.get('visible')) {
488 this.visible = this.model.get('visible');
489 if (this.visible) {
490 this.$el.show();
491 } else {
492 this.$el.hide();
493 }
496 }
494 }
497 }
495 }
496
498
497 if (this.model.css != undefined) {
499 if (this.model.css !== undefined) {
498 for (var selector in this.model.css) {
500 for (var selector in this.model.css) {
499 if (this.model.css.hasOwnProperty(selector)) {
501 if (this.model.css.hasOwnProperty(selector)) {
500
502
501 // Apply the css traits to all elements that match the selector.
503 // Apply the css traits to all elements that match the selector.
502 var elements = this._get_selector_element(selector);
504 var elements = this._get_selector_element(selector);
503 if (elements.length > 0) {
505 if (elements.length > 0) {
504 var css_traits = this.model.css[selector];
506 var css_traits = this.model.css[selector];
505 for (var css_key in css_traits) {
507 for (var css_key in css_traits) {
506 if (css_traits.hasOwnProperty(css_key)) {
508 if (css_traits.hasOwnProperty(css_key)) {
507 elements.css(css_key, css_traits[css_key]);
509 elements.css(css_key, css_traits[css_key]);
510 }
508 }
511 }
509 }
512 }
510 }
513 }
511 }
514 }
512 }
515 }
513 }
516 },
514 },
517
515
518 _get_selector_element: function(selector) {
516 _get_selector_element: function(selector) {
519 // Get the elements via the css selector. If the selector is
517 // Get the elements via the css selector. If the selector is
520 // blank, apply the style to the $el_to_style element. If
518 // blank, apply the style to the $el_to_style element. If
521 // the $el_to_style element is not defined, use apply the
519 // the $el_to_style element is not defined, use apply the
522 // style to the view's element.
520 // style to the view's element.
523 var elements = this.$el.find(selector);
521 var elements = this.$el.find(selector);
524 if (selector === undefined || selector === null || selector === '') {
522 if (selector===undefined || selector===null || selector=='') {
525 if (this.$el_to_style === undefined) {
523 if (this.$el_to_style == undefined) {
526 elements = this.$el;
524 elements = this.$el;
527 } else {
525 } else {
528 elements = this.$el_to_style;
526 elements = this.$el_to_style;
529 }
527 }
530 }
528 }
531 return elements;
529 return elements;
532 },
530 },
533 });
531 });
534
535
536 //--------------------------------------------------------------------
537 // WidgetManager class
538 //--------------------------------------------------------------------
539 var WidgetManager = function(){
540 this.comm_manager = null;
541 this.widget_model_types = {};
542 this.widget_view_types = {};
543
544 var that = this;
545 Backbone.sync = function(method, model, options, error) {
546 var result = model._handle_sync(method, options);
547 if (options.success) {
548 options.success(result);
549 }
550 };
551 };
532
552
533
553
534 //--------------------------------------------------------------------
554 WidgetManager.prototype.attach_comm_manager = function (comm_manager) {
535 // WidgetManager class
555 this.comm_manager = comm_manager;
536 //--------------------------------------------------------------------
556
537 var WidgetManager = function(){
557 // Register already register widget model types with the comm manager.
538 this.comm_manager = null;
558 for (var widget_model_name in this.widget_model_types) {
539 this.widget_model_types = {};
559 this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_com_open, this));
540 this.widget_view_types = {};
541
542 var that = this;
543 Backbone.sync = function(method, model, options, error) {
544 var result = model._handle_sync(method, options);
545 if (options.success) {
546 options.success(result);
547 }
560 }
548 };
561 };
549 }
550
562
551
563
552 WidgetManager.prototype.attach_comm_manager = function (comm_manager) {
564 WidgetManager.prototype.register_widget_model = function (widget_model_name, widget_model_type) {
553 this.comm_manager = comm_manager;
565 // Register the widget with the comm manager. Make sure to pass this object's context
566 // in so `this` works in the call back.
567 if (this.comm_manager !== null) {
568 this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_com_open, this));
569 }
570 this.widget_model_types[widget_model_name] = widget_model_type;
571 };
554
572
555 // Register already register widget model types with the comm manager.
556 for (var widget_model_name in this.widget_model_types) {
557 this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_com_open, this));
558 }
559 }
560
573
574 WidgetManager.prototype.register_widget_view = function (widget_view_name, widget_view_type) {
575 this.widget_view_types[widget_view_name] = widget_view_type;
576 };
561
577
562 WidgetManager.prototype.register_widget_model = function (widget_model_name, widget_model_type) {
563 // Register the widget with the comm manager. Make sure to pass this object's context
564 // in so `this` works in the call back.
565 if (this.comm_manager!=null) {
566 this.comm_manager.register_target(widget_model_name, $.proxy(this._handle_com_open, this));
567 }
568 this.widget_model_types[widget_model_name] = widget_model_type;
569 }
570
578
571
579 WidgetManager.prototype.get_msg_cell = function (msg_id) {
572 WidgetManager.prototype.register_widget_view = function (widget_view_name, widget_view_type) {
580 if (IPython.notebook !== undefined && IPython.notebook !== null) {
573 this.widget_view_types[widget_view_name] = widget_view_type;
581 return IPython.notebook.get_msg_cell(msg_id);
574 }
582 }
583 };
584
585
586 WidgetManager.prototype.on_create_widget = function (callback) {
587 this._create_widget_callback = callback;
588 };
575
589
576
590
577 WidgetManager.prototype.get_msg_cell = function (msg_id) {
591 WidgetManager.prototype._handle_create_widget = function (widget_model) {
578 if (IPython.notebook != undefined && IPython.notebook != null) {
592 if (this._create_widget_callback) {
579 return IPython.notebook.get_msg_cell(msg_id);
593 try {
580 }
594 this._create_widget_callback(widget_model);
581 }
595 } catch (e) {
582
596 console.log("Exception in WidgetManager callback", e, widget_model);
583
597 }
584 WidgetManager.prototype.on_create_widget = function (callback) {
585 this._create_widget_callback = callback;
586 }
587
588
589 WidgetManager.prototype._handle_create_widget = function (widget_model) {
590 if (this._create_widget_callback) {
591 try {
592 this._create_widget_callback(widget_model);
593 } catch (e) {
594 console.log("Exception in WidgetManager callback", e, widget_model);
595 }
598 }
596 }
599 };
597 }
598
600
599
601
600 WidgetManager.prototype._handle_com_open = function (comm, msg) {
602 WidgetManager.prototype._handle_com_open = function (comm, msg) {
601 var widget_type_name = msg.content.target_name;
603 var widget_type_name = msg.content.target_name;
602 var widget_model = new this.widget_model_types[widget_type_name](this.comm_manager, comm, this);
604 var widget_model = new this.widget_model_types[widget_type_name](this.comm_manager, comm, this);
603 this._handle_create_widget(widget_model);
605 this._handle_create_widget(widget_model);
604 }
606 };
605
607
606
608
607 //--------------------------------------------------------------------
609 //--------------------------------------------------------------------
608 // Init code
610 // Init code
609 //--------------------------------------------------------------------
611 //--------------------------------------------------------------------
610 IPython.WidgetManager = WidgetManager;
612 IPython.WidgetManager = WidgetManager;
611 IPython.WidgetModel = WidgetModel;
613 IPython.WidgetModel = WidgetModel;
612 IPython.WidgetView = WidgetView;
614 IPython.WidgetView = WidgetView;
613
615
614 if (IPython.widget_manager==undefined || IPython.widget_manager==null) {
616 if (IPython.widget_manager === undefined || IPython.widget_manager === null) {
615 IPython.widget_manager = new WidgetManager();
617 IPython.widget_manager = new WidgetManager();
616 }
618 }
617
619
618 return IPython.widget_manager;
620 return IPython.widget_manager;
619 });
621 });
622 }()); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now