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