##// END OF EJS Templates
Ignore the event object for handleTextChange
Gordon Ball -
Show More
@@ -1,413 +1,413
1 1 // Copyright (c) IPython Development Team.
2 2 // Distributed under the terms of the Modified BSD License.
3 3
4 4 define([
5 5 "widgets/js/widget",
6 6 "jqueryui",
7 7 "bootstrap",
8 8 ], function(widget, $){
9 9
10 10 var IntSliderView = widget.DOMWidgetView.extend({
11 11 render : function(){
12 12 // Called when view is rendered.
13 13 this.$el
14 14 .addClass('widget-hbox');
15 15 this.$label = $('<div />')
16 16 .appendTo(this.$el)
17 17 .addClass('widget-label')
18 18 .hide();
19 19
20 20 this.$slider = $('<div />')
21 21 .slider({})
22 22 .addClass('slider');
23 23 // Put the slider in a container
24 24 this.$slider_container = $('<div />')
25 25 .addClass('widget-hslider')
26 26 .append(this.$slider);
27 27 this.$el.append(this.$slider_container);
28 28
29 29 this.$readout = $('<div/>')
30 30 .appendTo(this.$el)
31 31 .addClass('widget-readout')
32 32 .attr('contentEditable', true)
33 33 .hide();
34 34
35 35 this.model.on('change:slider_color', function(sender, value) {
36 36 this.$slider.find('a').css('background', value);
37 37 }, this);
38 38 this.$slider.find('a').css('background', this.model.get('slider_color'));
39 39
40 40 // Set defaults.
41 41 this.update();
42 42 },
43 43
44 44 update_attr: function(name, value) {
45 45 // Set a css attr of the widget view.
46 46 if (name == 'color') {
47 47 this.$readout.css(name, value);
48 48 } else if (name.substring(0, 4) == 'font') {
49 49 this.$readout.css(name, value);
50 50 } else if (name.substring(0, 6) == 'border') {
51 51 this.$slider.find('a').css(name, value);
52 52 this.$slider_container.css(name, value);
53 53 } else if (name == 'width' || name == 'height' || name == 'background') {
54 54 this.$slider_container.css(name, value);
55 55 } else {
56 56 this.$slider.css(name, value);
57 57 }
58 58 },
59 59
60 60 update : function(options){
61 61 // Update the contents of this view
62 62 //
63 63 // Called when the model is changed. The model may have been
64 64 // changed by another view or by a state update from the back-end.
65 65 if (options === undefined || options.updated_view != this) {
66 66 // JQuery slider option keys. These keys happen to have a
67 67 // one-to-one mapping with the corrosponding keys of the model.
68 68 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
69 69 var that = this;
70 70 that.$slider.slider({});
71 71 _.each(jquery_slider_keys, function(key, i) {
72 72 var model_value = that.model.get(key);
73 73 if (model_value !== undefined) {
74 74 that.$slider.slider("option", key, model_value);
75 75 }
76 76 });
77 77 var range_value = this.model.get("_range");
78 78 if (range_value !== undefined) {
79 79 this.$slider.slider("option", "range", range_value);
80 80 }
81 81
82 82 // WORKAROUND FOR JQUERY SLIDER BUG.
83 83 // The horizontal position of the slider handle
84 84 // depends on the value of the slider at the time
85 85 // of orientation change. Before applying the new
86 86 // workaround, we set the value to the minimum to
87 87 // make sure that the horizontal placement of the
88 88 // handle in the vertical slider is always
89 89 // consistent.
90 90 var orientation = this.model.get('orientation');
91 91 var min = this.model.get('min');
92 92 var max = this.model.get('max');
93 93 if (this.model.get('_range')) {
94 94 this.$slider.slider('option', 'values', [min, min]);
95 95 } else {
96 96 this.$slider.slider('option', 'value', min);
97 97 }
98 98 this.$slider.slider('option', 'orientation', orientation);
99 99 var value = this.model.get('value');
100 100 if (this.model.get('_range')) {
101 101 // values for the range case are validated python-side in
102 102 // _Bounded{Int,Float}RangeWidget._validate
103 103 this.$slider.slider('option', 'values', value);
104 104 this.$readout.text(value.join("-"));
105 105 } else {
106 106 if(value > max) {
107 107 value = max;
108 108 }
109 109 else if(value < min){
110 110 value = min;
111 111 }
112 112 this.$slider.slider('option', 'value', value);
113 113 this.$readout.text(value);
114 114 }
115 115
116 116 if(this.model.get('value')!=value) {
117 117 this.model.set('value', value, {updated_view: this});
118 118 this.touch();
119 119 }
120 120
121 121 // Use the right CSS classes for vertical & horizontal sliders
122 122 if (orientation=='vertical') {
123 123 this.$slider_container
124 124 .removeClass('widget-hslider')
125 125 .addClass('widget-vslider');
126 126 this.$el
127 127 .removeClass('widget-hbox')
128 128 .addClass('widget-vbox');
129 129
130 130 } else {
131 131 this.$slider_container
132 132 .removeClass('widget-vslider')
133 133 .addClass('widget-hslider');
134 134 this.$el
135 135 .removeClass('widget-vbox')
136 136 .addClass('widget-hbox');
137 137 }
138 138
139 139 var description = this.model.get('description');
140 140 if (description.length === 0) {
141 141 this.$label.hide();
142 142 } else {
143 143 this.$label.text(description);
144 144 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
145 145 this.$label.show();
146 146 }
147 147
148 148 var readout = this.model.get('readout');
149 149 if (readout) {
150 150 this.$readout.show();
151 151 } else {
152 152 this.$readout.hide();
153 153 }
154 154 }
155 155 return IntSliderView.__super__.update.apply(this);
156 156 },
157 157
158 158 events: {
159 159 // Dictionary of events and their handlers.
160 160 "slide" : "handleSliderChange",
161 161 "blur [contentEditable=true]": "handleTextChange",
162 162 "keydown [contentEditable=true]": "handleKeyDown"
163 163 },
164 164
165 165 handleKeyDown: function(e) {
166 166 if (e.keyCode == 13) {
167 167 e.preventDefault();
168 this.handleTextChange(e);
168 this.handleTextChange();
169 169 }
170 170 },
171 171
172 handleTextChange: function(e) {
173 var text = $(e.target).text().trim();
172 handleTextChange: function() {
173 var text = this.$readout.text();
174 174 var value = this._validate_text_input(text);
175 175 if (isNaN(value)) {
176 176 this.$readout.text(this.model.get('value'));
177 177 } else {
178 178 //check for outside range
179 179 if (value > this.model.get('max')) value = this.model.get('max');
180 180 if (value < this.model.get('min')) value = this.model.get('min');
181 181
182 182 //update the readout unconditionally
183 183 //this covers eg, entering a float value which rounds to the
184 184 //existing int value, which will not trigger an update since the model
185 185 //doesn't change, but we should update the text to reflect that
186 186 //a float value isn't being used
187 187 this.$readout.text(value);
188 188
189 189 //note that the step size currently isn't enforced, so if an
190 190 //off-step value is input it will be retained
191 191
192 192 //update the model
193 193 this.model.set('value', value, {updated_view: this});
194 194 this.touch();
195 195 }
196 196 },
197 197
198 198 _validate_text_input: function(x) {
199 199 return parseInt(x);
200 200 },
201 201
202 202 handleSliderChange: function(e, ui) {
203 203 // Called when the slider value is changed.
204 204
205 205 // Calling model.set will trigger all of the other views of the
206 206 // model to update.
207 207 if (this.model.get("_range")) {
208 208 var actual_value = ui.values.map(this._validate_slide_value);
209 209 this.$readout.text(actual_value.join("-"));
210 210 } else {
211 211 var actual_value = this._validate_slide_value(ui.value);
212 212 this.$readout.text(actual_value);
213 213 }
214 214 this.model.set('value', actual_value, {updated_view: this});
215 215 this.touch();
216 216 },
217 217
218 218 _validate_slide_value: function(x) {
219 219 // Validate the value of the slider before sending it to the back-end
220 220 // and applying it to the other views on the page.
221 221
222 222 // Double bit-wise not truncates the decimel (int cast).
223 223 return ~~x;
224 224 },
225 225 });
226 226
227 227
228 228 var IntTextView = widget.DOMWidgetView.extend({
229 229 render : function(){
230 230 // Called when view is rendered.
231 231 this.$el
232 232 .addClass('widget-hbox');
233 233 this.$label = $('<div />')
234 234 .appendTo(this.$el)
235 235 .addClass('widget-label')
236 236 .hide();
237 237 this.$textbox = $('<input type="text" />')
238 238 .addClass('form-control')
239 239 .addClass('widget-numeric-text')
240 240 .appendTo(this.$el);
241 241 this.update(); // Set defaults.
242 242 },
243 243
244 244 update : function(options){
245 245 // Update the contents of this view
246 246 //
247 247 // Called when the model is changed. The model may have been
248 248 // changed by another view or by a state update from the back-end.
249 249 if (options === undefined || options.updated_view != this) {
250 250 var value = this.model.get('value');
251 251 if (this._parse_value(this.$textbox.val()) != value) {
252 252 this.$textbox.val(value);
253 253 }
254 254
255 255 if (this.model.get('disabled')) {
256 256 this.$textbox.attr('disabled','disabled');
257 257 } else {
258 258 this.$textbox.removeAttr('disabled');
259 259 }
260 260
261 261 var description = this.model.get('description');
262 262 if (description.length === 0) {
263 263 this.$label.hide();
264 264 } else {
265 265 this.$label.text(description);
266 266 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
267 267 this.$label.show();
268 268 }
269 269 }
270 270 return IntTextView.__super__.update.apply(this);
271 271 },
272 272
273 273 update_attr: function(name, value) {
274 274 // Set a css attr of the widget view.
275 275 this.$textbox.css(name, value);
276 276 },
277 277
278 278 events: {
279 279 // Dictionary of events and their handlers.
280 280 "keyup input" : "handleChanging",
281 281 "paste input" : "handleChanging",
282 282 "cut input" : "handleChanging",
283 283
284 284 // Fires only when control is validated or looses focus.
285 285 "change input" : "handleChanged"
286 286 },
287 287
288 288 handleChanging: function(e) {
289 289 // Handles and validates user input.
290 290
291 291 // Try to parse value as a int.
292 292 var numericalValue = 0;
293 293 if (e.target.value !== '') {
294 294 var trimmed = e.target.value.trim();
295 295 if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
296 296 numericalValue = this._parse_value(e.target.value);
297 297 }
298 298 }
299 299
300 300 // If parse failed, reset value to value stored in model.
301 301 if (isNaN(numericalValue)) {
302 302 e.target.value = this.model.get('value');
303 303 } else if (!isNaN(numericalValue)) {
304 304 if (this.model.get('max') !== undefined) {
305 305 numericalValue = Math.min(this.model.get('max'), numericalValue);
306 306 }
307 307 if (this.model.get('min') !== undefined) {
308 308 numericalValue = Math.max(this.model.get('min'), numericalValue);
309 309 }
310 310
311 311 // Apply the value if it has changed.
312 312 if (numericalValue != this.model.get('value')) {
313 313
314 314 // Calling model.set will trigger all of the other views of the
315 315 // model to update.
316 316 this.model.set('value', numericalValue, {updated_view: this});
317 317 this.touch();
318 318 }
319 319 }
320 320 },
321 321
322 322 handleChanged: function(e) {
323 323 // Applies validated input.
324 324 if (this.model.get('value') != e.target.value) {
325 325 e.target.value = this.model.get('value');
326 326 }
327 327 },
328 328
329 329 _parse_value: function(value) {
330 330 // Parse the value stored in a string.
331 331 return parseInt(value);
332 332 },
333 333 });
334 334
335 335
336 336 var ProgressView = widget.DOMWidgetView.extend({
337 337 render : function(){
338 338 // Called when view is rendered.
339 339 this.$el
340 340 .addClass('widget-hbox');
341 341 this.$label = $('<div />')
342 342 .appendTo(this.$el)
343 343 .addClass('widget-label')
344 344 .hide();
345 345 this.$progress = $('<div />')
346 346 .addClass('progress')
347 347 .addClass('widget-progress')
348 348 .appendTo(this.$el);
349 349 this.$bar = $('<div />')
350 350 .addClass('progress-bar')
351 351 .css('width', '50%')
352 352 .appendTo(this.$progress);
353 353 this.update(); // Set defaults.
354 354
355 355 this.model.on('change:bar_style', function(model, value) {
356 356 this.update_bar_style();
357 357 }, this);
358 358 this.update_bar_style('');
359 359 },
360 360
361 361 update : function(){
362 362 // Update the contents of this view
363 363 //
364 364 // Called when the model is changed. The model may have been
365 365 // changed by another view or by a state update from the back-end.
366 366 var value = this.model.get('value');
367 367 var max = this.model.get('max');
368 368 var min = this.model.get('min');
369 369 var percent = 100.0 * (value - min) / (max - min);
370 370 this.$bar.css('width', percent + '%');
371 371
372 372 var description = this.model.get('description');
373 373 if (description.length === 0) {
374 374 this.$label.hide();
375 375 } else {
376 376 this.$label.text(description);
377 377 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
378 378 this.$label.show();
379 379 }
380 380 return ProgressView.__super__.update.apply(this);
381 381 },
382 382
383 383 update_bar_style: function(previous_trait_value) {
384 384 var class_map = {
385 385 success: ['progress-bar-success'],
386 386 info: ['progress-bar-info'],
387 387 warning: ['progress-bar-warning'],
388 388 danger: ['progress-bar-danger']
389 389 };
390 390 this.update_mapped_classes(class_map, 'bar_style', previous_trait_value, this.$bar);
391 391 },
392 392
393 393 update_attr: function(name, value) {
394 394 // Set a css attr of the widget view.
395 395 if (name.substring(0, 6) == 'border' || name == 'width' ||
396 396 name == 'height' || name == 'background' || name == 'margin' ||
397 397 name == 'padding') {
398 398
399 399 this.$progress.css(name, value);
400 400 } else if (name == 'color') {
401 401 this.$bar.css('background', value);
402 402 } else {
403 403 this.$bar.css(name, value);
404 404 }
405 405 },
406 406 });
407 407
408 408 return {
409 409 'IntSliderView': IntSliderView,
410 410 'IntTextView': IntTextView,
411 411 'ProgressView': ProgressView,
412 412 };
413 413 });
General Comments 0
You need to be logged in to leave comments. Login now