##// END OF EJS Templates
Changed parent/child api widgets
Jonathan Frederic -
Show More
@@ -1,351 +1,351 b''
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // WidgetModel, WidgetView, and WidgetManager
10 10 //============================================================================
11 11 /**
12 12 * Base Widget classes
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule widget
16 16 */
17 17
18 18 "use strict";
19 19
20 20 // Use require.js 'define' method so that require.js is intelligent enough to
21 21 // syncronously load everything within this file when it is being 'required'
22 22 // elsewhere.
23 23 define(["../../components/underscore/underscore-min.js",
24 24 "../../components/backbone/backbone-min.js",
25 25 ], function(){
26 26
27 27 // Only run once on a notebook.
28 28 if (IPython.notebook.widget_manager == undefined) {
29 29
30 30 //--------------------------------------------------------------------
31 31 // WidgetModel class
32 32 //--------------------------------------------------------------------
33 33 var WidgetModel = Backbone.Model.extend({
34 34 constructor: function(comm_manager, comm, widget_view_types) {
35 35 this.comm_manager = comm_manager;
36 36 this.widget_view_types = widget_view_types;
37 37 this.pending_msgs = 0;
38 38 this.msg_throttle = 3;
39 39 this.msg_buffer = {};
40 40 this.views = {};
41 41
42 42 // Remember comm associated with the model.
43 43 this.comm = comm;
44 44 comm.model = this;
45 45
46 46 // Hook comm messages up to model.
47 47 comm.on_close($.proxy(this.handle_comm_closed, this));
48 48 comm.on_msg($.proxy(this.handle_comm_msg, this));
49 49
50 50 return Backbone.Model.apply(this);
51 51 },
52 52
53 53
54 54 update_other_views: function(caller) {
55 55 this.last_modified_view = caller;
56 56 this.save(this.changedAttributes(), {patch: true});
57 57
58 58 for (var cell_index in this.views) {
59 59 var view = this.views[cell_index];
60 60 if (view !== caller) {
61 61 view.update();
62 62 }
63 63 }
64 64 },
65 65
66 66
67 67 handle_status: function (output_area, msg) {
68 68 //execution_state : ('busy', 'idle', 'starting')
69 69 if (msg.content.execution_state=='idle') {
70 70
71 71 // Send buffer if this message caused another message to be
72 72 // throttled.
73 73 if (this.msg_throttle == this.pending_msgs &&
74 74 this.msg_buffer.length > 0) {
75 75
76 76 var output_area = this._get_msg_output_area(msg);
77 77 var callbacks = this._make_callbacks(output_area);
78 78 var data = {sync_method: 'patch', sync_data: this.msg_buffer};
79 79 comm.send(data, callbacks);
80 80 this.msg_buffer = {};
81 81 } else {
82 82
83 83 // Only decrease the pending message count if the buffer
84 84 // doesn't get flushed (sent).
85 85 --this.pending_msgs;
86 86 }
87 87 }
88 88 },
89 89
90 90
91 91 // Custom syncronization logic.
92 92 handle_sync: function (method, options) {
93 93 var model_json = this.toJSON();
94 94
95 95 // Only send updated state if the state hasn't been changed
96 96 // during an update.
97 97 if (!this.updating) {
98 98 if (this.pending_msgs >= this.msg_throttle) {
99 99 // The throttle has been exceeded, buffer the current msg so
100 100 // it can be sent once the kernel has finished processing
101 101 // some of the existing messages.
102 102 if (method=='patch') {
103 103 for (var attr in options.attrs) {
104 104 this.msg_buffer[attr] = options.attrs[attr];
105 105 }
106 106 } else {
107 107 this.msg_buffer = $.extend({}, model_json); // Copy
108 108 }
109 109
110 110 } else {
111 111 // We haven't exceeded the throttle, send the message like
112 112 // normal. If this is a patch operation, just send the
113 113 // changes.
114 114 var send_json = model_json;
115 115 if (method=='patch') {
116 116 send_json = {};
117 117 for (var attr in options.attrs) {
118 118 send_json[attr] = options.attrs[attr];
119 119 }
120 120 }
121 121
122 122 var data = {sync_method: method, sync_data: send_json};
123 123 var output_area = this._get_view_output_area(this.last_modified_view);
124 124 var callbacks = this._make_callbacks(output_area);
125 125 this.comm.send(data, callbacks);
126 126 this.pending_msgs++;
127 127 }
128 128 }
129 129
130 130 // Since the comm is a one-way communication, assume the message
131 131 // arrived.
132 132 return model_json;
133 133 },
134 134
135 135
136 136 // Handle incomming comm msg.
137 137 handle_comm_msg: function (msg) {
138 138 var method = msg.content.data.method;
139 139 switch (method){
140 140 case 'display':
141 141
142 142 ////////////////////////// TODO: Get cell index via currently executing cell.
143 143 var cell_index = IPython.notebook.get_selected_index()-1;
144 144
145 145 this.display_view(msg.content.data.view_name,
146 146 msg.content.data.parent,
147 147 cell_index);
148 148 break;
149 149 case 'update':
150 150 this.handle_update(msg.content.data.state);
151 151 break;
152 152 }
153 153 },
154 154
155 155
156 156 // Handle when a widget is updated via the python side.
157 157 handle_update: function (state) {
158 158 this.updating = true;
159 159 try {
160 160 for (var key in state) {
161 161 if (state.hasOwnProperty(key)) {
162 162 if (key == "_css"){
163 163 this.css = state[key];
164 164 } else {
165 165 this.set(key, state[key]);
166 166 }
167 167 }
168 168 }
169 169 this.id = this.comm.comm_id;
170 170 this.save();
171 171 } finally {
172 172 this.updating = false;
173 173 }
174 174 },
175 175
176 176
177 177 // Handle when a widget is closed.
178 178 handle_comm_closed: function (msg) {
179 179 for (var cell_index in this.views) {
180 180 var view = this.views[cell_index];
181 181 view.remove();
182 182 }
183 183 },
184 184
185 185
186 186 // Create view that represents the model.
187 187 display_view: function (view_name, parent_comm_id, cell_index) {
188 188 var view = new this.widget_view_types[view_name]({model: this});
189 189 view.render();
190 190 this.views[cell_index] = view;
191 191 view.cell_index = cell_index;
192 192
193 193 // Handle when the view element is remove from the page.
194 194 var that = this;
195 195 view.$el.on("remove", function(){
196 196 var index = that.views.indexOf(view);
197 197 if (index > -1) {
198 198 that.views.splice(index, 1);
199 199 }
200 200 view.remove(); // Clean-up view
201 201
202 202 // Close the comm if there are no views left.
203 203 if (that.views.length()==0) {
204 204 that.comm.close();
205 205 }
206 206 });
207 207
208 208 var displayed = false;
209 209 if (parent_comm_id != undefined) {
210 210 var parent_comm = this.comm_manager.comms[parent_comm_id];
211 211 var parent_model = parent_comm.model;
212 212 var parent_view = parent_model.views[cell_index];
213 213 if (parent_view.display_child != undefined) {
214 parent_view.display_child(view.$el);
214 parent_view.display_child(view);
215 215 displayed = true;
216 216 }
217 217 }
218 218
219 219 if (!displayed) {
220 220 // No parent view is defined or exists. Add the view's
221 221 // element to cell's widget div.
222 222 var cell = IPython.notebook.get_cell(cell_index);
223 223 cell.element.find('.widget_area').find('.widget_subarea')
224 224 .append(view.$el)
225 225 .parent().show(); // Show the widget_area (parent of widget_subarea)
226 226
227 227 }
228 228
229 229 // Update the view based on the model contents.
230 230 view.update();
231 231 },
232 232
233 233
234 234 // Build a callback dict.
235 235 _make_callbacks: function (output_area) {
236 236 var callbacks = {};
237 237 if (output_area != null) {
238 238 var that = this;
239 239 callbacks = {
240 240 iopub : {
241 241 output : $.proxy(output_area.handle_output, output_area),
242 242 clear_output : $.proxy(output_area.handle_clear_output, output_area),
243 243 status : function(msg){
244 244 that.handle_status(output_area, msg);
245 245 },
246 246 },
247 247 };
248 248 }
249 249 return callbacks;
250 250 },
251 251
252 252
253 253 // Get the cell output area corresponding to the view.
254 254 _get_view_output_area: function (view) {
255 255 return this._get_cell_output_area(view.cell_index);
256 256 },
257 257
258 258
259 259 // Get the cell output area corresponding to the cell id.
260 260 _get_cell_output_area: function (cell_id) {
261 261 var cell = IPython.notebook.get_cell(cell_id)
262 262 return cell.output_area;
263 263 },
264 264 });
265 265
266 266
267 267 //--------------------------------------------------------------------
268 268 // WidgetView class
269 269 //--------------------------------------------------------------------
270 270 var WidgetView = Backbone.View.extend({
271 271
272 272 initialize: function() {
273 273 this.model.on('change',this.update,this);
274 274 },
275 275
276 276 update: function() {
277 277 if (this.model.css != undefined) {
278 278 for (var selector in this.model.css) {
279 279 if (this.model.css.hasOwnProperty(selector)) {
280 280
281 281 // Get the elements via the css selector. If the selector is
282 282 // blank, assume the current element is the target.
283 283 var elements = this.$el.find(selector);
284 284 if (selector=='') {
285 285 elements = this.$el;
286 286 }
287 287
288 288 // Apply the css traits to all elements that match the selector.
289 289 if (elements.length>0){
290 290 var css_traits = this.model.css[selector];
291 291 for (var css_key in css_traits) {
292 292 if (css_traits.hasOwnProperty(css_key)) {
293 293 elements.css(css_key, css_traits[css_key]);
294 294 }
295 295 }
296 296 }
297 297 }
298 298 }
299 299 }
300 300 },
301 301 });
302 302
303 303
304 304 //--------------------------------------------------------------------
305 305 // WidgetManager class
306 306 //--------------------------------------------------------------------
307 307 var WidgetManager = function(comm_manager){
308 308 this.comm_manager = comm_manager;
309 309 this.widget_model_types = {};
310 310 this.widget_view_types = {};
311 311
312 312 var that = this;
313 313 Backbone.sync = function(method, model, options, error) {
314 314 var result = model.handle_sync(method, options);
315 315 if (options.success) {
316 316 options.success(result);
317 317 }
318 318 };
319 319 }
320 320
321 321
322 322 WidgetManager.prototype.register_widget_model = function (widget_model_name, widget_model_type) {
323 323 // Register the widget with the comm manager. Make sure to pass this object's context
324 324 // in so `this` works in the call back.
325 325 this.comm_manager.register_target(widget_model_name, $.proxy(this.handle_com_open, this));
326 326 this.widget_model_types[widget_model_name] = widget_model_type;
327 327 }
328 328
329 329
330 330 WidgetManager.prototype.register_widget_view = function (widget_view_name, widget_view_type) {
331 331 this.widget_view_types[widget_view_name] = widget_view_type;
332 332 }
333 333
334 334
335 335 WidgetManager.prototype.handle_com_open = function (comm, msg) {
336 336 var widget_type_name = msg.content.target_name;
337 337 var widget_model = new this.widget_model_types[widget_type_name](this.comm_manager, comm, this.widget_view_types);
338 338 }
339 339
340 340
341 341 //--------------------------------------------------------------------
342 342 // Init code
343 343 //--------------------------------------------------------------------
344 344 IPython.WidgetManager = WidgetManager;
345 345 IPython.WidgetModel = WidgetModel;
346 346 IPython.WidgetView = WidgetView;
347 347
348 348 IPython.notebook.widget_manager = new WidgetManager(IPython.notebook.kernel.comm_manager);
349 349
350 350 };
351 351 });
@@ -1,34 +1,34 b''
1 1 require(["../static/notebook/js/widget"], function(){
2 2 var ContainerModel = IPython.WidgetModel.extend({});
3 3 IPython.notebook.widget_manager.register_widget_model('ContainerWidgetModel', ContainerModel);
4 4
5 5 var ContainerView = IPython.WidgetView.extend({
6 6
7 7 render : function(){
8 8 this.$el = $('<div />')
9 9 .addClass('widget_container');
10 10 },
11 11
12 12 update : function(){
13 13
14 14 // Apply flexible box model properties by adding and removing
15 15 // corrosponding CSS classes.
16 16 // Defined in IPython/html/static/base/less/flexbox.less
17 17 var flex_properties = ['vbox', 'hbox', 'center', 'end', 'center'];
18 18 for (var index in flex_properties) {
19 19 if (this.model.get('_' + flex_properties[index])) {
20 20 this.$el.addClass(flex_properties[index]);
21 21 } else {
22 22 this.$el.removeClass(flex_properties[index]);
23 23 }
24 24 }
25 25 return IPython.WidgetView.prototype.update.call(this);
26 26 },
27 27
28 display_child : function($element) {
29 this.$el.append($element);
28 display_child : function(view) {
29 this.$el.append(view.$el);
30 30 },
31 31 });
32 32
33 33 IPython.notebook.widget_manager.register_widget_view('ContainerView', ContainerView);
34 34 }); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now