##// END OF EJS Templates
renaming once_displayed into after_displayed
Sylvain Corlay -
Show More
@@ -1,495 +1,495 b''
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define(["widgets/js/manager",
5 5 "underscore",
6 6 "backbone",
7 7 "jquery",
8 8 "base/js/namespace",
9 9 ], function(widgetmanager, _, Backbone, $, IPython){
10 10
11 11 var WidgetModel = Backbone.Model.extend({
12 12 constructor: function (widget_manager, model_id, comm) {
13 13 // Constructor
14 14 //
15 15 // Creates a WidgetModel instance.
16 16 //
17 17 // Parameters
18 18 // ----------
19 19 // widget_manager : WidgetManager instance
20 20 // model_id : string
21 21 // An ID unique to this model.
22 22 // comm : Comm instance (optional)
23 23 this.widget_manager = widget_manager;
24 24 this._buffered_state_diff = {};
25 25 this.pending_msgs = 0;
26 26 this.msg_buffer = null;
27 27 this.key_value_lock = null;
28 28 this.id = model_id;
29 29 this.views = [];
30 30
31 31 if (comm !== undefined) {
32 32 // Remember comm associated with the model.
33 33 this.comm = comm;
34 34 comm.model = this;
35 35
36 36 // Hook comm messages up to model.
37 37 comm.on_close($.proxy(this._handle_comm_closed, this));
38 38 comm.on_msg($.proxy(this._handle_comm_msg, this));
39 39 }
40 40 return Backbone.Model.apply(this);
41 41 },
42 42
43 43 send: function (content, callbacks) {
44 44 // Send a custom msg over the comm.
45 45 if (this.comm !== undefined) {
46 46 var data = {method: 'custom', content: content};
47 47 this.comm.send(data, callbacks);
48 48 this.pending_msgs++;
49 49 }
50 50 },
51 51
52 52 _handle_comm_closed: function (msg) {
53 53 // Handle when a widget is closed.
54 54 this.trigger('comm:close');
55 55 delete this.comm.model; // Delete ref so GC will collect widget model.
56 56 delete this.comm;
57 57 delete this.model_id; // Delete id from model so widget manager cleans up.
58 58 _.each(this.views, function(view, i) {
59 59 view.remove();
60 60 });
61 61 },
62 62
63 63 _handle_comm_msg: function (msg) {
64 64 // Handle incoming comm msg.
65 65 var method = msg.content.data.method;
66 66 switch (method) {
67 67 case 'update':
68 68 this.apply_update(msg.content.data.state);
69 69 break;
70 70 case 'custom':
71 71 this.trigger('msg:custom', msg.content.data.content);
72 72 break;
73 73 case 'display':
74 74 this.widget_manager.display_view(msg, this);
75 75 break;
76 76 }
77 77 },
78 78
79 79 apply_update: function (state) {
80 80 // Handle when a widget is updated via the python side.
81 81 var that = this;
82 82 _.each(state, function(value, key) {
83 83 that.key_value_lock = [key, value];
84 84 try {
85 85 WidgetModel.__super__.set.apply(that, [key, that._unpack_models(value)]);
86 86 } finally {
87 87 that.key_value_lock = null;
88 88 }
89 89 });
90 90 },
91 91
92 92 _handle_status: function (msg, callbacks) {
93 93 // Handle status msgs.
94 94
95 95 // execution_state : ('busy', 'idle', 'starting')
96 96 if (this.comm !== undefined) {
97 97 if (msg.content.execution_state ==='idle') {
98 98 // Send buffer if this message caused another message to be
99 99 // throttled.
100 100 if (this.msg_buffer !== null &&
101 101 (this.get('msg_throttle') || 3) === this.pending_msgs) {
102 102 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
103 103 this.comm.send(data, callbacks);
104 104 this.msg_buffer = null;
105 105 } else {
106 106 --this.pending_msgs;
107 107 }
108 108 }
109 109 }
110 110 },
111 111
112 112 callbacks: function(view) {
113 113 // Create msg callbacks for a comm msg.
114 114 var callbacks = this.widget_manager.callbacks(view);
115 115
116 116 if (callbacks.iopub === undefined) {
117 117 callbacks.iopub = {};
118 118 }
119 119
120 120 var that = this;
121 121 callbacks.iopub.status = function (msg) {
122 122 that._handle_status(msg, callbacks);
123 123 };
124 124 return callbacks;
125 125 },
126 126
127 127 set: function(key, val, options) {
128 128 // Set a value.
129 129 var return_value = WidgetModel.__super__.set.apply(this, arguments);
130 130
131 131 // Backbone only remembers the diff of the most recent set()
132 132 // operation. Calling set multiple times in a row results in a
133 133 // loss of diff information. Here we keep our own running diff.
134 134 this._buffered_state_diff = $.extend(this._buffered_state_diff, this.changedAttributes() || {});
135 135 return return_value;
136 136 },
137 137
138 138 sync: function (method, model, options) {
139 139 // Handle sync to the back-end. Called when a model.save() is called.
140 140
141 141 // Make sure a comm exists.
142 142 var error = options.error || function() {
143 143 console.error('Backbone sync error:', arguments);
144 144 };
145 145 if (this.comm === undefined) {
146 146 error();
147 147 return false;
148 148 }
149 149
150 150 // Delete any key value pairs that the back-end already knows about.
151 151 var attrs = (method === 'patch') ? options.attrs : model.toJSON(options);
152 152 if (this.key_value_lock !== null) {
153 153 var key = this.key_value_lock[0];
154 154 var value = this.key_value_lock[1];
155 155 if (attrs[key] === value) {
156 156 delete attrs[key];
157 157 }
158 158 }
159 159
160 160 // Only sync if there are attributes to send to the back-end.
161 161 attrs = this._pack_models(attrs);
162 162 if (_.size(attrs) > 0) {
163 163
164 164 // If this message was sent via backbone itself, it will not
165 165 // have any callbacks. It's important that we create callbacks
166 166 // so we can listen for status messages, etc...
167 167 var callbacks = options.callbacks || this.callbacks();
168 168
169 169 // Check throttle.
170 170 if (this.pending_msgs >= (this.get('msg_throttle') || 3)) {
171 171 // The throttle has been exceeded, buffer the current msg so
172 172 // it can be sent once the kernel has finished processing
173 173 // some of the existing messages.
174 174
175 175 // Combine updates if it is a 'patch' sync, otherwise replace updates
176 176 switch (method) {
177 177 case 'patch':
178 178 this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
179 179 break;
180 180 case 'update':
181 181 case 'create':
182 182 this.msg_buffer = attrs;
183 183 break;
184 184 default:
185 185 error();
186 186 return false;
187 187 }
188 188 this.msg_buffer_callbacks = callbacks;
189 189
190 190 } else {
191 191 // We haven't exceeded the throttle, send the message like
192 192 // normal.
193 193 var data = {method: 'backbone', sync_data: attrs};
194 194 this.comm.send(data, callbacks);
195 195 this.pending_msgs++;
196 196 }
197 197 }
198 198 // Since the comm is a one-way communication, assume the message
199 199 // arrived. Don't call success since we don't have a model back from the server
200 200 // this means we miss out on the 'sync' event.
201 201 this._buffered_state_diff = {};
202 202 },
203 203
204 204 save_changes: function(callbacks) {
205 205 // Push this model's state to the back-end
206 206 //
207 207 // This invokes a Backbone.Sync.
208 208 this.save(this._buffered_state_diff, {patch: true, callbacks: callbacks});
209 209 },
210 210
211 211 _pack_models: function(value) {
212 212 // Replace models with model ids recursively.
213 213 var that = this;
214 214 var packed;
215 215 if (value instanceof Backbone.Model) {
216 216 return value.id;
217 217
218 218 } else if ($.isArray(value)) {
219 219 packed = [];
220 220 _.each(value, function(sub_value, key) {
221 221 packed.push(that._pack_models(sub_value));
222 222 });
223 223 return packed;
224 224
225 225 } else if (value instanceof Object) {
226 226 packed = {};
227 227 _.each(value, function(sub_value, key) {
228 228 packed[key] = that._pack_models(sub_value);
229 229 });
230 230 return packed;
231 231
232 232 } else {
233 233 return value;
234 234 }
235 235 },
236 236
237 237 _unpack_models: function(value) {
238 238 // Replace model ids with models recursively.
239 239 var that = this;
240 240 var unpacked;
241 241 if ($.isArray(value)) {
242 242 unpacked = [];
243 243 _.each(value, function(sub_value, key) {
244 244 unpacked.push(that._unpack_models(sub_value));
245 245 });
246 246 return unpacked;
247 247
248 248 } else if (value instanceof Object) {
249 249 unpacked = {};
250 250 _.each(value, function(sub_value, key) {
251 251 unpacked[key] = that._unpack_models(sub_value);
252 252 });
253 253 return unpacked;
254 254
255 255 } else {
256 256 var model = this.widget_manager.get_model(value);
257 257 if (model) {
258 258 return model;
259 259 } else {
260 260 return value;
261 261 }
262 262 }
263 263 },
264 264
265 265 });
266 266 widgetmanager.WidgetManager.register_widget_model('WidgetModel', WidgetModel);
267 267
268 268
269 269 var WidgetView = Backbone.View.extend({
270 270 initialize: function(parameters) {
271 271 // Public constructor.
272 272 this.model.on('change',this.update,this);
273 273 this.options = parameters.options;
274 274 this.child_model_views = {};
275 275 this.child_views = {};
276 276 this.model.views.push(this);
277 277 this.id = this.id || IPython.utils.uuid();
278 278 this.on('displayed', function() {
279 279 this.is_displayed = true;
280 280 }, this);
281 281 },
282 282
283 283 update: function(){
284 284 // Triggered on model change.
285 285 //
286 286 // Update view to be consistent with this.model
287 287 },
288 288
289 289 create_child_view: function(child_model, options) {
290 290 // Create and return a child view.
291 291 //
292 292 // -given a model and (optionally) a view name if the view name is
293 293 // not given, it defaults to the model's default view attribute.
294 294
295 295 // TODO: this is hacky, and makes the view depend on this cell attribute and widget manager behavior
296 296 // it would be great to have the widget manager add the cell metadata
297 297 // to the subview without having to add it here.
298 298 options = $.extend({ parent: this }, options || {});
299 299 var child_view = this.model.widget_manager.create_view(child_model, options, this);
300 300
301 301 // Associate the view id with the model id.
302 302 if (this.child_model_views[child_model.id] === undefined) {
303 303 this.child_model_views[child_model.id] = [];
304 304 }
305 305 this.child_model_views[child_model.id].push(child_view.id);
306 306
307 307 // Remember the view by id.
308 308 this.child_views[child_view.id] = child_view;
309 309 return child_view;
310 310 },
311 311
312 312 pop_child_view: function(child_model) {
313 313 // Delete a child view that was previously created using create_child_view.
314 314 var view_ids = this.child_model_views[child_model.id];
315 315 if (view_ids !== undefined) {
316 316
317 317 // Only delete the first view in the list.
318 318 var view_id = view_ids[0];
319 319 var view = this.child_views[view_id];
320 320 delete this.child_views[view_id];
321 321 view_ids.splice(0,1);
322 322 child_model.views.pop(view);
323 323
324 324 // Remove the view list specific to this model if it is empty.
325 325 if (view_ids.length === 0) {
326 326 delete this.child_model_views[child_model.id];
327 327 }
328 328 return view;
329 329 }
330 330 return null;
331 331 },
332 332
333 333 do_diff: function(old_list, new_list, removed_callback, added_callback) {
334 334 // Difference a changed list and call remove and add callbacks for
335 335 // each removed and added item in the new list.
336 336 //
337 337 // Parameters
338 338 // ----------
339 339 // old_list : array
340 340 // new_list : array
341 341 // removed_callback : Callback(item)
342 342 // Callback that is called for each item removed.
343 343 // added_callback : Callback(item)
344 344 // Callback that is called for each item added.
345 345
346 346 // Walk the lists until an unequal entry is found.
347 347 var i;
348 348 for (i = 0; i < new_list.length; i++) {
349 349 if (i < old_list.length || new_list[i] !== old_list[i]) {
350 350 break;
351 351 }
352 352 }
353 353
354 354 // Remove the non-matching items from the old list.
355 355 for (var j = i; j < old_list.length; j++) {
356 356 removed_callback(old_list[j]);
357 357 }
358 358
359 359 // Add the rest of the new list items.
360 360 for (i; i < new_list.length; i++) {
361 361 added_callback(new_list[i]);
362 362 }
363 363 },
364 364
365 365 callbacks: function(){
366 366 // Create msg callbacks for a comm msg.
367 367 return this.model.callbacks(this);
368 368 },
369 369
370 370 render: function(){
371 371 // Render the view.
372 372 //
373 373 // By default, this is only called the first time the view is created
374 374 },
375 375
376 376 show: function(){
377 377 // Show the widget-area
378 378 if (this.options && this.options.cell &&
379 379 this.options.cell.widget_area !== undefined) {
380 380 this.options.cell.widget_area.show();
381 381 }
382 382 },
383 383
384 384 send: function (content) {
385 385 // Send a custom msg associated with this view.
386 386 this.model.send(content, this.callbacks());
387 387 },
388 388
389 389 touch: function () {
390 390 this.model.save_changes(this.callbacks());
391 391 },
392 392
393 once_displayed: function (callback, context) {
393 after_displayed: function (callback, context) {
394 394 // Calls the callback right away is the view is already displayed
395 395 // otherwise, register the callback to the 'displayed' event.
396 396 if (this.is_displayed) {
397 397 callback.apply(context);
398 398 } else {
399 399 this.on('displayed', callback, context);
400 400 }
401 401 },
402 402 });
403 403
404 404
405 405 var DOMWidgetView = WidgetView.extend({
406 406 initialize: function (options) {
407 407 // Public constructor
408 408
409 409 // In the future we may want to make changes more granular
410 410 // (e.g., trigger on visible:change).
411 411 this.model.on('change', this.update, this);
412 412 this.model.on('msg:custom', this.on_msg, this);
413 413 DOMWidgetView.__super__.initialize.apply(this, arguments);
414 414 this.on('displayed', this.show, this);
415 415 },
416 416
417 417 on_msg: function(msg) {
418 418 // Handle DOM specific msgs.
419 419 switch(msg.msg_type) {
420 420 case 'add_class':
421 421 this.add_class(msg.selector, msg.class_list);
422 422 break;
423 423 case 'remove_class':
424 424 this.remove_class(msg.selector, msg.class_list);
425 425 break;
426 426 }
427 427 },
428 428
429 429 add_class: function (selector, class_list) {
430 430 // Add a DOM class to an element.
431 431 this._get_selector_element(selector).addClass(class_list);
432 432 },
433 433
434 434 remove_class: function (selector, class_list) {
435 435 // Remove a DOM class from an element.
436 436 this._get_selector_element(selector).removeClass(class_list);
437 437 },
438 438
439 439 update: function () {
440 440 // Update the contents of this view
441 441 //
442 442 // Called when the model is changed. The model may have been
443 443 // changed by another view or by a state update from the back-end.
444 444 // The very first update seems to happen before the element is
445 445 // finished rendering so we use setTimeout to give the element time
446 446 // to render
447 447 var e = this.$el;
448 448 var visible = this.model.get('visible');
449 449 setTimeout(function() {e.toggle(visible);},0);
450 450
451 451 var css = this.model.get('_css');
452 452 if (css === undefined) {return;}
453 453 for (var i = 0; i < css.length; i++) {
454 454 // Apply the css traits to all elements that match the selector.
455 455 var selector = css[i][0];
456 456 var elements = this._get_selector_element(selector);
457 457 if (elements.length > 0) {
458 458 var trait_key = css[i][1];
459 459 var trait_value = css[i][2];
460 460 elements.css(trait_key ,trait_value);
461 461 }
462 462 }
463 463 },
464 464
465 465 _get_selector_element: function (selector) {
466 466 // Get the elements via the css selector.
467 467
468 468 // If the selector is blank, apply the style to the $el_to_style
469 469 // element. If the $el_to_style element is not defined, use apply
470 470 // the style to the view's element.
471 471 var elements;
472 472 if (!selector) {
473 473 if (this.$el_to_style === undefined) {
474 474 elements = this.$el;
475 475 } else {
476 476 elements = this.$el_to_style;
477 477 }
478 478 } else {
479 479 elements = this.$el.find(selector);
480 480 }
481 481 return elements;
482 482 },
483 483 });
484 484
485 485 var widget = {
486 486 'WidgetModel': WidgetModel,
487 487 'WidgetView': WidgetView,
488 488 'DOMWidgetView': DOMWidgetView,
489 489 };
490 490
491 491 // For backwards compatability.
492 492 $.extend(IPython, widget);
493 493
494 494 return widget;
495 495 });
@@ -1,292 +1,292 b''
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 "widgets/js/widget",
6 6 "jqueryui",
7 7 "components/bootstrap/js/bootstrap.min",
8 8 ], function(widget, $){
9 9
10 10 var ContainerView = widget.DOMWidgetView.extend({
11 11 render: function(){
12 12 // Called when view is rendered.
13 13 this.$el.addClass('widget-container')
14 14 .addClass('vbox');
15 15 this.update_children([], this.model.get('children'));
16 16 this.model.on('change:children', function(model, value, options) {
17 17 this.update_children(model.previous('children'), value);
18 18 }, this);
19 19 this.update();
20 20 },
21 21
22 22 update_children: function(old_list, new_list) {
23 23 // Called when the children list changes.
24 24 this.do_diff(old_list,
25 25 new_list,
26 26 $.proxy(this.remove_child_model, this),
27 27 $.proxy(this.add_child_model, this));
28 28 },
29 29
30 30 remove_child_model: function(model) {
31 31 // Called when a model is removed from the children list.
32 32 this.pop_child_view(model).remove();
33 33 },
34 34
35 35 add_child_model: function(model) {
36 36 // Called when a model is added to the children list.
37 37 var view = this.create_child_view(model);
38 38 this.$el.append(view.$el);
39 39
40 40 // Trigger the displayed event once this view is displayed.
41 this.once_displayed(function() {
41 this.after_displayed(function() {
42 42 view.trigger('displayed');
43 43 });
44 44 },
45 45
46 46 update: function(){
47 47 // Update the contents of this view
48 48 //
49 49 // Called when the model is changed. The model may have been
50 50 // changed by another view or by a state update from the back-end.
51 51 return ContainerView.__super__.update.apply(this);
52 52 },
53 53 });
54 54
55 55
56 56 var PopupView = widget.DOMWidgetView.extend({
57 57 render: function(){
58 58 // Called when view is rendered.
59 59 var that = this;
60 60
61 61 this.$el.on("remove", function(){
62 62 that.$backdrop.remove();
63 63 });
64 64 this.$backdrop = $('<div />')
65 65 .appendTo($('#notebook-container'))
66 66 .addClass('modal-dialog')
67 67 .css('position', 'absolute')
68 68 .css('left', '0px')
69 69 .css('top', '0px');
70 70 this.$window = $('<div />')
71 71 .appendTo(this.$backdrop)
72 72 .addClass('modal-content widget-modal')
73 73 .mousedown(function(){
74 74 that.bring_to_front();
75 75 });
76 76
77 77 // Set the elements array since the this.$window element is not child
78 78 // of this.$el and the parent widget manager or other widgets may
79 79 // need to know about all of the top-level widgets. The IPython
80 80 // widget manager uses this to register the elements with the
81 81 // keyboard manager.
82 82 this.additional_elements = [this.$window];
83 83
84 84 this.$title_bar = $('<div />')
85 85 .addClass('popover-title')
86 86 .appendTo(this.$window)
87 87 .mousedown(function(){
88 88 that.bring_to_front();
89 89 });
90 90 this.$close = $('<button />')
91 91 .addClass('close icon-remove')
92 92 .css('margin-left', '5px')
93 93 .appendTo(this.$title_bar)
94 94 .click(function(){
95 95 that.hide();
96 96 event.stopPropagation();
97 97 });
98 98 this.$minimize = $('<button />')
99 99 .addClass('close icon-arrow-down')
100 100 .appendTo(this.$title_bar)
101 101 .click(function(){
102 102 that.popped_out = !that.popped_out;
103 103 if (!that.popped_out) {
104 104 that.$minimize
105 105 .removeClass('icon-arrow-down')
106 106 .addClass('icon-arrow-up');
107 107
108 108 that.$window
109 109 .draggable('destroy')
110 110 .resizable('destroy')
111 111 .removeClass('widget-modal modal-content')
112 112 .addClass('docked-widget-modal')
113 113 .detach()
114 114 .insertBefore(that.$show_button);
115 115 that.$show_button.hide();
116 116 that.$close.hide();
117 117 } else {
118 118 that.$minimize
119 119 .addClass('icon-arrow-down')
120 120 .removeClass('icon-arrow-up');
121 121
122 122 that.$window
123 123 .removeClass('docked-widget-modal')
124 124 .addClass('widget-modal modal-content')
125 125 .detach()
126 126 .appendTo(that.$backdrop)
127 127 .draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'})
128 128 .resizable()
129 129 .children('.ui-resizable-handle').show();
130 130 that.show();
131 131 that.$show_button.show();
132 132 that.$close.show();
133 133 }
134 134 event.stopPropagation();
135 135 });
136 136 this.$title = $('<div />')
137 137 .addClass('widget-modal-title')
138 138 .html("&nbsp;")
139 139 .appendTo(this.$title_bar);
140 140 this.$body = $('<div />')
141 141 .addClass('modal-body')
142 142 .addClass('widget-modal-body')
143 143 .addClass('widget-container')
144 144 .addClass('vbox')
145 145 .appendTo(this.$window);
146 146
147 147 this.$show_button = $('<button />')
148 148 .html("&nbsp;")
149 149 .addClass('btn btn-info widget-modal-show')
150 150 .appendTo(this.$el)
151 151 .click(function(){
152 152 that.show();
153 153 });
154 154
155 155 this.$window.draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'});
156 156 this.$window.resizable();
157 157 this.$window.on('resize', function(){
158 158 that.$body.outerHeight(that.$window.innerHeight() - that.$title_bar.outerHeight());
159 159 });
160 160
161 161 this.$el_to_style = this.$body;
162 162 this._shown_once = false;
163 163 this.popped_out = true;
164 164
165 165 this.update_children([], this.model.get('children'));
166 166 this.model.on('change:children', function(model, value, options) {
167 167 this.update_children(model.previous('children'), value);
168 168 }, this);
169 169 this.update();
170 170 },
171 171
172 172 hide: function() {
173 173 // Called when the modal hide button is clicked.
174 174 this.$window.hide();
175 175 this.$show_button.removeClass('btn-info');
176 176 },
177 177
178 178 show: function() {
179 179 // Called when the modal show button is clicked.
180 180 this.$show_button.addClass('btn-info');
181 181 this.$window.show();
182 182 if (this.popped_out) {
183 183 this.$window.css("positon", "absolute");
184 184 this.$window.css("top", "0px");
185 185 this.$window.css("left", Math.max(0, (($('body').outerWidth() - this.$window.outerWidth()) / 2) +
186 186 $(window).scrollLeft()) + "px");
187 187 this.bring_to_front();
188 188 }
189 189 },
190 190
191 191 bring_to_front: function() {
192 192 // Make the modal top-most, z-ordered about the other modals.
193 193 var $widget_modals = $(".widget-modal");
194 194 var max_zindex = 0;
195 195 $widget_modals.each(function (index, el){
196 196 var zindex = parseInt($(el).css('z-index'));
197 197 if (!isNaN(zindex)) {
198 198 max_zindex = Math.max(max_zindex, zindex);
199 199 }
200 200 });
201 201
202 202 // Start z-index of widget modals at 2000
203 203 max_zindex = Math.max(max_zindex, 2000);
204 204
205 205 $widget_modals.each(function (index, el){
206 206 $el = $(el);
207 207 if (max_zindex == parseInt($el.css('z-index'))) {
208 208 $el.css('z-index', max_zindex - 1);
209 209 }
210 210 });
211 211 this.$window.css('z-index', max_zindex);
212 212 },
213 213
214 214 update_children: function(old_list, new_list) {
215 215 // Called when the children list is modified.
216 216 this.do_diff(old_list,
217 217 new_list,
218 218 $.proxy(this.remove_child_model, this),
219 219 $.proxy(this.add_child_model, this));
220 220 },
221 221
222 222 remove_child_model: function(model) {
223 223 // Called when a child is removed from children list.
224 224 this.pop_child_view(model).remove();
225 225 },
226 226
227 227 add_child_model: function(model) {
228 228 // Called when a child is added to children list.
229 229 var view = this.create_child_view(model);
230 230 this.$body.append(view.$el);
231 231
232 232 // Trigger the displayed event once this view is displayed.
233 this.once_displayed(function() {
233 this.after_displayed(function() {
234 234 view.trigger('displayed');
235 235 });
236 236 },
237 237
238 238 update: function(){
239 239 // Update the contents of this view
240 240 //
241 241 // Called when the model is changed. The model may have been
242 242 // changed by another view or by a state update from the back-end.
243 243 var description = this.model.get('description');
244 244 if (description.trim().length === 0) {
245 245 this.$title.html("&nbsp;"); // Preserve title height
246 246 } else {
247 247 this.$title.text(description);
248 248 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$title.get(0)]);
249 249 }
250 250
251 251 var button_text = this.model.get('button_text');
252 252 if (button_text.trim().length === 0) {
253 253 this.$show_button.html("&nbsp;"); // Preserve button height
254 254 } else {
255 255 this.$show_button.text(button_text);
256 256 }
257 257
258 258 if (!this._shown_once) {
259 259 this._shown_once = true;
260 260 this.show();
261 261 }
262 262
263 263 return PopupView.__super__.update.apply(this);
264 264 },
265 265
266 266 _get_selector_element: function(selector) {
267 267 // Get an element view a 'special' jquery selector. (see widget.js)
268 268 //
269 269 // Since the modal actually isn't within the $el in the DOM, we need to extend
270 270 // the selector logic to allow the user to set css on the modal if need be.
271 271 // The convention used is:
272 272 // "modal" - select the modal div
273 273 // "modal [selector]" - select element(s) within the modal div.
274 274 // "[selector]" - select elements within $el
275 275 // "" - select the $el_to_style
276 276 if (selector.substring(0, 5) == 'modal') {
277 277 if (selector == 'modal') {
278 278 return this.$window;
279 279 } else {
280 280 return this.$window.find(selector.substring(6));
281 281 }
282 282 } else {
283 283 return PopupView.__super__._get_selector_element.apply(this, [selector]);
284 284 }
285 285 },
286 286 });
287 287
288 288 return {
289 289 'ContainerView': ContainerView,
290 290 'PopupView': PopupView,
291 291 };
292 292 });
General Comments 0
You need to be logged in to leave comments. Login now