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