##// END OF EJS Templates
Merge pull request #6171 from tarzzz/slider-value-validate...
Jonathan Frederic -
r17680:eb4529c1 merge
parent child Browse files
Show More
@@ -1,298 +1,310 b''
1 // Copyright (c) IPython Development Team.
1 // Copyright (c) IPython Development Team.
2 // Distributed under the terms of the Modified BSD License.
2 // Distributed under the terms of the Modified BSD License.
3
3
4 define([
4 define([
5 "widgets/js/widget",
5 "widgets/js/widget",
6 "jqueryui",
6 "jqueryui",
7 "bootstrap",
7 "bootstrap",
8 ], function(widget, $){
8 ], function(widget, $){
9
9
10 var IntSliderView = widget.DOMWidgetView.extend({
10 var IntSliderView = widget.DOMWidgetView.extend({
11 render : function(){
11 render : function(){
12 // Called when view is rendered.
12 // Called when view is rendered.
13 this.$el
13 this.$el
14 .addClass('widget-hbox-single');
14 .addClass('widget-hbox-single');
15 this.$label = $('<div />')
15 this.$label = $('<div />')
16 .appendTo(this.$el)
16 .appendTo(this.$el)
17 .addClass('widget-hlabel')
17 .addClass('widget-hlabel')
18 .hide();
18 .hide();
19
19
20 this.$slider = $('<div />')
20 this.$slider = $('<div />')
21 .slider({})
21 .slider({})
22 .addClass('slider');
22 .addClass('slider');
23 // Put the slider in a container
23 // Put the slider in a container
24 this.$slider_container = $('<div />')
24 this.$slider_container = $('<div />')
25 .addClass('widget-hslider')
25 .addClass('widget-hslider')
26 .append(this.$slider);
26 .append(this.$slider);
27 this.$el.append(this.$slider_container);
27 this.$el.append(this.$slider_container);
28
28
29 this.$readout = $('<div/>')
29 this.$readout = $('<div/>')
30 .appendTo(this.$el)
30 .appendTo(this.$el)
31 .addClass('widget-hreadout')
31 .addClass('widget-hreadout')
32 .hide();
32 .hide();
33
33
34 // Set defaults.
34 // Set defaults.
35 this.update();
35 this.update();
36 },
36 },
37
37
38 update : function(options){
38 update : function(options){
39 // Update the contents of this view
39 // Update the contents of this view
40 //
40 //
41 // Called when the model is changed. The model may have been
41 // Called when the model is changed. The model may have been
42 // changed by another view or by a state update from the back-end.
42 // changed by another view or by a state update from the back-end.
43 if (options === undefined || options.updated_view != this) {
43 if (options === undefined || options.updated_view != this) {
44 // JQuery slider option keys. These keys happen to have a
44 // JQuery slider option keys. These keys happen to have a
45 // one-to-one mapping with the corrosponding keys of the model.
45 // one-to-one mapping with the corrosponding keys of the model.
46 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
46 var jquery_slider_keys = ['step', 'max', 'min', 'disabled'];
47 var that = this;
47 var that = this;
48 that.$slider.slider({});
48 that.$slider.slider({});
49 _.each(jquery_slider_keys, function(key, i) {
49 _.each(jquery_slider_keys, function(key, i) {
50 var model_value = that.model.get(key);
50 var model_value = that.model.get(key);
51 if (model_value !== undefined) {
51 if (model_value !== undefined) {
52 that.$slider.slider("option", key, model_value);
52 that.$slider.slider("option", key, model_value);
53 }
53 }
54 });
54 });
55
55
56 // WORKAROUND FOR JQUERY SLIDER BUG.
56 // WORKAROUND FOR JQUERY SLIDER BUG.
57 // The horizontal position of the slider handle
57 // The horizontal position of the slider handle
58 // depends on the value of the slider at the time
58 // depends on the value of the slider at the time
59 // of orientation change. Before applying the new
59 // of orientation change. Before applying the new
60 // workaround, we set the value to the minimum to
60 // workaround, we set the value to the minimum to
61 // make sure that the horizontal placement of the
61 // make sure that the horizontal placement of the
62 // handle in the vertical slider is always
62 // handle in the vertical slider is always
63 // consistent.
63 // consistent.
64 var orientation = this.model.get('orientation');
64 var orientation = this.model.get('orientation');
65 var value = this.model.get('min');
65 var min = this.model.get('min');
66 this.$slider.slider('option', 'value', value);
66 var max = this.model.get('max');
67 this.$slider.slider('option', 'value', min);
67 this.$slider.slider('option', 'orientation', orientation);
68 this.$slider.slider('option', 'orientation', orientation);
68 value = this.model.get('value');
69 var value = this.model.get('value');
70 if(value > max) {
71 value = max;
72 }
73 else if(value < min){
74 value = min;
75 }
69 this.$slider.slider('option', 'value', value);
76 this.$slider.slider('option', 'value', value);
70 this.$readout.text(value);
77 this.$readout.text(value);
71
78
79 if(this.model.get('value')!=value) {
80 this.model.set('value', value, {updated_view: this});
81 this.touch();
82 }
83
72 // Use the right CSS classes for vertical & horizontal sliders
84 // Use the right CSS classes for vertical & horizontal sliders
73 if (orientation=='vertical') {
85 if (orientation=='vertical') {
74 this.$slider_container
86 this.$slider_container
75 .removeClass('widget-hslider')
87 .removeClass('widget-hslider')
76 .addClass('widget-vslider');
88 .addClass('widget-vslider');
77 this.$el
89 this.$el
78 .removeClass('widget-hbox-single')
90 .removeClass('widget-hbox-single')
79 .addClass('widget-vbox-single');
91 .addClass('widget-vbox-single');
80 this.$label
92 this.$label
81 .removeClass('widget-hlabel')
93 .removeClass('widget-hlabel')
82 .addClass('widget-vlabel');
94 .addClass('widget-vlabel');
83 this.$readout
95 this.$readout
84 .removeClass('widget-hreadout')
96 .removeClass('widget-hreadout')
85 .addClass('widget-vreadout');
97 .addClass('widget-vreadout');
86
98
87 } else {
99 } else {
88 this.$slider_container
100 this.$slider_container
89 .removeClass('widget-vslider')
101 .removeClass('widget-vslider')
90 .addClass('widget-hslider');
102 .addClass('widget-hslider');
91 this.$el
103 this.$el
92 .removeClass('widget-vbox-single')
104 .removeClass('widget-vbox-single')
93 .addClass('widget-hbox-single');
105 .addClass('widget-hbox-single');
94 this.$label
106 this.$label
95 .removeClass('widget-vlabel')
107 .removeClass('widget-vlabel')
96 .addClass('widget-hlabel');
108 .addClass('widget-hlabel');
97 this.$readout
109 this.$readout
98 .removeClass('widget-vreadout')
110 .removeClass('widget-vreadout')
99 .addClass('widget-hreadout');
111 .addClass('widget-hreadout');
100 }
112 }
101
113
102 var description = this.model.get('description');
114 var description = this.model.get('description');
103 if (description.length === 0) {
115 if (description.length === 0) {
104 this.$label.hide();
116 this.$label.hide();
105 } else {
117 } else {
106 this.$label.text(description);
118 this.$label.text(description);
107 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
119 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
108 this.$label.show();
120 this.$label.show();
109 }
121 }
110
122
111 var readout = this.model.get('readout');
123 var readout = this.model.get('readout');
112 if (readout) {
124 if (readout) {
113 this.$readout.show();
125 this.$readout.show();
114 } else {
126 } else {
115 this.$readout.hide();
127 this.$readout.hide();
116 }
128 }
117 }
129 }
118 return IntSliderView.__super__.update.apply(this);
130 return IntSliderView.__super__.update.apply(this);
119 },
131 },
120
132
121 events: {
133 events: {
122 // Dictionary of events and their handlers.
134 // Dictionary of events and their handlers.
123 "slide" : "handleSliderChange"
135 "slide" : "handleSliderChange"
124 },
136 },
125
137
126 handleSliderChange: function(e, ui) {
138 handleSliderChange: function(e, ui) {
127 // Called when the slider value is changed.
139 // Called when the slider value is changed.
128
140
129 // Calling model.set will trigger all of the other views of the
141 // Calling model.set will trigger all of the other views of the
130 // model to update.
142 // model to update.
131 var actual_value = this._validate_slide_value(ui.value);
143 var actual_value = this._validate_slide_value(ui.value);
132 this.model.set('value', actual_value, {updated_view: this});
144 this.model.set('value', actual_value, {updated_view: this});
133 this.$readout.text(actual_value);
145 this.$readout.text(actual_value);
134 this.touch();
146 this.touch();
135 },
147 },
136
148
137 _validate_slide_value: function(x) {
149 _validate_slide_value: function(x) {
138 // Validate the value of the slider before sending it to the back-end
150 // Validate the value of the slider before sending it to the back-end
139 // and applying it to the other views on the page.
151 // and applying it to the other views on the page.
140
152
141 // Double bit-wise not truncates the decimel (int cast).
153 // Double bit-wise not truncates the decimel (int cast).
142 return ~~x;
154 return ~~x;
143 },
155 },
144 });
156 });
145
157
146
158
147 var IntTextView = widget.DOMWidgetView.extend({
159 var IntTextView = widget.DOMWidgetView.extend({
148 render : function(){
160 render : function(){
149 // Called when view is rendered.
161 // Called when view is rendered.
150 this.$el
162 this.$el
151 .addClass('widget-hbox-single');
163 .addClass('widget-hbox-single');
152 this.$label = $('<div />')
164 this.$label = $('<div />')
153 .appendTo(this.$el)
165 .appendTo(this.$el)
154 .addClass('widget-hlabel')
166 .addClass('widget-hlabel')
155 .hide();
167 .hide();
156 this.$textbox = $('<input type="text" />')
168 this.$textbox = $('<input type="text" />')
157 .addClass('form-control')
169 .addClass('form-control')
158 .addClass('widget-numeric-text')
170 .addClass('widget-numeric-text')
159 .appendTo(this.$el);
171 .appendTo(this.$el);
160 this.update(); // Set defaults.
172 this.update(); // Set defaults.
161 },
173 },
162
174
163 update : function(options){
175 update : function(options){
164 // Update the contents of this view
176 // Update the contents of this view
165 //
177 //
166 // Called when the model is changed. The model may have been
178 // Called when the model is changed. The model may have been
167 // changed by another view or by a state update from the back-end.
179 // changed by another view or by a state update from the back-end.
168 if (options === undefined || options.updated_view != this) {
180 if (options === undefined || options.updated_view != this) {
169 var value = this.model.get('value');
181 var value = this.model.get('value');
170 if (this._parse_value(this.$textbox.val()) != value) {
182 if (this._parse_value(this.$textbox.val()) != value) {
171 this.$textbox.val(value);
183 this.$textbox.val(value);
172 }
184 }
173
185
174 if (this.model.get('disabled')) {
186 if (this.model.get('disabled')) {
175 this.$textbox.attr('disabled','disabled');
187 this.$textbox.attr('disabled','disabled');
176 } else {
188 } else {
177 this.$textbox.removeAttr('disabled');
189 this.$textbox.removeAttr('disabled');
178 }
190 }
179
191
180 var description = this.model.get('description');
192 var description = this.model.get('description');
181 if (description.length === 0) {
193 if (description.length === 0) {
182 this.$label.hide();
194 this.$label.hide();
183 } else {
195 } else {
184 this.$label.text(description);
196 this.$label.text(description);
185 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
197 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
186 this.$label.show();
198 this.$label.show();
187 }
199 }
188 }
200 }
189 return IntTextView.__super__.update.apply(this);
201 return IntTextView.__super__.update.apply(this);
190 },
202 },
191
203
192 events: {
204 events: {
193 // Dictionary of events and their handlers.
205 // Dictionary of events and their handlers.
194 "keyup input" : "handleChanging",
206 "keyup input" : "handleChanging",
195 "paste input" : "handleChanging",
207 "paste input" : "handleChanging",
196 "cut input" : "handleChanging",
208 "cut input" : "handleChanging",
197
209
198 // Fires only when control is validated or looses focus.
210 // Fires only when control is validated or looses focus.
199 "change input" : "handleChanged"
211 "change input" : "handleChanged"
200 },
212 },
201
213
202 handleChanging: function(e) {
214 handleChanging: function(e) {
203 // Handles and validates user input.
215 // Handles and validates user input.
204
216
205 // Try to parse value as a int.
217 // Try to parse value as a int.
206 var numericalValue = 0;
218 var numericalValue = 0;
207 if (e.target.value !== '') {
219 if (e.target.value !== '') {
208 var trimmed = e.target.value.trim();
220 var trimmed = e.target.value.trim();
209 if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
221 if (!(['-', '-.', '.', '+.', '+'].indexOf(trimmed) >= 0)) {
210 numericalValue = this._parse_value(e.target.value);
222 numericalValue = this._parse_value(e.target.value);
211 }
223 }
212 }
224 }
213
225
214 // If parse failed, reset value to value stored in model.
226 // If parse failed, reset value to value stored in model.
215 if (isNaN(numericalValue)) {
227 if (isNaN(numericalValue)) {
216 e.target.value = this.model.get('value');
228 e.target.value = this.model.get('value');
217 } else if (!isNaN(numericalValue)) {
229 } else if (!isNaN(numericalValue)) {
218 if (this.model.get('max') !== undefined) {
230 if (this.model.get('max') !== undefined) {
219 numericalValue = Math.min(this.model.get('max'), numericalValue);
231 numericalValue = Math.min(this.model.get('max'), numericalValue);
220 }
232 }
221 if (this.model.get('min') !== undefined) {
233 if (this.model.get('min') !== undefined) {
222 numericalValue = Math.max(this.model.get('min'), numericalValue);
234 numericalValue = Math.max(this.model.get('min'), numericalValue);
223 }
235 }
224
236
225 // Apply the value if it has changed.
237 // Apply the value if it has changed.
226 if (numericalValue != this.model.get('value')) {
238 if (numericalValue != this.model.get('value')) {
227
239
228 // Calling model.set will trigger all of the other views of the
240 // Calling model.set will trigger all of the other views of the
229 // model to update.
241 // model to update.
230 this.model.set('value', numericalValue, {updated_view: this});
242 this.model.set('value', numericalValue, {updated_view: this});
231 this.touch();
243 this.touch();
232 }
244 }
233 }
245 }
234 },
246 },
235
247
236 handleChanged: function(e) {
248 handleChanged: function(e) {
237 // Applies validated input.
249 // Applies validated input.
238 if (this.model.get('value') != e.target.value) {
250 if (this.model.get('value') != e.target.value) {
239 e.target.value = this.model.get('value');
251 e.target.value = this.model.get('value');
240 }
252 }
241 },
253 },
242
254
243 _parse_value: function(value) {
255 _parse_value: function(value) {
244 // Parse the value stored in a string.
256 // Parse the value stored in a string.
245 return parseInt(value);
257 return parseInt(value);
246 },
258 },
247 });
259 });
248
260
249
261
250 var ProgressView = widget.DOMWidgetView.extend({
262 var ProgressView = widget.DOMWidgetView.extend({
251 render : function(){
263 render : function(){
252 // Called when view is rendered.
264 // Called when view is rendered.
253 this.$el
265 this.$el
254 .addClass('widget-hbox-single');
266 .addClass('widget-hbox-single');
255 this.$label = $('<div />')
267 this.$label = $('<div />')
256 .appendTo(this.$el)
268 .appendTo(this.$el)
257 .addClass('widget-hlabel')
269 .addClass('widget-hlabel')
258 .hide();
270 .hide();
259 this.$progress = $('<div />')
271 this.$progress = $('<div />')
260 .addClass('progress')
272 .addClass('progress')
261 .addClass('widget-progress')
273 .addClass('widget-progress')
262 .appendTo(this.$el);
274 .appendTo(this.$el);
263 this.$bar = $('<div />')
275 this.$bar = $('<div />')
264 .addClass('progress-bar')
276 .addClass('progress-bar')
265 .css('width', '50%')
277 .css('width', '50%')
266 .appendTo(this.$progress);
278 .appendTo(this.$progress);
267 this.update(); // Set defaults.
279 this.update(); // Set defaults.
268 },
280 },
269
281
270 update : function(){
282 update : function(){
271 // Update the contents of this view
283 // Update the contents of this view
272 //
284 //
273 // Called when the model is changed. The model may have been
285 // Called when the model is changed. The model may have been
274 // changed by another view or by a state update from the back-end.
286 // changed by another view or by a state update from the back-end.
275 var value = this.model.get('value');
287 var value = this.model.get('value');
276 var max = this.model.get('max');
288 var max = this.model.get('max');
277 var min = this.model.get('min');
289 var min = this.model.get('min');
278 var percent = 100.0 * (value - min) / (max - min);
290 var percent = 100.0 * (value - min) / (max - min);
279 this.$bar.css('width', percent + '%');
291 this.$bar.css('width', percent + '%');
280
292
281 var description = this.model.get('description');
293 var description = this.model.get('description');
282 if (description.length === 0) {
294 if (description.length === 0) {
283 this.$label.hide();
295 this.$label.hide();
284 } else {
296 } else {
285 this.$label.text(description);
297 this.$label.text(description);
286 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
298 MathJax.Hub.Queue(["Typeset",MathJax.Hub,this.$label.get(0)]);
287 this.$label.show();
299 this.$label.show();
288 }
300 }
289 return ProgressView.__super__.update.apply(this);
301 return ProgressView.__super__.update.apply(this);
290 },
302 },
291 });
303 });
292
304
293 return {
305 return {
294 'IntSliderView': IntSliderView,
306 'IntSliderView': IntSliderView,
295 'IntTextView': IntTextView,
307 'IntTextView': IntTextView,
296 'ProgressView': ProgressView,
308 'ProgressView': ProgressView,
297 };
309 };
298 });
310 });
General Comments 0
You need to be logged in to leave comments. Login now