##// END OF EJS Templates
Removed for () loops where necessary. Replaced with _.each
Jonathan Frederic -
Show More
@@ -1,189 +1,185
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // WidgetModel, WidgetView, and WidgetManager
10 10 //============================================================================
11 11 /**
12 12 * Base Widget classes
13 13 * @module IPython
14 14 * @namespace IPython
15 15 * @submodule widget
16 16 */
17 17
18 18 (function () {
19 19 "use strict";
20 20
21 21 // Use require.js 'define' method so that require.js is intelligent enough to
22 22 // syncronously load everything within this file when it is being 'required'
23 23 // elsewhere.
24 24 define(["underscore",
25 25 "backbone",
26 26 ], function (Underscore, Backbone) {
27 27
28 28 //--------------------------------------------------------------------
29 29 // WidgetManager class
30 30 //--------------------------------------------------------------------
31 31 var WidgetManager = function (comm_manager) {
32 32 // Public constructor
33 33 WidgetManager._managers.push(this);
34 34
35 35 // Attach a comm manager to the
36 36 this.comm_manager = comm_manager;
37 this._models = {}; /* Dictionary of model ids and model instances */
37 38
38 39 // Register already-registered widget model types with the comm manager.
39 for (var name in WidgetManager._model_types) {
40 if (WidgetManager._model_types.hasOwnProperty(name)) {
41 this.comm_manager.register_target(name, $.proxy(this._handle_comm_open, this));
42
43 }
44 }
40 _.each(WidgetManager._model_types, function(value, key) {
41 this.comm_manager.register_target(value, $.proxy(this._handle_comm_open, this));
42 });
45 43 };
46 44
47 45 //--------------------------------------------------------------------
48 46 // Class level
49 47 //--------------------------------------------------------------------
50 48 WidgetManager._model_types = {}; /* Dictionary of model type names (target_name) and model types. */
51 49 WidgetManager._view_types = {}; /* Dictionary of view names and view types. */
52 WidgetManager._models = {}; /* Dictionary of model ids and model instances */
53 50 WidgetManager._managers = []; /* List of widget managers */
54 51
55 52 WidgetManager.register_widget_model = function (model_name, model_type) {
56 53 // Registers a widget model by name.
57 54 WidgetManager._model_types[model_name] = model_type;
58 55
59 56 // Register the widget with the comm manager. Make sure to pass this object's context
60 57 // in so `this` works in the call back.
61 for (var i = 0; i < WidgetManager._managers.length; i++) {
62 var instance = WidgetManager._managers[i];
58 _.each(WidgetManager._managers, function(instance, i) {
63 59 if (instance.comm_manager !== null) {
64 60 instance.comm_manager.register_target(model_name, $.proxy(instance._handle_comm_open, instance));
65 61 }
66 }
62 });
67 63 };
68 64
69 65 WidgetManager.register_widget_view = function (view_name, view_type) {
70 66 // Registers a widget view by name.
71 67 WidgetManager._view_types[view_name] = view_type;
72 68 };
73 69
74 70 //--------------------------------------------------------------------
75 71 // Instance level
76 72 //--------------------------------------------------------------------
77 73 WidgetManager.prototype.display_view = function(msg, model) {
78 74 var cell = this.get_msg_cell(msg.parent_header.msg_id);
79 75 if (cell === null) {
80 76 console.log("Could not determine where the display" +
81 77 " message was from. Widget will not be displayed");
82 78 } else {
83 79 var view = this.create_view(model, {cell: cell});
84 80 if (view === undefined) {
85 81 console.error("View creation failed", model);
86 82 }
87 83 if (cell.widget_subarea !== undefined
88 84 && cell.widget_subarea !== null) {
89 85
90 86 cell.widget_area.show();
91 87 cell.widget_subarea.append(view.$el);
92 88 }
93 89 }
94 90 },
95 91
96 92 WidgetManager.prototype.create_view = function(model, options) {
97 93 var view_name = model.get('view_name');
98 94 var ViewType = WidgetManager._view_types[view_name];
99 95 if (ViewType !== undefined && ViewType !== null) {
100 96 var parameters = {model: model, options: options};
101 97 var view = new ViewType(parameters);
102 98 view.render();
103 99 IPython.keyboard_manager.register_events(view.$el);
104 100 model.views.push(view);
105 101 model.on('destroy', view.remove, view);
106 102 return view;
107 103 }
108 104 },
109 105
110 106 WidgetManager.prototype.get_msg_cell = function (msg_id) {
111 107 var cell = null;
112 108 // First, check to see if the msg was triggered by cell execution.
113 109 if (IPython.notebook !== undefined && IPython.notebook !== null) {
114 110 cell = IPython.notebook.get_msg_cell(msg_id);
115 111 }
116 112 if (cell !== null) {
117 113 return cell
118 114 }
119 115 // Second, check to see if a get_cell callback was defined
120 116 // for the message. get_cell callbacks are registered for
121 117 // widget messages, so this block is actually checking to see if the
122 118 // message was triggered by a widget.
123 119 var kernel = this.comm_manager.kernel;
124 120 if (kernel !== undefined && kernel !== null) {
125 121 var callbacks = kernel.get_callbacks_for_msg(msg_id);
126 122 if (callbacks !== undefined &&
127 123 callbacks.iopub !== undefined &&
128 124 callbacks.iopub.get_cell !== undefined) {
129 125
130 126 return callbacks.iopub.get_cell();
131 127 }
132 128 }
133 129
134 130 // Not triggered by a cell or widget (no get_cell callback
135 131 // exists).
136 132 return null;
137 133 };
138 134
139 135 WidgetManager.prototype.callbacks = function (view) {
140 136 // callback handlers specific a view
141 137 var callbacks = {};
142 138 var cell = view.options.cell;
143 139 if (cell !== null) {
144 140 // Try to get output handlers
145 141 var handle_output = null;
146 142 var handle_clear_output = null;
147 143 if (cell.output_area !== undefined && cell.output_area !== null) {
148 144 handle_output = $.proxy(cell.output_area.handle_output, cell.output_area);
149 145 handle_clear_output = $.proxy(cell.output_area.handle_clear_output, cell.output_area);
150 146 }
151 147
152 148 // Create callback dict using what is known
153 149 var that = this;
154 150 callbacks = {
155 151 iopub : {
156 152 output : handle_output,
157 153 clear_output : handle_clear_output,
158 154
159 155 // Special function only registered by widget messages.
160 156 // Allows us to get the cell for a message so we know
161 157 // where to add widgets if the code requires it.
162 158 get_cell : function () {
163 159 return cell;
164 160 },
165 161 },
166 162 };
167 163 }
168 164 return callbacks;
169 165 };
170 166
171 167 WidgetManager.prototype.get_model = function (model_id) {
172 var model = WidgetManager._models[model_id];
168 var model = this._models[model_id];
173 169 if (model !== undefined && model.id == model_id) {
174 170 return model;
175 171 }
176 172 return null;
177 173 };
178 174
179 175 WidgetManager.prototype._handle_comm_open = function (comm, msg) {
180 176 var model_id = comm.comm_id;
181 177 var widget_type_name = msg.content.target_name;
182 178 var widget_model = new WidgetManager._model_types[widget_type_name](this, model_id, comm);
183 WidgetManager._models[model_id] = widget_model;
179 this._models[model_id] = widget_model;
184 180 };
185 181
186 182 IPython.WidgetManager = WidgetManager;
187 183 return IPython.WidgetManager;
188 184 });
189 185 }());
@@ -1,414 +1,407
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // Base Widget Model and View classes
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgetmanager",
18 18 "underscore",
19 19 "backbone"],
20 20 function(WidgetManager, Underscore, Backbone){
21 21
22 22 var WidgetModel = Backbone.Model.extend({
23 23 constructor: function (widget_manager, model_id, comm) {
24 24 // Construcctor
25 25 //
26 26 // Creates a WidgetModel instance.
27 27 //
28 28 // Parameters
29 29 // ----------
30 30 // widget_manager : WidgetManager instance
31 31 // model_id : string
32 32 // An ID unique to this model.
33 33 // comm : Comm instance (optional)
34 34 this.widget_manager = widget_manager;
35 35 this.pending_msgs = 0;
36 36 this.msg_throttle = 2;
37 37 this.msg_buffer = null;
38 38 this.key_value_lock = null;
39 39 this.id = model_id;
40 40 this.views = [];
41 41
42 42 if (comm !== undefined) {
43 43 // Remember comm associated with the model.
44 44 this.comm = comm;
45 45 comm.model = this;
46 46
47 47 // Hook comm messages up to model.
48 48 comm.on_close($.proxy(this._handle_comm_closed, this));
49 49 comm.on_msg($.proxy(this._handle_comm_msg, this));
50 50 }
51 51 return Backbone.Model.apply(this);
52 52 },
53 53
54 54 send: function (content, callbacks) {
55 55 // Send a custom msg over the comm.
56 56 if (this.comm !== undefined) {
57 57 var data = {method: 'custom', content: content};
58 58 this.comm.send(data, callbacks);
59 59 }
60 60 },
61 61
62 62 _handle_comm_closed: function (msg) {
63 63 // Handle when a widget is closed.
64 64 this.trigger('comm:close');
65 65 delete this.comm.model; // Delete ref so GC will collect widget model.
66 66 delete this.comm;
67 67 delete this.model_id; // Delete id from model so widget manager cleans up.
68 68 // TODO: Handle deletion, like this.destroy(), and delete views, etc.
69 69 },
70 70
71 71 _handle_comm_msg: function (msg) {
72 72 // Handle incoming comm msg.
73 73 var method = msg.content.data.method;
74 74 switch (method) {
75 75 case 'update':
76 76 this.apply_update(msg.content.data.state);
77 77 break;
78 78 case 'custom':
79 79 this.trigger('msg:custom', msg.content.data.content);
80 80 break;
81 81 case 'display':
82 82 this.widget_manager.display_view(msg, this);
83 83 break;
84 84 }
85 85 },
86 86
87 87 apply_update: function (state) {
88 88 // Handle when a widget is updated via the python side.
89 for (var key in state) {
90 if (state.hasOwnProperty(key)) {
91 var value = state[key];
92 this.key_value_lock = [key, value];
93 try {
94 this.set(key, this._unpack_models(value));
95 } finally {
96 this.key_value_lock = null;
97 }
89 _.each(state, function(value, key) {
90 this.key_value_lock = [key, value];
91 try {
92 this.set(key, this._unpack_models(value));
93 } finally {
94 this.key_value_lock = null;
98 95 }
99 }
96 });
100 97 },
101 98
102 99 _handle_status: function (msg, callbacks) {
103 100 // Handle status msgs.
104 101
105 102 // execution_state : ('busy', 'idle', 'starting')
106 103 if (this.comm !== undefined) {
107 104 if (msg.content.execution_state ==='idle') {
108 105 // Send buffer if this message caused another message to be
109 106 // throttled.
110 107 if (this.msg_buffer !== null &&
111 108 this.msg_throttle === this.pending_msgs) {
112 109 var data = {method: 'backbone', sync_method: 'update', sync_data: this.msg_buffer};
113 110 this.comm.send(data, callbacks);
114 111 this.msg_buffer = null;
115 112 } else {
116 113 --this.pending_msgs;
117 114 }
118 115 }
119 116 }
120 117 },
121 118
122 119 callbacks: function(view) {
123 120 // Create msg callbacks for a comm msg.
124 121 var callbacks = this.widget_manager.callbacks(view);
125 122
126 123 if (callbacks.iopub === undefined) {
127 124 callbacks.iopub = {};
128 125 }
129 126
130 127 var that = this;
131 128 callbacks.iopub.status = function (msg) {
132 129 that._handle_status(msg, callbacks);
133 130 }
134 131 return callbacks;
135 132 },
136 133
137 134 sync: function (method, model, options) {
138 135
139 136 // Make sure a comm exists.
140 137 var error = options.error || function() {
141 138 console.error('Backbone sync error:', arguments);
142 139 }
143 140 if (this.comm === undefined) {
144 141 error();
145 142 return false;
146 143 }
147 144
148 145 // Delete any key value pairs that the back-end already knows about.
149 146 var attrs = (method === 'patch') ? options.attrs : model.toJSON(options);
150 147 if (this.key_value_lock !== null) {
151 148 var key = this.key_value_lock[0];
152 149 var value = this.key_value_lock[1];
153 150 if (attrs[key] === value) {
154 151 delete attrs[key];
155 152 }
156 153 }
157 154
158 155 // Only sync if there are attributes to send to the back-end.
159 156 if (attr.length > 0) {
160 157 var callbacks = options.callbacks || {};
161 158 if (this.pending_msgs >= this.msg_throttle) {
162 159 // The throttle has been exceeded, buffer the current msg so
163 160 // it can be sent once the kernel has finished processing
164 161 // some of the existing messages.
165 162
166 163 // Combine updates if it is a 'patch' sync, otherwise replace updates
167 164 switch (method) {
168 165 case 'patch':
169 166 this.msg_buffer = $.extend(this.msg_buffer || {}, attrs);
170 167 break;
171 168 case 'update':
172 169 case 'create':
173 170 this.msg_buffer = attrs;
174 171 break;
175 172 default:
176 173 error();
177 174 return false;
178 175 }
179 176 this.msg_buffer_callbacks = callbacks;
180 177
181 178 } else {
182 179 // We haven't exceeded the throttle, send the message like
183 180 // normal. If this is a patch operation, just send the
184 181 // changes.
185 182 var data = {method: 'backbone', sync_data: attrs};
186 183 this.comm.send(data, callbacks);
187 184 this.pending_msgs++;
188 185 }
189 186 }
190 187 // Since the comm is a one-way communication, assume the message
191 188 // arrived. Don't call success since we don't have a model back from the server
192 189 // this means we miss out on the 'sync' event.
193 190 },
194 191
195 192 save_changes: function(callbacks) {
196 193 // Push this model's state to the back-end
197 194 //
198 195 // This invokes a Backbone.Sync.
199 196 this.save(this.changedAttributes(), {patch: true, callbacks: callbacks});
200 197 },
201 198
202 199 _pack_models: function(value) {
203 200 // Replace models with model ids recursively.
204 201 if (value instanceof Backbone.Model) {
205 202 return value.id;
206 203 } else if (value instanceof Object) {
207 204 var packed = {};
208 for (var key in value) {
209 packed[key] = this._pack_models(value[key]);
210 }
205 _.each(value, function(sub_value, key) {
206 packed[key] = this._pack_models(sub_value);
207 });
211 208 return packed;
212 209 } else {
213 210 return value;
214 211 }
215 212 },
216 213
217 214 _unpack_models: function(value) {
218 215 // Replace model ids with models recursively.
219 216 if (value instanceof Object) {
220 217 var unpacked = {};
221 for (var key in value) {
222 unpacked[key] = this._unpack_models(value[key]);
223 }
218 _.each(value, function(sub_value, key) {
219 unpacked[key] = this._unpack_models(sub_value);
220 });
224 221 return unpacked;
225 222 } else {
226 223 var model = this.widget_manager.get_model(value);
227 224 if (model !== null) {
228 225 return model;
229 226 } else {
230 227 return value;
231 228 }
232 229 }
233 230 },
234 231
235 232 });
236 233 WidgetManager.register_widget_model('WidgetModel', WidgetModel);
237 234
238 235
239 236 var WidgetView = Backbone.View.extend({
240 237 initialize: function(parameters) {
241 238 // Public constructor.
242 239 this.model.on('change',this.update,this);
243 240 this.options = parameters.options;
244 241 this.child_views = [];
245 242 this.model.views.push(this);
246 243 },
247 244
248 245 update: function(){
249 246 // Triggered on model change.
250 247 //
251 248 // Update view to be consistent with this.model
252 249 },
253 250
254 251 create_child_view: function(child_model, options) {
255 252 // Create and return a child view.
256 253 //
257 254 // -given a model and (optionally) a view name if the view name is
258 255 // not given, it defaults to the model's default view attribute.
259 256
260 257 // TODO: this is hacky, and makes the view depend on this cell attribute and widget manager behavior
261 258 // it would be great to have the widget manager add the cell metadata
262 259 // to the subview without having to add it here.
263 260 options = options || {};
264 261 options.cell = this.options.cell;
265 262 var child_view = this.model.widget_manager.create_view(child_model, options);
266 263 this.child_views[child_model.id] = child_view;
267 264 return child_view;
268 265 },
269 266
270 267 delete_child_view: function(child_model, options) {
271 268 // Delete a child view that was previously created using create_child_view.
272 269 var view = this.child_views[child_model.id];
273 270 delete this.child_views[child_model.id];
274 271 view.remove();
275 272 },
276 273
277 274 do_diff: function(old_list, new_list, removed_callback, added_callback) {
278 275 // Difference a changed list and call remove and add callbacks for
279 276 // each removed and added item in the new list.
280 277 //
281 278 // Parameters
282 279 // ----------
283 280 // old_list : array
284 281 // new_list : array
285 282 // removed_callback : Callback(item)
286 283 // Callback that is called for each item removed.
287 284 // added_callback : Callback(item)
288 285 // Callback that is called for each item added.
289 286
290 287
291 288 // removed items
292 289 _.each(_.difference(old_list, new_list), function(item, index, list) {
293 290 removed_callback(item);
294 291 }, this);
295 292
296 293 // added items
297 294 _.each(_.difference(new_list, old_list), function(item, index, list) {
298 295 added_callback(item);
299 296 }, this);
300 297 },
301 298
302 299 callbacks: function(){
303 300 // Create msg callbacks for a comm msg.
304 301 return this.model.callbacks(this);
305 302 },
306 303
307 304 render: function(){
308 305 // Render the view.
309 306 //
310 307 // By default, this is only called the first time the view is created
311 308 },
312 309
313 310 send: function (content) {
314 311 // Send a custom msg associated with this view.
315 312 this.model.send(content, this.callbacks());
316 313 },
317 314
318 315 touch: function () {
319 316 this.model.save_changes(this.callbacks());
320 317 },
321 318
322 319 });
323 320
324 321
325 322 var DOMWidgetView = WidgetView.extend({
326 323 initialize: function (options) {
327 324 // Public constructor
328 325
329 326 // In the future we may want to make changes more granular
330 327 // (e.g., trigger on visible:change).
331 328 this.model.on('change', this.update, this);
332 329 this.model.on('msg:custom', this.on_msg, this);
333 330 DOMWidgetView.__super__.initialize.apply(this, arguments);
334 331 },
335 332
336 333 on_msg: function(msg) {
337 334 // Handle DOM specific msgs.
338 335 switch(msg.msg_type) {
339 336 case 'add_class':
340 337 this.add_class(msg.selector, msg.class_list);
341 338 break;
342 339 case 'remove_class':
343 340 this.remove_class(msg.selector, msg.class_list);
344 341 break;
345 342 }
346 343 },
347 344
348 345 add_class: function (selector, class_list) {
349 346 // Add a DOM class to an element.
350 347 this._get_selector_element(selector).addClass(class_list);
351 348 },
352 349
353 350 remove_class: function (selector, class_list) {
354 351 // Remove a DOM class from an element.
355 352 this._get_selector_element(selector).removeClass(class_list);
356 353 },
357 354
358 355 update: function () {
359 356 // Update the contents of this view
360 357 //
361 358 // Called when the model is changed. The model may have been
362 359 // changed by another view or by a state update from the back-end.
363 360 // The very first update seems to happen before the element is
364 361 // finished rendering so we use setTimeout to give the element time
365 362 // to render
366 363 var e = this.$el;
367 364 var visible = this.model.get('visible');
368 365 setTimeout(function() {e.toggle(visible)},0);
369 366
370 367 var css = this.model.get('_css');
371 368 if (css === undefined) {return;}
372 for (var selector in css) {
373 if (css.hasOwnProperty(selector)) {
374 // Apply the css traits to all elements that match the selector.
375 var elements = this._get_selector_element(selector);
376 if (elements.length > 0) {
377 var css_traits = css[selector];
378 for (var css_key in css_traits) {
379 if (css_traits.hasOwnProperty(css_key)) {
380 elements.css(css_key, css_traits[css_key]);
381 }
382 }
383 }
369 _.each(css, function(css_traits, selector){
370 // Apply the css traits to all elements that match the selector.
371 var elements = this._get_selector_element(selector);
372 if (elements.length > 0) {
373 _.each(css_traits, function(css_value, css_key){
374 elements.css(css_key, css_value);
375 });
384 376 }
385 }
377 });
378
386 379 },
387 380
388 381 _get_selector_element: function (selector) {
389 382 // Get the elements via the css selector.
390 383
391 384 // If the selector is blank, apply the style to the $el_to_style
392 385 // element. If the $el_to_style element is not defined, use apply
393 386 // the style to the view's element.
394 387 var elements;
395 388 if (selector === undefined || selector === null || selector === '') {
396 389 if (this.$el_to_style === undefined) {
397 390 elements = this.$el;
398 391 } else {
399 392 elements = this.$el_to_style;
400 393 }
401 394 } else {
402 395 elements = this.$el.find(selector);
403 396 }
404 397 return elements;
405 398 },
406 399 });
407 400
408 401 IPython.WidgetModel = WidgetModel;
409 402 IPython.WidgetView = WidgetView;
410 403 IPython.DOMWidgetView = DOMWidgetView;
411 404
412 405 // Pass through WidgetManager namespace.
413 406 return WidgetManager;
414 407 });
@@ -1,267 +1,267
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // FloatRangeWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(WidgetManager){
18 18
19 19 var FloatSliderView = IPython.DOMWidgetView.extend({
20 20 render : function(){
21 21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .appendTo(this.$el)
26 26 .addClass('widget-hlabel')
27 27 .hide();
28 28 this.$slider = $('<div />')
29 29 .slider({})
30 30 .addClass('slider');
31 31
32 32 // Put the slider in a container
33 33 this.$slider_container = $('<div />')
34 34 .addClass('widget-hslider')
35 35 .append(this.$slider);
36 36 this.$el_to_style = this.$slider_container; // Set default element to style
37 37 this.$el.append(this.$slider_container);
38 38
39 39 // Set defaults.
40 40 this.update();
41 41 },
42 42
43 43 update : function(options){
44 44 // Update the contents of this view
45 45 //
46 46 // Called when the model is changed. The model may have been
47 47 // changed by another view or by a state update from the back-end.
48 48
49 49 if (options === undefined || options.updated_view != this) {
50 50 // JQuery slider option keys. These keys happen to have a
51 51 // one-to-one mapping with the corrosponding keys of the model.
52 52 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
53 for (var index in jquery_slider_keys) {
54 var key = jquery_slider_keys[index];
55 if (this.model.get(key) !== undefined) {
56 this.$slider.slider("option", key, this.model.get(key));
53 _.each(jquery_slider_keys, function(key, i) {
54 var model_value = this.model.get(key);
55 if (model_value !== undefined) {
56 this.$slider.slider("option", key, model_value);
57 57 }
58 }
58 });
59 59
60 60 // WORKAROUND FOR JQUERY SLIDER BUG.
61 61 // The horizontal position of the slider handle
62 62 // depends on the value of the slider at the time
63 63 // of orientation change. Before applying the new
64 64 // workaround, we set the value to the minimum to
65 65 // make sure that the horizontal placement of the
66 66 // handle in the vertical slider is always
67 67 // consistent.
68 68 var orientation = this.model.get('orientation');
69 69 var value = this.model.get('min');
70 70 this.$slider.slider('option', 'value', value);
71 71 this.$slider.slider('option', 'orientation', orientation);
72 72 value = this.model.get('value');
73 73 this.$slider.slider('option', 'value', value);
74 74
75 75 // Use the right CSS classes for vertical & horizontal sliders
76 76 if (orientation=='vertical') {
77 77 this.$slider_container
78 78 .removeClass('widget-hslider')
79 79 .addClass('widget-vslider');
80 80 this.$el
81 81 .removeClass('widget-hbox-single')
82 82 .addClass('widget-vbox-single');
83 83 this.$label
84 84 .removeClass('widget-hlabel')
85 85 .addClass('widget-vlabel');
86 86
87 87 } else {
88 88 this.$slider_container
89 89 .removeClass('widget-vslider')
90 90 .addClass('widget-hslider');
91 91 this.$el
92 92 .removeClass('widget-vbox-single')
93 93 .addClass('widget-hbox-single');
94 94 this.$label
95 95 .removeClass('widget-vlabel')
96 96 .addClass('widget-hlabel');
97 97 }
98 98
99 99 var description = this.model.get('description');
100 100 if (description.length === 0) {
101 101 this.$label.hide();
102 102 } else {
103 103 this.$label.text(description);
104 104 this.$label.show();
105 105 }
106 106 }
107 107 return FloatSliderView.__super__.update.apply(this);
108 108 },
109 109
110 110 events: {
111 111 // Dictionary of events and their handlers.
112 112 "slide" : "handleSliderChange"
113 113 },
114 114
115 115 handleSliderChange: function(e, ui) {
116 116 // Handle when the slider value is changed.
117 117
118 118 // Calling model.set will trigger all of the other views of the
119 119 // model to update.
120 120 this.model.set('value', ui.value, {updated_view: this});
121 121 this.touch();
122 122 },
123 123 });
124 124 WidgetManager.register_widget_view('FloatSliderView', FloatSliderView);
125 125
126 126
127 127 var FloatTextView = IPython.DOMWidgetView.extend({
128 128 render : function(){
129 129 // Called when view is rendered.
130 130 this.$el
131 131 .addClass('widget-hbox-single');
132 132 this.$label = $('<div />')
133 133 .appendTo(this.$el)
134 134 .addClass('widget-hlabel')
135 135 .hide();
136 136 this.$textbox = $('<input type="text" />')
137 137 .addClass('input')
138 138 .addClass('widget-numeric-text')
139 139 .appendTo(this.$el);
140 140 this.$el_to_style = this.$textbox; // Set default element to style
141 141 this.update(); // Set defaults.
142 142 },
143 143
144 144 update : function(options){
145 145 // Update the contents of this view
146 146 //
147 147 // Called when the model is changed. The model may have been
148 148 // changed by another view or by a state update from the back-end.
149 149 if (options === undefined || options.updated_view != this) {
150 150 var value = this.model.get('value');
151 151 if (parseFloat(this.$textbox.val()) != value) {
152 152 this.$textbox.val(value);
153 153 }
154 154
155 155 if (this.model.get('disabled')) {
156 156 this.$textbox.attr('disabled','disabled');
157 157 } else {
158 158 this.$textbox.removeAttr('disabled');
159 159 }
160 160
161 161 var description = this.model.get('description');
162 162 if (description.length === 0) {
163 163 this.$label.hide();
164 164 } else {
165 165 this.$label.text(description);
166 166 this.$label.show();
167 167 }
168 168 }
169 169 return FloatTextView.__super__.update.apply(this);
170 170 },
171 171
172 172 events: {
173 173 // Dictionary of events and their handlers.
174 174
175 175 "keyup input" : "handleChanging",
176 176 "paste input" : "handleChanging",
177 177 "cut input" : "handleChanging",
178 178
179 179 // Fires only when control is validated or looses focus.
180 180 "change input" : "handleChanged"
181 181 },
182 182
183 183 handleChanging: function(e) {
184 184 // Handles and validates user input.
185 185
186 186 // Try to parse value as a float.
187 187 var numericalValue = 0.0;
188 188 if (e.target.value !== '') {
189 189 numericalValue = parseFloat(e.target.value);
190 190 }
191 191
192 192 // If parse failed, reset value to value stored in model.
193 193 if (isNaN(numericalValue)) {
194 194 e.target.value = this.model.get('value');
195 195 } else if (!isNaN(numericalValue)) {
196 196 if (this.model.get('max') !== undefined) {
197 197 numericalValue = Math.min(this.model.get('max'), numericalValue);
198 198 }
199 199 if (this.model.get('min') !== undefined) {
200 200 numericalValue = Math.max(this.model.get('min'), numericalValue);
201 201 }
202 202
203 203 // Apply the value if it has changed.
204 204 if (numericalValue != this.model.get('value')) {
205 205
206 206 // Calling model.set will trigger all of the other views of the
207 207 // model to update.
208 208 this.model.set('value', numericalValue, {updated_view: this});
209 209 this.touch();
210 210 }
211 211 }
212 212 },
213 213
214 214 handleChanged: function(e) {
215 215 // Applies validated input.
216 216 if (this.model.get('value') != e.target.value) {
217 217 e.target.value = this.model.get('value');
218 218 }
219 219 }
220 220 });
221 221 WidgetManager.register_widget_view('FloatTextView', FloatTextView);
222 222
223 223
224 224 var ProgressView = IPython.DOMWidgetView.extend({
225 225 render : function(){
226 226 // Called when view is rendered.
227 227 this.$el
228 228 .addClass('widget-hbox-single');
229 229 this.$label = $('<div />')
230 230 .appendTo(this.$el)
231 231 .addClass('widget-hlabel')
232 232 .hide();
233 233 this.$progress = $('<div />')
234 234 .addClass('progress')
235 235 .addClass('widget-progress')
236 236 .appendTo(this.$el);
237 237 this.$el_to_style = this.$progress; // Set default element to style
238 238 this.$bar = $('<div />')
239 239 .addClass('bar')
240 240 .css('width', '50%')
241 241 .appendTo(this.$progress);
242 242 this.update(); // Set defaults.
243 243 },
244 244
245 245 update : function(){
246 246 // Update the contents of this view
247 247 //
248 248 // Called when the model is changed. The model may have been
249 249 // changed by another view or by a state update from the back-end.
250 250 var value = this.model.get('value');
251 251 var max = this.model.get('max');
252 252 var min = this.model.get('min');
253 253 var percent = 100.0 * (value - min) / (max - min);
254 254 this.$bar.css('width', percent + '%');
255 255
256 256 var description = this.model.get('description');
257 257 if (description.length === 0) {
258 258 this.$label.hide();
259 259 } else {
260 260 this.$label.text(description);
261 261 this.$label.show();
262 262 }
263 263 return ProgressView.__super__.update.apply(this);
264 264 },
265 265 });
266 266 WidgetManager.register_widget_view('ProgressView', ProgressView);
267 267 });
@@ -1,220 +1,220
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // IntRangeWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(WidgetManager){
18 18
19 19 var IntSliderView = IPython.DOMWidgetView.extend({
20 20 render : function(){
21 21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .appendTo(this.$el)
26 26 .addClass('widget-hlabel')
27 27 .hide();
28 28 this.$slider = $('<div />')
29 29 .slider({})
30 30 .addClass('slider');
31 31
32 32 // Put the slider in a container
33 33 this.$slider_container = $('<div />')
34 34 .addClass('widget-hslider')
35 35 .append(this.$slider);
36 36 this.$el_to_style = this.$slider_container; // Set default element to style
37 37 this.$el.append(this.$slider_container);
38 38
39 39 // Set defaults.
40 40 this.update();
41 41 },
42 42
43 43 update : function(options){
44 44 // Update the contents of this view
45 45 //
46 46 // Called when the model is changed. The model may have been
47 47 // changed by another view or by a state update from the back-end.
48 48 if (options === undefined || options.updated_view != this) {
49 49 // JQuery slider option keys. These keys happen to have a
50 50 // one-to-one mapping with the corrosponding keys of the model.
51 51 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
52 for (var index in jquery_slider_keys) {
53 var key = jquery_slider_keys[index];
54 if (this.model.get(key) !== undefined) {
55 this.$slider.slider("option", key, this.model.get(key));
52 _.each(jquery_slider_keys, function(key, i) {
53 var model_value = this.model.get(key);
54 if (model_value !== undefined) {
55 this.$slider.slider("option", key, model_value);
56 56 }
57 }
57 });
58 58
59 59 // WORKAROUND FOR JQUERY SLIDER BUG.
60 60 // The horizontal position of the slider handle
61 61 // depends on the value of the slider at the time
62 62 // of orientation change. Before applying the new
63 63 // workaround, we set the value to the minimum to
64 64 // make sure that the horizontal placement of the
65 65 // handle in the vertical slider is always
66 66 // consistent.
67 67 var orientation = this.model.get('orientation');
68 68 var value = this.model.get('min');
69 69 this.$slider.slider('option', 'value', value);
70 70 this.$slider.slider('option', 'orientation', orientation);
71 71 value = this.model.get('value');
72 72 this.$slider.slider('option', 'value', value);
73 73
74 74 // Use the right CSS classes for vertical & horizontal sliders
75 75 if (orientation=='vertical') {
76 76 this.$slider_container
77 77 .removeClass('widget-hslider')
78 78 .addClass('widget-vslider');
79 79 this.$el
80 80 .removeClass('widget-hbox-single')
81 81 .addClass('widget-vbox-single');
82 82 this.$label
83 83 .removeClass('widget-hlabel')
84 84 .addClass('widget-vlabel');
85 85
86 86 } else {
87 87 this.$slider_container
88 88 .removeClass('widget-vslider')
89 89 .addClass('widget-hslider');
90 90 this.$el
91 91 .removeClass('widget-vbox-single')
92 92 .addClass('widget-hbox-single');
93 93 this.$label
94 94 .removeClass('widget-vlabel')
95 95 .addClass('widget-hlabel');
96 96 }
97 97
98 98 var description = this.model.get('description');
99 99 if (description.length === 0) {
100 100 this.$label.hide();
101 101 } else {
102 102 this.$label.text(description);
103 103 this.$label.show();
104 104 }
105 105 }
106 106 return IntSliderView.__super__.update.apply(this);
107 107 },
108 108
109 109 events: {
110 110 // Dictionary of events and their handlers.
111 111 "slide" : "handleSliderChange"
112 112 },
113 113
114 114 handleSliderChange: function(e, ui) {
115 115 // Called when the slider value is changed.
116 116
117 117 // Calling model.set will trigger all of the other views of the
118 118 // model to update.
119 119 this.model.set('value', ~~ui.value, {updated_view: this}); // Double bit-wise not to truncate decimel
120 120 this.touch();
121 121 },
122 122 });
123 123 WidgetManager.register_widget_view('IntSliderView', IntSliderView);
124 124
125 125
126 126 var IntTextView = IPython.DOMWidgetView.extend({
127 127 render : function(){
128 128 // Called when view is rendered.
129 129 this.$el
130 130 .addClass('widget-hbox-single');
131 131 this.$label = $('<div />')
132 132 .appendTo(this.$el)
133 133 .addClass('widget-hlabel')
134 134 .hide();
135 135 this.$textbox = $('<input type="text" />')
136 136 .addClass('input')
137 137 .addClass('widget-numeric-text')
138 138 .appendTo(this.$el);
139 139 this.$el_to_style = this.$textbox; // Set default element to style
140 140 this.update(); // Set defaults.
141 141 },
142 142
143 143 update : function(options){
144 144 // Update the contents of this view
145 145 //
146 146 // Called when the model is changed. The model may have been
147 147 // changed by another view or by a state update from the back-end.
148 148 if (options === undefined || options.updated_view != this) {
149 149 var value = this.model.get('value');
150 150 if (parseInt(this.$textbox.val()) != value) {
151 151 this.$textbox.val(value);
152 152 }
153 153
154 154 if (this.model.get('disabled')) {
155 155 this.$textbox.attr('disabled','disabled');
156 156 } else {
157 157 this.$textbox.removeAttr('disabled');
158 158 }
159 159
160 160 var description = this.model.get('description');
161 161 if (description.length === 0) {
162 162 this.$label.hide();
163 163 } else {
164 164 this.$label.text(description);
165 165 this.$label.show();
166 166 }
167 167 }
168 168 return IntTextView.__super__.update.apply(this);
169 169 },
170 170
171 171 events: {
172 172 // Dictionary of events and their handlers.
173 173 "keyup input" : "handleChanging",
174 174 "paste input" : "handleChanging",
175 175 "cut input" : "handleChanging",
176 176
177 177 // Fires only when control is validated or looses focus.
178 178 "change input" : "handleChanged"
179 179 },
180 180
181 181 handleChanging: function(e) {
182 182 // Handles and validates user input.
183 183
184 184 // Try to parse value as a float.
185 185 var numericalValue = 0;
186 186 if (e.target.value !== '') {
187 187 numericalValue = parseInt(e.target.value);
188 188 }
189 189
190 190 // If parse failed, reset value to value stored in model.
191 191 if (isNaN(numericalValue)) {
192 192 e.target.value = this.model.get('value');
193 193 } else if (!isNaN(numericalValue)) {
194 194 if (this.model.get('max') !== undefined) {
195 195 numericalValue = Math.min(this.model.get('max'), numericalValue);
196 196 }
197 197 if (this.model.get('min') !== undefined) {
198 198 numericalValue = Math.max(this.model.get('min'), numericalValue);
199 199 }
200 200
201 201 // Apply the value if it has changed.
202 202 if (numericalValue != this.model.get('value')) {
203 203
204 204 // Calling model.set will trigger all of the other views of the
205 205 // model to update.
206 206 this.model.set('value', numericalValue, {updated_view: this});
207 207 this.touch();
208 208 }
209 209 }
210 210 },
211 211
212 212 handleChanged: function(e) {
213 213 // Applies validated input.
214 214 if (this.model.get('value') != e.target.value) {
215 215 e.target.value = this.model.get('value');
216 216 }
217 217 }
218 218 });
219 219 WidgetManager.register_widget_view('IntTextView', IntTextView);
220 220 });
@@ -1,372 +1,372
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // SelectionWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(WidgetManager){
18 18
19 19 var DropdownView = IPython.DOMWidgetView.extend({
20 20 render : function(){
21 21 // Called when view is rendered.
22 22 this.$el
23 23 .addClass('widget-hbox-single');
24 24 this.$label = $('<div />')
25 25 .appendTo(this.$el)
26 26 .addClass('widget-hlabel')
27 27 .hide();
28 28 this.$buttongroup = $('<div />')
29 29 .addClass('widget_item')
30 30 .addClass('btn-group')
31 31 .appendTo(this.$el);
32 32 this.$el_to_style = this.$buttongroup; // Set default element to style
33 33 this.$droplabel = $('<button />')
34 34 .addClass('btn')
35 35 .addClass('widget-combo-btn')
36 36 .text(' ')
37 37 .appendTo(this.$buttongroup);
38 38 this.$dropbutton = $('<button />')
39 39 .addClass('btn')
40 40 .addClass('dropdown-toggle')
41 41 .addClass('widget-combo-carrot-btn')
42 42 .attr('data-toggle', 'dropdown')
43 43 .append($('<span />').addClass("caret"))
44 44 .appendTo(this.$buttongroup);
45 45 this.$droplist = $('<ul />')
46 46 .addClass('dropdown-menu')
47 47 .appendTo(this.$buttongroup);
48 48
49 49 // Set defaults.
50 50 this.update();
51 51 },
52 52
53 53 update : function(options){
54 54 // Update the contents of this view
55 55 //
56 56 // Called when the model is changed. The model may have been
57 57 // changed by another view or by a state update from the back-end.
58 58
59 59 if (options === undefined || options.updated_view != this) {
60 60 var selected_item_text = this.model.get('value');
61 61 if (selected_item_text.length === 0) {
62 62 this.$droplabel.text(' ');
63 63 } else {
64 64 this.$droplabel.text(selected_item_text);
65 65 }
66 66
67 67 var items = this.model.get('values');
68 68 var $replace_droplist = $('<ul />')
69 69 .addClass('dropdown-menu');
70 for (var index in items) {
71 var that = this;
70 _.each(items, function(item, i) {
72 71 var item_button = $('<a href="#"/>')
73 .text(items[index])
72 .text(item)
74 73 .on('click', $.proxy(this.handle_click, this));
75 74 $replace_droplist.append($('<li />').append(item_button));
76 }
75 });
76
77 77 this.$droplist.replaceWith($replace_droplist);
78 78 this.$droplist.remove();
79 79 this.$droplist = $replace_droplist;
80 80
81 81 if (this.model.get('disabled')) {
82 82 this.$buttongroup.attr('disabled','disabled');
83 83 this.$droplabel.attr('disabled','disabled');
84 84 this.$dropbutton.attr('disabled','disabled');
85 85 this.$droplist.attr('disabled','disabled');
86 86 } else {
87 87 this.$buttongroup.removeAttr('disabled');
88 88 this.$droplabel.removeAttr('disabled');
89 89 this.$dropbutton.removeAttr('disabled');
90 90 this.$droplist.removeAttr('disabled');
91 91 }
92 92
93 93 var description = this.model.get('description');
94 94 if (description.length === 0) {
95 95 this.$label.hide();
96 96 } else {
97 97 this.$label.text(description);
98 98 this.$label.show();
99 99 }
100 100 }
101 101 return DropdownView.__super__.update.apply(this);
102 102 },
103 103
104 104 handle_click: function (e) {
105 105 // Handle when a value is clicked.
106 106
107 107 // Calling model.set will trigger all of the other views of the
108 108 // model to update.
109 109 this.model.set('value', $(e.target).text(), {updated_view: this});
110 110 this.touch();
111 111 },
112 112
113 113 });
114 114 WidgetManager.register_widget_view('DropdownView', DropdownView);
115 115
116 116
117 117 var RadioButtonsView = IPython.DOMWidgetView.extend({
118 118 render : function(){
119 119 // Called when view is rendered.
120 120 this.$el
121 121 .addClass('widget-hbox');
122 122 this.$label = $('<div />')
123 123 .appendTo(this.$el)
124 124 .addClass('widget-hlabel')
125 125 .hide();
126 126 this.$container = $('<div />')
127 127 .appendTo(this.$el)
128 128 .addClass('widget-container')
129 129 .addClass('vbox');
130 130 this.$el_to_style = this.$container; // Set default element to style
131 131 this.update();
132 132 },
133 133
134 134 update : function(options){
135 135 // Update the contents of this view
136 136 //
137 137 // Called when the model is changed. The model may have been
138 138 // changed by another view or by a state update from the back-end.
139 139 if (options === undefined || options.updated_view != this) {
140 140 // Add missing items to the DOM.
141 141 var items = this.model.get('values');
142 142 var disabled = this.model.get('disabled');
143 for (var index in items) {
144 var item_query = ' :input[value="' + items[index] + '"]';
143 _.each(items, function(item, index) {
144 var item_query = ' :input[value="' + item + '"]';
145 145 if (this.$el.find(item_query).length === 0) {
146 146 var $label = $('<label />')
147 147 .addClass('radio')
148 .text(items[index])
148 .text(item)
149 149 .appendTo(this.$container);
150 150
151 151 $('<input />')
152 152 .attr('type', 'radio')
153 153 .addClass(this.model)
154 .val(items[index])
154 .val(item)
155 155 .prependTo($label)
156 156 .on('click', $.proxy(this.handle_click, this));
157 157 }
158 158
159 159 var $item_element = this.$container.find(item_query);
160 if (this.model.get('value') == items[index]) {
160 if (this.model.get('value') == item) {
161 161 $item_element.prop('checked', true);
162 162 } else {
163 163 $item_element.prop('checked', false);
164 164 }
165 165 $item_element.prop('disabled', disabled);
166 }
166 });
167 167
168 168 // Remove items that no longer exist.
169 169 this.$container.find('input').each(function(i, obj) {
170 170 var value = $(obj).val();
171 171 var found = false;
172 for (var index in items) {
173 if (items[index] == value) {
172 _.each(items, function(item, index) {
173 if (item == value) {
174 174 found = true;
175 175 break;
176 176 }
177 }
177 });
178 178
179 179 if (!found) {
180 180 $(obj).parent().remove();
181 181 }
182 182 });
183 183
184 184 var description = this.model.get('description');
185 185 if (description.length === 0) {
186 186 this.$label.hide();
187 187 } else {
188 188 this.$label.text(description);
189 189 this.$label.show();
190 190 }
191 191 }
192 192 return RadioButtonsView.__super__.update.apply(this);
193 193 },
194 194
195 195 handle_click: function (e) {
196 196 // Handle when a value is clicked.
197 197
198 198 // Calling model.set will trigger all of the other views of the
199 199 // model to update.
200 200 this.model.set('value', $(e.target).val(), {updated_view: this});
201 201 this.touch();
202 202 },
203 203 });
204 204 WidgetManager.register_widget_view('RadioButtonsView', RadioButtonsView);
205 205
206 206
207 207 var ToggleButtonsView = IPython.DOMWidgetView.extend({
208 208 render : function(){
209 209 // Called when view is rendered.
210 210 this.$el
211 211 .addClass('widget-hbox-single');
212 212 this.$label = $('<div />')
213 213 .appendTo(this.$el)
214 214 .addClass('widget-hlabel')
215 215 .hide();
216 216 this.$buttongroup = $('<div />')
217 217 .addClass('btn-group')
218 218 .attr('data-toggle', 'buttons-radio')
219 219 .appendTo(this.$el);
220 220 this.$el_to_style = this.$buttongroup; // Set default element to style
221 221 this.update();
222 222 },
223 223
224 224 update : function(options){
225 225 // Update the contents of this view
226 226 //
227 227 // Called when the model is changed. The model may have been
228 228 // changed by another view or by a state update from the back-end.
229 229 if (options === undefined || options.updated_view != this) {
230 230 // Add missing items to the DOM.
231 231 var items = this.model.get('values');
232 232 var disabled = this.model.get('disabled');
233 for (var index in items) {
234 var item_query = ' :contains("' + items[index] + '")';
233 _.each(items, function(item, index) {
234 var item_query = ' :contains("' + item + '")';
235 235 if (this.$buttongroup.find(item_query).length === 0) {
236 236 $('<button />')
237 237 .attr('type', 'button')
238 238 .addClass('btn')
239 .text(items[index])
239 .text(item)
240 240 .appendTo(this.$buttongroup)
241 241 .on('click', $.proxy(this.handle_click, this));
242 242 }
243 243
244 244 var $item_element = this.$buttongroup.find(item_query);
245 if (this.model.get('value') == items[index]) {
245 if (this.model.get('value') == item) {
246 246 $item_element.addClass('active');
247 247 } else {
248 248 $item_element.removeClass('active');
249 249 }
250 $item_element.prop('disabled', disabled);
251 }
250 $item_element.prop('disabled', disabled);
251 });
252 252
253 253 // Remove items that no longer exist.
254 254 this.$buttongroup.find('button').each(function(i, obj) {
255 255 var value = $(obj).text();
256 256 var found = false;
257 for (var index in items) {
258 if (items[index] == value) {
257 _.each(items, function(item, index) {
258 if (item == value) {
259 259 found = true;
260 260 break;
261 261 }
262 }
263
262 });
263
264 264 if (!found) {
265 265 $(obj).remove();
266 266 }
267 267 });
268 268
269 269 var description = this.model.get('description');
270 270 if (description.length === 0) {
271 271 this.$label.hide();
272 272 } else {
273 273 this.$label.text(description);
274 274 this.$label.show();
275 275 }
276 276 }
277 277 return ToggleButtonsView.__super__.update.apply(this);
278 278 },
279 279
280 280 handle_click: function (e) {
281 281 // Handle when a value is clicked.
282 282
283 283 // Calling model.set will trigger all of the other views of the
284 284 // model to update.
285 285 this.model.set('value', $(e.target).text(), {updated_view: this});
286 286 this.touch();
287 287 },
288 288 });
289 289 WidgetManager.register_widget_view('ToggleButtonsView', ToggleButtonsView);
290 290
291 291
292 292 var ListBoxView = IPython.DOMWidgetView.extend({
293 293 render : function(){
294 294 // Called when view is rendered.
295 295 this.$el
296 296 .addClass('widget-hbox');
297 297 this.$label = $('<div />')
298 298 .appendTo(this.$el)
299 299 .addClass('widget-hlabel')
300 300 .hide();
301 301 this.$listbox = $('<select />')
302 302 .addClass('widget-listbox')
303 303 .attr('size', 6)
304 304 .appendTo(this.$el);
305 305 this.$el_to_style = this.$listbox; // Set default element to style
306 306 this.update();
307 307 },
308 308
309 309 update : function(options){
310 310 // Update the contents of this view
311 311 //
312 312 // Called when the model is changed. The model may have been
313 313 // changed by another view or by a state update from the back-end.
314 314 if (options === undefined || options.updated_view != this) {
315 315 // Add missing items to the DOM.
316 316 var items = this.model.get('values');
317 for (var index in items) {
318 var item_query = ' :contains("' + items[index] + '")';
317 _.each(items, function(item, index) {
318 var item_query = ' :contains("' + item + '")';
319 319 if (this.$listbox.find(item_query).length === 0) {
320 320 $('<option />')
321 .text(items[index])
322 .attr('value', items[index])
321 .text(item)
322 .attr('value', item)
323 323 .appendTo(this.$listbox)
324 324 .on('click', $.proxy(this.handle_click, this));
325 }
326 }
325 }
326 });
327 327
328 328 // Select the correct element
329 329 this.$listbox.val(this.model.get('value'));
330 330
331 331 // Disable listbox if needed
332 332 var disabled = this.model.get('disabled');
333 333 this.$listbox.prop('disabled', disabled);
334 334
335 335 // Remove items that no longer exist.
336 336 this.$listbox.find('option').each(function(i, obj) {
337 337 var value = $(obj).text();
338 338 var found = false;
339 for (var index in items) {
340 if (items[index] == value) {
339 _.each(items, function(item, index) {
340 if (item == value) {
341 341 found = true;
342 342 break;
343 343 }
344 }
344 });
345 345
346 346 if (!found) {
347 347 $(obj).remove();
348 348 }
349 349 });
350 350
351 351 var description = this.model.get('description');
352 352 if (description.length === 0) {
353 353 this.$label.hide();
354 354 } else {
355 355 this.$label.text(description);
356 356 this.$label.show();
357 357 }
358 358 }
359 359 return ListBoxView.__super__.update.apply(this);
360 360 },
361 361
362 362 handle_click: function (e) {
363 363 // Handle when a value is clicked.
364 364
365 365 // Calling model.set will trigger all of the other views of the
366 366 // model to update.
367 367 this.model.set('value', $(e.target).text(), {updated_view: this});
368 368 this.touch();
369 369 },
370 370 });
371 371 WidgetManager.register_widget_view('ListBoxView', ListBoxView);
372 372 });
@@ -1,244 +1,242
1 1 //----------------------------------------------------------------------------
2 2 // Copyright (C) 2013 The IPython Development Team
3 3 //
4 4 // Distributed under the terms of the BSD License. The full license is in
5 5 // the file COPYING, distributed as part of this software.
6 6 //----------------------------------------------------------------------------
7 7
8 8 //============================================================================
9 9 // SelectionContainerWidget
10 10 //============================================================================
11 11
12 12 /**
13 13 * @module IPython
14 14 * @namespace IPython
15 15 **/
16 16
17 17 define(["notebook/js/widgets/widget"], function(WidgetManager){
18 18
19 19 var AccordionView = IPython.DOMWidgetView.extend({
20 20 render: function(){
21 21 // Called when view is rendered.
22 22 var guid = 'accordion' + IPython.utils.uuid();
23 23 this.$el
24 24 .attr('id', guid)
25 25 .addClass('accordion');
26 26 this.containers = [];
27 27 this.model_containers = {};
28 28 this.update_children([], this.model.get('_children'));
29 29 this.model.on('change:_children', function(model, value, options) {
30 30 this.update_children(model.previous('_children'), value);
31 31 }, this);
32 32 },
33 33
34 34 update: function(options) {
35 35 // Update the contents of this view
36 36 //
37 37 // Called when the model is changed. The model may have been
38 38 // changed by another view or by a state update from the back-end.
39 39 if (options === undefined || options.updated_view != this) {
40 40 // Set tab titles
41 41 var titles = this.model.get('_titles');
42 for (var page_index in titles) {
43
42 _.each(titles, function(title, page_index) {
44 43 var accordian = this.containers[page_index];
45 44 if (accordian !== undefined) {
46 45 accordian
47 46 .find('.accordion-heading')
48 47 .find('.accordion-toggle')
49 .text(titles[page_index]);
48 .text(title);
50 49 }
51 }
50 });
52 51
53 52 // Set selected page
54 53 var selected_index = this.model.get("selected_index");
55 54 if (0 <= selected_index && selected_index < this.containers.length) {
56 for (var index in this.containers) {
55 _.each(this.containers, function(container, index) {
57 56 if (index==selected_index) {
58 this.containers[index].find('.accordion-body').collapse('show');
57 container.find('.accordion-body').collapse('show');
59 58 } else {
60 this.containers[index].find('.accordion-body').collapse('hide');
59 container.find('.accordion-body').collapse('hide');
61 60 }
62
63 }
61 });
64 62 }
65 63 }
66 64 return AccordionView.__super__.update.apply(this);
67 65 },
68 66
69 67 update_children: function(old_list, new_list) {
70 68 // Called when the children list is modified.
71 69 this.do_diff(old_list,
72 70 new_list,
73 71 $.proxy(this.remove_child_model, this),
74 72 $.proxy(this.add_child_model, this));
75 73 },
76 74
77 75 remove_child_model: function(model) {
78 76 // Called when a child is removed from children list.
79 77 var accordion_group = this.model_containers[model.id];
80 78 this.containers.splice(accordion_group.container_index, 1);
81 79 delete this.model_containers[model.id];
82 80 accordion_group.remove();
83 81 this.delete_child_view(model);
84 82 },
85 83
86 84 add_child_model: function(model) {
87 85 // Called when a child is added to children list.
88 86 var view = this.create_child_view(model);
89 87 var index = this.containers.length;
90 88 var uuid = IPython.utils.uuid();
91 89 var accordion_group = $('<div />')
92 90 .addClass('accordion-group')
93 91 .appendTo(this.$el);
94 92 var accordion_heading = $('<div />')
95 93 .addClass('accordion-heading')
96 94 .appendTo(accordion_group);
97 95 var that = this;
98 96 var accordion_toggle = $('<a />')
99 97 .addClass('accordion-toggle')
100 98 .attr('data-toggle', 'collapse')
101 99 .attr('data-parent', '#' + this.$el.attr('id'))
102 100 .attr('href', '#' + uuid)
103 101 .click(function(evt){
104 102
105 103 // Calling model.set will trigger all of the other views of the
106 104 // model to update.
107 105 that.model.set("selected_index", index, {updated_view: this});
108 106 that.touch();
109 107 })
110 108 .text('Page ' + index)
111 109 .appendTo(accordion_heading);
112 110 var accordion_body = $('<div />', {id: uuid})
113 111 .addClass('accordion-body collapse')
114 112 .appendTo(accordion_group);
115 113 var accordion_inner = $('<div />')
116 114 .addClass('accordion-inner')
117 115 .appendTo(accordion_body);
118 116 var container_index = this.containers.push(accordion_group) - 1;
119 117 accordion_group.container_index = container_index;
120 118 this.model_containers[model.id] = accordion_group;
121 119 accordion_inner.append(view.$el);
122 120
123 121 this.update();
124 122
125 123 // Stupid workaround to close the bootstrap accordion tabs which
126 124 // open by default even though they don't have the `in` class
127 125 // attached to them. For some reason a delay is required.
128 126 // TODO: Better fix.
129 127 setTimeout(function(){ that.update(); }, 500);
130 128 },
131 129 });
132 130 WidgetManager.register_widget_view('AccordionView', AccordionView);
133 131
134 132
135 133 var TabView = IPython.DOMWidgetView.extend({
136 134 initialize: function() {
137 135 // Public constructor.
138 136 this.containers = [];
139 137 TabView.__super__.initialize.apply(this, arguments);
140 138 },
141 139
142 140 render: function(){
143 141 // Called when view is rendered.
144 142 var uuid = 'tabs'+IPython.utils.uuid();
145 143 var that = this;
146 144 this.$tabs = $('<div />', {id: uuid})
147 145 .addClass('nav')
148 146 .addClass('nav-tabs')
149 147 .appendTo(this.$el);
150 148 this.$tab_contents = $('<div />', {id: uuid + 'Content'})
151 149 .addClass('tab-content')
152 150 .appendTo(this.$el);
153 151 this.containers = [];
154 152 this.update_children([], this.model.get('_children'));
155 153 this.model.on('change:_children', function(model, value, options) {
156 154 this.update_children(model.previous('_children'), value);
157 155 }, this);
158 156 },
159 157
160 158 update_children: function(old_list, new_list) {
161 159 // Called when the children list is modified.
162 160 this.do_diff(old_list,
163 161 new_list,
164 162 $.proxy(this.remove_child_model, this),
165 163 $.proxy(this.add_child_model, this));
166 164 },
167 165
168 166 remove_child_model: function(model) {
169 167 // Called when a child is removed from children list.
170 168 var view = this.child_views[model.id];
171 169 this.containers.splice(view.parent_tab.tab_text_index, 1);
172 170 view.parent_tab.remove();
173 171 view.parent_container.remove();
174 172 view.remove();
175 173 this.delete_child_view(model);
176 174 },
177 175
178 176 add_child_model: function(model) {
179 177 // Called when a child is added to children list.
180 178 var view = this.create_child_view(model);
181 179 var index = this.containers.length;
182 180 var uuid = IPython.utils.uuid();
183 181
184 182 var that = this;
185 183 var tab = $('<li />')
186 184 .css('list-style-type', 'none')
187 185 .appendTo(this.$tabs);
188 186 view.parent_tab = tab;
189 187
190 188 var tab_text = $('<a />')
191 189 .attr('href', '#' + uuid)
192 190 .attr('data-toggle', 'tab')
193 191 .text('Page ' + index)
194 192 .appendTo(tab)
195 193 .click(function (e) {
196 194
197 195 // Calling model.set will trigger all of the other views of the
198 196 // model to update.
199 197 that.model.set("selected_index", index, {updated_view: this});
200 198 that.touch();
201 199 that.select_page(index);
202 200 });
203 201 tab.tab_text_index = this.containers.push(tab_text) - 1;
204 202
205 203 var contents_div = $('<div />', {id: uuid})
206 204 .addClass('tab-pane')
207 205 .addClass('fade')
208 206 .append(view.$el)
209 207 .appendTo(this.$tab_contents);
210 208 view.parent_container = contents_div;
211 209 },
212 210
213 211 update: function(options) {
214 212 // Update the contents of this view
215 213 //
216 214 // Called when the model is changed. The model may have been
217 215 // changed by another view or by a state update from the back-end.
218 216 if (options === undefined || options.updated_view != this) {
219 217 // Set tab titles
220 218 var titles = this.model.get('_titles');
221 for (var page_index in titles) {
222 var tab_text = this.containers[page_index];
219 _.each(titles, function(title, page_index) {
220 var tab_text = this.containers[page_index];
223 221 if (tab_text !== undefined) {
224 tab_text.text(titles[page_index]);
225 }
226 }
222 tab_text.text(title);
223 }
224 });
227 225
228 226 var selected_index = this.model.get('selected_index');
229 227 if (0 <= selected_index && selected_index < this.containers.length) {
230 228 this.select_page(selected_index);
231 229 }
232 230 }
233 231 return TabView.__super__.update.apply(this);
234 232 },
235 233
236 234 select_page: function(index) {
237 235 // Select a page.
238 236 this.$tabs.find('li')
239 237 .removeClass('active');
240 238 this.containers[index].tab('show');
241 239 },
242 240 });
243 241 WidgetManager.register_widget_view('TabView', TabView);
244 242 });
General Comments 0
You need to be logged in to leave comments. Login now