##// END OF EJS Templates
wrong css class for widget-int
Sylvain Corlay -
Show More
@@ -1,491 +1,491
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 "base/js/keyboard",
8 8 "bootstrap"
9 9 ], function(widget, $, keyboard){
10 10
11 11 var IntSliderView = widget.DOMWidgetView.extend({
12 12 render : function(){
13 13 /**
14 14 * Called when view is rendered.
15 15 */
16 16 this.$el
17 17 .addClass('widget-hbox widget-slider');
18 18 this.$label = $('<div />')
19 19 .appendTo(this.$el)
20 20 .addClass('widget-label')
21 21 .hide();
22 22
23 23 this.$slider = $('<div />')
24 24 .slider({})
25 25 .addClass('slider');
26 26 // Put the slider in a container
27 27 this.$slider_container = $('<div />')
28 28 .addClass('widget-hslider')
29 29 .append(this.$slider);
30 30 this.$el.append(this.$slider_container);
31 31
32 32 this.$readout = $('<div/>')
33 33 .appendTo(this.$el)
34 34 .addClass('widget-readout')
35 35 .attr('contentEditable', true)
36 36 .hide();
37 37
38 38 this.model.on('change:slider_color', function(sender, value) {
39 39 this.$slider.find('a').css('background', value);
40 40 }, this);
41 41 this.$slider.find('a').css('background', this.model.get('slider_color'));
42 42
43 43 // Set defaults.
44 44 this.update();
45 45 },
46 46
47 47 update_attr: function(name, value) {
48 48 /**
49 49 * Set a css attr of the widget view.
50 50 */
51 51 if (name == 'color') {
52 52 this.$readout.css(name, value);
53 53 } else if (name.substring(0, 4) == 'font') {
54 54 this.$readout.css(name, value);
55 55 } else if (name.substring(0, 6) == 'border') {
56 56 this.$slider.find('a').css(name, value);
57 57 this.$slider_container.css(name, value);
58 58 } else if (name == 'width' || name == 'height' || name == 'background') {
59 59 this.$slider_container.css(name, value);
60 60 } else if (name == 'padding' || name == 'margin') {
61 61 this.$el.css(name, value);
62 62 } else {
63 63 this.$slider.css(name, value);
64 64 }
65 65 },
66 66
67 67 update : function(options){
68 68 /**
69 69 * Update the contents of this view
70 70 *
71 71 * Called when the model is changed. The model may have been
72 72 * changed by another view or by a state update from the back-end.
73 73 */
74 74 if (options === undefined || options.updated_view != this) {
75 75 // JQuery slider option keys. These keys happen to have a
76 76 // one-to-one mapping with the corrosponding keys of the model.
77 77 var jquery_slider_keys = ['step', 'disabled'];
78 78 var that = this;
79 79 that.$slider.slider({});
80 80 _.each(jquery_slider_keys, function(key, i) {
81 81 var model_value = that.model.get(key);
82 82 if (model_value !== undefined) {
83 83 that.$slider.slider("option", key, model_value);
84 84 }
85 85 });
86 86
87 87 var max = this.model.get('max');
88 88 var min = this.model.get('min');
89 89 if (min <= max) {
90 90 if (max !== undefined) this.$slider.slider('option', 'max', max);
91 91 if (min !== undefined) this.$slider.slider('option', 'min', min);
92 92 }
93 93
94 94 var range_value = this.model.get("_range");
95 95 if (range_value !== undefined) {
96 96 this.$slider.slider("option", "range", range_value);
97 97 }
98 98
99 99 // WORKAROUND FOR JQUERY SLIDER BUG.
100 100 // The horizontal position of the slider handle
101 101 // depends on the value of the slider at the time
102 102 // of orientation change. Before applying the new
103 103 // workaround, we set the value to the minimum to
104 104 // make sure that the horizontal placement of the
105 105 // handle in the vertical slider is always
106 106 // consistent.
107 107 var orientation = this.model.get('orientation');
108 108 var min = this.model.get('min');
109 109 var max = this.model.get('max');
110 110 if (this.model.get('_range')) {
111 111 this.$slider.slider('option', 'values', [min, min]);
112 112 } else {
113 113 this.$slider.slider('option', 'value', min);
114 114 }
115 115 this.$slider.slider('option', 'orientation', orientation);
116 116 var value = this.model.get('value');
117 117 if (this.model.get('_range')) {
118 118 // values for the range case are validated python-side in
119 119 // _Bounded{Int,Float}RangeWidget._validate
120 120 this.$slider.slider('option', 'values', value);
121 121 this.$readout.text(value.join("-"));
122 122 } else {
123 123 if(value > max) {
124 124 value = max;
125 125 }
126 126 else if(value < min){
127 127 value = min;
128 128 }
129 129 this.$slider.slider('option', 'value', value);
130 130 this.$readout.text(value);
131 131 }
132 132
133 133 if(this.model.get('value')!=value) {
134 134 this.model.set('value', value, {updated_view: this});
135 135 this.touch();
136 136 }
137 137
138 138 // Use the right CSS classes for vertical & horizontal sliders
139 139 if (orientation=='vertical') {
140 140 this.$slider_container
141 141 .removeClass('widget-hslider')
142 142 .addClass('widget-vslider');
143 143 this.$el
144 144 .removeClass('widget-hbox')
145 145 .addClass('widget-vbox');
146 146
147 147 } else {
148 148 this.$slider_container
149 149 .removeClass('widget-vslider')
150 150 .addClass('widget-hslider');
151 151 this.$el
152 152 .removeClass('widget-vbox')
153 153 .addClass('widget-hbox');
154 154 }
155 155
156 156 var description = this.model.get('description');
157 157 if (description.length === 0) {
158 158 this.$label.hide();
159 159 } else {
160 160 this.typeset(this.$label, description);
161 161 this.$label.show();
162 162 }
163 163
164 164 var readout = this.model.get('readout');
165 165 if (readout) {
166 166 this.$readout.show();
167 167 } else {
168 168 this.$readout.hide();
169 169 }
170 170 }
171 171 return IntSliderView.__super__.update.apply(this);
172 172 },
173 173
174 174 events: {
175 175 // Dictionary of events and their handlers.
176 176 "slide" : "handleSliderChange",
177 177 "blur [contentEditable=true]": "handleTextChange",
178 178 "keydown [contentEditable=true]": "handleKeyDown"
179 179 },
180 180
181 181 handleKeyDown: function(e) {
182 182 if (e.keyCode == keyboard.keycodes.enter) {
183 183 e.preventDefault();
184 184 this.handleTextChange();
185 185 }
186 186 },
187 187
188 188 handleTextChange: function() {
189 189 /**
190 190 * this handles the entry of text into the contentEditable label
191 191 * first, the value is checked if it contains a parseable number
192 192 * (or pair of numbers, for the _range case)
193 193 * then it is clamped within the min-max range of the slider
194 194 * finally, the model is updated if the value is to be changed
195 195 *
196 196 * if any of these conditions are not met, the text is reset
197 197 *
198 198 * the step size is not enforced
199 199 */
200 200
201 201 var text = this.$readout.text();
202 202 var vmin = this.model.get('min');
203 203 var vmax = this.model.get('max');
204 204 if (this.model.get("_range")) {
205 205 // range case
206 206 // ranges can be expressed either "val-val" or "val:val" (+spaces)
207 207 var match = this._range_regex.exec(text);
208 208 if (match) {
209 209 var values = [this._parse_value(match[1]),
210 210 this._parse_value(match[2])];
211 211 // reject input where NaN or lower > upper
212 212 if (isNaN(values[0]) ||
213 213 isNaN(values[1]) ||
214 214 (values[0] > values[1])) {
215 215 this.$readout.text(this.model.get('value').join('-'));
216 216 } else {
217 217 // clamp to range
218 218 values = [Math.max(Math.min(values[0], vmax), vmin),
219 219 Math.max(Math.min(values[1], vmax), vmin)];
220 220
221 221 if ((values[0] != this.model.get('value')[0]) ||
222 222 (values[1] != this.model.get('value')[1])) {
223 223 this.$readout.text(values.join('-'));
224 224 this.model.set('value', values, {updated_view: this});
225 225 this.touch();
226 226 } else {
227 227 this.$readout.text(this.model.get('value').join('-'));
228 228 }
229 229 }
230 230 } else {
231 231 this.$readout.text(this.model.get('value').join('-'));
232 232 }
233 233 } else {
234 234 // single value case
235 235 var value = this._parse_value(text);
236 236 if (isNaN(value)) {
237 237 this.$readout.text(this.model.get('value'));
238 238 } else {
239 239 value = Math.max(Math.min(value, vmax), vmin);
240 240
241 241 if (value != this.model.get('value')) {
242 242 this.$readout.text(value);
243 243 this.model.set('value', value, {updated_view: this});
244 244 this.touch();
245 245 } else {
246 246 this.$readout.text(this.model.get('value'));
247 247 }
248 248 }
249 249 }
250 250 },
251 251
252 252 _parse_value: parseInt,
253 253
254 254 _range_regex: /^\s*([+-]?\d+)\s*[-:]\s*([+-]?\d+)/,
255 255
256 256 handleSliderChange: function(e, ui) {
257 257 /**
258 258 * Called when the slider value is changed.
259 259 *
260 260 * Calling model.set will trigger all of the other views of the
261 261 * model to update.
262 262 */
263 263 var actual_value;
264 264 if (this.model.get("_range")) {
265 265 actual_value = ui.values.map(this._validate_slide_value);
266 266 this.$readout.text(actual_value.join("-"));
267 267 } else {
268 268 actual_value = this._validate_slide_value(ui.value);
269 269 this.$readout.text(actual_value);
270 270 }
271 271 this.model.set('value', actual_value, {updated_view: this});
272 272 this.touch();
273 273 },
274 274
275 275 _validate_slide_value: function(x) {
276 276 /**
277 277 * Validate the value of the slider before sending it to the back-end
278 278 * and applying it to the other views on the page.
279 279 *
280 280 * Double bit-wise not truncates the decimel (int cast).
281 281 */
282 282 return ~~x;
283 283 },
284 284 });
285 285
286 286
287 287 var IntTextView = widget.DOMWidgetView.extend({
288 288 render : function(){
289 289 /**
290 290 * Called when view is rendered.
291 291 */
292 292 this.$el
293 .addClass('widget-hbox widget-text');
293 .addClass('widget-hbox widget-numeric-text');
294 294 this.$label = $('<div />')
295 295 .appendTo(this.$el)
296 296 .addClass('widget-label')
297 297 .hide();
298 298 this.$textbox = $('<input type="text" />')
299 299 .addClass('form-control')
300 300 .addClass('widget-numeric-text')
301 301 .appendTo(this.$el);
302 302 this.update(); // Set defaults.
303 303 },
304 304
305 305 update : function(options){
306 306 /**
307 307 * Update the contents of this view
308 308 *
309 309 * Called when the model is changed. The model may have been
310 310 * changed by another view or by a state update from the back-end.
311 311 */
312 312 if (options === undefined || options.updated_view != this) {
313 313 var value = this.model.get('value');
314 314 if (this._parse_value(this.$textbox.val()) != value) {
315 315 this.$textbox.val(value);
316 316 }
317 317
318 318 if (this.model.get('disabled')) {
319 319 this.$textbox.attr('disabled','disabled');
320 320 } else {
321 321 this.$textbox.removeAttr('disabled');
322 322 }
323 323
324 324 var description = this.model.get('description');
325 325 if (description.length === 0) {
326 326 this.$label.hide();
327 327 } else {
328 328 this.typeset(this.$label, description);
329 329 this.$label.show();
330 330 }
331 331 }
332 332 return IntTextView.__super__.update.apply(this);
333 333 },
334 334
335 335 update_attr: function(name, value) {
336 336 /**
337 337 * Set a css attr of the widget view.
338 338 */
339 339 if (name == 'padding' || name == 'margin') {
340 340 this.$el.css(name, value);
341 341 } else {
342 342 this.$textbox.css(name, value);
343 343 }
344 344 },
345 345
346 346 events: {
347 347 // Dictionary of events and their handlers.
348 348 "keyup input" : "handleChanging",
349 349 "paste input" : "handleChanging",
350 350 "cut input" : "handleChanging",
351 351
352 352 // Fires only when control is validated or looses focus.
353 353 "change input" : "handleChanged"
354 354 },
355 355
356 356 handleChanging: function(e) {
357 357 /**
358 358 * Handles and validates user input.
359 359 *
360 360 * Try to parse value as a int.
361 361 */
362 362 var numericalValue = 0;
363 363 var trimmed = e.target.value.trim();
364 364 if (trimmed === '') {
365 365 return;
366 366 } else {
367 367 if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
368 368 numericalValue = this._parse_value(e.target.value);
369 369 }
370 370 }
371 371
372 372 // If parse failed, reset value to value stored in model.
373 373 if (isNaN(numericalValue)) {
374 374 e.target.value = this.model.get('value');
375 375 } else if (!isNaN(numericalValue)) {
376 376 if (this.model.get('max') !== undefined) {
377 377 numericalValue = Math.min(this.model.get('max'), numericalValue);
378 378 }
379 379 if (this.model.get('min') !== undefined) {
380 380 numericalValue = Math.max(this.model.get('min'), numericalValue);
381 381 }
382 382
383 383 // Apply the value if it has changed.
384 384 if (numericalValue != this.model.get('value')) {
385 385
386 386 // Calling model.set will trigger all of the other views of the
387 387 // model to update.
388 388 this.model.set('value', numericalValue, {updated_view: this});
389 389 this.touch();
390 390 }
391 391 }
392 392 },
393 393
394 394 handleChanged: function(e) {
395 395 /**
396 396 * Applies validated input.
397 397 */
398 398 if (e.target.value.trim() === '' || e.target.value !== this.model.get('value')) {
399 399 e.target.value = this.model.get('value');
400 400 }
401 401 },
402 402
403 403 _parse_value: parseInt
404 404 });
405 405
406 406
407 407 var ProgressView = widget.DOMWidgetView.extend({
408 408 render : function(){
409 409 /**
410 410 * Called when view is rendered.
411 411 */
412 412 this.$el
413 413 .addClass('widget-hbox widget-progress');
414 414 this.$label = $('<div />')
415 415 .appendTo(this.$el)
416 416 .addClass('widget-label')
417 417 .hide();
418 418 this.$progress = $('<div />')
419 419 .addClass('progress')
420 420 .addClass('widget-progress')
421 421 .appendTo(this.$el);
422 422 this.$bar = $('<div />')
423 423 .addClass('progress-bar')
424 424 .css('width', '50%')
425 425 .appendTo(this.$progress);
426 426 this.update(); // Set defaults.
427 427
428 428 this.model.on('change:bar_style', function(model, value) {
429 429 this.update_bar_style();
430 430 }, this);
431 431 this.update_bar_style('');
432 432 },
433 433
434 434 update : function(){
435 435 /**
436 436 * Update the contents of this view
437 437 *
438 438 * Called when the model is changed. The model may have been
439 439 * changed by another view or by a state update from the back-end.
440 440 */
441 441 var value = this.model.get('value');
442 442 var max = this.model.get('max');
443 443 var min = this.model.get('min');
444 444 var percent = 100.0 * (value - min) / (max - min);
445 445 this.$bar.css('width', percent + '%');
446 446
447 447 var description = this.model.get('description');
448 448 if (description.length === 0) {
449 449 this.$label.hide();
450 450 } else {
451 451 this.typeset(this.$label, description);
452 452 this.$label.show();
453 453 }
454 454 return ProgressView.__super__.update.apply(this);
455 455 },
456 456
457 457 update_bar_style: function(previous_trait_value) {
458 458 var class_map = {
459 459 success: ['progress-bar-success'],
460 460 info: ['progress-bar-info'],
461 461 warning: ['progress-bar-warning'],
462 462 danger: ['progress-bar-danger']
463 463 };
464 464 this.update_mapped_classes(class_map, 'bar_style', previous_trait_value, this.$bar);
465 465 },
466 466
467 467 update_attr: function(name, value) {
468 468 /**
469 469 * Set a css attr of the widget view.
470 470 */
471 471 if (name.substring(0, 6) == 'border' || name == 'width' ||
472 472 name == 'height' || name == 'background' || name == 'margin' ||
473 473 name == 'padding') {
474 474
475 475 this.$progress.css(name, value);
476 476 } else if (name == 'color') {
477 477 this.$bar.css('background', value);
478 478 } else if (name == 'padding' || name == 'margin') {
479 479 this.$el.css(name, value);
480 480 } else {
481 481 this.$bar.css(name, value);
482 482 }
483 483 },
484 484 });
485 485
486 486 return {
487 487 'IntSliderView': IntSliderView,
488 488 'IntTextView': IntTextView,
489 489 'ProgressView': ProgressView,
490 490 };
491 491 });
General Comments 0
You need to be logged in to leave comments. Login now