##// END OF EJS Templates
Moved touch logic out of model into view....
Jonathan Frederic -
Show More
@@ -1,562 +1,555
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 "components/underscore/underscore-min",
18 "components/underscore/underscore-min",
19 "components/backbone/backbone-min"],
19 "components/backbone/backbone-min"],
20 function(widget_manager, underscore, backbone){
20 function(widget_manager, underscore, backbone){
21
21
22 //--------------------------------------------------------------------
22 //--------------------------------------------------------------------
23 // WidgetModel class
23 // WidgetModel class
24 //--------------------------------------------------------------------
24 //--------------------------------------------------------------------
25 var WidgetModel = Backbone.Model.extend({
25 var WidgetModel = Backbone.Model.extend({
26 constructor: function (widget_manager, widget_id, comm) {
26 constructor: function (widget_manager, widget_id, comm) {
27 this.widget_manager = widget_manager;
27 this.widget_manager = widget_manager;
28 this.pending_msgs = 0;
28 this.pending_msgs = 0;
29 this.msg_throttle = 3;
29 this.msg_throttle = 3;
30 this.msg_buffer = null;
30 this.msg_buffer = null;
31 this.views = [];
31 this.views = [];
32 this.id = widget_id;
32 this.id = widget_id;
33 this._custom_msg_callbacks = [];
33 this._custom_msg_callbacks = [];
34
34
35 if (comm !== undefined) {
35 if (comm !== undefined) {
36
36
37 // Remember comm associated with the model.
37 // Remember comm associated with the model.
38 this.comm = comm;
38 this.comm = comm;
39 comm.model = this;
39 comm.model = this;
40
40
41 // Hook comm messages up to model.
41 // Hook comm messages up to model.
42 comm.on_close($.proxy(this._handle_comm_closed, this));
42 comm.on_close($.proxy(this._handle_comm_closed, this));
43 comm.on_msg($.proxy(this._handle_comm_msg, this));
43 comm.on_msg($.proxy(this._handle_comm_msg, this));
44 }
44 }
45
45
46 return Backbone.Model.apply(this);
46 return Backbone.Model.apply(this);
47 },
47 },
48
49
50 update_other_views: function (caller) {
51 this.last_modified_view = caller;
52 this.save(this.changedAttributes(), {patch: true});
53
54 for (var view_index in this.views) {
55 var view = this.views[view_index];
56 if (view !== caller) {
57 view.update();
58 }
59 }
60 },
61
48
62
49
63 send: function (content, cell) {
50 send: function (content, cell) {
64 if (this._has_comm()) {
51 if (this._has_comm()) {
65 // Used the last modified view as the sender of the message. This
52 // Used the last modified view as the sender of the message. This
66 // will insure that any python code triggered by the sent message
53 // will insure that any python code triggered by the sent message
67 // can create and display widgets and output.
54 // can create and display widgets and output.
68 if (cell === undefined) {
55 if (cell === undefined) {
69 if (this.last_modified_view !== undefined &&
56 if (this.last_modified_view !== undefined &&
70 this.last_modified_view.cell !== undefined) {
57 this.last_modified_view.cell !== undefined) {
71 cell = this.last_modified_view.cell;
58 cell = this.last_modified_view.cell;
72 }
59 }
73 }
60 }
74 var callbacks = this._make_callbacks(cell);
61 var callbacks = this._make_callbacks(cell);
75 var data = {method: 'custom', custom_content: content};
62 var data = {method: 'custom', custom_content: content};
76
63
77 this.comm.send(data, callbacks);
64 this.comm.send(data, callbacks);
78 }
65 }
79 },
66 },
80
67
81
68
82 on_view_created: function (callback) {
69 on_view_created: function (callback) {
83 this._view_created_callback = callback;
70 this._view_created_callback = callback;
84 },
71 },
85
72
86
73
87 on_close: function (callback) {
74 on_close: function (callback) {
88 this._close_callback = callback;
75 this._close_callback = callback;
89 },
76 },
90
77
91
78
92 on_msg: function (callback, remove) {
79 on_msg: function (callback, remove) {
93 if (remove) {
80 if (remove) {
94 var found_index = -1;
81 var found_index = -1;
95 for (var index in this._custom_msg_callbacks) {
82 for (var index in this._custom_msg_callbacks) {
96 if (callback === this._custom_msg_callbacks[index]) {
83 if (callback === this._custom_msg_callbacks[index]) {
97 found_index = index;
84 found_index = index;
98 break;
85 break;
99 }
86 }
100 }
87 }
101
88
102 if (found_index >= 0) {
89 if (found_index >= 0) {
103 this._custom_msg_callbacks.splice(found_index, 1);
90 this._custom_msg_callbacks.splice(found_index, 1);
104 }
91 }
105 } else {
92 } else {
106 this._custom_msg_callbacks.push(callback);
93 this._custom_msg_callbacks.push(callback);
107 }
94 }
108 },
95 },
109
96
110
97
111 _handle_custom_msg: function (content) {
98 _handle_custom_msg: function (content) {
112 for (var index in this._custom_msg_callbacks) {
99 for (var index in this._custom_msg_callbacks) {
113 try {
100 try {
114 this._custom_msg_callbacks[index](content);
101 this._custom_msg_callbacks[index](content);
115 } catch (e) {
102 } catch (e) {
116 console.log("Exception in widget model msg callback", e, content);
103 console.log("Exception in widget model msg callback", e, content);
117 }
104 }
118 }
105 }
119 },
106 },
120
107
121
108
122 // Handle when a widget is closed.
109 // Handle when a widget is closed.
123 _handle_comm_closed: function (msg) {
110 _handle_comm_closed: function (msg) {
124 this._execute_views_method('remove');
111 this._execute_views_method('remove');
125 if (this._has_comm()) {
112 if (this._has_comm()) {
126 delete this.comm.model; // Delete ref so GC will collect widget model.
113 delete this.comm.model; // Delete ref so GC will collect widget model.
127 delete this.comm;
114 delete this.comm;
128 }
115 }
129 delete this.widget_id; // Delete id from model so widget manager cleans up.
116 delete this.widget_id; // Delete id from model so widget manager cleans up.
130 },
117 },
131
118
132
119
133 // Handle incomming comm msg.
120 // Handle incomming comm msg.
134 _handle_comm_msg: function (msg) {
121 _handle_comm_msg: function (msg) {
135 var method = msg.content.data.method;
122 var method = msg.content.data.method;
136 switch (method) {
123 switch (method) {
137 case 'display':
124 case 'display':
138
125
139 // Try to get the cell.
126 // Try to get the cell.
140 var cell = this._get_msg_cell(msg.parent_header.msg_id);
127 var cell = this._get_msg_cell(msg.parent_header.msg_id);
141 if (cell === null) {
128 if (cell === null) {
142 console.log("Could not determine where the display" +
129 console.log("Could not determine where the display" +
143 " message was from. Widget will not be displayed");
130 " message was from. Widget will not be displayed");
144 } else {
131 } else {
145 this.create_views(msg.content.data.view_name,
132 this.create_views(msg.content.data.view_name,
146 msg.content.data.parent,
133 msg.content.data.parent,
147 cell);
134 cell);
148 }
135 }
149 break;
136 break;
150 case 'update':
137 case 'update':
151 this.apply_update(msg.content.data.state);
138 this.apply_update(msg.content.data.state);
152 break;
139 break;
153 case 'add_class':
140 case 'add_class':
154 case 'remove_class':
141 case 'remove_class':
155 var selector = msg.content.data.selector;
142 var selector = msg.content.data.selector;
156 if (selector === undefined) {
143 if (selector === undefined) {
157 selector = '';
144 selector = '';
158 }
145 }
159
146
160 var class_list = msg.content.data.class_list;
147 var class_list = msg.content.data.class_list;
161 this._execute_views_method(method, selector, class_list);
148 this._execute_views_method(method, selector, class_list);
162 break;
149 break;
163 case 'set_snapshot':
150 case 'set_snapshot':
164 var cell = this._get_msg_cell(msg.parent_header.msg_id);
151 var cell = this._get_msg_cell(msg.parent_header.msg_id);
165 cell.metadata.snapshot = msg.content.data.snapshot;
152 cell.metadata.snapshot = msg.content.data.snapshot;
166 break;
153 break;
167 case 'custom':
154 case 'custom':
168 this._handle_custom_msg(msg.content.data.custom_content);
155 this._handle_custom_msg(msg.content.data.custom_content);
169 break;
156 break;
170 }
157 }
171 },
158 },
172
159
173
160
174 // Handle when a widget is updated via the python side.
161 // Handle when a widget is updated via the python side.
175 apply_update: function (state) {
162 apply_update: function (state) {
176 this.updating = true;
163 this.updating = true;
177 try {
164 try {
178 for (var key in state) {
165 for (var key in state) {
179 if (state.hasOwnProperty(key)) {
166 if (state.hasOwnProperty(key)) {
180 if (key == "_css") {
167 if (key == "_css") {
181
168
182 // Set the css value of the model as an attribute
169 // Set the css value of the model as an attribute
183 // instead of a backbone trait because we are only
170 // instead of a backbone trait because we are only
184 // interested in backend css -> frontend css. In
171 // interested in backend css -> frontend css. In
185 // other words, if the css dict changes in the
172 // other words, if the css dict changes in the
186 // frontend, we don't need to push the changes to
173 // frontend, we don't need to push the changes to
187 // the backend.
174 // the backend.
188 this.css = state[key];
175 this.css = state[key];
189 } else {
176 } else {
190 this.set(key, state[key]);
177 this.set(key, state[key]);
191 }
178 }
192 }
179 }
193 }
180 }
194 this.save();
181 this.save();
195 } finally {
182 } finally {
196 this.updating = false;
183 this.updating = false;
197 }
184 }
198 },
185 },
199
186
200
187
201 _handle_status: function (cell, msg) {
188 _handle_status: function (cell, msg) {
202 //execution_state : ('busy', 'idle', 'starting')
189 //execution_state : ('busy', 'idle', 'starting')
203 if (this._has_comm()) {
190 if (this._has_comm()) {
204 if (msg.content.execution_state=='idle') {
191 if (msg.content.execution_state=='idle') {
205
192
206 // Send buffer if this message caused another message to be
193 // Send buffer if this message caused another message to be
207 // throttled.
194 // throttled.
208 if (this.msg_buffer !== null &&
195 if (this.msg_buffer !== null &&
209 this.msg_throttle == this.pending_msgs) {
196 this.msg_throttle == this.pending_msgs) {
210
197
211 var callbacks = this._make_callbacks(cell);
198 var callbacks = this._make_callbacks(cell);
212 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
199 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
213 this.comm.send(data, callbacks);
200 this.comm.send(data, callbacks);
214 this.msg_buffer = null;
201 this.msg_buffer = null;
215 } else {
202 } else {
216
203
217 // Only decrease the pending message count if the buffer
204 // Only decrease the pending message count if the buffer
218 // doesn't get flushed (sent).
205 // doesn't get flushed (sent).
219 --this.pending_msgs;
206 --this.pending_msgs;
220 }
207 }
221 }
208 }
222 }
209 }
223 },
210 },
224
211
225
212
226 // Custom syncronization logic.
213 // Custom syncronization logic.
227 _handle_sync: function (method, options) {
214 _handle_sync: function (method, options) {
228 var model_json = this.toJSON();
215 var model_json = this.toJSON();
229 var attr;
216 var attr;
230
217
231 // Only send updated state if the state hasn't been changed
218 // Only send updated state if the state hasn't been changed
232 // during an update.
219 // during an update.
233 if (this._has_comm()) {
220 if (this._has_comm()) {
234 if (!this.updating) {
221 if (!this.updating) {
235 if (this.pending_msgs >= this.msg_throttle) {
222 if (this.pending_msgs >= this.msg_throttle) {
236 // The throttle has been exceeded, buffer the current msg so
223 // The throttle has been exceeded, buffer the current msg so
237 // it can be sent once the kernel has finished processing
224 // it can be sent once the kernel has finished processing
238 // some of the existing messages.
225 // some of the existing messages.
239 if (method=='patch') {
226 if (method=='patch') {
240 if (this.msg_buffer === null) {
227 if (this.msg_buffer === null) {
241 this.msg_buffer = $.extend({}, model_json); // Copy
228 this.msg_buffer = $.extend({}, model_json); // Copy
242 }
229 }
243 for (attr in options.attrs) {
230 for (attr in options.attrs) {
244 this.msg_buffer[attr] = options.attrs[attr];
231 this.msg_buffer[attr] = options.attrs[attr];
245 }
232 }
246 } else {
233 } else {
247 this.msg_buffer = $.extend({}, model_json); // Copy
234 this.msg_buffer = $.extend({}, model_json); // Copy
248 }
235 }
249
236
250 } else {
237 } else {
251 // We haven't exceeded the throttle, send the message like
238 // We haven't exceeded the throttle, send the message like
252 // normal. If this is a patch operation, just send the
239 // normal. If this is a patch operation, just send the
253 // changes.
240 // changes.
254 var send_json = model_json;
241 var send_json = model_json;
255 if (method =='patch') {
242 if (method =='patch') {
256 send_json = {};
243 send_json = {};
257 for (attr in options.attrs) {
244 for (attr in options.attrs) {
258 send_json[attr] = options.attrs[attr];
245 send_json[attr] = options.attrs[attr];
259 }
246 }
260 }
247 }
261
248
262 var data = {method: 'backbone', sync_method: method, sync_data: send_json};
249 var data = {method: 'backbone', sync_method: method, sync_data: send_json};
263
250
264 var cell = null;
251 var cell = null;
265 if (this.last_modified_view !== undefined && this.last_modified_view !== null) {
252 if (this.last_modified_view !== undefined && this.last_modified_view !== null) {
266 cell = this.last_modified_view.cell;
253 cell = this.last_modified_view.cell;
267 }
254 }
268
255
269 var callbacks = this._make_callbacks(cell);
256 var callbacks = this._make_callbacks(cell);
270 this.comm.send(data, callbacks);
257 this.comm.send(data, callbacks);
271 this.pending_msgs++;
258 this.pending_msgs++;
272 }
259 }
273 }
260 }
274 }
261 }
275
262
276 // Since the comm is a one-way communication, assume the message
263 // Since the comm is a one-way communication, assume the message
277 // arrived.
264 // arrived.
278 return model_json;
265 return model_json;
279 },
266 },
280
267
281
268
282 _handle_view_created: function (view) {
269 _handle_view_created: function (view) {
283 if (this._view_created_callback) {
270 if (this._view_created_callback) {
284 try {
271 try {
285 this._view_created_callback(view);
272 this._view_created_callback(view);
286 } catch (e) {
273 } catch (e) {
287 console.log("Exception in widget model view displayed callback", e, view, this);
274 console.log("Exception in widget model view displayed callback", e, view, this);
288 }
275 }
289 }
276 }
290 },
277 },
291
278
292
279
293 _execute_views_method: function (/* method_name, [argument0], [argument1], [...] */) {
280 _execute_views_method: function (/* method_name, [argument0], [argument1], [...] */) {
294 var method_name = arguments[0];
281 var method_name = arguments[0];
295 var args = null;
282 var args = null;
296 if (arguments.length > 1) {
283 if (arguments.length > 1) {
297 args = [].splice.call(arguments,1);
284 args = [].splice.call(arguments,1);
298 }
285 }
299
286
300 for (var view_index in this.views) {
287 for (var view_index in this.views) {
301 var view = this.views[view_index];
288 var view = this.views[view_index];
302 var method = view[method_name];
289 var method = view[method_name];
303 if (args === null) {
290 if (args === null) {
304 method.apply(view);
291 method.apply(view);
305 } else {
292 } else {
306 method.apply(view, args);
293 method.apply(view, args);
307 }
294 }
308 }
295 }
309 },
296 },
310
297
311
298
312 // Create view that represents the model.
299 // Create view that represents the model.
313 create_views: function (view_name, parent_id, cell) {
300 create_views: function (view_name, parent_id, cell) {
314 var new_views = [];
301 var new_views = [];
315 var view;
302 var view;
316
303
317 // Try creating and adding the view to it's parent.
304 // Try creating and adding the view to it's parent.
318 var displayed = false;
305 var displayed = false;
319 if (parent_id !== undefined) {
306 if (parent_id !== undefined) {
320 var parent_model = this.widget_manager.get_model(parent_id);
307 var parent_model = this.widget_manager.get_model(parent_id);
321 if (parent_model !== null) {
308 if (parent_model !== null) {
322 var parent_views = parent_model.views;
309 var parent_views = parent_model.views;
323 for (var parent_view_index in parent_views) {
310 for (var parent_view_index in parent_views) {
324 var parent_view = parent_views[parent_view_index];
311 var parent_view = parent_views[parent_view_index];
325 if (parent_view.cell === cell) {
312 if (parent_view.cell === cell) {
326 if (parent_view.display_child !== undefined) {
313 if (parent_view.display_child !== undefined) {
327 view = this._create_view(view_name, cell);
314 view = this._create_view(view_name, cell);
328 if (view !== null) {
315 if (view !== null) {
329 new_views.push(view);
316 new_views.push(view);
330 parent_view.display_child(view);
317 parent_view.display_child(view);
331 displayed = true;
318 displayed = true;
332 this._handle_view_created(view);
319 this._handle_view_created(view);
333 }
320 }
334 }
321 }
335 }
322 }
336 }
323 }
337 }
324 }
338 }
325 }
339
326
340 // If no parent view is defined or exists. Add the view's
327 // If no parent view is defined or exists. Add the view's
341 // element to cell's widget div.
328 // element to cell's widget div.
342 if (!displayed) {
329 if (!displayed) {
343 view = this._create_view(view_name, cell);
330 view = this._create_view(view_name, cell);
344 if (view !== null) {
331 if (view !== null) {
345 new_views.push(view);
332 new_views.push(view);
346
333
347 if (cell.widget_subarea !== undefined && cell.widget_subarea !== null) {
334 if (cell.widget_subarea !== undefined && cell.widget_subarea !== null) {
348 cell.widget_area.show();
335 cell.widget_area.show();
349 cell.widget_subarea.append(view.$el);
336 cell.widget_subarea.append(view.$el);
350 this._handle_view_created(view);
337 this._handle_view_created(view);
351 }
338 }
352 }
339 }
353 }
340 }
354
341
355 // Force the new view(s) to update their selves
342 // Force the new view(s) to update their selves
356 for (var view_index in new_views) {
343 for (var view_index in new_views) {
357 view = new_views[view_index];
344 view = new_views[view_index];
358 view.update();
345 view.update();
359 }
346 }
360 },
347 },
361
348
362
349
363 // Create a view
350 // Create a view
364 _create_view: function (view_name, cell) {
351 _create_view: function (view_name, cell) {
365 var ViewType = this.widget_manager.widget_view_types[view_name];
352 var ViewType = this.widget_manager.widget_view_types[view_name];
366 if (ViewType !== undefined && ViewType !== null) {
353 if (ViewType !== undefined && ViewType !== null) {
367 var view = new ViewType({model: this});
354 var view = new ViewType({model: this});
368 view.render();
355 view.render();
369 this.views.push(view);
356 this.views.push(view);
370 view.cell = cell;
357 view.cell = cell;
371
358
372 // Handle when the view element is remove from the page.
359 // Handle when the view element is remove from the page.
373 var that = this;
360 var that = this;
374 view.$el.on("remove", function () {
361 view.$el.on("remove", function () {
375 var index = that.views.indexOf(view);
362 var index = that.views.indexOf(view);
376 if (index > -1) {
363 if (index > -1) {
377 that.views.splice(index, 1);
364 that.views.splice(index, 1);
378 }
365 }
379 view.remove(); // Clean-up view
366 view.remove(); // Clean-up view
380
367
381 // Close the comm if there are no views left.
368 // Close the comm if there are no views left.
382 if (that.views.length() === 0) {
369 if (that.views.length() === 0) {
383 if (that._close_callback) {
370 if (that._close_callback) {
384 try {
371 try {
385 that._close_callback(that);
372 that._close_callback(that);
386 } catch (e) {
373 } catch (e) {
387 console.log("Exception in widget model close callback", e, that);
374 console.log("Exception in widget model close callback", e, that);
388 }
375 }
389 }
376 }
390
377
391 if (that._has_comm()) {
378 if (that._has_comm()) {
392 that.comm.close();
379 that.comm.close();
393 delete that.comm.model; // Delete ref so GC will collect widget model.
380 delete that.comm.model; // Delete ref so GC will collect widget model.
394 delete that.comm;
381 delete that.comm;
395 }
382 }
396 delete that.widget_id; // Delete id from model so widget manager cleans up.
383 delete that.widget_id; // Delete id from model so widget manager cleans up.
397 }
384 }
398 });
385 });
399 return view;
386 return view;
400 }
387 }
401 return null;
388 return null;
402 },
389 },
403
390
404
391
405 // Build a callback dict.
392 // Build a callback dict.
406 _make_callbacks: function (cell) {
393 _make_callbacks: function (cell) {
407 var callbacks = {};
394 var callbacks = {};
408 if (cell !== null) {
395 if (cell !== null) {
409
396
410 // Try to get output handlers
397 // Try to get output handlers
411 var handle_output = null;
398 var handle_output = null;
412 var handle_clear_output = null;
399 var handle_clear_output = null;
413 if (cell.output_area !== undefined && cell.output_area !== null) {
400 if (cell.output_area !== undefined && cell.output_area !== null) {
414 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
401 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
415 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
402 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
416 }
403 }
417
404
418 // Create callback dict usign what is known
405 // Create callback dict usign what is known
419 var that = this;
406 var that = this;
420 callbacks = {
407 callbacks = {
421 iopub : {
408 iopub : {
422 output : handle_output,
409 output : handle_output,
423 clear_output : handle_clear_output,
410 clear_output : handle_clear_output,
424
411
425 status : function (msg) {
412 status : function (msg) {
426 that._handle_status(cell, msg);
413 that._handle_status(cell, msg);
427 },
414 },
428
415
429 // Special function only registered by widget messages.
416 // Special function only registered by widget messages.
430 // Allows us to get the cell for a message so we know
417 // Allows us to get the cell for a message so we know
431 // where to add widgets if the code requires it.
418 // where to add widgets if the code requires it.
432 get_cell : function () {
419 get_cell : function () {
433 return cell;
420 return cell;
434 },
421 },
435 },
422 },
436 };
423 };
437 }
424 }
438 return callbacks;
425 return callbacks;
439 },
426 },
440
427
441
428
442 // Get the output area corresponding to the msg_id.
429 // Get the output area corresponding to the msg_id.
443 // cell is an instance of IPython.Cell
430 // cell is an instance of IPython.Cell
444 _get_msg_cell: function (msg_id) {
431 _get_msg_cell: function (msg_id) {
445
432
446 // First, check to see if the msg was triggered by cell execution.
433 // First, check to see if the msg was triggered by cell execution.
447 var cell = this.widget_manager.get_msg_cell(msg_id);
434 var cell = this.widget_manager.get_msg_cell(msg_id);
448 if (cell !== null) {
435 if (cell !== null) {
449 return cell;
436 return cell;
450 }
437 }
451
438
452 // Second, check to see if a get_cell callback was defined
439 // Second, check to see if a get_cell callback was defined
453 // for the message. get_cell callbacks are registered for
440 // for the message. get_cell callbacks are registered for
454 // widget messages, so this block is actually checking to see if the
441 // widget messages, so this block is actually checking to see if the
455 // message was triggered by a widget.
442 // message was triggered by a widget.
456 var kernel = this.widget_manager.get_kernel();
443 var kernel = this.widget_manager.get_kernel();
457 if (kernel !== undefined && kernel !== null) {
444 if (kernel !== undefined && kernel !== null) {
458 var callbacks = kernel.get_callbacks_for_msg(msg_id);
445 var callbacks = kernel.get_callbacks_for_msg(msg_id);
459 if (callbacks !== undefined &&
446 if (callbacks !== undefined &&
460 callbacks.iopub !== undefined &&
447 callbacks.iopub !== undefined &&
461 callbacks.iopub.get_cell !== undefined) {
448 callbacks.iopub.get_cell !== undefined) {
462
449
463 return callbacks.iopub.get_cell();
450 return callbacks.iopub.get_cell();
464 }
451 }
465 }
452 }
466
453
467 // Not triggered by a cell or widget (no get_cell callback
454 // Not triggered by a cell or widget (no get_cell callback
468 // exists).
455 // exists).
469 return null;
456 return null;
470 },
457 },
471
458
472
459
473 // Function that checks if a comm has been attached to this widget
460 // Function that checks if a comm has been attached to this widget
474 // model. Returns True if a valid comm is attached.
461 // model. Returns True if a valid comm is attached.
475 _has_comm: function() {
462 _has_comm: function() {
476 return this.comm !== undefined && this.comm !== null;
463 return this.comm !== undefined && this.comm !== null;
477 },
464 },
478 });
465 });
479
466
480
467
481 //--------------------------------------------------------------------
468 //--------------------------------------------------------------------
482 // WidgetView class
469 // WidgetView class
483 //--------------------------------------------------------------------
470 //--------------------------------------------------------------------
484 var WidgetView = Backbone.View.extend({
471 var WidgetView = Backbone.View.extend({
485
472
486 initialize: function () {
473 initialize: function () {
487 this.visible = true;
474 this.visible = true;
488 this.model.on('sync',this.update,this);
475 this.model.on('sync',this.update,this);
489 },
476 },
490
477
491 add_class: function (selector, class_list) {
478 add_class: function (selector, class_list) {
492 var elements = this._get_selector_element(selector);
479 var elements = this._get_selector_element(selector);
493 if (elements.length > 0) {
480 if (elements.length > 0) {
494 elements.addClass(class_list);
481 elements.addClass(class_list);
495 }
482 }
496 },
483 },
497
484
498 remove_class: function (selector, class_list) {
485 remove_class: function (selector, class_list) {
499 var elements = this._get_selector_element(selector);
486 var elements = this._get_selector_element(selector);
500 if (elements.length > 0) {
487 if (elements.length > 0) {
501 elements.removeClass(class_list);
488 elements.removeClass(class_list);
502 }
489 }
503 },
490 },
504
491
505
492
506 send: function (content) {
493 send: function (content) {
507 this.model.send(content, this.cell);
494 this.model.send(content, this.cell);
508 },
495 },
509
496
497
498 touch: function () {
499 this.model.last_modified_view = this;
500 this.model.save(this.model.changedAttributes(), {patch: true});
501 },
502
510 update: function () {
503 update: function () {
511 if (this.model.get('visible') !== undefined) {
504 if (this.model.get('visible') !== undefined) {
512 if (this.visible != this.model.get('visible')) {
505 if (this.visible != this.model.get('visible')) {
513 this.visible = this.model.get('visible');
506 this.visible = this.model.get('visible');
514 if (this.visible) {
507 if (this.visible) {
515 this.$el.show();
508 this.$el.show();
516 } else {
509 } else {
517 this.$el.hide();
510 this.$el.hide();
518 }
511 }
519 }
512 }
520 }
513 }
521
514
522 if (this.model.css !== undefined) {
515 if (this.model.css !== undefined) {
523 for (var selector in this.model.css) {
516 for (var selector in this.model.css) {
524 if (this.model.css.hasOwnProperty(selector)) {
517 if (this.model.css.hasOwnProperty(selector)) {
525
518
526 // Apply the css traits to all elements that match the selector.
519 // Apply the css traits to all elements that match the selector.
527 var elements = this._get_selector_element(selector);
520 var elements = this._get_selector_element(selector);
528 if (elements.length > 0) {
521 if (elements.length > 0) {
529 var css_traits = this.model.css[selector];
522 var css_traits = this.model.css[selector];
530 for (var css_key in css_traits) {
523 for (var css_key in css_traits) {
531 if (css_traits.hasOwnProperty(css_key)) {
524 if (css_traits.hasOwnProperty(css_key)) {
532 elements.css(css_key, css_traits[css_key]);
525 elements.css(css_key, css_traits[css_key]);
533 }
526 }
534 }
527 }
535 }
528 }
536 }
529 }
537 }
530 }
538 }
531 }
539 },
532 },
540
533
541 _get_selector_element: function (selector) {
534 _get_selector_element: function (selector) {
542 // Get the elements via the css selector. If the selector is
535 // Get the elements via the css selector. If the selector is
543 // blank, apply the style to the $el_to_style element. If
536 // blank, apply the style to the $el_to_style element. If
544 // the $el_to_style element is not defined, use apply the
537 // the $el_to_style element is not defined, use apply the
545 // style to the view's element.
538 // style to the view's element.
546 var elements = this.$el.find(selector);
539 var elements = this.$el.find(selector);
547 if (selector === undefined || selector === null || selector === '') {
540 if (selector === undefined || selector === null || selector === '') {
548 if (this.$el_to_style === undefined) {
541 if (this.$el_to_style === undefined) {
549 elements = this.$el;
542 elements = this.$el;
550 } else {
543 } else {
551 elements = this.$el_to_style;
544 elements = this.$el_to_style;
552 }
545 }
553 }
546 }
554 return elements;
547 return elements;
555 },
548 },
556 });
549 });
557
550
558 IPython.WidgetModel = WidgetModel;
551 IPython.WidgetModel = WidgetModel;
559 IPython.WidgetView = WidgetView;
552 IPython.WidgetView = WidgetView;
560
553
561 return widget_manager;
554 return widget_manager;
562 }); No newline at end of file
555 });
@@ -1,123 +1,123
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/base"], function(widget_manager){
17 define(["notebook/js/widgets/base"], function(widget_manager){
18
18
19 var BoolWidgetModel = IPython.WidgetModel.extend({});
19 var BoolWidgetModel = IPython.WidgetModel.extend({});
20 widget_manager.register_widget_model('BoolWidgetModel', BoolWidgetModel);
20 widget_manager.register_widget_model('BoolWidgetModel', BoolWidgetModel);
21
21
22 var CheckboxView = IPython.WidgetView.extend({
22 var CheckboxView = IPython.WidgetView.extend({
23
23
24 // Called when view is rendered.
24 // Called when view is rendered.
25 render : function(){
25 render : function(){
26 this.$el
26 this.$el
27 .addClass('widget-hbox-single');
27 .addClass('widget-hbox-single');
28 this.$label = $('<div />')
28 this.$label = $('<div />')
29 .addClass('widget-hlabel')
29 .addClass('widget-hlabel')
30 .appendTo(this.$el)
30 .appendTo(this.$el)
31 .hide();
31 .hide();
32 var that = this;
32 var that = this;
33 this.$checkbox = $('<input />')
33 this.$checkbox = $('<input />')
34 .attr('type', 'checkbox')
34 .attr('type', 'checkbox')
35 .click(function(el) {
35 .click(function(el) {
36 that.user_invoked_update = true;
36 that.user_invoked_update = true;
37 that.model.set('value', that.$checkbox.prop('checked'));
37 that.model.set('value', that.$checkbox.prop('checked'));
38 that.model.update_other_views(that);
38 that.touch();
39 that.user_invoked_update = false;
39 that.user_invoked_update = false;
40 })
40 })
41 .appendTo(this.$el);
41 .appendTo(this.$el);
42
42
43 this.$el_to_style = this.$checkbox; // Set default element to style
43 this.$el_to_style = this.$checkbox; // Set default element to style
44 this.update(); // Set defaults.
44 this.update(); // Set defaults.
45 },
45 },
46
46
47 // Handles: Backend -> Frontend Sync
47 // Handles: Backend -> Frontend Sync
48 // Frontent -> Frontend Sync
48 // Frontent -> Frontend Sync
49 update : function(){
49 update : function(){
50 if (!this.user_invoked_update) {
50 if (!this.user_invoked_update) {
51 this.$checkbox.prop('checked', this.model.get('value'));
51 this.$checkbox.prop('checked', this.model.get('value'));
52
52
53 var disabled = this.model.get('disabled');
53 var disabled = this.model.get('disabled');
54 this.$checkbox.prop('disabled', disabled);
54 this.$checkbox.prop('disabled', disabled);
55
55
56 var description = this.model.get('description');
56 var description = this.model.get('description');
57 if (description.length === 0) {
57 if (description.length === 0) {
58 this.$label.hide();
58 this.$label.hide();
59 } else {
59 } else {
60 this.$label.html(description);
60 this.$label.html(description);
61 this.$label.show();
61 this.$label.show();
62 }
62 }
63 }
63 }
64 return IPython.WidgetView.prototype.update.call(this);
64 return IPython.WidgetView.prototype.update.call(this);
65 },
65 },
66
66
67 });
67 });
68
68
69 widget_manager.register_widget_view('CheckboxView', CheckboxView);
69 widget_manager.register_widget_view('CheckboxView', CheckboxView);
70
70
71 var ToggleButtonView = IPython.WidgetView.extend({
71 var ToggleButtonView = IPython.WidgetView.extend({
72
72
73 // Called when view is rendered.
73 // Called when view is rendered.
74 render : function(){
74 render : function(){
75 this.$el
75 this.$el
76 .html('');
76 .html('');
77 this.$button = $('<button />')
77 this.$button = $('<button />')
78 .addClass('btn')
78 .addClass('btn')
79 .attr('type', 'button')
79 .attr('type', 'button')
80 .attr('data-toggle', 'button')
80 .attr('data-toggle', 'button')
81 .appendTo(this.$el);
81 .appendTo(this.$el);
82 this.$el_to_style = this.$button; // Set default element to style
82 this.$el_to_style = this.$button; // Set default element to style
83
83
84 this.update(); // Set defaults.
84 this.update(); // Set defaults.
85 },
85 },
86
86
87 // Handles: Backend -> Frontend Sync
87 // Handles: Backend -> Frontend Sync
88 // Frontent -> Frontend Sync
88 // Frontent -> Frontend Sync
89 update : function(){
89 update : function(){
90 if (!this.user_invoked_update) {
90 if (!this.user_invoked_update) {
91 if (this.model.get('value')) {
91 if (this.model.get('value')) {
92 this.$button.addClass('active');
92 this.$button.addClass('active');
93 } else {
93 } else {
94 this.$button.removeClass('active');
94 this.$button.removeClass('active');
95 }
95 }
96
96
97 var disabled = this.model.get('disabled');
97 var disabled = this.model.get('disabled');
98 this.$button.prop('disabled', disabled);
98 this.$button.prop('disabled', disabled);
99
99
100 var description = this.model.get('description');
100 var description = this.model.get('description');
101 if (description.length === 0) {
101 if (description.length === 0) {
102 this.$button.html(' '); // Preserve button height
102 this.$button.html(' '); // Preserve button height
103 } else {
103 } else {
104 this.$button.html(description);
104 this.$button.html(description);
105 }
105 }
106 }
106 }
107 return IPython.WidgetView.prototype.update.call(this);
107 return IPython.WidgetView.prototype.update.call(this);
108 },
108 },
109
109
110 events: {"click button" : "handleClick"},
110 events: {"click button" : "handleClick"},
111
111
112 // Handles and validates user input.
112 // Handles and validates user input.
113 handleClick: function(e) {
113 handleClick: function(e) {
114 this.user_invoked_update = true;
114 this.user_invoked_update = true;
115 this.model.set('value', ! $(e.target).hasClass('active'));
115 this.model.set('value', ! $(e.target).hasClass('active'));
116 this.model.update_other_views(this);
116 this.touch();
117 this.user_invoked_update = false;
117 this.user_invoked_update = false;
118 },
118 },
119 });
119 });
120
120
121 widget_manager.register_widget_view('ToggleButtonView', ToggleButtonView);
121 widget_manager.register_widget_view('ToggleButtonView', ToggleButtonView);
122
122
123 });
123 });
@@ -1,255 +1,255
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/base"], function(widget_manager){
17 define(["notebook/js/widgets/base"], function(widget_manager){
18 var FloatRangeWidgetModel = IPython.WidgetModel.extend({});
18 var FloatRangeWidgetModel = IPython.WidgetModel.extend({});
19 widget_manager.register_widget_model('FloatRangeWidgetModel', FloatRangeWidgetModel);
19 widget_manager.register_widget_model('FloatRangeWidgetModel', FloatRangeWidgetModel);
20
20
21 var FloatSliderView = IPython.WidgetView.extend({
21 var FloatSliderView = IPython.WidgetView.extend({
22
22
23 // Called when view is rendered.
23 // Called when view is rendered.
24 render : function(){
24 render : function(){
25 this.$el
25 this.$el
26 .addClass('widget-hbox-single')
26 .addClass('widget-hbox-single')
27 .html('');
27 .html('');
28 this.$label = $('<div />')
28 this.$label = $('<div />')
29 .appendTo(this.$el)
29 .appendTo(this.$el)
30 .addClass('widget-hlabel')
30 .addClass('widget-hlabel')
31 .hide();
31 .hide();
32 this.$slider = $('<div />')
32 this.$slider = $('<div />')
33 .slider({})
33 .slider({})
34 .addClass('slider');
34 .addClass('slider');
35
35
36 // Put the slider in a container
36 // Put the slider in a container
37 this.$slider_container = $('<div />')
37 this.$slider_container = $('<div />')
38 .addClass('widget-hslider')
38 .addClass('widget-hslider')
39 .append(this.$slider);
39 .append(this.$slider);
40 this.$el_to_style = this.$slider_container; // Set default element to style
40 this.$el_to_style = this.$slider_container; // Set default element to style
41 this.$el.append(this.$slider_container);
41 this.$el.append(this.$slider_container);
42
42
43 // Set defaults.
43 // Set defaults.
44 this.update();
44 this.update();
45 },
45 },
46
46
47 // Handles: Backend -> Frontend Sync
47 // Handles: Backend -> Frontend Sync
48 // Frontent -> Frontend Sync
48 // Frontent -> Frontend Sync
49 update : function(){
49 update : function(){
50 // Slider related keys.
50 // Slider related keys.
51 var _keys = ['step', 'max', 'min', 'disabled'];
51 var _keys = ['step', 'max', 'min', 'disabled'];
52 for (var index in _keys) {
52 for (var index in _keys) {
53 var key = _keys[index];
53 var key = _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 return IPython.WidgetView.prototype.update.call(this);
105 return IPython.WidgetView.prototype.update.call(this);
106 },
106 },
107
107
108 // Handles: User input
108 // Handles: User input
109 events: { "slide" : "handleSliderChange" },
109 events: { "slide" : "handleSliderChange" },
110 handleSliderChange: function(e, ui) {
110 handleSliderChange: function(e, ui) {
111 this.model.set('value', ui.value);
111 this.model.set('value', ui.value);
112 this.model.update_other_views(this);
112 this.touch();
113 },
113 },
114 });
114 });
115
115
116 widget_manager.register_widget_view('FloatSliderView', FloatSliderView);
116 widget_manager.register_widget_view('FloatSliderView', FloatSliderView);
117
117
118
118
119 var FloatTextView = IPython.WidgetView.extend({
119 var FloatTextView = IPython.WidgetView.extend({
120
120
121 // Called when view is rendered.
121 // Called when view is rendered.
122 render : function(){
122 render : function(){
123 this.$el
123 this.$el
124 .addClass('widget-hbox-single')
124 .addClass('widget-hbox-single')
125 .html('');
125 .html('');
126 this.$label = $('<div />')
126 this.$label = $('<div />')
127 .appendTo(this.$el)
127 .appendTo(this.$el)
128 .addClass('widget-hlabel')
128 .addClass('widget-hlabel')
129 .hide();
129 .hide();
130 this.$textbox = $('<input type="text" />')
130 this.$textbox = $('<input type="text" />')
131 .addClass('input')
131 .addClass('input')
132 .addClass('widget-numeric-text')
132 .addClass('widget-numeric-text')
133 .appendTo(this.$el);
133 .appendTo(this.$el);
134 this.$el_to_style = this.$textbox; // Set default element to style
134 this.$el_to_style = this.$textbox; // Set default element to style
135 this.update(); // Set defaults.
135 this.update(); // Set defaults.
136 },
136 },
137
137
138 // Handles: Backend -> Frontend Sync
138 // Handles: Backend -> Frontend Sync
139 // Frontent -> Frontend Sync
139 // Frontent -> Frontend Sync
140 update : function(){
140 update : function(){
141 var value = this.model.get('value');
141 var value = this.model.get('value');
142 if (!this.changing && parseFloat(this.$textbox.val()) != value) {
142 if (!this.changing && parseFloat(this.$textbox.val()) != value) {
143 this.$textbox.val(value);
143 this.$textbox.val(value);
144 }
144 }
145
145
146 if (this.model.get('disabled')) {
146 if (this.model.get('disabled')) {
147 this.$textbox.attr('disabled','disabled');
147 this.$textbox.attr('disabled','disabled');
148 } else {
148 } else {
149 this.$textbox.removeAttr('disabled');
149 this.$textbox.removeAttr('disabled');
150 }
150 }
151
151
152 var description = this.model.get('description');
152 var description = this.model.get('description');
153 if (description.length === 0) {
153 if (description.length === 0) {
154 this.$label.hide();
154 this.$label.hide();
155 } else {
155 } else {
156 this.$label.html(description);
156 this.$label.html(description);
157 this.$label.show();
157 this.$label.show();
158 }
158 }
159 return IPython.WidgetView.prototype.update.call(this);
159 return IPython.WidgetView.prototype.update.call(this);
160 },
160 },
161
161
162
162
163 events: {"keyup input" : "handleChanging",
163 events: {"keyup input" : "handleChanging",
164 "paste input" : "handleChanging",
164 "paste input" : "handleChanging",
165 "cut input" : "handleChanging",
165 "cut input" : "handleChanging",
166 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
166 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
167
167
168 // Handles and validates user input.
168 // Handles and validates user input.
169 handleChanging: function(e) {
169 handleChanging: function(e) {
170
170
171 // Try to parse value as a float.
171 // Try to parse value as a float.
172 var numericalValue = 0.0;
172 var numericalValue = 0.0;
173 if (e.target.value !== '') {
173 if (e.target.value !== '') {
174 numericalValue = parseFloat(e.target.value);
174 numericalValue = parseFloat(e.target.value);
175 }
175 }
176
176
177 // If parse failed, reset value to value stored in model.
177 // If parse failed, reset value to value stored in model.
178 if (isNaN(numericalValue)) {
178 if (isNaN(numericalValue)) {
179 e.target.value = this.model.get('value');
179 e.target.value = this.model.get('value');
180 } else if (!isNaN(numericalValue)) {
180 } else if (!isNaN(numericalValue)) {
181 if (this.model.get('max') !== undefined) {
181 if (this.model.get('max') !== undefined) {
182 numericalValue = Math.min(this.model.get('max'), numericalValue);
182 numericalValue = Math.min(this.model.get('max'), numericalValue);
183 }
183 }
184 if (this.model.get('min') !== undefined) {
184 if (this.model.get('min') !== undefined) {
185 numericalValue = Math.max(this.model.get('min'), numericalValue);
185 numericalValue = Math.max(this.model.get('min'), numericalValue);
186 }
186 }
187
187
188 // Apply the value if it has changed.
188 // Apply the value if it has changed.
189 if (numericalValue != this.model.get('value')) {
189 if (numericalValue != this.model.get('value')) {
190 this.changing = true;
190 this.changing = true;
191 this.model.set('value', numericalValue);
191 this.model.set('value', numericalValue);
192 this.model.update_other_views(this);
192 this.touch();
193 this.changing = false;
193 this.changing = false;
194 }
194 }
195 }
195 }
196 },
196 },
197
197
198 // Applies validated input.
198 // Applies validated input.
199 handleChanged: function(e) {
199 handleChanged: function(e) {
200 // Update the textbox
200 // Update the textbox
201 if (this.model.get('value') != e.target.value) {
201 if (this.model.get('value') != e.target.value) {
202 e.target.value = this.model.get('value');
202 e.target.value = this.model.get('value');
203 }
203 }
204 }
204 }
205 });
205 });
206
206
207 widget_manager.register_widget_view('FloatTextView', FloatTextView);
207 widget_manager.register_widget_view('FloatTextView', FloatTextView);
208
208
209
209
210 var ProgressView = IPython.WidgetView.extend({
210 var ProgressView = IPython.WidgetView.extend({
211
211
212 // Called when view is rendered.
212 // Called when view is rendered.
213 render : function(){
213 render : function(){
214 this.$el
214 this.$el
215 .addClass('widget-hbox-single')
215 .addClass('widget-hbox-single')
216 .html('');
216 .html('');
217 this.$label = $('<div />')
217 this.$label = $('<div />')
218 .appendTo(this.$el)
218 .appendTo(this.$el)
219 .addClass('widget-hlabel')
219 .addClass('widget-hlabel')
220 .hide();
220 .hide();
221 this.$progress = $('<div />')
221 this.$progress = $('<div />')
222 .addClass('progress')
222 .addClass('progress')
223 .addClass('widget-progress')
223 .addClass('widget-progress')
224 .appendTo(this.$el);
224 .appendTo(this.$el);
225 this.$el_to_style = this.$progress; // Set default element to style
225 this.$el_to_style = this.$progress; // Set default element to style
226 this.$bar = $('<div />')
226 this.$bar = $('<div />')
227 .addClass('bar')
227 .addClass('bar')
228 .css('width', '50%')
228 .css('width', '50%')
229 .appendTo(this.$progress);
229 .appendTo(this.$progress);
230 this.update(); // Set defaults.
230 this.update(); // Set defaults.
231 },
231 },
232
232
233 // Handles: Backend -> Frontend Sync
233 // Handles: Backend -> Frontend Sync
234 // Frontent -> Frontend Sync
234 // Frontent -> Frontend Sync
235 update : function(){
235 update : function(){
236 var value = this.model.get('value');
236 var value = this.model.get('value');
237 var max = this.model.get('max');
237 var max = this.model.get('max');
238 var min = this.model.get('min');
238 var min = this.model.get('min');
239 var percent = 100.0 * (value - min) / (max - min);
239 var percent = 100.0 * (value - min) / (max - min);
240 this.$bar.css('width', percent + '%');
240 this.$bar.css('width', percent + '%');
241
241
242 var description = this.model.get('description');
242 var description = this.model.get('description');
243 if (description.length === 0) {
243 if (description.length === 0) {
244 this.$label.hide();
244 this.$label.hide();
245 } else {
245 } else {
246 this.$label.html(description);
246 this.$label.html(description);
247 this.$label.show();
247 this.$label.show();
248 }
248 }
249 return IPython.WidgetView.prototype.update.call(this);
249 return IPython.WidgetView.prototype.update.call(this);
250 },
250 },
251
251
252 });
252 });
253
253
254 widget_manager.register_widget_view('ProgressView', ProgressView);
254 widget_manager.register_widget_view('ProgressView', ProgressView);
255 });
255 });
@@ -1,207 +1,207
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/base"], function(widget_manager){
17 define(["notebook/js/widgets/base"], function(widget_manager){
18 var IntRangeWidgetModel = IPython.WidgetModel.extend({});
18 var IntRangeWidgetModel = IPython.WidgetModel.extend({});
19 widget_manager.register_widget_model('IntRangeWidgetModel', IntRangeWidgetModel);
19 widget_manager.register_widget_model('IntRangeWidgetModel', IntRangeWidgetModel);
20
20
21 var IntSliderView = IPython.WidgetView.extend({
21 var IntSliderView = IPython.WidgetView.extend({
22
22
23 // Called when view is rendered.
23 // Called when view is rendered.
24 render : function(){
24 render : function(){
25 this.$el
25 this.$el
26 .addClass('widget-hbox-single')
26 .addClass('widget-hbox-single')
27 .html('');
27 .html('');
28 this.$label = $('<div />')
28 this.$label = $('<div />')
29 .appendTo(this.$el)
29 .appendTo(this.$el)
30 .addClass('widget-hlabel')
30 .addClass('widget-hlabel')
31 .hide();
31 .hide();
32 this.$slider = $('<div />')
32 this.$slider = $('<div />')
33 .slider({})
33 .slider({})
34 .addClass('slider');
34 .addClass('slider');
35
35
36 // Put the slider in a container
36 // Put the slider in a container
37 this.$slider_container = $('<div />')
37 this.$slider_container = $('<div />')
38 .addClass('widget-hslider')
38 .addClass('widget-hslider')
39 .append(this.$slider);
39 .append(this.$slider);
40 this.$el_to_style = this.$slider_container; // Set default element to style
40 this.$el_to_style = this.$slider_container; // Set default element to style
41 this.$el.append(this.$slider_container);
41 this.$el.append(this.$slider_container);
42
42
43 // Set defaults.
43 // Set defaults.
44 this.update();
44 this.update();
45 },
45 },
46
46
47 // Handles: Backend -> Frontend Sync
47 // Handles: Backend -> Frontend Sync
48 // Frontent -> Frontend Sync
48 // Frontent -> Frontend Sync
49 update : function(){
49 update : function(){
50 // Slider related keys.
50 // Slider related keys.
51 var _keys = ['step', 'max', 'min', 'disabled'];
51 var _keys = ['step', 'max', 'min', 'disabled'];
52 for (var index in _keys) {
52 for (var index in _keys) {
53 var key = _keys[index];
53 var key = _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 return IPython.WidgetView.prototype.update.call(this);
105 return IPython.WidgetView.prototype.update.call(this);
106 },
106 },
107
107
108 // Handles: User input
108 // Handles: User input
109 events: { "slide" : "handleSliderChange" },
109 events: { "slide" : "handleSliderChange" },
110 handleSliderChange: function(e, ui) {
110 handleSliderChange: function(e, ui) {
111 this.model.set('value', ~~ui.value); // Double bit-wise not to truncate decimel
111 this.model.set('value', ~~ui.value); // Double bit-wise not to truncate decimel
112 this.model.update_other_views(this);
112 this.touch();
113 },
113 },
114 });
114 });
115
115
116 widget_manager.register_widget_view('IntSliderView', IntSliderView);
116 widget_manager.register_widget_view('IntSliderView', IntSliderView);
117
117
118 var IntTextView = IPython.WidgetView.extend({
118 var IntTextView = IPython.WidgetView.extend({
119
119
120 // Called when view is rendered.
120 // Called when view is rendered.
121 render : function(){
121 render : function(){
122 this.$el
122 this.$el
123 .addClass('widget-hbox-single')
123 .addClass('widget-hbox-single')
124 .html('');
124 .html('');
125 this.$label = $('<div />')
125 this.$label = $('<div />')
126 .appendTo(this.$el)
126 .appendTo(this.$el)
127 .addClass('widget-hlabel')
127 .addClass('widget-hlabel')
128 .hide();
128 .hide();
129 this.$textbox = $('<input type="text" />')
129 this.$textbox = $('<input type="text" />')
130 .addClass('input')
130 .addClass('input')
131 .addClass('widget-numeric-text')
131 .addClass('widget-numeric-text')
132 .appendTo(this.$el);
132 .appendTo(this.$el);
133 this.$el_to_style = this.$textbox; // Set default element to style
133 this.$el_to_style = this.$textbox; // Set default element to style
134 this.update(); // Set defaults.
134 this.update(); // Set defaults.
135 },
135 },
136
136
137 // Handles: Backend -> Frontend Sync
137 // Handles: Backend -> Frontend Sync
138 // Frontent -> Frontend Sync
138 // Frontent -> Frontend Sync
139 update : function(){
139 update : function(){
140 var value = this.model.get('value');
140 var value = this.model.get('value');
141 if (!this.changing && parseInt(this.$textbox.val()) != value) {
141 if (!this.changing && parseInt(this.$textbox.val()) != value) {
142 this.$textbox.val(value);
142 this.$textbox.val(value);
143 }
143 }
144
144
145 if (this.model.get('disabled')) {
145 if (this.model.get('disabled')) {
146 this.$textbox.attr('disabled','disabled');
146 this.$textbox.attr('disabled','disabled');
147 } else {
147 } else {
148 this.$textbox.removeAttr('disabled');
148 this.$textbox.removeAttr('disabled');
149 }
149 }
150
150
151 var description = this.model.get('description');
151 var description = this.model.get('description');
152 if (description.length === 0) {
152 if (description.length === 0) {
153 this.$label.hide();
153 this.$label.hide();
154 } else {
154 } else {
155 this.$label.html(description);
155 this.$label.html(description);
156 this.$label.show();
156 this.$label.show();
157 }
157 }
158 return IPython.WidgetView.prototype.update.call(this);
158 return IPython.WidgetView.prototype.update.call(this);
159 },
159 },
160
160
161
161
162 events: {"keyup input" : "handleChanging",
162 events: {"keyup input" : "handleChanging",
163 "paste input" : "handleChanging",
163 "paste input" : "handleChanging",
164 "cut input" : "handleChanging",
164 "cut input" : "handleChanging",
165 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
165 "change input" : "handleChanged"}, // Fires only when control is validated or looses focus.
166
166
167 // Handles and validates user input.
167 // Handles and validates user input.
168 handleChanging: function(e) {
168 handleChanging: function(e) {
169
169
170 // Try to parse value as a float.
170 // Try to parse value as a float.
171 var numericalValue = 0;
171 var numericalValue = 0;
172 if (e.target.value !== '') {
172 if (e.target.value !== '') {
173 numericalValue = parseInt(e.target.value);
173 numericalValue = parseInt(e.target.value);
174 }
174 }
175
175
176 // If parse failed, reset value to value stored in model.
176 // If parse failed, reset value to value stored in model.
177 if (isNaN(numericalValue)) {
177 if (isNaN(numericalValue)) {
178 e.target.value = this.model.get('value');
178 e.target.value = this.model.get('value');
179 } else if (!isNaN(numericalValue)) {
179 } else if (!isNaN(numericalValue)) {
180 if (this.model.get('max') !== undefined) {
180 if (this.model.get('max') !== undefined) {
181 numericalValue = Math.min(this.model.get('max'), numericalValue);
181 numericalValue = Math.min(this.model.get('max'), numericalValue);
182 }
182 }
183 if (this.model.get('min') !== undefined) {
183 if (this.model.get('min') !== undefined) {
184 numericalValue = Math.max(this.model.get('min'), numericalValue);
184 numericalValue = Math.max(this.model.get('min'), numericalValue);
185 }
185 }
186
186
187 // Apply the value if it has changed.
187 // Apply the value if it has changed.
188 if (numericalValue != this.model.get('value')) {
188 if (numericalValue != this.model.get('value')) {
189 this.changing = true;
189 this.changing = true;
190 this.model.set('value', numericalValue);
190 this.model.set('value', numericalValue);
191 this.model.update_other_views(this);
191 this.touch();
192 this.changing = false;
192 this.changing = false;
193 }
193 }
194 }
194 }
195 },
195 },
196
196
197 // Applies validated input.
197 // Applies validated input.
198 handleChanged: function(e) {
198 handleChanged: function(e) {
199 // Update the textbox
199 // Update the textbox
200 if (this.model.get('value') != e.target.value) {
200 if (this.model.get('value') != e.target.value) {
201 e.target.value = this.model.get('value');
201 e.target.value = this.model.get('value');
202 }
202 }
203 }
203 }
204 });
204 });
205
205
206 widget_manager.register_widget_view('IntTextView', IntTextView);
206 widget_manager.register_widget_view('IntTextView', IntTextView);
207 });
207 });
@@ -1,179 +1,179
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 // MultiContainerWidget
9 // MultiContainerWidget
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/base"], function(widget_manager){
17 define(["notebook/js/widgets/base"], function(widget_manager){
18 var MulticontainerModel = IPython.WidgetModel.extend({});
18 var MulticontainerModel = IPython.WidgetModel.extend({});
19 widget_manager.register_widget_model('MulticontainerWidgetModel', MulticontainerModel);
19 widget_manager.register_widget_model('MulticontainerWidgetModel', MulticontainerModel);
20
20
21 var AccordionView = IPython.WidgetView.extend({
21 var AccordionView = IPython.WidgetView.extend({
22
22
23 render: function(){
23 render: function(){
24 var guid = 'accordion' + IPython.utils.uuid();
24 var guid = 'accordion' + IPython.utils.uuid();
25 this.$el
25 this.$el
26 .attr('id', guid)
26 .attr('id', guid)
27 .addClass('accordion');
27 .addClass('accordion');
28 this.containers = [];
28 this.containers = [];
29 },
29 },
30
30
31 update: function() {
31 update: function() {
32 // Set tab titles
32 // Set tab titles
33 var titles = this.model.get('_titles');
33 var titles = this.model.get('_titles');
34 for (var page_index in titles) {
34 for (var page_index in titles) {
35
35
36 var accordian = this.containers[page_index];
36 var accordian = this.containers[page_index];
37 if (accordian !== undefined) {
37 if (accordian !== undefined) {
38 accordian
38 accordian
39 .find('.accordion-heading')
39 .find('.accordion-heading')
40 .find('.accordion-toggle')
40 .find('.accordion-toggle')
41 .html(titles[page_index]);
41 .html(titles[page_index]);
42 }
42 }
43 }
43 }
44
44
45 // Set selected page
45 // Set selected page
46 var selected_index = this.model.get("selected_index");
46 var selected_index = this.model.get("selected_index");
47 if (0 <= selected_index && selected_index < this.containers.length) {
47 if (0 <= selected_index && selected_index < this.containers.length) {
48 for (var index in this.containers) {
48 for (var index in this.containers) {
49 if (index==selected_index) {
49 if (index==selected_index) {
50 this.containers[index].find('.accordion-body').collapse('show');
50 this.containers[index].find('.accordion-body').collapse('show');
51 } else {
51 } else {
52 this.containers[index].find('.accordion-body').collapse('hide');
52 this.containers[index].find('.accordion-body').collapse('hide');
53 }
53 }
54
54
55 }
55 }
56 }
56 }
57
57
58 return IPython.WidgetView.prototype.update.call(this);
58 return IPython.WidgetView.prototype.update.call(this);
59 },
59 },
60
60
61 display_child: function(view) {
61 display_child: function(view) {
62
62
63 var index = this.containers.length;
63 var index = this.containers.length;
64 var uuid = IPython.utils.uuid();
64 var uuid = IPython.utils.uuid();
65 var accordion_group = $('<div />')
65 var accordion_group = $('<div />')
66 .addClass('accordion-group')
66 .addClass('accordion-group')
67 .appendTo(this.$el);
67 .appendTo(this.$el);
68 var accordion_heading = $('<div />')
68 var accordion_heading = $('<div />')
69 .addClass('accordion-heading')
69 .addClass('accordion-heading')
70 .appendTo(accordion_group);
70 .appendTo(accordion_group);
71 var that = this;
71 var that = this;
72 var accordion_toggle = $('<a />')
72 var accordion_toggle = $('<a />')
73 .addClass('accordion-toggle')
73 .addClass('accordion-toggle')
74 .attr('data-toggle', 'collapse')
74 .attr('data-toggle', 'collapse')
75 .attr('data-parent', '#' + this.$el.attr('id'))
75 .attr('data-parent', '#' + this.$el.attr('id'))
76 .attr('href', '#' + uuid)
76 .attr('href', '#' + uuid)
77 .click(function(evt){
77 .click(function(evt){
78 that.model.set("selected_index", index);
78 that.model.set("selected_index", index);
79 that.model.update_other_views(that);
79 that.touch();
80 })
80 })
81 .html('Page ' + index)
81 .html('Page ' + index)
82 .appendTo(accordion_heading);
82 .appendTo(accordion_heading);
83 var accordion_body = $('<div />', {id: uuid})
83 var accordion_body = $('<div />', {id: uuid})
84 .addClass('accordion-body collapse')
84 .addClass('accordion-body collapse')
85 .appendTo(accordion_group);
85 .appendTo(accordion_group);
86 var accordion_inner = $('<div />')
86 var accordion_inner = $('<div />')
87 .addClass('accordion-inner')
87 .addClass('accordion-inner')
88 .appendTo(accordion_body);
88 .appendTo(accordion_body);
89 this.containers.push(accordion_group);
89 this.containers.push(accordion_group);
90 accordion_inner.append(view.$el);
90 accordion_inner.append(view.$el);
91
91
92 this.update();
92 this.update();
93
93
94 // Stupid workaround to close the bootstrap accordion tabs which
94 // Stupid workaround to close the bootstrap accordion tabs which
95 // open by default even though they don't have the `in` class
95 // open by default even though they don't have the `in` class
96 // attached to them. For some reason a delay is required.
96 // attached to them. For some reason a delay is required.
97 // TODO: Better fix.
97 // TODO: Better fix.
98 setTimeout(function(){ that.update(); }, 500);
98 setTimeout(function(){ that.update(); }, 500);
99 },
99 },
100 });
100 });
101
101
102 widget_manager.register_widget_view('AccordionView', AccordionView);
102 widget_manager.register_widget_view('AccordionView', AccordionView);
103
103
104 var TabView = IPython.WidgetView.extend({
104 var TabView = IPython.WidgetView.extend({
105
105
106 render: function(){
106 render: function(){
107 var uuid = 'tabs'+IPython.utils.uuid();
107 var uuid = 'tabs'+IPython.utils.uuid();
108 var that = this;
108 var that = this;
109 this.$tabs = $('<div />', {id: uuid})
109 this.$tabs = $('<div />', {id: uuid})
110 .addClass('nav')
110 .addClass('nav')
111 .addClass('nav-tabs')
111 .addClass('nav-tabs')
112 .appendTo(this.$el);
112 .appendTo(this.$el);
113 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
113 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
114 .addClass('tab-content')
114 .addClass('tab-content')
115 .appendTo(this.$el);
115 .appendTo(this.$el);
116
116
117 this.containers = [];
117 this.containers = [];
118 },
118 },
119
119
120 update: function() {
120 update: function() {
121 // Set tab titles
121 // Set tab titles
122 var titles = this.model.get('_titles');
122 var titles = this.model.get('_titles');
123 for (var page_index in titles) {
123 for (var page_index in titles) {
124 var tab_text = this.containers[page_index];
124 var tab_text = this.containers[page_index];
125 if (tab_text !== undefined) {
125 if (tab_text !== undefined) {
126 tab_text.html(titles[page_index]);
126 tab_text.html(titles[page_index]);
127 }
127 }
128 }
128 }
129
129
130 var selected_index = this.model.get('selected_index');
130 var selected_index = this.model.get('selected_index');
131 if (0 <= selected_index && selected_index < this.containers.length) {
131 if (0 <= selected_index && selected_index < this.containers.length) {
132 this.select_page(selected_index);
132 this.select_page(selected_index);
133 }
133 }
134
134
135 return IPython.WidgetView.prototype.update.call(this);
135 return IPython.WidgetView.prototype.update.call(this);
136 },
136 },
137
137
138 display_child: function(view) {
138 display_child: function(view) {
139
139
140 var index = this.containers.length;
140 var index = this.containers.length;
141 var uuid = IPython.utils.uuid();
141 var uuid = IPython.utils.uuid();
142
142
143 var that = this;
143 var that = this;
144 var tab = $('<li />')
144 var tab = $('<li />')
145 .css('list-style-type', 'none')
145 .css('list-style-type', 'none')
146 .appendTo(this.$tabs);
146 .appendTo(this.$tabs);
147 var tab_text = $('<a />')
147 var tab_text = $('<a />')
148 .attr('href', '#' + uuid)
148 .attr('href', '#' + uuid)
149 .attr('data-toggle', 'tab')
149 .attr('data-toggle', 'tab')
150 .html('Page ' + index)
150 .html('Page ' + index)
151 .appendTo(tab)
151 .appendTo(tab)
152 .click(function (e) {
152 .click(function (e) {
153 that.model.set("selected_index", index);
153 that.model.set("selected_index", index);
154 that.model.update_other_views(that);
154 that.touch();
155 that.select_page(index);
155 that.select_page(index);
156 });
156 });
157 this.containers.push(tab_text);
157 this.containers.push(tab_text);
158
158
159 var contents_div = $('<div />', {id: uuid})
159 var contents_div = $('<div />', {id: uuid})
160 .addClass('tab-pane')
160 .addClass('tab-pane')
161 .addClass('fade')
161 .addClass('fade')
162 .append(view.$el)
162 .append(view.$el)
163 .appendTo(this.$tab_contents);
163 .appendTo(this.$tab_contents);
164
164
165 if (index === 0) {
165 if (index === 0) {
166 tab_text.tab('show');
166 tab_text.tab('show');
167 }
167 }
168 this.update();
168 this.update();
169 },
169 },
170
170
171 select_page: function(index) {
171 select_page: function(index) {
172 this.$tabs.find('li')
172 this.$tabs.find('li')
173 .removeClass('active');
173 .removeClass('active');
174 this.containers[index].tab('show');
174 this.containers[index].tab('show');
175 },
175 },
176 });
176 });
177
177
178 widget_manager.register_widget_view('TabView', TabView);
178 widget_manager.register_widget_view('TabView', TabView);
179 });
179 });
@@ -1,360 +1,360
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/base"], function(widget_manager){
17 define(["notebook/js/widgets/base"], function(widget_manager){
18 var SelectionWidgetModel = IPython.WidgetModel.extend({});
18 var SelectionWidgetModel = IPython.WidgetModel.extend({});
19 widget_manager.register_widget_model('SelectionWidgetModel', SelectionWidgetModel);
19 widget_manager.register_widget_model('SelectionWidgetModel', SelectionWidgetModel);
20
20
21 var DropdownView = IPython.WidgetView.extend({
21 var DropdownView = IPython.WidgetView.extend({
22
22
23 // Called when view is rendered.
23 // Called when view is rendered.
24 render : function(){
24 render : function(){
25
25
26 this.$el
26 this.$el
27 .addClass('widget-hbox-single')
27 .addClass('widget-hbox-single')
28 .html('');
28 .html('');
29 this.$label = $('<div />')
29 this.$label = $('<div />')
30 .appendTo(this.$el)
30 .appendTo(this.$el)
31 .addClass('widget-hlabel')
31 .addClass('widget-hlabel')
32 .hide();
32 .hide();
33 this.$buttongroup = $('<div />')
33 this.$buttongroup = $('<div />')
34 .addClass('widget_item')
34 .addClass('widget_item')
35 .addClass('btn-group')
35 .addClass('btn-group')
36 .appendTo(this.$el);
36 .appendTo(this.$el);
37 this.$el_to_style = this.$buttongroup; // Set default element to style
37 this.$el_to_style = this.$buttongroup; // Set default element to style
38 this.$droplabel = $('<button />')
38 this.$droplabel = $('<button />')
39 .addClass('btn')
39 .addClass('btn')
40 .addClass('widget-combo-btn')
40 .addClass('widget-combo-btn')
41 .html('&nbsp;')
41 .html('&nbsp;')
42 .appendTo(this.$buttongroup);
42 .appendTo(this.$buttongroup);
43 this.$dropbutton = $('<button />')
43 this.$dropbutton = $('<button />')
44 .addClass('btn')
44 .addClass('btn')
45 .addClass('dropdown-toggle')
45 .addClass('dropdown-toggle')
46 .addClass('widget-combo-carrot-btn')
46 .addClass('widget-combo-carrot-btn')
47 .attr('data-toggle', 'dropdown')
47 .attr('data-toggle', 'dropdown')
48 .html('<span class="caret"></span>')
48 .html('<span class="caret"></span>')
49 .appendTo(this.$buttongroup);
49 .appendTo(this.$buttongroup);
50 this.$droplist = $('<ul />')
50 this.$droplist = $('<ul />')
51 .addClass('dropdown-menu')
51 .addClass('dropdown-menu')
52 .appendTo(this.$buttongroup);
52 .appendTo(this.$buttongroup);
53
53
54 // Set defaults.
54 // Set defaults.
55 this.update();
55 this.update();
56 },
56 },
57
57
58 // Handles: Backend -> Frontend Sync
58 // Handles: Backend -> Frontend Sync
59 // Frontent -> Frontend Sync
59 // Frontent -> Frontend Sync
60 update : function(){
60 update : function(){
61
61
62 var selected_item_text = this.model.get('value');
62 var selected_item_text = this.model.get('value');
63 selected_item_text = selected_item_text.replace(/ /g, '&nbsp;');
63 selected_item_text = selected_item_text.replace(/ /g, '&nbsp;');
64 selected_item_text = selected_item_text.replace(/\n/g, '<br>\n');
64 selected_item_text = selected_item_text.replace(/\n/g, '<br>\n');
65 if (selected_item_text.length === 0) {
65 if (selected_item_text.length === 0) {
66 this.$droplabel.html('&nbsp;');
66 this.$droplabel.html('&nbsp;');
67 } else {
67 } else {
68 this.$droplabel.html(selected_item_text);
68 this.$droplabel.html(selected_item_text);
69 }
69 }
70
70
71 var items = this.model.get('values');
71 var items = this.model.get('values');
72 this.$droplist.html('');
72 this.$droplist.html('');
73 for (var index in items) {
73 for (var index in items) {
74 var that = this;
74 var that = this;
75 var item_button = $('<a href="#"/>')
75 var item_button = $('<a href="#"/>')
76 .html(items[index])
76 .html(items[index])
77 .on('click', $.proxy(this.handle_click, this));
77 .on('click', $.proxy(this.handle_click, this));
78 this.$droplist.append($('<li />').append(item_button));
78 this.$droplist.append($('<li />').append(item_button));
79 }
79 }
80
80
81 if (this.model.get('disabled')) {
81 if (this.model.get('disabled')) {
82 this.$buttongroup.attr('disabled','disabled');
82 this.$buttongroup.attr('disabled','disabled');
83 this.$droplabel.attr('disabled','disabled');
83 this.$droplabel.attr('disabled','disabled');
84 this.$dropbutton.attr('disabled','disabled');
84 this.$dropbutton.attr('disabled','disabled');
85 this.$droplist.attr('disabled','disabled');
85 this.$droplist.attr('disabled','disabled');
86 } else {
86 } else {
87 this.$buttongroup.removeAttr('disabled');
87 this.$buttongroup.removeAttr('disabled');
88 this.$droplabel.removeAttr('disabled');
88 this.$droplabel.removeAttr('disabled');
89 this.$dropbutton.removeAttr('disabled');
89 this.$dropbutton.removeAttr('disabled');
90 this.$droplist.removeAttr('disabled');
90 this.$droplist.removeAttr('disabled');
91 }
91 }
92
92
93 var description = this.model.get('description');
93 var description = this.model.get('description');
94 if (description.length === 0) {
94 if (description.length === 0) {
95 this.$label.hide();
95 this.$label.hide();
96 } else {
96 } else {
97 this.$label.html(description);
97 this.$label.html(description);
98 this.$label.show();
98 this.$label.show();
99 }
99 }
100 return IPython.WidgetView.prototype.update.call(this);
100 return IPython.WidgetView.prototype.update.call(this);
101 },
101 },
102
102
103 // Handle when a value is clicked.
103 // Handle when a value is clicked.
104 handle_click: function (e) {
104 handle_click: function (e) {
105 this.model.set('value', $(e.target).html(), this);
105 this.model.set('value', $(e.target).html(), this);
106 this.model.update_other_views(this);
106 this.touch();
107 },
107 },
108
108
109 });
109 });
110
110
111 widget_manager.register_widget_view('DropdownView', DropdownView);
111 widget_manager.register_widget_view('DropdownView', DropdownView);
112
112
113 var RadioButtonsView = IPython.WidgetView.extend({
113 var RadioButtonsView = IPython.WidgetView.extend({
114
114
115 // Called when view is rendered.
115 // Called when view is rendered.
116 render : function(){
116 render : function(){
117 this.$el
117 this.$el
118 .addClass('widget-hbox')
118 .addClass('widget-hbox')
119 .html('');
119 .html('');
120 this.$label = $('<div />')
120 this.$label = $('<div />')
121 .appendTo(this.$el)
121 .appendTo(this.$el)
122 .addClass('widget-hlabel')
122 .addClass('widget-hlabel')
123 .hide();
123 .hide();
124 this.$container = $('<div />')
124 this.$container = $('<div />')
125 .appendTo(this.$el)
125 .appendTo(this.$el)
126 .addClass('widget-container')
126 .addClass('widget-container')
127 .addClass('vbox');
127 .addClass('vbox');
128 this.$el_to_style = this.$container; // Set default element to style
128 this.$el_to_style = this.$container; // Set default element to style
129 this.update();
129 this.update();
130 },
130 },
131
131
132 // Handles: Backend -> Frontend Sync
132 // Handles: Backend -> Frontend Sync
133 // Frontent -> Frontend Sync
133 // Frontent -> Frontend Sync
134 update : function(){
134 update : function(){
135
135
136 // Add missing items to the DOM.
136 // Add missing items to the DOM.
137 var items = this.model.get('values');
137 var items = this.model.get('values');
138 var disabled = this.model.get('disabled');
138 var disabled = this.model.get('disabled');
139 for (var index in items) {
139 for (var index in items) {
140 var item_query = ' :input[value="' + items[index] + '"]';
140 var item_query = ' :input[value="' + items[index] + '"]';
141 if (this.$el.find(item_query).length === 0) {
141 if (this.$el.find(item_query).length === 0) {
142 var $label = $('<label />')
142 var $label = $('<label />')
143 .addClass('radio')
143 .addClass('radio')
144 .html(items[index])
144 .html(items[index])
145 .appendTo(this.$container);
145 .appendTo(this.$container);
146
146
147 $('<input />')
147 $('<input />')
148 .attr('type', 'radio')
148 .attr('type', 'radio')
149 .addClass(this.model)
149 .addClass(this.model)
150 .val(items[index])
150 .val(items[index])
151 .prependTo($label)
151 .prependTo($label)
152 .on('click', $.proxy(this.handle_click, this));
152 .on('click', $.proxy(this.handle_click, this));
153 }
153 }
154
154
155 var $item_element = this.$container.find(item_query);
155 var $item_element = this.$container.find(item_query);
156 if (this.model.get('value') == items[index]) {
156 if (this.model.get('value') == items[index]) {
157 $item_element.prop('checked', true);
157 $item_element.prop('checked', true);
158 } else {
158 } else {
159 $item_element.prop('checked', false);
159 $item_element.prop('checked', false);
160 }
160 }
161 $item_element.prop('disabled', disabled);
161 $item_element.prop('disabled', disabled);
162 }
162 }
163
163
164 // Remove items that no longer exist.
164 // Remove items that no longer exist.
165 this.$container.find('input').each(function(i, obj) {
165 this.$container.find('input').each(function(i, obj) {
166 var value = $(obj).val();
166 var value = $(obj).val();
167 var found = false;
167 var found = false;
168 for (var index in items) {
168 for (var index in items) {
169 if (items[index] == value) {
169 if (items[index] == value) {
170 found = true;
170 found = true;
171 break;
171 break;
172 }
172 }
173 }
173 }
174
174
175 if (!found) {
175 if (!found) {
176 $(obj).parent().remove();
176 $(obj).parent().remove();
177 }
177 }
178 });
178 });
179
179
180 var description = this.model.get('description');
180 var description = this.model.get('description');
181 if (description.length === 0) {
181 if (description.length === 0) {
182 this.$label.hide();
182 this.$label.hide();
183 } else {
183 } else {
184 this.$label.html(description);
184 this.$label.html(description);
185 this.$label.show();
185 this.$label.show();
186 }
186 }
187 return IPython.WidgetView.prototype.update.call(this);
187 return IPython.WidgetView.prototype.update.call(this);
188 },
188 },
189
189
190 // Handle when a value is clicked.
190 // Handle when a value is clicked.
191 handle_click: function (e) {
191 handle_click: function (e) {
192 this.model.set('value', $(e.target).val(), this);
192 this.model.set('value', $(e.target).val(), this);
193 this.model.update_other_views(this);
193 this.touch();
194 },
194 },
195 });
195 });
196
196
197 widget_manager.register_widget_view('RadioButtonsView', RadioButtonsView);
197 widget_manager.register_widget_view('RadioButtonsView', RadioButtonsView);
198
198
199
199
200 var ToggleButtonsView = IPython.WidgetView.extend({
200 var ToggleButtonsView = IPython.WidgetView.extend({
201
201
202 // Called when view is rendered.
202 // Called when view is rendered.
203 render : function(){
203 render : function(){
204 this.$el
204 this.$el
205 .addClass('widget-hbox-single')
205 .addClass('widget-hbox-single')
206 .html('');
206 .html('');
207 this.$label = $('<div />')
207 this.$label = $('<div />')
208 .appendTo(this.$el)
208 .appendTo(this.$el)
209 .addClass('widget-hlabel')
209 .addClass('widget-hlabel')
210 .hide();
210 .hide();
211 this.$buttongroup = $('<div />')
211 this.$buttongroup = $('<div />')
212 .addClass('btn-group')
212 .addClass('btn-group')
213 .attr('data-toggle', 'buttons-radio')
213 .attr('data-toggle', 'buttons-radio')
214 .appendTo(this.$el);
214 .appendTo(this.$el);
215 this.$el_to_style = this.$buttongroup; // Set default element to style
215 this.$el_to_style = this.$buttongroup; // Set default element to style
216 this.update();
216 this.update();
217 },
217 },
218
218
219 // Handles: Backend -> Frontend Sync
219 // Handles: Backend -> Frontend Sync
220 // Frontent -> Frontend Sync
220 // Frontent -> Frontend Sync
221 update : function(){
221 update : function(){
222
222
223 // Add missing items to the DOM.
223 // Add missing items to the DOM.
224 var items = this.model.get('values');
224 var items = this.model.get('values');
225 var disabled = this.model.get('disabled');
225 var disabled = this.model.get('disabled');
226 for (var index in items) {
226 for (var index in items) {
227 var item_query = ' :contains("' + items[index] + '")';
227 var item_query = ' :contains("' + items[index] + '")';
228 if (this.$buttongroup.find(item_query).length === 0) {
228 if (this.$buttongroup.find(item_query).length === 0) {
229 $('<button />')
229 $('<button />')
230 .attr('type', 'button')
230 .attr('type', 'button')
231 .addClass('btn')
231 .addClass('btn')
232 .html(items[index])
232 .html(items[index])
233 .appendTo(this.$buttongroup)
233 .appendTo(this.$buttongroup)
234 .on('click', $.proxy(this.handle_click, this));
234 .on('click', $.proxy(this.handle_click, this));
235 }
235 }
236
236
237 var $item_element = this.$buttongroup.find(item_query);
237 var $item_element = this.$buttongroup.find(item_query);
238 if (this.model.get('value') == items[index]) {
238 if (this.model.get('value') == items[index]) {
239 $item_element.addClass('active');
239 $item_element.addClass('active');
240 } else {
240 } else {
241 $item_element.removeClass('active');
241 $item_element.removeClass('active');
242 }
242 }
243 $item_element.prop('disabled', disabled);
243 $item_element.prop('disabled', disabled);
244 }
244 }
245
245
246 // Remove items that no longer exist.
246 // Remove items that no longer exist.
247 this.$buttongroup.find('button').each(function(i, obj) {
247 this.$buttongroup.find('button').each(function(i, obj) {
248 var value = $(obj).html();
248 var value = $(obj).html();
249 var found = false;
249 var found = false;
250 for (var index in items) {
250 for (var index in items) {
251 if (items[index] == value) {
251 if (items[index] == value) {
252 found = true;
252 found = true;
253 break;
253 break;
254 }
254 }
255 }
255 }
256
256
257 if (!found) {
257 if (!found) {
258 $(obj).remove();
258 $(obj).remove();
259 }
259 }
260 });
260 });
261
261
262 var description = this.model.get('description');
262 var description = this.model.get('description');
263 if (description.length === 0) {
263 if (description.length === 0) {
264 this.$label.hide();
264 this.$label.hide();
265 } else {
265 } else {
266 this.$label.html(description);
266 this.$label.html(description);
267 this.$label.show();
267 this.$label.show();
268 }
268 }
269 return IPython.WidgetView.prototype.update.call(this);
269 return IPython.WidgetView.prototype.update.call(this);
270 },
270 },
271
271
272 // Handle when a value is clicked.
272 // Handle when a value is clicked.
273 handle_click: function (e) {
273 handle_click: function (e) {
274 this.model.set('value', $(e.target).html(), this);
274 this.model.set('value', $(e.target).html(), this);
275 this.model.update_other_views(this);
275 this.touch();
276 },
276 },
277
277
278 });
278 });
279
279
280 widget_manager.register_widget_view('ToggleButtonsView', ToggleButtonsView);
280 widget_manager.register_widget_view('ToggleButtonsView', ToggleButtonsView);
281
281
282 var ListBoxView = IPython.WidgetView.extend({
282 var ListBoxView = IPython.WidgetView.extend({
283
283
284 // Called when view is rendered.
284 // Called when view is rendered.
285 render : function(){
285 render : function(){
286 this.$el
286 this.$el
287 .addClass('widget-hbox')
287 .addClass('widget-hbox')
288 .html('');
288 .html('');
289 this.$label = $('<div />')
289 this.$label = $('<div />')
290 .appendTo(this.$el)
290 .appendTo(this.$el)
291 .addClass('widget-hlabel')
291 .addClass('widget-hlabel')
292 .hide();
292 .hide();
293 this.$listbox = $('<select />')
293 this.$listbox = $('<select />')
294 .addClass('widget-listbox')
294 .addClass('widget-listbox')
295 .attr('size', 6)
295 .attr('size', 6)
296 .appendTo(this.$el);
296 .appendTo(this.$el);
297 this.$el_to_style = this.$listbox; // Set default element to style
297 this.$el_to_style = this.$listbox; // Set default element to style
298 this.update();
298 this.update();
299 },
299 },
300
300
301 // Handles: Backend -> Frontend Sync
301 // Handles: Backend -> Frontend Sync
302 // Frontent -> Frontend Sync
302 // Frontent -> Frontend Sync
303 update : function(){
303 update : function(){
304
304
305 // Add missing items to the DOM.
305 // Add missing items to the DOM.
306 var items = this.model.get('values');
306 var items = this.model.get('values');
307 for (var index in items) {
307 for (var index in items) {
308 var item_query = ' :contains("' + items[index] + '")';
308 var item_query = ' :contains("' + items[index] + '")';
309 if (this.$listbox.find(item_query).length === 0) {
309 if (this.$listbox.find(item_query).length === 0) {
310 $('<option />')
310 $('<option />')
311 .html(items[index])
311 .html(items[index])
312 .attr('value', items[index])
312 .attr('value', items[index])
313 .appendTo(this.$listbox)
313 .appendTo(this.$listbox)
314 .on('click', $.proxy(this.handle_click, this));
314 .on('click', $.proxy(this.handle_click, this));
315 }
315 }
316 }
316 }
317
317
318 // Select the correct element
318 // Select the correct element
319 this.$listbox.val(this.model.get('value'));
319 this.$listbox.val(this.model.get('value'));
320
320
321 // Disable listbox if needed
321 // Disable listbox if needed
322 var disabled = this.model.get('disabled');
322 var disabled = this.model.get('disabled');
323 this.$listbox.prop('disabled', disabled);
323 this.$listbox.prop('disabled', disabled);
324
324
325 // Remove items that no longer exist.
325 // Remove items that no longer exist.
326 this.$listbox.find('option').each(function(i, obj) {
326 this.$listbox.find('option').each(function(i, obj) {
327 var value = $(obj).html();
327 var value = $(obj).html();
328 var found = false;
328 var found = false;
329 for (var index in items) {
329 for (var index in items) {
330 if (items[index] == value) {
330 if (items[index] == value) {
331 found = true;
331 found = true;
332 break;
332 break;
333 }
333 }
334 }
334 }
335
335
336 if (!found) {
336 if (!found) {
337 $(obj).remove();
337 $(obj).remove();
338 }
338 }
339 });
339 });
340
340
341 var description = this.model.get('description');
341 var description = this.model.get('description');
342 if (description.length === 0) {
342 if (description.length === 0) {
343 this.$label.hide();
343 this.$label.hide();
344 } else {
344 } else {
345 this.$label.html(description);
345 this.$label.html(description);
346 this.$label.show();
346 this.$label.show();
347 }
347 }
348 return IPython.WidgetView.prototype.update.call(this);
348 return IPython.WidgetView.prototype.update.call(this);
349 },
349 },
350
350
351 // Handle when a value is clicked.
351 // Handle when a value is clicked.
352 handle_click: function (e) {
352 handle_click: function (e) {
353 this.model.set('value', $(e.target).html(), this);
353 this.model.set('value', $(e.target).html(), this);
354 this.model.update_other_views(this);
354 this.touch();
355 },
355 },
356
356
357 });
357 });
358
358
359 widget_manager.register_widget_view('ListBoxView', ListBoxView);
359 widget_manager.register_widget_view('ListBoxView', ListBoxView);
360 });
360 });
@@ -1,188 +1,188
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/base"], function(widget_manager){
17 define(["notebook/js/widgets/base"], function(widget_manager){
18 var StringWidgetModel = IPython.WidgetModel.extend({});
18 var StringWidgetModel = IPython.WidgetModel.extend({});
19 widget_manager.register_widget_model('StringWidgetModel', StringWidgetModel);
19 widget_manager.register_widget_model('StringWidgetModel', StringWidgetModel);
20
20
21 var HTMLView = IPython.WidgetView.extend({
21 var HTMLView = IPython.WidgetView.extend({
22
22
23 // Called when view is rendered.
23 // Called when view is rendered.
24 render : function(){
24 render : function(){
25 this.update(); // Set defaults.
25 this.update(); // Set defaults.
26 },
26 },
27
27
28 // Handles: Backend -> Frontend Sync
28 // Handles: Backend -> Frontend Sync
29 // Frontent -> Frontend Sync
29 // Frontent -> Frontend Sync
30 update : function(){
30 update : function(){
31 this.$el.html(this.model.get('value'));
31 this.$el.html(this.model.get('value'));
32 return IPython.WidgetView.prototype.update.call(this);
32 return IPython.WidgetView.prototype.update.call(this);
33 },
33 },
34
34
35 });
35 });
36
36
37 widget_manager.register_widget_view('HTMLView', HTMLView);
37 widget_manager.register_widget_view('HTMLView', HTMLView);
38
38
39
39
40 var LatexView = IPython.WidgetView.extend({
40 var LatexView = IPython.WidgetView.extend({
41
41
42 // Called when view is rendered.
42 // Called when view is rendered.
43 render : function(){
43 render : function(){
44 this.update(); // Set defaults.
44 this.update(); // Set defaults.
45 },
45 },
46
46
47 // Handles: Backend -> Frontend Sync
47 // Handles: Backend -> Frontend Sync
48 // Frontent -> Frontend Sync
48 // Frontent -> Frontend Sync
49 update : function(){
49 update : function(){
50 this.$el.html(this.model.get('value'));
50 this.$el.html(this.model.get('value'));
51 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$el.get(0)]);
51 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$el.get(0)]);
52
52
53 return IPython.WidgetView.prototype.update.call(this);
53 return IPython.WidgetView.prototype.update.call(this);
54 },
54 },
55
55
56 });
56 });
57
57
58 widget_manager.register_widget_view('LatexView', LatexView);
58 widget_manager.register_widget_view('LatexView', LatexView);
59
59
60 var TextAreaView = IPython.WidgetView.extend({
60 var TextAreaView = IPython.WidgetView.extend({
61
61
62 // Called when view is rendered.
62 // Called when view is rendered.
63 render: function(){
63 render: function(){
64 this.$el
64 this.$el
65 .addClass('widget-hbox')
65 .addClass('widget-hbox')
66 .html('');
66 .html('');
67 this.$label = $('<div />')
67 this.$label = $('<div />')
68 .appendTo(this.$el)
68 .appendTo(this.$el)
69 .addClass('widget-hlabel')
69 .addClass('widget-hlabel')
70 .hide();
70 .hide();
71 this.$textbox = $('<textarea />')
71 this.$textbox = $('<textarea />')
72 .attr('rows', 5)
72 .attr('rows', 5)
73 .addClass('widget-text')
73 .addClass('widget-text')
74 .appendTo(this.$el);
74 .appendTo(this.$el);
75 this.$el_to_style = this.$textbox; // Set default element to style
75 this.$el_to_style = this.$textbox; // Set default element to style
76 this.update(); // Set defaults.
76 this.update(); // Set defaults.
77
77
78 this.model.on_msg($.proxy(this._handle_textarea_msg, this));
78 this.model.on_msg($.proxy(this._handle_textarea_msg, this));
79 },
79 },
80
80
81
81
82 _handle_textarea_msg: function (content){
82 _handle_textarea_msg: function (content){
83 if (content.method == "scroll_to_bottom") {
83 if (content.method == "scroll_to_bottom") {
84 this.scroll_to_bottom();
84 this.scroll_to_bottom();
85 }
85 }
86 },
86 },
87
87
88
88
89 scroll_to_bottom: function (){
89 scroll_to_bottom: function (){
90 this.$textbox.scrollTop(this.$textbox[0].scrollHeight);
90 this.$textbox.scrollTop(this.$textbox[0].scrollHeight);
91 },
91 },
92
92
93
93
94 // Handles: Backend -> Frontend Sync
94 // Handles: Backend -> Frontend Sync
95 // Frontent -> Frontend Sync
95 // Frontent -> Frontend Sync
96 update: function(){
96 update: function(){
97 if (!this.user_invoked_update) {
97 if (!this.user_invoked_update) {
98 this.$textbox.val(this.model.get('value'));
98 this.$textbox.val(this.model.get('value'));
99 }
99 }
100
100
101 var disabled = this.model.get('disabled');
101 var disabled = this.model.get('disabled');
102 this.$textbox.prop('disabled', disabled);
102 this.$textbox.prop('disabled', disabled);
103
103
104 var description = this.model.get('description');
104 var description = this.model.get('description');
105 if (description.length === 0) {
105 if (description.length === 0) {
106 this.$label.hide();
106 this.$label.hide();
107 } else {
107 } else {
108 this.$label.html(description);
108 this.$label.html(description);
109 this.$label.show();
109 this.$label.show();
110 }
110 }
111 return IPython.WidgetView.prototype.update.call(this);
111 return IPython.WidgetView.prototype.update.call(this);
112 },
112 },
113
113
114 events: {"keyup textarea": "handleChanging",
114 events: {"keyup textarea": "handleChanging",
115 "paste textarea": "handleChanging",
115 "paste textarea": "handleChanging",
116 "cut textarea": "handleChanging"},
116 "cut textarea": "handleChanging"},
117
117
118 // Handles and validates user input.
118 // Handles and validates user input.
119 handleChanging: function(e) {
119 handleChanging: function(e) {
120 this.user_invoked_update = true;
120 this.user_invoked_update = true;
121 this.model.set('value', e.target.value);
121 this.model.set('value', e.target.value);
122 this.model.update_other_views(this);
122 this.touch();
123 this.user_invoked_update = false;
123 this.user_invoked_update = false;
124 },
124 },
125 });
125 });
126
126
127 widget_manager.register_widget_view('TextAreaView', TextAreaView);
127 widget_manager.register_widget_view('TextAreaView', TextAreaView);
128
128
129 var TextBoxView = IPython.WidgetView.extend({
129 var TextBoxView = IPython.WidgetView.extend({
130
130
131 // Called when view is rendered.
131 // Called when view is rendered.
132 render: function(){
132 render: function(){
133 this.$el
133 this.$el
134 .addClass('widget-hbox-single')
134 .addClass('widget-hbox-single')
135 .html('');
135 .html('');
136 this.$label = $('<div />')
136 this.$label = $('<div />')
137 .addClass('widget-hlabel')
137 .addClass('widget-hlabel')
138 .appendTo(this.$el)
138 .appendTo(this.$el)
139 .hide();
139 .hide();
140 this.$textbox = $('<input type="text" />')
140 this.$textbox = $('<input type="text" />')
141 .addClass('input')
141 .addClass('input')
142 .addClass('widget-text')
142 .addClass('widget-text')
143 .appendTo(this.$el);
143 .appendTo(this.$el);
144 this.$el_to_style = this.$textbox; // Set default element to style
144 this.$el_to_style = this.$textbox; // Set default element to style
145 this.update(); // Set defaults.
145 this.update(); // Set defaults.
146 },
146 },
147
147
148 // Handles: Backend -> Frontend Sync
148 // Handles: Backend -> Frontend Sync
149 // Frontent -> Frontend Sync
149 // Frontent -> Frontend Sync
150 update: function(){
150 update: function(){
151 if (this.$textbox.val() != this.model.get('value')) {
151 if (this.$textbox.val() != this.model.get('value')) {
152 this.$textbox.val(this.model.get('value'));
152 this.$textbox.val(this.model.get('value'));
153 }
153 }
154
154
155 var disabled = this.model.get('disabled');
155 var disabled = this.model.get('disabled');
156 this.$textbox.prop('disabled', disabled);
156 this.$textbox.prop('disabled', disabled);
157
157
158 var description = this.model.get('description');
158 var description = this.model.get('description');
159 if (description.length === 0) {
159 if (description.length === 0) {
160 this.$label.hide();
160 this.$label.hide();
161 } else {
161 } else {
162 this.$label.html(description);
162 this.$label.html(description);
163 this.$label.show();
163 this.$label.show();
164 }
164 }
165 return IPython.WidgetView.prototype.update.call(this);
165 return IPython.WidgetView.prototype.update.call(this);
166 },
166 },
167
167
168 events: {"keyup input": "handleChanging",
168 events: {"keyup input": "handleChanging",
169 "paste input": "handleChanging",
169 "paste input": "handleChanging",
170 "cut input": "handleChanging",
170 "cut input": "handleChanging",
171 "keypress input": "handleKeypress"},
171 "keypress input": "handleKeypress"},
172
172
173 // Handles and validates user input.
173 // Handles and validates user input.
174 handleChanging: function(e) {
174 handleChanging: function(e) {
175 this.model.set('value', e.target.value);
175 this.model.set('value', e.target.value);
176 this.model.update_other_views(this);
176 this.touch();
177 },
177 },
178
178
179 // Handles text submition
179 // Handles text submition
180 handleKeypress: function(e) {
180 handleKeypress: function(e) {
181 if (e.keyCode == 13) { // Return key
181 if (e.keyCode == 13) { // Return key
182 this.send({event: 'submit'});
182 this.send({event: 'submit'});
183 }
183 }
184 },
184 },
185 });
185 });
186
186
187 widget_manager.register_widget_view('TextBoxView', TextBoxView);
187 widget_manager.register_widget_view('TextBoxView', TextBoxView);
188 });
188 });
General Comments 0
You need to be logged in to leave comments. Login now