##// END OF EJS Templates
Added PEP8 style comments to all of the JS code.
Jonathan Frederic -
Show More
@@ -1,366 +1,381
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 // Base Widget Model and View classes
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgetmanager",
18 18 "underscore",
19 19 "backbone"],
20 20 function(widget_manager, underscore, backbone){
21
22 //--------------------------------------------------------------------
23 // WidgetModel class
24 //--------------------------------------------------------------------
21
25 22 var WidgetModel = Backbone.Model.extend({
26 23 constructor: function (widget_manager, model_id, comm) {
27 24 // Construcctor
28 25 //
29 26 // Creates a WidgetModel instance.
30 27 //
31 28 // Parameters
32 29 // ----------
33 30 // widget_manager : WidgetManager instance
34 31 // model_id : string
35 32 // An ID unique to this model.
36 33 // comm : Comm instance (optional)
37 34 this.widget_manager = widget_manager;
38 35 this.pending_msgs = 0;
39 36 this.msg_throttle = 2;
40 37 this.msg_buffer = null;
41 38 this.key_value_lock = null;
42 39 this.id = model_id;
43 40 this.views = [];
44 41
45 42 if (comm !== undefined) {
46 43 // Remember comm associated with the model.
47 44 this.comm = comm;
48 45 comm.model = this;
49 46
50 47 // Hook comm messages up to model.
51 48 comm.on_close($.proxy(this._handle_comm_closed, this));
52 49 comm.on_msg($.proxy(this._handle_comm_msg, this));
53 50 }
54 51 return Backbone.Model.apply(this);
55 52 },
56 53
57 54 send: function (content, callbacks) {
55 // Send a custom msg over the comm.
58 56 if (this.comm !== undefined) {
59 57 var data = {method: 'custom', custom_content: content};
60 58 this.comm.send(data, callbacks);
61 59 }
62 60 },
63 61
64 // Handle when a widget is closed.
65 62 _handle_comm_closed: function (msg) {
63 // Handle when a widget is closed.
66 64 this.trigger('comm:close');
67 65 delete this.comm.model; // Delete ref so GC will collect widget model.
68 66 delete this.comm;
69 67 delete this.model_id; // Delete id from model so widget manager cleans up.
70 68 // TODO: Handle deletion, like this.destroy(), and delete views, etc.
71 69 },
72 70
73
74 // Handle incoming comm msg.
75 71 _handle_comm_msg: function (msg) {
72 // Handle incoming comm msg.
76 73 var method = msg.content.data.method;
77 74 switch (method) {
78 75 case 'update':
79 76 this.apply_update(msg.content.data.state);
80 77 break;
81 78 case 'custom':
82 79 this.trigger('msg:custom', msg.content.data.custom_content);
83 80 break;
84 81 case 'display':
85 82 this.widget_manager.display_view(msg.parent_header.msg_id, this);
86 83 break;
87 84 }
88 85 },
89 86
90
91 // Handle when a widget is updated via the python side.
92 87 apply_update: function (state) {
88 // Handle when a widget is updated via the python side.
93 89 for (var key in state) {
94 90 if (state.hasOwnProperty(key)) {
95 91 var value = state[key];
96 92 this.key_value_lock = [key, value];
97 93 try {
98 94 this.set(key, this._unpack_models(value));
99 95 } finally {
100 96 this.key_value_lock = null;
101 97 }
102 98 }
103 99 }
104 100 //TODO: are there callbacks that make sense in this case? If so, attach them here as an option
105 101 this.save();
106 102 },
107 103
108
109 104 _handle_status: function (msg, callbacks) {
110 //execution_state : ('busy', 'idle', 'starting')
105 // Handle status msgs.
106
107 // execution_state : ('busy', 'idle', 'starting')
111 108 if (this.comm !== undefined) {
112 109 if (msg.content.execution_state ==='idle') {
113 110 // Send buffer if this message caused another message to be
114 111 // throttled.
115 112 if (this.msg_buffer !== null &&
116 113 this.msg_throttle === this.pending_msgs) {
117 114 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
118 115 this.comm.send(data, callbacks);
119 116 this.msg_buffer = null;
120 117 } else {
121 118 --this.pending_msgs;
122 119 }
123 120 }
124 121 }
125 122 },
126 123
127
128 // Custom syncronization logic.
129 124 _handle_sync: function (method, options) {
125 // Custom syncronization logic.
130 126 var model_json = this.toJSON();
131 127 var attr;
132 128
133 129 // Only send updated state if the state hasn't been changed
134 130 // during an update.
135 131 if (this.comm !== undefined) {
136 132 if (this.pending_msgs >= this.msg_throttle) {
137 133 // The throttle has been exceeded, buffer the current msg so
138 134 // it can be sent once the kernel has finished processing
139 135 // some of the existing messages.
140 136 if (this.msg_buffer === null) {
141 137 this.msg_buffer = $.extend({}, model_json); // Copy
142 138 }
143 139 for (attr in options.attrs) {
144 140 var value = this._pack_models(options.attrs[attr]);
145 141 if (this.key_value_lock === null || attr !== this.key_value_lock[0] || value !== this.key_value_lock[1]) {
146 142 this.msg_buffer[attr] = value;
147 143 }
148 144 }
149 145
150 146 } else {
151 147 // We haven't exceeded the throttle, send the message like
152 148 // normal. If this is a patch operation, just send the
153 149 // changes.
154 150 var send_json = model_json;
155 151 send_json = {};
156 152 for (attr in options.attrs) {
157 153 var value = this._pack_models(options.attrs[attr]);
158 154 if (this.key_value_lock === null || attr !== this.key_value_lock[0] || value !== this.key_value_lock[1]) {
159 155 send_json[attr] = value;
160 156 }
161 157 }
162 158
163 159 var is_empty = true;
164 160 for (var prop in send_json) if (send_json.hasOwnProperty(prop)) is_empty = false;
165 161 if (!is_empty) {
166 162 ++this.pending_msgs;
167 163 var data = {method: 'backbone', sync_data: send_json};
168 164 this.comm.send(data, options.callbacks);
169 165 }
170 166 }
171 167 }
172 168
173 169 // Since the comm is a one-way communication, assume the message
174 170 // arrived.
175 171 return model_json;
176 172 },
177 173
178 174 _pack_models: function(value) {
175 // Replace models with model ids recursively.
179 176 if (value instanceof Backbone.Model) {
180 177 return value.id;
181 178 } else if (value instanceof Object) {
182 179 var packed = {};
183 180 for (var key in value) {
184 181 packed[key] = this._pack_models(value[key]);
185 182 }
186 183 return packed;
187 184 } else {
188 185 return value;
189 186 }
190 187 },
191 188
192 189 _unpack_models: function(value) {
190 // Replace model ids with models recursively.
193 191 if (value instanceof Object) {
194 192 var unpacked = {};
195 193 for (var key in value) {
196 194 unpacked[key] = this._unpack_models(value[key]);
197 195 }
198 196 return unpacked;
199 197 } else {
200 198 var model = this.widget_manager.get_model(value);
201 199 if (model !== null) {
202 200 return model;
203 201 } else {
204 202 return value;
205 203 }
206 204 }
207 205 },
208 206
209 207 });
210 208 widget_manager.register_widget_model('WidgetModel', WidgetModel);
211 209
212 210
213 //--------------------------------------------------------------------
214 // WidgetView class
215 //--------------------------------------------------------------------
216 211 var WidgetView = Backbone.View.extend({
217 212 initialize: function(parameters) {
213 // Public constructor.
218 214 this.model.on('change',this.update,this);
219 215 this.options = parameters.options;
220 216 this.child_views = [];
221 217 this.model.views.push(this);
222 218 },
223 219
224 220 update: function(){
225 // update view to be consistent with this.model
226 // triggered on model change
221 // Triggered on model change.
222 //
223 // Update view to be consistent with this.model
227 224 },
228 225
229 226 create_child_view: function(child_model, options) {
230 // create and return a child view, given a model and (optionally) a view name
231 // if the view name is not given, it defaults to the model's default view attribute
227 // Create and return a child view.
228 //
229 // - given a model and (optionally) a view name if the view name is
230 // not given, it defaults to the model's default view attribute.
232 231 var child_view = this.model.widget_manager.create_view(child_model, options);
233 232 this.child_views[child_model.id] = child_view;
234 233 return child_view;
235 234 },
236 235
237 236 delete_child_view: function(child_model, options) {
237 // Delete a child view that was previously created using create_child_view.
238 238 var view = this.child_views[child_model.id];
239 239 delete this.child_views[child_model.id];
240 240 view.remove();
241 241 },
242 242
243 243 do_diff: function(old_list, new_list, removed_callback, added_callback) {
244 244 // Difference a changed list and call remove and add callbacks for
245 245 // each removed and added item in the new list.
246 246 //
247 247 // Parameters
248 248 // ----------
249 249 // old_list : array
250 250 // new_list : array
251 251 // removed_callback : Callback(item)
252 252 // Callback that is called for each item removed.
253 253 // added_callback : Callback(item)
254 254 // Callback that is called for each item added.
255 255
256 256
257 257 // removed items
258 258 _.each(_.difference(old_list, new_list), function(item, index, list) {
259 259 removed_callback(item);
260 260 }, this);
261 261
262 262 // added items
263 263 _.each(_.difference(new_list, old_list), function(item, index, list) {
264 264 added_callback(item);
265 265 }, this);
266 266 },
267 267
268 268 callbacks: function(){
269 // Create msg callbacks for a comm msg.
269 270 return this.model.widget_manager.callbacks(this);
270 271 },
271 272
272 273 render: function(){
273 // render the view. By default, this is only called the first time the view is created
274 // Render the view.
275 //
276 // By default, this is only called the first time the view is created
274 277 },
278
275 279 send: function (content) {
280 // Send a custom msg associated with this view.
276 281 this.model.send(content, this.callbacks());
277 282 },
278 283
279 284 touch: function () {
285 // Associate recent model changes with this notebook.
280 286 this.model.save(this.model.changedAttributes(), {patch: true, callbacks: this.callbacks()});
281 287 },
282 288
283 289 });
284 290
291
285 292 var DOMWidgetView = WidgetView.extend({
286 293 initialize: function (options) {
287 // TODO: make changes more granular (e.g., trigger on visible:change)
294 // Public constructor
295
296 // In the future we may want to make changes more granular
297 // (e.g., trigger on visible:change).
288 298 this.model.on('change', this.update, this);
289 299 this.model.on('msg:custom', this.on_msg, this);
290 300 DOMWidgetView.__super__.initialize.apply(this, arguments);
291 301 },
292 302
293 303 on_msg: function(msg) {
304 // Handle DOM specific msgs.
294 305 switch(msg.msg_type) {
295 306 case 'add_class':
296 307 this.add_class(msg.selector, msg.class_list);
297 308 break;
298 309 case 'remove_class':
299 310 this.remove_class(msg.selector, msg.class_list);
300 311 break;
301 312 }
302 313 },
303 314
304 315 add_class: function (selector, class_list) {
316 // Add a DOM class to an element.
305 317 this._get_selector_element(selector).addClass(class_list);
306 318 },
307 319
308 320 remove_class: function (selector, class_list) {
321 // Remove a DOM class from an element.
309 322 this._get_selector_element(selector).removeClass(class_list);
310 323 },
311 324
312 325 update: function () {
313 326 // Update the contents of this view
314 327 //
315 328 // Called when the model is changed. The model may have been
316 329 // changed by another view or by a state update from the back-end.
317 330 // The very first update seems to happen before the element is
318 331 // finished rendering so we use setTimeout to give the element time
319 332 // to render
320 333 var e = this.$el;
321 334 var visible = this.model.get('visible');
322 335 setTimeout(function() {e.toggle(visible)},0);
323 336
324 337 var css = this.model.get('_css');
325 338 if (css === undefined) {return;}
326 339 for (var selector in css) {
327 340 if (css.hasOwnProperty(selector)) {
328 341 // Apply the css traits to all elements that match the selector.
329 342 var elements = this._get_selector_element(selector);
330 343 if (elements.length > 0) {
331 344 var css_traits = css[selector];
332 345 for (var css_key in css_traits) {
333 346 if (css_traits.hasOwnProperty(css_key)) {
334 347 elements.css(css_key, css_traits[css_key]);
335 348 }
336 349 }
337 350 }
338 351 }
339 352 }
340 353 },
341 354
342 355 _get_selector_element: function (selector) {
343 // Get the elements via the css selector. If the selector is
344 // blank, apply the style to the $el_to_style element. If
345 // the $el_to_style element is not defined, use apply the
346 // style to the view's element.
356 // Get the elements via the css selector.
357
358 // If the selector is blank, apply the style to the $el_to_style
359 // element. If the $el_to_style element is not defined, use apply
360 // the style to the view's element.
347 361 var elements;
348 362 if (selector === undefined || selector === null || selector === '') {
349 363 if (this.$el_to_style === undefined) {
350 364 elements = this.$el;
351 365 } else {
352 366 elements = this.$el_to_style;
353 367 }
354 368 } else {
355 369 elements = this.$el.find(selector);
356 370 }
357 371 return elements;
358 372 },
359 373 });
360 374
361 375 IPython.WidgetModel = WidgetModel;
362 376 IPython.WidgetView = WidgetView;
363 377 IPython.DOMWidgetView = DOMWidgetView;
364 378
379 // Pass through widget_manager instance (probably not a good practice).
365 380 return widget_manager;
366 381 });
@@ -1,126 +1,125
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 // BoolWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18
18 19 var CheckBoxView = IPython.DOMWidgetView.extend({
19
20 // Called when view is rendered.
21 20 render : function(){
21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .addClass('widget-hlabel')
26 26 .appendTo(this.$el)
27 27 .hide();
28 28 this.$checkbox = $('<input />')
29 29 .attr('type', 'checkbox')
30 30 .appendTo(this.$el)
31 31 .click($.proxy(this.handle_click, this));
32 32
33 33 this.$el_to_style = this.$checkbox; // Set default element to style
34 34 this.update(); // Set defaults.
35 35 },
36 36
37 37 handle_click: function() {
38 // Handles when the checkbox is clicked.
39
38 40 // Calling model.set will trigger all of the other views of the
39 41 // model to update.
40 42 var value = this.model.get('value');
41 43 this.model.set('value', ! value, {updated_view: this});
42 44 this.touch();
43 45 },
44 46
45 47 update : function(options){
46 48 // Update the contents of this view
47 49 //
48 50 // Called when the model is changed. The model may have been
49 51 // changed by another view or by a state update from the back-end.
50 52 this.$checkbox.prop('checked', this.model.get('value'));
51 53
52 54 if (options === undefined || options.updated_view != this) {
53 55 var disabled = this.model.get('disabled');
54 56 this.$checkbox.prop('disabled', disabled);
55 57
56 58 var description = this.model.get('description');
57 59 if (description.length === 0) {
58 60 this.$label.hide();
59 61 } else {
60 62 this.$label.html(description);
61 63 this.$label.show();
62 64 }
63 65 }
64 66 return CheckBoxView.__super__.update.apply(this);
65 67 },
66 68
67 69 });
68
69 70 widget_manager.register_widget_view('CheckBoxView', CheckBoxView);
70 71
72
71 73 var ToggleButtonView = IPython.DOMWidgetView.extend({
72
73 // Called when view is rendered.
74 74 render : function() {
75 // Called when view is rendered.
75 76 var that = this;
76 77 this.setElement($('<button />')
77 78 .addClass('btn')
78 79 .attr('type', 'button')
79 80 .on('click', function (e) {
80 81 e.preventDefault();
81 82 that.handle_click();
82 83 }));
83 84
84 85 this.update(); // Set defaults.
85 86 },
86 87
87 88 update : function(options){
88 89 // Update the contents of this view
89 90 //
90 91 // Called when the model is changed. The model may have been
91 92 // changed by another view or by a state update from the back-end.
92 93 if (this.model.get('value')) {
93 94 this.$el.addClass('active');
94 95 } else {
95 96 this.$el.removeClass('active');
96 97 }
97 98
98 99 if (options === undefined || options.updated_view != this) {
99 100
100 101 var disabled = this.model.get('disabled');
101 102 this.$el.prop('disabled', disabled);
102 103
103 104 var description = this.model.get('description');
104 105 if (description.length === 0) {
105 106 this.$el.html(' '); // Preserve button height
106 107 } else {
107 108 this.$el.html(description);
108 109 }
109 110 }
110 111 return ToggleButtonView.__super__.update.apply(this);
111 112 },
112 113
113 // Handles and validates user input.
114 114 handle_click: function(e) {
115 // Handles and validates user input.
115 116
116 117 // Calling model.set will trigger all of the other views of the
117 118 // model to update.
118 119 var value = this.model.get('value');
119 120 this.model.set('value', ! value, {updated_view: this});
120 121 this.touch();
121 122 },
122 123 });
123
124 124 widget_manager.register_widget_view('ToggleButtonView', ToggleButtonView);
125
126 125 });
@@ -1,62 +1,62
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 // ButtonWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18 var ButtonView = IPython.DOMWidgetView.extend({
19
20 // Called when view is rendered.
18
19 var ButtonView = IPython.DOMWidgetView.extend({
21 20 render : function(){
21 // Called when view is rendered.
22 22 this.setElement($("<button />")
23 23 .addClass('btn'));
24 24
25 25 this.update(); // Set defaults.
26 26 },
27 27
28 28 update : function(){
29 29 // Update the contents of this view
30 30 //
31 31 // Called when the model is changed. The model may have been
32 32 // changed by another view or by a state update from the back-end.
33 33 var description = this.model.get('description');
34 34 description = description.replace(/ /g, '&nbsp;', 'm');
35 35 description = description.replace(/\n/g, '<br>\n', 'm');
36 36 if (description.length === 0) {
37 37 this.$el.html('&nbsp;'); // Preserve button height
38 38 } else {
39 39 this.$el.html(description);
40 40 }
41 41
42 42 if (this.model.get('disabled')) {
43 43 this.$el.attr('disabled','disabled');
44 44 } else {
45 45 this.$el.removeAttr('disabled');
46 46 }
47 47
48 48 return ButtonView.__super__.update.apply(this);
49 49 },
50 50
51 51 events: {
52 // Dictionary of events and their handlers.
52 53 'click': '_handle_click',
53 54 },
54 55
55 56 _handle_click: function(){
57 // Handles when the button is clicked.
56 58 this.send({event: 'click'});
57 59 },
58 60 });
59
60 61 widget_manager.register_widget_view('ButtonView', ButtonView);
61
62 62 });
@@ -1,270 +1,277
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 // ContainerWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager) {
18 var ContainerView = IPython.DOMWidgetView.extend({
19
18
19 var ContainerView = IPython.DOMWidgetView.extend({
20 20 render: function(){
21 // Called when view is rendered.
21 22 this.$el
22 23 .addClass('widget-container');
23 24 this.children={};
24 25 this.update_children([], this.model.get('children'));
25 26 this.model.on('change:children', function(model, value, options) {
26 27 this.update_children(model.previous('children'), value);
27 28 }, this);
28 29 this.update()
29 30 },
30 31
31 32 update_children: function(old_list, new_list) {
33 // Called when the children list changes.
32 34 this.do_diff(old_list,
33 35 new_list,
34 36 $.proxy(this.remove_child_model, this),
35 37 $.proxy(this.add_child_model, this));
36 38 },
37 39
38 40 remove_child_model: function(model) {
41 // Called when a model is removed from the children list.
39 42 this.child_views[model.id].remove();
40 43 this.delete_child_view(model);
41 44 },
42 45
43 46 add_child_model: function(model) {
47 // Called when a model is added to the children list.
44 48 var view = this.create_child_view(model);
45 49 this.$el.append(view.$el);
46 50 },
47 51
48 52 update: function(){
49 53 // Update the contents of this view
50 54 //
51 55 // Called when the model is changed. The model may have been
52 56 // changed by another view or by a state update from the back-end.
53 57 return ContainerView.__super__.update.apply(this);
54 58 },
55 59 });
56
57 60 widget_manager.register_widget_view('ContainerView', ContainerView);
58 61
59 62
60 var ModalView = IPython.DOMWidgetView.extend({
61
63 var ModalView = IPython.DOMWidgetView.extend({
62 64 render: function(){
65 // Called when view is rendered.
63 66 var that = this;
64 67 this.children={};
65 68 this.update_children([], this.model.get('children'));
66 69 this.model.on('change:children', function(model, value, options) {
67 70 this.update_children(model.previous('children'), value);
68 71 }, this);
69 72
70 73 this.$el
71 74 .html('')
72 75 .on("remove", function(){
73 76 that.$window.remove();
74 77 });
75 78 this.$window = $('<div />')
76 79 .addClass('modal widget-modal')
77 80 .appendTo($('#notebook-container'))
78 81 .mousedown(function(){
79 82 that.bring_to_front();
80 83 });
81 84 this.$title_bar = $('<div />')
82 85 .addClass('popover-title')
83 86 .appendTo(this.$window)
84 87 .mousedown(function(){
85 88 that.bring_to_front();
86 89 });
87 90 this.$close = $('<button />')
88 91 .addClass('close icon-remove')
89 92 .css('margin-left', '5px')
90 93 .appendTo(this.$title_bar)
91 94 .click(function(){
92 95 that.hide();
93 96 event.stopPropagation();
94 97 });
95 98 this.$minimize = $('<button />')
96 99 .addClass('close icon-arrow-down')
97 100 .appendTo(this.$title_bar)
98 101 .click(function(){
99 102 that.popped_out = !that.popped_out;
100 103 if (!that.popped_out) {
101 104 that.$minimize
102 105 .removeClass('icon-arrow-down')
103 106 .addClass('icon-arrow-up');
104 107
105 108 that.$window
106 109 .draggable('destroy')
107 110 .resizable('destroy')
108 111 .removeClass('widget-modal modal')
109 112 .addClass('docked-widget-modal')
110 113 .detach()
111 114 .insertBefore(that.$show_button);
112 115 that.$show_button.hide();
113 116 that.$close.hide();
114 117 } else {
115 118 that.$minimize
116 119 .addClass('icon-arrow-down')
117 120 .removeClass('icon-arrow-up');
118 121
119 122 that.$window
120 123 .removeClass('docked-widget-modal')
121 124 .addClass('widget-modal modal')
122 125 .detach()
123 126 .appendTo($('#notebook-container'))
124 127 .draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'})
125 128 .resizable()
126 129 .children('.ui-resizable-handle').show();
127 130 that.show();
128 131 that.$show_button.show();
129 132 that.$close.show();
130 133 }
131 134 event.stopPropagation();
132 135 });
133 136 this.$title = $('<div />')
134 137 .addClass('widget-modal-title')
135 138 .html('&nbsp;')
136 139 .appendTo(this.$title_bar);
137 140 this.$body = $('<div />')
138 141 .addClass('modal-body')
139 142 .addClass('widget-modal-body')
140 143 .addClass('widget-container')
141 144 .appendTo(this.$window);
142 145
143 146 this.$show_button = $('<button />')
144 147 .html('&nbsp;')
145 148 .addClass('btn btn-info widget-modal-show')
146 149 .appendTo(this.$el)
147 150 .click(function(){
148 151 that.show();
149 152 });
150 153
151 154 this.$window.draggable({handle: '.popover-title', snap: '#notebook, .modal', snapMode: 'both'});
152 155 this.$window.resizable();
153 156 this.$window.on('resize', function(){
154 157 that.$body.outerHeight(that.$window.innerHeight() - that.$title_bar.outerHeight());
155 158 });
156 159
157 160 this.$el_to_style = this.$body;
158 161 this._shown_once = false;
159 162 this.popped_out = true;
160 163 },
161 164
162 165 hide: function() {
166 // Called when the modal hide button is clicked.
163 167 this.$window.hide();
164 168 this.$show_button.removeClass('btn-info');
165 169 },
166 170
167 171 show: function() {
172 // Called when the modal show button is clicked.
168 173 this.$show_button.addClass('btn-info');
169
170 174 this.$window.show();
171 175 if (this.popped_out) {
172 176 this.$window.css("positon", "absolute");
173 177 this.$window.css("top", "0px");
174 178 this.$window.css("left", Math.max(0, (($('body').outerWidth() - this.$window.outerWidth()) / 2) +
175 179 $(window).scrollLeft()) + "px");
176 180 this.bring_to_front();
177 181 }
178 182 },
179 183
180 184 bring_to_front: function() {
185 // Make the modal top-most, z-ordered about the other modals.
181 186 var $widget_modals = $(".widget-modal");
182 187 var max_zindex = 0;
183 188 $widget_modals.each(function (index, el){
184 189 max_zindex = Math.max(max_zindex, parseInt($(el).css('z-index')));
185 190 });
186 191
187 192 // Start z-index of widget modals at 2000
188 193 max_zindex = Math.max(max_zindex, 2000);
189 194
190 195 $widget_modals.each(function (index, el){
191 196 $el = $(el);
192 197 if (max_zindex == parseInt($el.css('z-index'))) {
193 198 $el.css('z-index', max_zindex - 1);
194 199 }
195 200 });
196 201 this.$window.css('z-index', max_zindex);
197 202 },
198 203
199 204 update_children: function(old_list, new_list) {
205 // Called when the children list is modified.
200 206 this.do_diff(old_list,
201 207 new_list,
202 208 $.proxy(this.remove_child_model, this),
203 209 $.proxy(this.add_child_model, this));
204 210 },
205 211
206 212 remove_child_model: function(model) {
213 // Called when a child is removed from children list.
207 214 this.child_views[model.id].remove();
208 215 this.delete_child_view(model);
209 216 },
210 217
211 218 add_child_model: function(model) {
219 // Called when a child is added to children list.
212 220 var view = this.create_child_view(model);
213 221 this.$body.append(view.$el);
214 222 },
215 223
216 224 update: function(){
217 225 // Update the contents of this view
218 226 //
219 227 // Called when the model is changed. The model may have been
220 228 // changed by another view or by a state update from the back-end.
221 229 var description = this.model.get('description');
222 230 description = description.replace(/ /g, '&nbsp;', 'm');
223 231 description = description.replace(/\n/g, '<br>\n', 'm');
224 232 if (description.length === 0) {
225 233 this.$title.html('&nbsp;'); // Preserve title height
226 234 } else {
227 235 this.$title.html(description);
228 236 }
229 237
230 238 var button_text = this.model.get('button_text');
231 239 button_text = button_text.replace(/ /g, '&nbsp;', 'm');
232 240 button_text = button_text.replace(/\n/g, '<br>\n', 'm');
233 241 if (button_text.length === 0) {
234 242 this.$show_button.html('&nbsp;'); // Preserve button height
235 243 } else {
236 244 this.$show_button.html(button_text);
237 245 }
238 246
239 247 if (!this._shown_once) {
240 248 this._shown_once = true;
241 249 this.show();
242 250 }
243 251
244 252 return ModalView.__super__.update.apply(this);
245 253 },
246 254
247 255 _get_selector_element: function(selector) {
248
256 // Get an element view a 'special' jquery selector. (see widget.js)
257 //
249 258 // Since the modal actually isn't within the $el in the DOM, we need to extend
250 259 // the selector logic to allow the user to set css on the modal if need be.
251 260 // The convention used is:
252 261 // "modal" - select the modal div
253 262 // "modal [selector]" - select element(s) within the modal div.
254 263 // "[selector]" - select elements within $el
255 264 // "" - select the $el_to_style
256 265 if (selector.substring(0, 5) == 'modal') {
257 266 if (selector == 'modal') {
258 267 return this.$window;
259 268 } else {
260 269 return this.$window.find(selector.substring(6));
261 270 }
262 271 } else {
263 272 return ModalView.__super__._get_selector_element.apply(this, [selector]);
264 273 }
265 274 },
266
267 275 });
268
269 276 widget_manager.register_widget_view('ModalView', ModalView);
270 277 });
@@ -1,266 +1,267
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 // FloatRangeWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18
18 19 var FloatSliderView = IPython.DOMWidgetView.extend({
19
20 // Called when view is rendered.
21 20 render : function(){
21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .appendTo(this.$el)
26 26 .addClass('widget-hlabel')
27 27 .hide();
28 28 this.$slider = $('<div />')
29 29 .slider({})
30 30 .addClass('slider');
31 31
32 32 // Put the slider in a container
33 33 this.$slider_container = $('<div />')
34 34 .addClass('widget-hslider')
35 35 .append(this.$slider);
36 36 this.$el_to_style = this.$slider_container; // Set default element to style
37 37 this.$el.append(this.$slider_container);
38 38
39 39 // Set defaults.
40 40 this.update();
41 41 },
42 42
43 43 update : function(options){
44 44 // Update the contents of this view
45 45 //
46 46 // Called when the model is changed. The model may have been
47 47 // changed by another view or by a state update from the back-end.
48 48
49 49 if (options === undefined || options.updated_view != this) {
50 50 // JQuery slider option keys. These keys happen to have a
51 51 // one-to-one mapping with the corrosponding keys of the model.
52 52 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
53 53 for (var index in jquery_slider_keys) {
54 54 var key = jquery_slider_keys[index];
55 55 if (this.model.get(key) !== undefined) {
56 56 this.$slider.slider("option", key, this.model.get(key));
57 57 }
58 58 }
59 59
60 60 // WORKAROUND FOR JQUERY SLIDER BUG.
61 61 // The horizontal position of the slider handle
62 62 // depends on the value of the slider at the time
63 63 // of orientation change. Before applying the new
64 64 // workaround, we set the value to the minimum to
65 65 // make sure that the horizontal placement of the
66 66 // handle in the vertical slider is always
67 67 // consistent.
68 68 var orientation = this.model.get('orientation');
69 69 var value = this.model.get('min');
70 70 this.$slider.slider('option', 'value', value);
71 71 this.$slider.slider('option', 'orientation', orientation);
72 72 value = this.model.get('value');
73 73 this.$slider.slider('option', 'value', value);
74 74
75 75 // Use the right CSS classes for vertical & horizontal sliders
76 76 if (orientation=='vertical') {
77 77 this.$slider_container
78 78 .removeClass('widget-hslider')
79 79 .addClass('widget-vslider');
80 80 this.$el
81 81 .removeClass('widget-hbox-single')
82 82 .addClass('widget-vbox-single');
83 83 this.$label
84 84 .removeClass('widget-hlabel')
85 85 .addClass('widget-vlabel');
86 86
87 87 } else {
88 88 this.$slider_container
89 89 .removeClass('widget-vslider')
90 90 .addClass('widget-hslider');
91 91 this.$el
92 92 .removeClass('widget-vbox-single')
93 93 .addClass('widget-hbox-single');
94 94 this.$label
95 95 .removeClass('widget-vlabel')
96 96 .addClass('widget-hlabel');
97 97 }
98 98
99 99 var description = this.model.get('description');
100 100 if (description.length === 0) {
101 101 this.$label.hide();
102 102 } else {
103 103 this.$label.html(description);
104 104 this.$label.show();
105 105 }
106 106 }
107 107 return FloatSliderView.__super__.update.apply(this);
108 108 },
109 109
110 // Handles: User input
111 events: { "slide" : "handleSliderChange" },
110 events: {
111 // Dictionary of events and their handlers.
112 "slide" : "handleSliderChange"
113 },
114
112 115 handleSliderChange: function(e, ui) {
113
116 // Handle when the slider value is changed.
117
114 118 // Calling model.set will trigger all of the other views of the
115 119 // model to update.
116 120 this.model.set('value', ui.value, {updated_view: this});
117 121 this.touch();
118 122 },
119 123 });
120
121 124 widget_manager.register_widget_view('FloatSliderView', FloatSliderView);
122 125
123 126
124 var FloatTextView = IPython.DOMWidgetView.extend({
125
126 // Called when view is rendered.
127 var FloatTextView = IPython.DOMWidgetView.extend({
127 128 render : function(){
129 // Called when view is rendered.
128 130 this.$el
129 131 .addClass('widget-hbox-single');
130 132 this.$label = $('<div />')
131 133 .appendTo(this.$el)
132 134 .addClass('widget-hlabel')
133 135 .hide();
134 136 this.$textbox = $('<input type="text" />')
135 137 .addClass('input')
136 138 .addClass('widget-numeric-text')
137 139 .appendTo(this.$el);
138 140 this.$el_to_style = this.$textbox; // Set default element to style
139 141 this.update(); // Set defaults.
140 142 },
141 143
142 144 update : function(options){
143 145 // Update the contents of this view
144 146 //
145 147 // Called when the model is changed. The model may have been
146 148 // changed by another view or by a state update from the back-end.
147
148 149 if (options === undefined || options.updated_view != this) {
149 150 var value = this.model.get('value');
150 151 if (parseFloat(this.$textbox.val()) != value) {
151 152 this.$textbox.val(value);
152 153 }
153 154
154 155 if (this.model.get('disabled')) {
155 156 this.$textbox.attr('disabled','disabled');
156 157 } else {
157 158 this.$textbox.removeAttr('disabled');
158 159 }
159 160
160 161 var description = this.model.get('description');
161 162 if (description.length === 0) {
162 163 this.$label.hide();
163 164 } else {
164 165 this.$label.html(description);
165 166 this.$label.show();
166 167 }
167 168 }
168 169 return FloatTextView.__super__.update.apply(this);
169 170 },
170 171
172 events: {
173 // Dictionary of events and their handlers.
174
175 "keyup input" : "handleChanging",
176 "paste input" : "handleChanging",
177 "cut input" : "handleChanging",
178
179 // Fires only when control is validated or looses focus.
180 "change input" : "handleChanged"
181 },
171 182
172 events: {"keyup input" : "handleChanging",
173 "paste input" : "handleChanging",
174 "cut input" : "handleChanging",
175 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
176
177 // Handles and validates user input.
178 183 handleChanging: function(e) {
179
184 // Handles and validates user input.
185
180 186 // Try to parse value as a float.
181 187 var numericalValue = 0.0;
182 188 if (e.target.value !== '') {
183 189 numericalValue = parseFloat(e.target.value);
184 190 }
185 191
186 192 // If parse failed, reset value to value stored in model.
187 193 if (isNaN(numericalValue)) {
188 194 e.target.value = this.model.get('value');
189 195 } else if (!isNaN(numericalValue)) {
190 196 if (this.model.get('max') !== undefined) {
191 197 numericalValue = Math.min(this.model.get('max'), numericalValue);
192 198 }
193 199 if (this.model.get('min') !== undefined) {
194 200 numericalValue = Math.max(this.model.get('min'), numericalValue);
195 201 }
196 202
197 203 // Apply the value if it has changed.
198 204 if (numericalValue != this.model.get('value')) {
199 205
200 206 // Calling model.set will trigger all of the other views of the
201 207 // model to update.
202 208 this.model.set('value', numericalValue, {updated_view: this});
203 209 this.touch();
204 210 }
205 211 }
206 212 },
207 213
208 // Applies validated input.
209 214 handleChanged: function(e) {
210 // Update the textbox
215 // Applies validated input.
211 216 if (this.model.get('value') != e.target.value) {
212 217 e.target.value = this.model.get('value');
213 218 }
214 219 }
215 220 });
216
217 221 widget_manager.register_widget_view('FloatTextView', FloatTextView);
218 222
219 223
220 var ProgressView = IPython.DOMWidgetView.extend({
221
222 // Called when view is rendered.
224 var ProgressView = IPython.DOMWidgetView.extend({
223 225 render : function(){
226 // Called when view is rendered.
224 227 this.$el
225 228 .addClass('widget-hbox-single');
226 229 this.$label = $('<div />')
227 230 .appendTo(this.$el)
228 231 .addClass('widget-hlabel')
229 232 .hide();
230 233 this.$progress = $('<div />')
231 234 .addClass('progress')
232 235 .addClass('widget-progress')
233 236 .appendTo(this.$el);
234 237 this.$el_to_style = this.$progress; // Set default element to style
235 238 this.$bar = $('<div />')
236 239 .addClass('bar')
237 240 .css('width', '50%')
238 241 .appendTo(this.$progress);
239 242 this.update(); // Set defaults.
240 243 },
241 244
242 245 update : function(){
243 246 // Update the contents of this view
244 247 //
245 248 // Called when the model is changed. The model may have been
246 249 // changed by another view or by a state update from the back-end.
247 250 var value = this.model.get('value');
248 251 var max = this.model.get('max');
249 252 var min = this.model.get('min');
250 253 var percent = 100.0 * (value - min) / (max - min);
251 254 this.$bar.css('width', percent + '%');
252 255
253 256 var description = this.model.get('description');
254 257 if (description.length === 0) {
255 258 this.$label.hide();
256 259 } else {
257 260 this.$label.html(description);
258 261 this.$label.show();
259 262 }
260 263 return ProgressView.__super__.update.apply(this);
261 },
262
264 },
263 265 });
264
265 266 widget_manager.register_widget_view('ProgressView', ProgressView);
266 267 });
@@ -1,54 +1,51
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 // ImageWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18 var ImageView = IPython.DOMWidgetView.extend({
19
20 // Called when view is rendered.
18
19 var ImageView = IPython.DOMWidgetView.extend({
21 20 render : function(){
21 // Called when view is rendered.
22 22 this.setElement($("<img />"));
23 23 this.update(); // Set defaults.
24 24 },
25 25
26 26 update : function(){
27 27 // Update the contents of this view
28 28 //
29 29 // Called when the model is changed. The model may have been
30 30 // changed by another view or by a state update from the back-end.
31 31 var image_src = 'data:image/' + this.model.get('format') + ';base64,' + this.model.get('_b64value');
32 32 this.$el.attr('src', image_src);
33 33
34 34 var width = this.model.get('width');
35 35 if (width !== undefined && width.length > 0) {
36 36 this.$el.attr('width', width);
37 37 } else {
38 38 this.$el.removeAttr('width');
39 39 }
40 40
41 41 var height = this.model.get('height');
42 42 if (height !== undefined && height.length > 0) {
43 43 this.$el.attr('height', height);
44 44 } else {
45 45 this.$el.removeAttr('height');
46 46 }
47 47 return ImageView.__super__.update.apply(this);
48 48 },
49
50 49 });
51
52 50 widget_manager.register_widget_view('ImageView', ImageView);
53
54 51 });
@@ -1,215 +1,220
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 // IntRangeWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18 var IntSliderView = IPython.DOMWidgetView.extend({
19
20 // Called when view is rendered.
18
19 var IntSliderView = IPython.DOMWidgetView.extend({
21 20 render : function(){
21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .appendTo(this.$el)
26 26 .addClass('widget-hlabel')
27 27 .hide();
28 28 this.$slider = $('<div />')
29 29 .slider({})
30 30 .addClass('slider');
31 31
32 32 // Put the slider in a container
33 33 this.$slider_container = $('<div />')
34 34 .addClass('widget-hslider')
35 35 .append(this.$slider);
36 36 this.$el_to_style = this.$slider_container; // Set default element to style
37 37 this.$el.append(this.$slider_container);
38 38
39 39 // Set defaults.
40 40 this.update();
41 41 },
42 42
43 43 update : function(options){
44 44 // Update the contents of this view
45 45 //
46 46 // Called when the model is changed. The model may have been
47 47 // changed by another view or by a state update from the back-end.
48 48 if (options === undefined || options.updated_view != this) {
49 49 // JQuery slider option keys. These keys happen to have a
50 50 // one-to-one mapping with the corrosponding keys of the model.
51 51 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
52 52 for (var index in jquery_slider_keys) {
53 53 var key = jquery_slider_keys[index];
54 54 if (this.model.get(key) !== undefined) {
55 55 this.$slider.slider("option", key, this.model.get(key));
56 56 }
57 57 }
58 58
59 59 // WORKAROUND FOR JQUERY SLIDER BUG.
60 60 // The horizontal position of the slider handle
61 61 // depends on the value of the slider at the time
62 62 // of orientation change. Before applying the new
63 63 // workaround, we set the value to the minimum to
64 64 // make sure that the horizontal placement of the
65 65 // handle in the vertical slider is always
66 66 // consistent.
67 67 var orientation = this.model.get('orientation');
68 68 var value = this.model.get('min');
69 69 this.$slider.slider('option', 'value', value);
70 70 this.$slider.slider('option', 'orientation', orientation);
71 71 value = this.model.get('value');
72 72 this.$slider.slider('option', 'value', value);
73 73
74 74 // Use the right CSS classes for vertical & horizontal sliders
75 75 if (orientation=='vertical') {
76 76 this.$slider_container
77 77 .removeClass('widget-hslider')
78 78 .addClass('widget-vslider');
79 79 this.$el
80 80 .removeClass('widget-hbox-single')
81 81 .addClass('widget-vbox-single');
82 82 this.$label
83 83 .removeClass('widget-hlabel')
84 84 .addClass('widget-vlabel');
85 85
86 86 } else {
87 87 this.$slider_container
88 88 .removeClass('widget-vslider')
89 89 .addClass('widget-hslider');
90 90 this.$el
91 91 .removeClass('widget-vbox-single')
92 92 .addClass('widget-hbox-single');
93 93 this.$label
94 94 .removeClass('widget-vlabel')
95 95 .addClass('widget-hlabel');
96 96 }
97 97
98 98 var description = this.model.get('description');
99 99 if (description.length === 0) {
100 100 this.$label.hide();
101 101 } else {
102 102 this.$label.html(description);
103 103 this.$label.show();
104 104 }
105 105 }
106 106 return IntSliderView.__super__.update.apply(this);
107 107 },
108 108
109 // Handles: User input
110 events: { "slide" : "handleSliderChange" },
109 events: {
110 // Dictionary of events and their handlers.
111 "slide" : "handleSliderChange"
112 },
113
111 114 handleSliderChange: function(e, ui) {
112
115 // Called when the slider value is changed.
116
113 117 // Calling model.set will trigger all of the other views of the
114 118 // model to update.
115 119 this.model.set('value', ~~ui.value, {updated_view: this}); // Double bit-wise not to truncate decimel
116 120 this.touch();
117 121 },
118 122 });
119
120 123 widget_manager.register_widget_view('IntSliderView', IntSliderView);
121 124
122 var IntTextView = IPython.DOMWidgetView.extend({
123
124 // Called when view is rendered.
125
126 var IntTextView = IPython.DOMWidgetView.extend({
125 127 render : function(){
128 // Called when view is rendered.
126 129 this.$el
127 130 .addClass('widget-hbox-single');
128 131 this.$label = $('<div />')
129 132 .appendTo(this.$el)
130 133 .addClass('widget-hlabel')
131 134 .hide();
132 135 this.$textbox = $('<input type="text" />')
133 136 .addClass('input')
134 137 .addClass('widget-numeric-text')
135 138 .appendTo(this.$el);
136 139 this.$el_to_style = this.$textbox; // Set default element to style
137 140 this.update(); // Set defaults.
138 141 },
139 142
140 143 update : function(options){
141 144 // Update the contents of this view
142 145 //
143 146 // Called when the model is changed. The model may have been
144 147 // changed by another view or by a state update from the back-end.
145 148 if (options === undefined || options.updated_view != this) {
146 149 var value = this.model.get('value');
147 150 if (parseInt(this.$textbox.val()) != value) {
148 151 this.$textbox.val(value);
149 152 }
150 153
151 154 if (this.model.get('disabled')) {
152 155 this.$textbox.attr('disabled','disabled');
153 156 } else {
154 157 this.$textbox.removeAttr('disabled');
155 158 }
156 159
157 160 var description = this.model.get('description');
158 161 if (description.length === 0) {
159 162 this.$label.hide();
160 163 } else {
161 164 this.$label.html(description);
162 165 this.$label.show();
163 166 }
164 167 }
165 168 return IntTextView.__super__.update.apply(this);
166 169 },
170
171 events: {
172 // Dictionary of events and their handlers.
173 "keyup input" : "handleChanging",
174 "paste input" : "handleChanging",
175 "cut input" : "handleChanging",
176
177 // Fires only when control is validated or looses focus.
178 "change input" : "handleChanged"
179 },
167 180
168
169 events: {"keyup input" : "handleChanging",
170 "paste input" : "handleChanging",
171 "cut input" : "handleChanging",
172 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
173
174 // Handles and validates user input.
175 181 handleChanging: function(e) {
182 // Handles and validates user input.
176 183
177 184 // Try to parse value as a float.
178 185 var numericalValue = 0;
179 186 if (e.target.value !== '') {
180 187 numericalValue = parseInt(e.target.value);
181 188 }
182 189
183 190 // If parse failed, reset value to value stored in model.
184 191 if (isNaN(numericalValue)) {
185 192 e.target.value = this.model.get('value');
186 193 } else if (!isNaN(numericalValue)) {
187 194 if (this.model.get('max') !== undefined) {
188 195 numericalValue = Math.min(this.model.get('max'), numericalValue);
189 196 }
190 197 if (this.model.get('min') !== undefined) {
191 198 numericalValue = Math.max(this.model.get('min'), numericalValue);
192 199 }
193 200
194 201 // Apply the value if it has changed.
195 202 if (numericalValue != this.model.get('value')) {
196 203
197 204 // Calling model.set will trigger all of the other views of the
198 205 // model to update.
199 206 this.model.set('value', numericalValue, {updated_view: this});
200 207 this.touch();
201 208 }
202 209 }
203 210 },
204 211
205 // Applies validated input.
206 212 handleChanged: function(e) {
207 // Update the textbox
213 // Applies validated input.
208 214 if (this.model.get('value') != e.target.value) {
209 215 e.target.value = this.model.get('value');
210 216 }
211 217 }
212 218 });
213
214 219 widget_manager.register_widget_view('IntTextView', IntTextView);
215 220 });
@@ -1,379 +1,371
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 // SelectionWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18
18 19 var DropdownView = IPython.DOMWidgetView.extend({
19
20 // Called when view is rendered.
21 20 render : function(){
22
21 // Called when view is rendered.
23 22 this.$el
24 23 .addClass('widget-hbox-single')
25 24 .html('');
26 25 this.$label = $('<div />')
27 26 .appendTo(this.$el)
28 27 .addClass('widget-hlabel')
29 28 .hide();
30 29 this.$buttongroup = $('<div />')
31 30 .addClass('widget_item')
32 31 .addClass('btn-group')
33 32 .appendTo(this.$el);
34 33 this.$el_to_style = this.$buttongroup; // Set default element to style
35 34 this.$droplabel = $('<button />')
36 35 .addClass('btn')
37 36 .addClass('widget-combo-btn')
38 37 .html('&nbsp;')
39 38 .appendTo(this.$buttongroup);
40 39 this.$dropbutton = $('<button />')
41 40 .addClass('btn')
42 41 .addClass('dropdown-toggle')
43 42 .addClass('widget-combo-carrot-btn')
44 43 .attr('data-toggle', 'dropdown')
45 44 .html('<span class="caret"></span>')
46 45 .appendTo(this.$buttongroup);
47 46 this.$droplist = $('<ul />')
48 47 .addClass('dropdown-menu')
49 48 .appendTo(this.$buttongroup);
50 49
51 50 // Set defaults.
52 51 this.update();
53 52 },
54 53
55 54 update : function(options){
56 55 // Update the contents of this view
57 56 //
58 57 // Called when the model is changed. The model may have been
59 58 // changed by another view or by a state update from the back-end.
60 59
61 60 if (options === undefined || options.updated_view != this) {
62 61 var selected_item_text = this.model.get('value');
63 62 selected_item_text = selected_item_text.replace(/ /g, '&nbsp;');
64 63 selected_item_text = selected_item_text.replace(/\n/g, '<br>\n');
65 64 if (selected_item_text.length === 0) {
66 65 this.$droplabel.html('&nbsp;');
67 66 } else {
68 67 this.$droplabel.html(selected_item_text);
69 68 }
70 69
71 70 var items = this.model.get('values');
72 71 this.$droplist.html('');
73 72 for (var index in items) {
74 73 var that = this;
75 74 var item_button = $('<a href="#"/>')
76 75 .html(items[index])
77 76 .on('click', $.proxy(this.handle_click, this));
78 77 this.$droplist.append($('<li />').append(item_button));
79 78 }
80 79
81 80 if (this.model.get('disabled')) {
82 81 this.$buttongroup.attr('disabled','disabled');
83 82 this.$droplabel.attr('disabled','disabled');
84 83 this.$dropbutton.attr('disabled','disabled');
85 84 this.$droplist.attr('disabled','disabled');
86 85 } else {
87 86 this.$buttongroup.removeAttr('disabled');
88 87 this.$droplabel.removeAttr('disabled');
89 88 this.$dropbutton.removeAttr('disabled');
90 89 this.$droplist.removeAttr('disabled');
91 90 }
92 91
93 92 var description = this.model.get('description');
94 93 if (description.length === 0) {
95 94 this.$label.hide();
96 95 } else {
97 96 this.$label.html(description);
98 97 this.$label.show();
99 98 }
100 99 }
101 100 return DropdownView.__super__.update.apply(this);
102 101 },
103 102
104 // Handle when a value is clicked.
105 103 handle_click: function (e) {
104 // Handle when a value is clicked.
106 105
107 106 // Calling model.set will trigger all of the other views of the
108 107 // model to update.
109 108 this.model.set('value', $(e.target).html(), {updated_view: this});
110 109 this.touch();
111 110 },
112 111
113 112 });
114
115 113 widget_manager.register_widget_view('DropdownView', DropdownView);
116 114
117 var RadioButtonsView = IPython.DOMWidgetView.extend({
118
119 // Called when view is rendered.
115
116 var RadioButtonsView = IPython.DOMWidgetView.extend({
120 117 render : function(){
118 // Called when view is rendered.
121 119 this.$el
122 120 .addClass('widget-hbox');
123 121 this.$label = $('<div />')
124 122 .appendTo(this.$el)
125 123 .addClass('widget-hlabel')
126 124 .hide();
127 125 this.$container = $('<div />')
128 126 .appendTo(this.$el)
129 127 .addClass('widget-container')
130 128 .addClass('vbox');
131 129 this.$el_to_style = this.$container; // Set default element to style
132 130 this.update();
133 131 },
134 132
135 133 update : function(options){
136 134 // Update the contents of this view
137 135 //
138 136 // Called when the model is changed. The model may have been
139 137 // changed by another view or by a state update from the back-end.
140 138 if (options === undefined || options.updated_view != this) {
141 139 // Add missing items to the DOM.
142 140 var items = this.model.get('values');
143 141 var disabled = this.model.get('disabled');
144 142 for (var index in items) {
145 143 var item_query = ' :input[value="' + items[index] + '"]';
146 144 if (this.$el.find(item_query).length === 0) {
147 145 var $label = $('<label />')
148 146 .addClass('radio')
149 147 .html(items[index])
150 148 .appendTo(this.$container);
151 149
152 150 $('<input />')
153 151 .attr('type', 'radio')
154 152 .addClass(this.model)
155 153 .val(items[index])
156 154 .prependTo($label)
157 155 .on('click', $.proxy(this.handle_click, this));
158 156 }
159 157
160 158 var $item_element = this.$container.find(item_query);
161 159 if (this.model.get('value') == items[index]) {
162 160 $item_element.prop('checked', true);
163 161 } else {
164 162 $item_element.prop('checked', false);
165 163 }
166 164 $item_element.prop('disabled', disabled);
167 165 }
168 166
169 167 // Remove items that no longer exist.
170 168 this.$container.find('input').each(function(i, obj) {
171 169 var value = $(obj).val();
172 170 var found = false;
173 171 for (var index in items) {
174 172 if (items[index] == value) {
175 173 found = true;
176 174 break;
177 175 }
178 176 }
179 177
180 178 if (!found) {
181 179 $(obj).parent().remove();
182 180 }
183 181 });
184 182
185 183 var description = this.model.get('description');
186 184 if (description.length === 0) {
187 185 this.$label.hide();
188 186 } else {
189 187 this.$label.html(description);
190 188 this.$label.show();
191 189 }
192 190 }
193 191 return RadioButtonsView.__super__.update.apply(this);
194 192 },
195 193
196 // Handle when a value is clicked.
197 194 handle_click: function (e) {
195 // Handle when a value is clicked.
198 196
199 197 // Calling model.set will trigger all of the other views of the
200 198 // model to update.
201 199 this.model.set('value', $(e.target).val(), {updated_view: this});
202 200 this.touch();
203 201 },
204 202 });
205
206 203 widget_manager.register_widget_view('RadioButtonsView', RadioButtonsView);
207 204
208 205
209 var ToggleButtonsView = IPython.DOMWidgetView.extend({
210
211 // Called when view is rendered.
206 var ToggleButtonsView = IPython.DOMWidgetView.extend({
212 207 render : function(){
208 // Called when view is rendered.
213 209 this.$el
214 210 .addClass('widget-hbox-single');
215 211 this.$label = $('<div />')
216 212 .appendTo(this.$el)
217 213 .addClass('widget-hlabel')
218 214 .hide();
219 215 this.$buttongroup = $('<div />')
220 216 .addClass('btn-group')
221 217 .attr('data-toggle', 'buttons-radio')
222 218 .appendTo(this.$el);
223 219 this.$el_to_style = this.$buttongroup; // Set default element to style
224 220 this.update();
225 221 },
226 222
227 223 update : function(options){
228 224 // Update the contents of this view
229 225 //
230 226 // Called when the model is changed. The model may have been
231 227 // changed by another view or by a state update from the back-end.
232 228 if (options === undefined || options.updated_view != this) {
233 229 // Add missing items to the DOM.
234 230 var items = this.model.get('values');
235 231 var disabled = this.model.get('disabled');
236 232 for (var index in items) {
237 233 var item_query = ' :contains("' + items[index] + '")';
238 234 if (this.$buttongroup.find(item_query).length === 0) {
239 235 $('<button />')
240 236 .attr('type', 'button')
241 237 .addClass('btn')
242 238 .html(items[index])
243 239 .appendTo(this.$buttongroup)
244 240 .on('click', $.proxy(this.handle_click, this));
245 241 }
246 242
247 243 var $item_element = this.$buttongroup.find(item_query);
248 244 if (this.model.get('value') == items[index]) {
249 245 $item_element.addClass('active');
250 246 } else {
251 247 $item_element.removeClass('active');
252 248 }
253 249 $item_element.prop('disabled', disabled);
254 250 }
255 251
256 252 // Remove items that no longer exist.
257 253 this.$buttongroup.find('button').each(function(i, obj) {
258 254 var value = $(obj).html();
259 255 var found = false;
260 256 for (var index in items) {
261 257 if (items[index] == value) {
262 258 found = true;
263 259 break;
264 260 }
265 261 }
266 262
267 263 if (!found) {
268 264 $(obj).remove();
269 265 }
270 266 });
271 267
272 268 var description = this.model.get('description');
273 269 if (description.length === 0) {
274 270 this.$label.hide();
275 271 } else {
276 272 this.$label.html(description);
277 273 this.$label.show();
278 274 }
279 275 }
280 276 return ToggleButtonsView.__super__.update.apply(this);
281 277 },
282 278
283 // Handle when a value is clicked.
284 279 handle_click: function (e) {
280 // Handle when a value is clicked.
285 281
286 282 // Calling model.set will trigger all of the other views of the
287 283 // model to update.
288 284 this.model.set('value', $(e.target).html(), {updated_view: this});
289 285 this.touch();
290 },
291
286 },
292 287 });
293
294 288 widget_manager.register_widget_view('ToggleButtonsView', ToggleButtonsView);
295 289
296 var ListBoxView = IPython.DOMWidgetView.extend({
297
298 // Called when view is rendered.
290
291 var ListBoxView = IPython.DOMWidgetView.extend({
299 292 render : function(){
293 // Called when view is rendered.
300 294 this.$el
301 295 .addClass('widget-hbox');
302 296 this.$label = $('<div />')
303 297 .appendTo(this.$el)
304 298 .addClass('widget-hlabel')
305 299 .hide();
306 300 this.$listbox = $('<select />')
307 301 .addClass('widget-listbox')
308 302 .attr('size', 6)
309 303 .appendTo(this.$el);
310 304 this.$el_to_style = this.$listbox; // Set default element to style
311 305 this.update();
312 306 },
313 307
314 308 update : function(options){
315 309 // Update the contents of this view
316 310 //
317 311 // Called when the model is changed. The model may have been
318 312 // changed by another view or by a state update from the back-end.
319 313 if (options === undefined || options.updated_view != this) {
320 314 // Add missing items to the DOM.
321 315 var items = this.model.get('values');
322 316 for (var index in items) {
323 317 var item_query = ' :contains("' + items[index] + '")';
324 318 if (this.$listbox.find(item_query).length === 0) {
325 319 $('<option />')
326 320 .html(items[index])
327 321 .attr('value', items[index])
328 322 .appendTo(this.$listbox)
329 323 .on('click', $.proxy(this.handle_click, this));
330 324 }
331 325 }
332 326
333 327 // Select the correct element
334 328 this.$listbox.val(this.model.get('value'));
335 329
336 330 // Disable listbox if needed
337 331 var disabled = this.model.get('disabled');
338 332 this.$listbox.prop('disabled', disabled);
339 333
340 334 // Remove items that no longer exist.
341 335 this.$listbox.find('option').each(function(i, obj) {
342 336 var value = $(obj).html();
343 337 var found = false;
344 338 for (var index in items) {
345 339 if (items[index] == value) {
346 340 found = true;
347 341 break;
348 342 }
349 343 }
350 344
351 345 if (!found) {
352 346 $(obj).remove();
353 347 }
354 348 });
355 349
356 350 var description = this.model.get('description');
357 351 if (description.length === 0) {
358 352 this.$label.hide();
359 353 } else {
360 354 this.$label.html(description);
361 355 this.$label.show();
362 356 }
363 357 }
364 358 return ListBoxView.__super__.update.apply(this);
365 359 },
366 360
367 // Handle when a value is clicked.
368 361 handle_click: function (e) {
362 // Handle when a value is clicked.
369 363
370 364 // Calling model.set will trigger all of the other views of the
371 365 // model to update.
372 366 this.model.set('value', $(e.target).html(), {updated_view: this});
373 367 this.touch();
374 },
375
368 },
376 369 });
377
378 370 widget_manager.register_widget_view('ListBoxView', ListBoxView);
379 371 });
@@ -1,248 +1,244
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 // SelectionContainerWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18
18 19 var AccordionView = IPython.DOMWidgetView.extend({
19
20 20 render: function(){
21 // Called when view is rendered.
21 22 var guid = 'accordion' + IPython.utils.uuid();
22 23 this.$el
23 24 .attr('id', guid)
24 25 .addClass('accordion');
25 26 this.containers = [];
26 27 this.model_containers = {};
27 28 this.update_children([], this.model.get('children'));
28 29 this.model.on('change:children', function(model, value, options) {
29 30 this.update_children(model.previous('children'), value);
30 31 }, this);
31 32 },
32 33
33 34 update: function(options) {
34 35 // Update the contents of this view
35 36 //
36 37 // Called when the model is changed. The model may have been
37 38 // changed by another view or by a state update from the back-end.
38
39 39 if (options === undefined || options.updated_view != this) {
40 40 // Set tab titles
41 41 var titles = this.model.get('_titles');
42 42 for (var page_index in titles) {
43 43
44 44 var accordian = this.containers[page_index];
45 45 if (accordian !== undefined) {
46 46 accordian
47 47 .find('.accordion-heading')
48 48 .find('.accordion-toggle')
49 49 .html(titles[page_index]);
50 50 }
51 51 }
52 52
53 53 // Set selected page
54 54 var selected_index = this.model.get("selected_index");
55 55 if (0 <= selected_index && selected_index < this.containers.length) {
56 56 for (var index in this.containers) {
57 57 if (index==selected_index) {
58 58 this.containers[index].find('.accordion-body').collapse('show');
59 59 } else {
60 60 this.containers[index].find('.accordion-body').collapse('hide');
61 61 }
62 62
63 63 }
64 64 }
65 65 }
66 66 return AccordionView.__super__.update.apply(this);
67 67 },
68 68
69 69 update_children: function(old_list, new_list) {
70 // Called when the children list is modified.
70 71 this.do_diff(old_list,
71 72 new_list,
72 73 $.proxy(this.remove_child_model, this),
73 74 $.proxy(this.add_child_model, this));
74 75 },
75 76
76 77 remove_child_model: function(model) {
78 // Called when a child is removed from children list.
77 79 var accordion_group = this.model_containers[model.id];
78 80 this.containers.splice(accordion_group.container_index, 1);
79 81 delete this.model_containers[model.id];
80 82 accordion_group.remove();
81 83 this.delete_child_view(model);
82 84 },
83 85
84 86 add_child_model: function(model) {
87 // Called when a child is added to children list.
85 88 var view = this.create_child_view(model);
86 89 var index = this.containers.length;
87 90 var uuid = IPython.utils.uuid();
88 91 var accordion_group = $('<div />')
89 92 .addClass('accordion-group')
90 93 .appendTo(this.$el);
91 94 var accordion_heading = $('<div />')
92 95 .addClass('accordion-heading')
93 96 .appendTo(accordion_group);
94 97 var that = this;
95 98 var accordion_toggle = $('<a />')
96 99 .addClass('accordion-toggle')
97 100 .attr('data-toggle', 'collapse')
98 101 .attr('data-parent', '#' + this.$el.attr('id'))
99 102 .attr('href', '#' + uuid)
100 103 .click(function(evt){
101 104
102 105 // Calling model.set will trigger all of the other views of the
103 106 // model to update.
104 107 that.model.set("selected_index", index, {updated_view: this});
105 108 that.touch();
106 109 })
107 110 .html('Page ' + index)
108 111 .appendTo(accordion_heading);
109 112 var accordion_body = $('<div />', {id: uuid})
110 113 .addClass('accordion-body collapse')
111 114 .appendTo(accordion_group);
112 115 var accordion_inner = $('<div />')
113 116 .addClass('accordion-inner')
114 117 .appendTo(accordion_body);
115 118 var container_index = this.containers.push(accordion_group) - 1;
116 119 accordion_group.container_index = container_index;
117 120 this.model_containers[model.id] = accordion_group;
118 121 accordion_inner.append(view.$el);
119 122
120 123 this.update();
121 124
122 125 // Stupid workaround to close the bootstrap accordion tabs which
123 126 // open by default even though they don't have the `in` class
124 127 // attached to them. For some reason a delay is required.
125 128 // TODO: Better fix.
126 129 setTimeout(function(){ that.update(); }, 500);
127 130 },
128 131 });
129
130 132 widget_manager.register_widget_view('AccordionView', AccordionView);
131 133
132 var TabView = IPython.DOMWidgetView.extend({
133
134
135 var TabView = IPython.DOMWidgetView.extend({
134 136 initialize: function() {
137 // Public constructor.
135 138 this.containers = [];
136 139 TabView.__super__.initialize.apply(this, arguments);
137 140 },
138 141
139 142 render: function(){
143 // Called when view is rendered.
140 144 var uuid = 'tabs'+IPython.utils.uuid();
141 145 var that = this;
142 146 this.$tabs = $('<div />', {id: uuid})
143 147 .addClass('nav')
144 148 .addClass('nav-tabs')
145 149 .appendTo(this.$el);
146 150 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
147 151 .addClass('tab-content')
148 152 .appendTo(this.$el);
149 153 this.containers = [];
150 154 this.update_children([], this.model.get('children'));
151 155 this.model.on('change:children', function(model, value, options) {
152 156 this.update_children(model.previous('children'), value);
153 157 }, this);
154 158 },
155
156 update_children: function(old_list, new_list) {
157 _.each(this.containers, function(element, index, list) {
158 element.remove();
159 }, this);
160 this.containers = [];
161 this.update_child_views(old_list, new_list);
162 _.each(new_list, function(element, index, list) {
163 this.add_child_view(this.child_views[element]);
164 }, this)
165 },
166
159
167 160 update_children: function(old_list, new_list) {
161 // Called when the children list is modified.
168 162 this.do_diff(old_list,
169 163 new_list,
170 164 $.proxy(this.remove_child_model, this),
171 165 $.proxy(this.add_child_model, this));
172 166 },
173 167
174 168 remove_child_model: function(model) {
169 // Called when a child is removed from children list.
175 170 var view = this.child_views[model.id];
176 171 this.containers.splice(view.parent_tab.tab_text_index, 1);
177 172 view.parent_tab.remove();
178 173 view.parent_container.remove();
179 174 view.remove();
180 175 this.delete_child_view(model);
181 176 },
182 177
183 178 add_child_model: function(model) {
179 // Called when a child is added to children list.
184 180 var view = this.create_child_view(model);
185 181 var index = this.containers.length;
186 182 var uuid = IPython.utils.uuid();
187 183
188 184 var that = this;
189 185 var tab = $('<li />')
190 186 .css('list-style-type', 'none')
191 187 .appendTo(this.$tabs);
192 188 view.parent_tab = tab;
193 189
194 190 var tab_text = $('<a />')
195 191 .attr('href', '#' + uuid)
196 192 .attr('data-toggle', 'tab')
197 193 .html('Page ' + index)
198 194 .appendTo(tab)
199 195 .click(function (e) {
200 196
201 197 // Calling model.set will trigger all of the other views of the
202 198 // model to update.
203 199 that.model.set("selected_index", index, {updated_view: this});
204 200 that.touch();
205 201 that.select_page(index);
206 202 });
207 203 tab.tab_text_index = this.containers.push(tab_text) - 1;
208 204
209 205 var contents_div = $('<div />', {id: uuid})
210 206 .addClass('tab-pane')
211 207 .addClass('fade')
212 208 .append(view.$el)
213 209 .appendTo(this.$tab_contents);
214 210 view.parent_container = contents_div;
215 211 },
216 212
217 213 update: function(options) {
218 214 // Update the contents of this view
219 215 //
220 216 // Called when the model is changed. The model may have been
221 217 // changed by another view or by a state update from the back-end.
222 218 if (options === undefined || options.updated_view != this) {
223 219 // Set tab titles
224 220 var titles = this.model.get('_titles');
225 221 for (var page_index in titles) {
226 222 var tab_text = this.containers[page_index];
227 223 if (tab_text !== undefined) {
228 224 tab_text.html(titles[page_index]);
229 225 }
230 226 }
231 227
232 228 var selected_index = this.model.get('selected_index');
233 229 if (0 <= selected_index && selected_index < this.containers.length) {
234 230 this.select_page(selected_index);
235 231 }
236 232 }
237 233 return TabView.__super__.update.apply(this);
238 234 },
239 235
240 236 select_page: function(index) {
237 // Select a page.
241 238 this.$tabs.find('li')
242 239 .removeClass('active');
243 240 this.containers[index].tab('show');
244 241 },
245 242 });
246
247 243 widget_manager.register_widget_view('TabView', TabView);
248 244 });
@@ -1,199 +1,197
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 // StringWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(widget_manager){
18 var HTMLView = IPython.DOMWidgetView.extend({
19
20 // Called when view is rendered.
18
19 var HTMLView = IPython.DOMWidgetView.extend({
21 20 render : function(){
21 // Called when view is rendered.
22 22 this.update(); // Set defaults.
23 23 },
24 24
25 25 update : function(){
26 26 // Update the contents of this view
27 27 //
28 28 // Called when the model is changed. The model may have been
29 29 // changed by another view or by a state update from the back-end.
30 30 this.$el.html(this.model.get('value'));
31 31 return HTMLView.__super__.update.apply(this);
32 32 },
33
34 33 });
35
36 34 widget_manager.register_widget_view('HTMLView', HTMLView);
37 35
38 36
39 var LatexView = IPython.DOMWidgetView.extend({
40
41 // Called when view is rendered.
37 var LatexView = IPython.DOMWidgetView.extend({
42 38 render : function(){
39 // Called when view is rendered.
43 40 this.update(); // Set defaults.
44 41 },
45 42
46 43 update : function(){
47 44 // Update the contents of this view
48 45 //
49 46 // Called when the model is changed. The model may have been
50 47 // changed by another view or by a state update from the back-end.
51 48 this.$el.html(this.model.get('value'));
52 49 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$el.get(0)]);
53 50
54 51 return LatexView.__super__.update.apply(this);
55 },
56
52 },
57 53 });
58
59 54 widget_manager.register_widget_view('LatexView', LatexView);
60 55
61 var TextAreaView = IPython.DOMWidgetView.extend({
62
63 // Called when view is rendered.
56
57 var TextAreaView = IPython.DOMWidgetView.extend({
64 58 render: function(){
59 // Called when view is rendered.
65 60 this.$el
66 61 .addClass('widget-hbox')
67 62 .html('');
68 63 this.$label = $('<div />')
69 64 .appendTo(this.$el)
70 65 .addClass('widget-hlabel')
71 66 .hide();
72 67 this.$textbox = $('<textarea />')
73 68 .attr('rows', 5)
74 69 .addClass('widget-text')
75 70 .appendTo(this.$el);
76 71 this.$el_to_style = this.$textbox; // Set default element to style
77 72 this.update(); // Set defaults.
78 73
79 74 this.model.on('msg:custom', $.proxy(this._handle_textarea_msg, this));
80 75 },
81 76
82
83 77 _handle_textarea_msg: function (content){
78 // Handle when a custom msg is recieved from the back-end.
84 79 if (content.method == "scroll_to_bottom") {
85 80 this.scroll_to_bottom();
86 81 }
87 82 },
88 83
89
90 84 scroll_to_bottom: function (){
85 // Scroll the text-area view to the bottom.
91 86 this.$textbox.scrollTop(this.$textbox[0].scrollHeight);
92 87 },
93 88
94
95 89 update: function(options){
96 90 // Update the contents of this view
97 91 //
98 92 // Called when the model is changed. The model may have been
99 93 // changed by another view or by a state update from the back-end.
100 94 if (options === undefined || options.updated_view != this) {
101 95 this.$textbox.val(this.model.get('value'));
102 96
103 97 var disabled = this.model.get('disabled');
104 98 this.$textbox.prop('disabled', disabled);
105 99
106 100 var description = this.model.get('description');
107 101 if (description.length === 0) {
108 102 this.$label.hide();
109 103 } else {
110 104 this.$label.html(description);
111 105 this.$label.show();
112 106 }
113 107 }
114 108 return TextAreaView.__super__.update.apply(this);
115 109 },
116 110
117 events: {"keyup textarea": "handleChanging",
118 "paste textarea": "handleChanging",
119 "cut textarea": "handleChanging"},
111 events: {
112 // Dictionary of events and their handlers.
113 "keyup textarea": "handleChanging",
114 "paste textarea": "handleChanging",
115 "cut textarea": "handleChanging"
116 },
120 117
121 // Handles and validates user input.
122 118 handleChanging: function(e) {
119 // Handles and validates user input.
123 120
124 121 // Calling model.set will trigger all of the other views of the
125 122 // model to update.
126 123 this.model.set('value', e.target.value, {updated_view: this});
127 124 this.touch();
128 125 },
129 126 });
130
131 127 widget_manager.register_widget_view('TextAreaView', TextAreaView);
132 128
133 var TextBoxView = IPython.DOMWidgetView.extend({
134
135 // Called when view is rendered.
129
130 var TextBoxView = IPython.DOMWidgetView.extend({
136 131 render: function(){
132 // Called when view is rendered.
137 133 this.$el
138 134 .addClass('widget-hbox-single')
139 135 .html('');
140 136 this.$label = $('<div />')
141 137 .addClass('widget-hlabel')
142 138 .appendTo(this.$el)
143 139 .hide();
144 140 this.$textbox = $('<input type="text" />')
145 141 .addClass('input')
146 142 .addClass('widget-text')
147 143 .appendTo(this.$el);
148 144 this.$el_to_style = this.$textbox; // Set default element to style
149 145 this.update(); // Set defaults.
150 146 },
151 147
152 148 update: function(options){
153 149 // Update the contents of this view
154 150 //
155 151 // Called when the model is changed. The model may have been
156 152 // changed by another view or by a state update from the back-end.
157 153 if (options === undefined || options.updated_view != this) {
158 154 if (this.$textbox.val() != this.model.get('value')) {
159 155 this.$textbox.val(this.model.get('value'));
160 156 }
161 157
162 158 var disabled = this.model.get('disabled');
163 159 this.$textbox.prop('disabled', disabled);
164 160
165 161 var description = this.model.get('description');
166 162 if (description.length === 0) {
167 163 this.$label.hide();
168 164 } else {
169 165 this.$label.html(description);
170 166 this.$label.show();
171 167 }
172 168 }
173 169 return TextBoxView.__super__.update.apply(this);
174 170 },
175 171
176 events: {"keyup input": "handleChanging",
177 "paste input": "handleChanging",
178 "cut input": "handleChanging",
179 "keypress input": "handleKeypress"},
172 events: {
173 // Dictionary of events and their handlers.
174 "keyup input": "handleChanging",
175 "paste input": "handleChanging",
176 "cut input": "handleChanging",
177 "keypress input": "handleKeypress"
178 },
180 179
181 // Handles and validates user input.
182 180 handleChanging: function(e) {
181 // Handles and validates user input.
183 182
184 183 // Calling model.set will trigger all of the other views of the
185 184 // model to update.
186 185 this.model.set('value', e.target.value, {updated_view: this});
187 186 this.touch();
188 187 },
189 188
190 // Handles text submition
191 189 handleKeypress: function(e) {
190 // Handles text submition
192 191 if (e.keyCode == 13) { // Return key
193 192 this.send({event: 'submit'});
194 193 }
195 194 },
196 195 });
197
198 196 widget_manager.register_widget_view('TextBoxView', TextBoxView);
199 197 });
General Comments 0
You need to be logged in to leave comments. Login now