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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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(' ') |
|
41 | .html(' ') | |
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, ' '); |
|
63 | selected_item_text = selected_item_text.replace(/ /g, ' '); | |
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(' '); |
|
66 | this.$droplabel.html(' '); | |
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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. |
|
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