##// END OF EJS Templates
listening for change on SelectView and SelectMultipleView
Nicholas Bollweg -
Show More
@@ -1,551 +1,565 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 "base/js/utils",
6 "base/js/utils",
7 "jquery",
7 "jquery",
8 "underscore",
8 "underscore",
9 "bootstrap",
9 "bootstrap",
10 ], function(widget, utils, $, _){
10 ], function(widget, utils, $, _){
11
11
12 var DropdownView = widget.DOMWidgetView.extend({
12 var DropdownView = widget.DOMWidgetView.extend({
13 render : function(){
13 render : function(){
14 /**
14 /**
15 * Called when view is rendered.
15 * Called when view is rendered.
16 */
16 */
17 this.$el
17 this.$el
18 .addClass('widget-hbox widget-dropdown');
18 .addClass('widget-hbox widget-dropdown');
19 this.$label = $('<div />')
19 this.$label = $('<div />')
20 .appendTo(this.$el)
20 .appendTo(this.$el)
21 .addClass('widget-label')
21 .addClass('widget-label')
22 .hide();
22 .hide();
23 this.$buttongroup = $('<div />')
23 this.$buttongroup = $('<div />')
24 .addClass('widget_item')
24 .addClass('widget_item')
25 .addClass('btn-group')
25 .addClass('btn-group')
26 .appendTo(this.$el);
26 .appendTo(this.$el);
27 this.$droplabel = $('<button />')
27 this.$droplabel = $('<button />')
28 .addClass('btn btn-default')
28 .addClass('btn btn-default')
29 .addClass('widget-combo-btn')
29 .addClass('widget-combo-btn')
30 .html("&nbsp;")
30 .html("&nbsp;")
31 .appendTo(this.$buttongroup);
31 .appendTo(this.$buttongroup);
32 this.$dropbutton = $('<button />')
32 this.$dropbutton = $('<button />')
33 .addClass('btn btn-default')
33 .addClass('btn btn-default')
34 .addClass('dropdown-toggle')
34 .addClass('dropdown-toggle')
35 .addClass('widget-combo-carrot-btn')
35 .addClass('widget-combo-carrot-btn')
36 .attr('data-toggle', 'dropdown')
36 .attr('data-toggle', 'dropdown')
37 .append($('<span />').addClass("caret"))
37 .append($('<span />').addClass("caret"))
38 .appendTo(this.$buttongroup);
38 .appendTo(this.$buttongroup);
39 this.$droplist = $('<ul />')
39 this.$droplist = $('<ul />')
40 .addClass('dropdown-menu')
40 .addClass('dropdown-menu')
41 .appendTo(this.$buttongroup);
41 .appendTo(this.$buttongroup);
42
42
43 this.model.on('change:button_style', function(model, value) {
43 this.model.on('change:button_style', function(model, value) {
44 this.update_button_style();
44 this.update_button_style();
45 }, this);
45 }, this);
46 this.update_button_style('');
46 this.update_button_style('');
47
47
48 // Set defaults.
48 // Set defaults.
49 this.update();
49 this.update();
50 },
50 },
51
51
52 update : function(options){
52 update : function(options){
53 /**
53 /**
54 * Update the contents of this view
54 * Update the contents of this view
55 *
55 *
56 * Called when the model is changed. The model may have been
56 * Called when the model is changed. The model may have been
57 * changed by another view or by a state update from the back-end.
57 * changed by another view or by a state update from the back-end.
58 */
58 */
59
59
60 if (options === undefined || options.updated_view != this) {
60 if (options === undefined || options.updated_view != this) {
61 var selected_item_text = this.model.get('selected_label');
61 var selected_item_text = this.model.get('selected_label');
62 if (selected_item_text.trim().length === 0) {
62 if (selected_item_text.trim().length === 0) {
63 this.$droplabel.html("&nbsp;");
63 this.$droplabel.html("&nbsp;");
64 } else {
64 } else {
65 this.$droplabel.text(selected_item_text);
65 this.$droplabel.text(selected_item_text);
66 }
66 }
67
67
68 var items = this.model.get('_options_labels');
68 var items = this.model.get('_options_labels');
69 var $replace_droplist = $('<ul />')
69 var $replace_droplist = $('<ul />')
70 .addClass('dropdown-menu');
70 .addClass('dropdown-menu');
71 // Copy the style
71 // Copy the style
72 $replace_droplist.attr('style', this.$droplist.attr('style'));
72 $replace_droplist.attr('style', this.$droplist.attr('style'));
73 var that = this;
73 var that = this;
74 _.each(items, function(item, i) {
74 _.each(items, function(item, i) {
75 var item_button = $('<a href="#"/>')
75 var item_button = $('<a href="#"/>')
76 .text(item)
76 .text(item)
77 .on('click', $.proxy(that.handle_click, that));
77 .on('click', $.proxy(that.handle_click, that));
78 $replace_droplist.append($('<li />').append(item_button));
78 $replace_droplist.append($('<li />').append(item_button));
79 });
79 });
80
80
81 this.$droplist.replaceWith($replace_droplist);
81 this.$droplist.replaceWith($replace_droplist);
82 this.$droplist.remove();
82 this.$droplist.remove();
83 this.$droplist = $replace_droplist;
83 this.$droplist = $replace_droplist;
84
84
85 if (this.model.get('disabled')) {
85 if (this.model.get('disabled')) {
86 this.$buttongroup.attr('disabled','disabled');
86 this.$buttongroup.attr('disabled','disabled');
87 this.$droplabel.attr('disabled','disabled');
87 this.$droplabel.attr('disabled','disabled');
88 this.$dropbutton.attr('disabled','disabled');
88 this.$dropbutton.attr('disabled','disabled');
89 this.$droplist.attr('disabled','disabled');
89 this.$droplist.attr('disabled','disabled');
90 } else {
90 } else {
91 this.$buttongroup.removeAttr('disabled');
91 this.$buttongroup.removeAttr('disabled');
92 this.$droplabel.removeAttr('disabled');
92 this.$droplabel.removeAttr('disabled');
93 this.$dropbutton.removeAttr('disabled');
93 this.$dropbutton.removeAttr('disabled');
94 this.$droplist.removeAttr('disabled');
94 this.$droplist.removeAttr('disabled');
95 }
95 }
96
96
97 var description = this.model.get('description');
97 var description = this.model.get('description');
98 if (description.length === 0) {
98 if (description.length === 0) {
99 this.$label.hide();
99 this.$label.hide();
100 } else {
100 } else {
101 this.typeset(this.$label, description);
101 this.typeset(this.$label, description);
102 this.$label.show();
102 this.$label.show();
103 }
103 }
104 }
104 }
105 return DropdownView.__super__.update.apply(this);
105 return DropdownView.__super__.update.apply(this);
106 },
106 },
107
107
108 update_button_style: function(previous_trait_value) {
108 update_button_style: function(previous_trait_value) {
109 var class_map = {
109 var class_map = {
110 primary: ['btn-primary'],
110 primary: ['btn-primary'],
111 success: ['btn-success'],
111 success: ['btn-success'],
112 info: ['btn-info'],
112 info: ['btn-info'],
113 warning: ['btn-warning'],
113 warning: ['btn-warning'],
114 danger: ['btn-danger']
114 danger: ['btn-danger']
115 };
115 };
116 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$droplabel);
116 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$droplabel);
117 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$dropbutton);
117 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$dropbutton);
118 },
118 },
119
119
120 update_attr: function(name, value) {
120 update_attr: function(name, value) {
121 /**
121 /**
122 * Set a css attr of the widget view.
122 * Set a css attr of the widget view.
123 */
123 */
124 if (name.substring(0, 6) == 'border' || name == 'background' || name == 'color') {
124 if (name.substring(0, 6) == 'border' || name == 'background' || name == 'color') {
125 this.$droplabel.css(name, value);
125 this.$droplabel.css(name, value);
126 this.$dropbutton.css(name, value);
126 this.$dropbutton.css(name, value);
127 this.$droplist.css(name, value);
127 this.$droplist.css(name, value);
128 } else if (name == 'width') {
128 } else if (name == 'width') {
129 this.$droplist.css(name, value);
129 this.$droplist.css(name, value);
130 this.$droplabel.css(name, value);
130 this.$droplabel.css(name, value);
131 } else if (name == 'padding') {
131 } else if (name == 'padding') {
132 this.$droplist.css(name, value);
132 this.$droplist.css(name, value);
133 this.$buttongroup.css(name, value);
133 this.$buttongroup.css(name, value);
134 } else if (name == 'margin') {
134 } else if (name == 'margin') {
135 this.$buttongroup.css(name, value);
135 this.$buttongroup.css(name, value);
136 } else if (name == 'height') {
136 } else if (name == 'height') {
137 this.$droplabel.css(name, value);
137 this.$droplabel.css(name, value);
138 this.$dropbutton.css(name, value);
138 this.$dropbutton.css(name, value);
139 } else if (name == 'padding' || name == 'margin') {
139 } else if (name == 'padding' || name == 'margin') {
140 this.$el.css(name, value);
140 this.$el.css(name, value);
141 } else {
141 } else {
142 this.$droplist.css(name, value);
142 this.$droplist.css(name, value);
143 this.$droplabel.css(name, value);
143 this.$droplabel.css(name, value);
144 }
144 }
145 },
145 },
146
146
147 handle_click: function (e) {
147 handle_click: function (e) {
148 /**
148 /**
149 * Handle when a value is clicked.
149 * Handle when a value is clicked.
150 *
150 *
151 * Calling model.set will trigger all of the other views of the
151 * Calling model.set will trigger all of the other views of the
152 * model to update.
152 * model to update.
153 */
153 */
154 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
154 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
155 this.touch();
155 this.touch();
156
156
157 // Manually hide the droplist.
157 // Manually hide the droplist.
158 e.stopPropagation();
158 e.stopPropagation();
159 e.preventDefault();
159 e.preventDefault();
160 this.$buttongroup.removeClass('open');
160 this.$buttongroup.removeClass('open');
161 },
161 },
162
162
163 });
163 });
164
164
165
165
166 var RadioButtonsView = widget.DOMWidgetView.extend({
166 var RadioButtonsView = widget.DOMWidgetView.extend({
167 render : function(){
167 render : function(){
168 /**
168 /**
169 * Called when view is rendered.
169 * Called when view is rendered.
170 */
170 */
171 this.$el
171 this.$el
172 .addClass('widget-hbox widget-radio');
172 .addClass('widget-hbox widget-radio');
173 this.$label = $('<div />')
173 this.$label = $('<div />')
174 .appendTo(this.$el)
174 .appendTo(this.$el)
175 .addClass('widget-label')
175 .addClass('widget-label')
176 .hide();
176 .hide();
177 this.$container = $('<div />')
177 this.$container = $('<div />')
178 .appendTo(this.$el)
178 .appendTo(this.$el)
179 .addClass('widget-radio-box');
179 .addClass('widget-radio-box');
180 this.update();
180 this.update();
181 },
181 },
182
182
183 update : function(options){
183 update : function(options){
184 /**
184 /**
185 * Update the contents of this view
185 * Update the contents of this view
186 *
186 *
187 * Called when the model is changed. The model may have been
187 * Called when the model is changed. The model may have been
188 * changed by another view or by a state update from the back-end.
188 * changed by another view or by a state update from the back-end.
189 */
189 */
190 if (options === undefined || options.updated_view != this) {
190 if (options === undefined || options.updated_view != this) {
191 // Add missing items to the DOM.
191 // Add missing items to the DOM.
192 var items = this.model.get('_options_labels');
192 var items = this.model.get('_options_labels');
193 var disabled = this.model.get('disabled');
193 var disabled = this.model.get('disabled');
194 var that = this;
194 var that = this;
195 _.each(items, function(item, index) {
195 _.each(items, function(item, index) {
196 var item_query = ' :input[data-value="' + encodeURIComponent(item) + '"]';
196 var item_query = ' :input[data-value="' + encodeURIComponent(item) + '"]';
197 if (that.$el.find(item_query).length === 0) {
197 if (that.$el.find(item_query).length === 0) {
198 var $label = $('<label />')
198 var $label = $('<label />')
199 .addClass('radio')
199 .addClass('radio')
200 .text(item)
200 .text(item)
201 .appendTo(that.$container);
201 .appendTo(that.$container);
202
202
203 $('<input />')
203 $('<input />')
204 .attr('type', 'radio')
204 .attr('type', 'radio')
205 .addClass(that.model)
205 .addClass(that.model)
206 .val(item)
206 .val(item)
207 .attr('data-value', encodeURIComponent(item))
207 .attr('data-value', encodeURIComponent(item))
208 .prependTo($label)
208 .prependTo($label)
209 .on('click', $.proxy(that.handle_click, that));
209 .on('click', $.proxy(that.handle_click, that));
210 }
210 }
211
211
212 var $item_element = that.$container.find(item_query);
212 var $item_element = that.$container.find(item_query);
213 if (that.model.get('selected_label') == item) {
213 if (that.model.get('selected_label') == item) {
214 $item_element.prop('checked', true);
214 $item_element.prop('checked', true);
215 } else {
215 } else {
216 $item_element.prop('checked', false);
216 $item_element.prop('checked', false);
217 }
217 }
218 $item_element.prop('disabled', disabled);
218 $item_element.prop('disabled', disabled);
219 });
219 });
220
220
221 // Remove items that no longer exist.
221 // Remove items that no longer exist.
222 this.$container.find('input').each(function(i, obj) {
222 this.$container.find('input').each(function(i, obj) {
223 var value = $(obj).val();
223 var value = $(obj).val();
224 var found = false;
224 var found = false;
225 _.each(items, function(item, index) {
225 _.each(items, function(item, index) {
226 if (item == value) {
226 if (item == value) {
227 found = true;
227 found = true;
228 return false;
228 return false;
229 }
229 }
230 });
230 });
231
231
232 if (!found) {
232 if (!found) {
233 $(obj).parent().remove();
233 $(obj).parent().remove();
234 }
234 }
235 });
235 });
236
236
237 var description = this.model.get('description');
237 var description = this.model.get('description');
238 if (description.length === 0) {
238 if (description.length === 0) {
239 this.$label.hide();
239 this.$label.hide();
240 } else {
240 } else {
241 this.$label.text(description);
241 this.$label.text(description);
242 this.typeset(this.$label, description);
242 this.typeset(this.$label, description);
243 this.$label.show();
243 this.$label.show();
244 }
244 }
245 }
245 }
246 return RadioButtonsView.__super__.update.apply(this);
246 return RadioButtonsView.__super__.update.apply(this);
247 },
247 },
248
248
249 update_attr: function(name, value) {
249 update_attr: function(name, value) {
250 /**
250 /**
251 * Set a css attr of the widget view.
251 * Set a css attr of the widget view.
252 */
252 */
253 if (name == 'padding' || name == 'margin') {
253 if (name == 'padding' || name == 'margin') {
254 this.$el.css(name, value);
254 this.$el.css(name, value);
255 } else {
255 } else {
256 this.$container.css(name, value);
256 this.$container.css(name, value);
257 }
257 }
258 },
258 },
259
259
260 handle_click: function (e) {
260 handle_click: function (e) {
261 /**
261 /**
262 * Handle when a value is clicked.
262 * Handle when a value is clicked.
263 *
263 *
264 * Calling model.set will trigger all of the other views of the
264 * Calling model.set will trigger all of the other views of the
265 * model to update.
265 * model to update.
266 */
266 */
267 this.model.set('selected_label', $(e.target).val(), {updated_view: this});
267 this.model.set('selected_label', $(e.target).val(), {updated_view: this});
268 this.touch();
268 this.touch();
269 },
269 },
270 });
270 });
271
271
272
272
273 var ToggleButtonsView = widget.DOMWidgetView.extend({
273 var ToggleButtonsView = widget.DOMWidgetView.extend({
274 initialize: function() {
274 initialize: function() {
275 this._css_state = {};
275 this._css_state = {};
276 ToggleButtonsView.__super__.initialize.apply(this, arguments);
276 ToggleButtonsView.__super__.initialize.apply(this, arguments);
277 },
277 },
278
278
279 render: function() {
279 render: function() {
280 /**
280 /**
281 * Called when view is rendered.
281 * Called when view is rendered.
282 */
282 */
283 this.$el
283 this.$el
284 .addClass('widget-hbox widget-toggle-buttons');
284 .addClass('widget-hbox widget-toggle-buttons');
285 this.$label = $('<div />')
285 this.$label = $('<div />')
286 .appendTo(this.$el)
286 .appendTo(this.$el)
287 .addClass('widget-label')
287 .addClass('widget-label')
288 .hide();
288 .hide();
289 this.$buttongroup = $('<div />')
289 this.$buttongroup = $('<div />')
290 .addClass('btn-group')
290 .addClass('btn-group')
291 .appendTo(this.$el);
291 .appendTo(this.$el);
292
292
293 this.model.on('change:button_style', function(model, value) {
293 this.model.on('change:button_style', function(model, value) {
294 this.update_button_style();
294 this.update_button_style();
295 }, this);
295 }, this);
296 this.update_button_style('');
296 this.update_button_style('');
297 this.update();
297 this.update();
298 },
298 },
299
299
300 update : function(options){
300 update : function(options){
301 /**
301 /**
302 * Update the contents of this view
302 * Update the contents of this view
303 *
303 *
304 * Called when the model is changed. The model may have been
304 * Called when the model is changed. The model may have been
305 * changed by another view or by a state update from the back-end.
305 * changed by another view or by a state update from the back-end.
306 */
306 */
307 if (options === undefined || options.updated_view != this) {
307 if (options === undefined || options.updated_view != this) {
308 // Add missing items to the DOM.
308 // Add missing items to the DOM.
309 var items = this.model.get('_options_labels');
309 var items = this.model.get('_options_labels');
310 var disabled = this.model.get('disabled');
310 var disabled = this.model.get('disabled');
311 var that = this;
311 var that = this;
312 var item_html;
312 var item_html;
313 _.each(items, function(item, index) {
313 _.each(items, function(item, index) {
314 if (item.trim().length === 0) {
314 if (item.trim().length === 0) {
315 item_html = "&nbsp;";
315 item_html = "&nbsp;";
316 } else {
316 } else {
317 item_html = utils.escape_html(item);
317 item_html = utils.escape_html(item);
318 }
318 }
319 var item_query = '[data-value="' + encodeURIComponent(item) + '"]';
319 var item_query = '[data-value="' + encodeURIComponent(item) + '"]';
320 var $item_element = that.$buttongroup.find(item_query);
320 var $item_element = that.$buttongroup.find(item_query);
321 if (!$item_element.length) {
321 if (!$item_element.length) {
322 $item_element = $('<button/>')
322 $item_element = $('<button/>')
323 .attr('type', 'button')
323 .attr('type', 'button')
324 .addClass('btn btn-default')
324 .addClass('btn btn-default')
325 .html(item_html)
325 .html(item_html)
326 .appendTo(that.$buttongroup)
326 .appendTo(that.$buttongroup)
327 .attr('data-value', encodeURIComponent(item))
327 .attr('data-value', encodeURIComponent(item))
328 .attr('value', item)
328 .attr('value', item)
329 .on('click', $.proxy(that.handle_click, that));
329 .on('click', $.proxy(that.handle_click, that));
330 that.update_style_traits($item_element);
330 that.update_style_traits($item_element);
331 }
331 }
332 if (that.model.get('selected_label') == item) {
332 if (that.model.get('selected_label') == item) {
333 $item_element.addClass('active');
333 $item_element.addClass('active');
334 } else {
334 } else {
335 $item_element.removeClass('active');
335 $item_element.removeClass('active');
336 }
336 }
337 $item_element.prop('disabled', disabled);
337 $item_element.prop('disabled', disabled);
338 });
338 });
339
339
340 // Remove items that no longer exist.
340 // Remove items that no longer exist.
341 this.$buttongroup.find('button').each(function(i, obj) {
341 this.$buttongroup.find('button').each(function(i, obj) {
342 var value = $(obj).attr('value');
342 var value = $(obj).attr('value');
343 var found = false;
343 var found = false;
344 _.each(items, function(item, index) {
344 _.each(items, function(item, index) {
345 if (item == value) {
345 if (item == value) {
346 found = true;
346 found = true;
347 return false;
347 return false;
348 }
348 }
349 });
349 });
350
350
351 if (!found) {
351 if (!found) {
352 $(obj).remove();
352 $(obj).remove();
353 }
353 }
354 });
354 });
355
355
356 var description = this.model.get('description');
356 var description = this.model.get('description');
357 if (description.length === 0) {
357 if (description.length === 0) {
358 this.$label.hide();
358 this.$label.hide();
359 } else {
359 } else {
360 this.$label.text();
360 this.$label.text();
361 this.typeset(this.$label, description);
361 this.typeset(this.$label, description);
362 this.$label.show();
362 this.$label.show();
363 }
363 }
364 }
364 }
365 return ToggleButtonsView.__super__.update.apply(this);
365 return ToggleButtonsView.__super__.update.apply(this);
366 },
366 },
367
367
368 update_attr: function(name, value) {
368 update_attr: function(name, value) {
369 /**
369 /**
370 * Set a css attr of the widget view.
370 * Set a css attr of the widget view.
371 */
371 */
372 if (name == 'padding' || name == 'margin') {
372 if (name == 'padding' || name == 'margin') {
373 this.$el.css(name, value);
373 this.$el.css(name, value);
374 } else {
374 } else {
375 this._css_state[name] = value;
375 this._css_state[name] = value;
376 this.update_style_traits();
376 this.update_style_traits();
377 }
377 }
378 },
378 },
379
379
380 update_style_traits: function(button) {
380 update_style_traits: function(button) {
381 for (var name in this._css_state) {
381 for (var name in this._css_state) {
382 if (this._css_state.hasOwnProperty(name)) {
382 if (this._css_state.hasOwnProperty(name)) {
383 if (name == 'margin') {
383 if (name == 'margin') {
384 this.$buttongroup.css(name, this._css_state[name]);
384 this.$buttongroup.css(name, this._css_state[name]);
385 } else if (name != 'width') {
385 } else if (name != 'width') {
386 if (button) {
386 if (button) {
387 button.css(name, this._css_state[name]);
387 button.css(name, this._css_state[name]);
388 } else {
388 } else {
389 this.$buttongroup.find('button').css(name, this._css_state[name]);
389 this.$buttongroup.find('button').css(name, this._css_state[name]);
390 }
390 }
391 }
391 }
392 }
392 }
393 }
393 }
394 },
394 },
395
395
396 update_button_style: function(previous_trait_value) {
396 update_button_style: function(previous_trait_value) {
397 var class_map = {
397 var class_map = {
398 primary: ['btn-primary'],
398 primary: ['btn-primary'],
399 success: ['btn-success'],
399 success: ['btn-success'],
400 info: ['btn-info'],
400 info: ['btn-info'],
401 warning: ['btn-warning'],
401 warning: ['btn-warning'],
402 danger: ['btn-danger']
402 danger: ['btn-danger']
403 };
403 };
404 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$buttongroup.find('button'));
404 this.update_mapped_classes(class_map, 'button_style', previous_trait_value, this.$buttongroup.find('button'));
405 },
405 },
406
406
407 handle_click: function (e) {
407 handle_click: function (e) {
408 /**
408 /**
409 * Handle when a value is clicked.
409 * Handle when a value is clicked.
410 *
410 *
411 * Calling model.set will trigger all of the other views of the
411 * Calling model.set will trigger all of the other views of the
412 * model to update.
412 * model to update.
413 */
413 */
414 this.model.set('selected_label', $(e.target).attr('value'), {updated_view: this});
414 this.model.set('selected_label', $(e.target).attr('value'), {updated_view: this});
415 this.touch();
415 this.touch();
416 },
416 },
417 });
417 });
418
418
419
419
420 var SelectView = widget.DOMWidgetView.extend({
420 var SelectView = widget.DOMWidgetView.extend({
421 render : function(){
421 render : function(){
422 /**
422 /**
423 * Called when view is rendered.
423 * Called when view is rendered.
424 */
424 */
425 this.$el
425 this.$el
426 .addClass('widget-hbox widget-select');
426 .addClass('widget-hbox widget-select');
427 this.$label = $('<div />')
427 this.$label = $('<div />')
428 .appendTo(this.$el)
428 .appendTo(this.$el)
429 .addClass('widget-label')
429 .addClass('widget-label')
430 .hide();
430 .hide();
431 this.$listbox = $('<select />')
431 this.$listbox = $('<select />')
432 .addClass('widget-listbox form-control')
432 .addClass('widget-listbox form-control')
433 .attr('size', 6)
433 .attr('size', 6)
434 .appendTo(this.$el);
434 .appendTo(this.$el)
435 .on('change', $.proxy(this.handle_change, this));
435 this.update();
436 this.update();
436 },
437 },
437
438
438 update : function(options){
439 update : function(options){
439 /**
440 /**
440 * Update the contents of this view
441 * Update the contents of this view
441 *
442 *
442 * Called when the model is changed. The model may have been
443 * Called when the model is changed. The model may have been
443 * changed by another view or by a state update from the back-end.
444 * changed by another view or by a state update from the back-end.
444 */
445 */
445 if (options === undefined || options.updated_view != this) {
446 if (options === undefined || options.updated_view != this) {
446 // Add missing items to the DOM.
447 // Add missing items to the DOM.
447 var items = this.model.get('_options_labels');
448 var items = this.model.get('_options_labels');
448 var that = this;
449 var that = this;
449 _.each(items, function(item, index) {
450 _.each(items, function(item, index) {
450 var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
451 var item_query = 'option[data-value="' + encodeURIComponent(item) + '"]';
451 if (that.$listbox.find(item_query).length === 0) {
452 if (that.$listbox.find(item_query).length === 0) {
452 $('<option />')
453 $('<option />')
453 .text(item)
454 .text(item)
454 .attr('data-value', encodeURIComponent(item))
455 .attr('data-value', encodeURIComponent(item))
455 .attr('selected_label', item)
456 .attr('selected_label', item)
456 .appendTo(that.$listbox)
457 .appendTo(that.$listbox);
457 .on('click', $.proxy(that.handle_click, that));
458 }
458 }
459 });
459 });
460
460
461 // Select the correct element
461 // Select the correct element
462 this.$listbox.val(this.model.get('selected_label'));
462 this.$listbox.val(this.model.get('selected_label'));
463
463
464 // Disable listbox if needed
464 // Disable listbox if needed
465 var disabled = this.model.get('disabled');
465 var disabled = this.model.get('disabled');
466 this.$listbox.prop('disabled', disabled);
466 this.$listbox.prop('disabled', disabled);
467
467
468 // Remove items that no longer exist.
468 // Remove items that no longer exist.
469 this.$listbox.find('option').each(function(i, obj) {
469 this.$listbox.find('option').each(function(i, obj) {
470 var value = $(obj).text();
470 var value = $(obj).text();
471 var found = false;
471 var found = false;
472 _.each(items, function(item, index) {
472 _.each(items, function(item, index) {
473 if (item == value) {
473 if (item == value) {
474 found = true;
474 found = true;
475 return false;
475 return false;
476 }
476 }
477 });
477 });
478
478
479 if (!found) {
479 if (!found) {
480 $(obj).remove();
480 $(obj).remove();
481 }
481 }
482 });
482 });
483
483
484 var description = this.model.get('description');
484 var description = this.model.get('description');
485 if (description.length === 0) {
485 if (description.length === 0) {
486 this.$label.hide();
486 this.$label.hide();
487 } else {
487 } else {
488 this.typeset(this.$label, description);
488 this.typeset(this.$label, description);
489 this.$label.show();
489 this.$label.show();
490 }
490 }
491 }
491 }
492 return SelectView.__super__.update.apply(this);
492 return SelectView.__super__.update.apply(this);
493 },
493 },
494
494
495 update_attr: function(name, value) {
495 update_attr: function(name, value) {
496 /**
496 /**
497 * Set a css attr of the widget view.
497 * Set a css attr of the widget view.
498 */
498 */
499 if (name == 'padding' || name == 'margin') {
499 if (name == 'padding' || name == 'margin') {
500 this.$el.css(name, value);
500 this.$el.css(name, value);
501 } else {
501 } else {
502 this.$listbox.css(name, value);
502 this.$listbox.css(name, value);
503 }
503 }
504 },
504 },
505
505
506 handle_click: function (e) {
506 handle_change: function (e) {
507 /**
507 /**
508 * Handle when a value is clicked.
508 * Handle when a new value is selected.
509 *
509 *
510 * Calling model.set will trigger all of the other views of the
510 * Calling model.set will trigger all of the other views of the
511 * model to update.
511 * model to update.
512 */
512 */
513 this.model.set('selected_label', $(e.target).text(), {updated_view: this});
513 this.model.set('selected_label', this.$listbox.val(), {updated_view: this});
514 this.touch();
514 this.touch();
515 },
515 },
516 });
516 });
517
518
517 var SelectMultipleView = SelectView.extend({
519 var SelectMultipleView = SelectView.extend({
518 render: function(){
520 render: function(){
521 /**
522 * Called when view is rendered.
523 */
519 SelectMultipleView.__super__.render.apply(this);
524 SelectMultipleView.__super__.render.apply(this);
520 this.$el.removeClass('widget-select')
525 this.$el.removeClass('widget-select')
521 .addClass('widget-select-multiple');
526 .addClass('widget-select-multiple');
522 this.$listbox.attr('multiple', true)
527 this.$listbox.attr('multiple', true)
523 .on('input', $.proxy(this.handle_click, this));
528 .on('change', $.proxy(this.handle_change, this));
524 return this;
529 return this;
525 },
530 },
526
531
527 update: function(){
532 update: function(){
533 /**
534 * Update the contents of this view
535 *
536 * Called when the model is changed. The model may have been
537 * changed by another view or by a state update from the back-end.
538 */
528 SelectMultipleView.__super__.update.apply(this, arguments);
539 SelectMultipleView.__super__.update.apply(this, arguments);
529 this.$listbox.val(this.model.get('selected_labels'));
540 this.$listbox.val(this.model.get('selected_labels'));
530 },
541 },
531
542
532 handle_click: function (e) {
543 handle_change: function (e) {
533 // Handle when a value is clicked.
544 /**
534
545 * Handle when a new value is selected.
535 // Calling model.set will trigger all of the other views of the
546 *
536 // model to update.
547 * Calling model.set will trigger all of the other views of the
548 * model to update.
549 */
537 this.model.set('selected_labels',
550 this.model.set('selected_labels',
538 (this.$listbox.val() || []).slice(),
551 (this.$listbox.val() || []).slice(),
539 {updated_view: this});
552 {updated_view: this});
540 this.touch();
553 this.touch();
541 },
554 },
542 });
555 });
543
556
557
544 return {
558 return {
545 'DropdownView': DropdownView,
559 'DropdownView': DropdownView,
546 'RadioButtonsView': RadioButtonsView,
560 'RadioButtonsView': RadioButtonsView,
547 'ToggleButtonsView': ToggleButtonsView,
561 'ToggleButtonsView': ToggleButtonsView,
548 'SelectView': SelectView,
562 'SelectView': SelectView,
549 'SelectMultipleView': SelectMultipleView,
563 'SelectMultipleView': SelectMultipleView,
550 };
564 };
551 });
565 });
General Comments 0
You need to be logged in to leave comments. Login now