1 // ==========================================================================
  2 // Project:   The M-Project - Mobile HTML5 Application Framework
  3 // Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
  4 //            (c) 2011 panacoda GmbH. All rights reserved.
  5 // Creator:   Dominik
  6 // Date:      30.11.2010
  7 // License:   Dual licensed under the MIT or GPL Version 2 licenses.
  8 //            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
  9 //            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
 10 // ==========================================================================
 11 
 12 /**
 13  * A constant value for single selection mode.
 14  *
 15  * @type String
 16  */
 17 M.SINGLE_SELECTION = 'radio';
 18 
 19 /**
 20  * A constant value for multiple selection mode.
 21  *
 22  * @type String
 23  */
 24 M.MULTIPLE_SELECTION = 'checkbox';
 25 
 26 /**
 27  * A constant value for single selection mode in a dialog / popup.
 28  *
 29  * @type String
 30  */
 31 M.SINGLE_SELECTION_DIALOG = 'select';
 32 
 33 /**
 34  * A constant value for multiple selection mode in a dialog / popup.
 35  *
 36  * @type String
 37  */
 38 M.MULTIPLE_SELECTION_DIALOG = 'select_multiple';
 39 
 40 m_require('ui/selection_list_item.js');
 41 
 42 /**
 43  * @class
 44  *
 45  * This defines the prototype of any selection list view. A selection list view displays
 46  * a list with several items of which either only one single item (M.SINGLE_SELECTION /
 47  * M.SINGLE_SELECTION_DIALOG) or many items (M.MULTIPLE_SELECTION /
 48  * M.MULTIPLE_SELECTION_DIALOG) can be selected.
 49  *
 50  * @extends M.View
 51  */
 52 M.SelectionListView = M.View.extend(
 53 /** @scope M.SelectionListView.prototype */ {
 54 
 55     /**
 56      * The type of this object.
 57      *
 58      * @type String
 59      */
 60     type: 'M.SelectionListView',
 61 
 62     /**
 63      * Determines whether to remove all item if the list is updated or not.
 64      *
 65      * @type Boolean
 66      */
 67     removeItemsOnUpdate: YES,
 68 
 69     /**
 70      * The selection mode for this selection list. This can either be single or
 71      * multiple selection. To set this value use one of the three constants:
 72      *
 73      * - M.SINGLE_SELECTION
 74      *
 75      *   This selection mode will render a selection list with several list items
 76      *   of which only one can be selected. Whenever a new item is selected, the
 77      *   previously selected item automatically gets de-selected. This selection
 78      *   mode's behaviour is equivalent to the plain HTML's radio button.
 79      *
 80      *
 81      * - M.SINGLE_SELECTION_DIALOG
 82      *
 83      *   This selection mode will render a selection list equivalent to the plain
 84      *   HTML's select menu. Only the currently selected item will be visible, and
 85      *   by clicking on this item, the selection list will be displayed in a dialog
 86      *   respectively a popup view. By selecting on of the items, this popup will
 87      *   automatically close and the selected value will be displayed.
 88      *
 89      *
 90      * - M.MULTIPLE_SELECTION
 91      *
 92      *   This selection mode will render a selection list with several list items
 93      *   of which all be selected. So the selection of a new item doesn't lead to
 94      *   automatic de-selected of previously selected items. This selection mode's
 95      *   behaviour is equivalent to the plain HTML's checkboxes.
 96      *
 97      *
 98      * - M.MULTIPLE_SELECTION_DIALOG
 99      *
100      *   This selection mode will render a selection list equivalent to the plain
101      *   HTML's select menu, but with the possibility to select multiple options.
102      *   In contrast to the single selection dialog mode, it also is possible to
103      *   select no option at all. As with the multiple selecton mode, the selection
104      *   of a new item doesn't lead to automatic de-selected of previously selected
105      *   items.
106      *
107      *   Note: This mode currently only works on mobile devices!!
108      *
109      * @type String
110      */
111     selectionMode: M.SINGLE_SELECTION,
112 
113     /**
114      * The selected item(s) of this list.
115      *
116      * @type String, Array
117      */
118     selection: null,
119 
120     /**
121      * This property defines the tab bar's name. This is used internally to identify
122      * the selection list inside the DOM.
123      *
124      * @type String
125      */
126     name: null,
127 
128     
129     /**
130      * This property is used to specify an initial value for the selection list if
131      * it is running in 'multiple selection dialog' (M.MULTIPLE_SELECTION_DIALOG) mode.
132      * This value is then displayed at startup. You would typically use this e.g. to
133      * specify something like: 'Please select...'.
134      *
135      * As long as this initial value is 'selected', the getSelection() of this selection
136      * list will return nothing. Once a 'real' option is selected, this value will visually
137      * disappear. If at some point no option will be selected again, this initial text
138      * will be shown again.
139      *
140      * @type String
141      */
142     initialText: null,
143 
144     /**
145      * The label proeprty defines a text that is shown above or next to the selection list as a 'title'
146      * for the selection list. e.g. "Name:". If no label is specified, no label will be displayed.
147      *
148      * @type String
149      */
150     label: null,
151 
152     /**
153      * Determines whether to display the selection list grouped with the label specified with the label property.
154      * If set to YES, the selection list and its label are wrapped in a container and styled as a unit 'out of
155      * the box'. If set to NO, custom styling could be necessary.
156      *
157      * @type Boolean
158      */
159     isGrouped: NO,
160 
161     /**
162      * This property is used internally to store the selection list's initial state. This is used to be able
163      * to reset the selection list later on using the resetSelection method.
164      *
165      * Note: This property is only used if the selection list's child views are specified directly (without
166      * content binding). Otherwise the state is stored within the content binding and does not need to be
167      * stored with this selection list.
168      *
169      * @private
170      * @type Object
171      */
172     initialState: null,
173 
174     /**
175      * This property specifies the recommended events for this type of view.
176      *
177      * @type Array
178      */
179     recommendedEvents: ['change'],
180 
181     /**
182      * Renders a selection list.
183      *
184      * @private
185      * @returns {String} The selection list view's html representation.
186      */
187     render: function() {
188 
189         /* initialize the initialState property as new array */
190         this.initialState = [];
191 
192         this.html += '<div id="' + this.id + '_container"';
193 
194         if(this.isGrouped) {
195             this.html += ' data-role="fieldcontain"';
196         }
197 
198         if(this.cssClass) {
199             this.html += ' class="';
200             var cssClasses = $.trim(this.cssClass).split(' ');            
201             for(var i in cssClasses) {
202                 this.html += (i > 0 ? ' ' : '') + cssClasses[i] + '_container';
203             }
204             this.html += '"';
205         }
206 
207         this.html += '>';
208 
209         if(this.selectionMode === M.SINGLE_SELECTION_DIALOG || this.selectionMode === M.MULTIPLE_SELECTION_DIALOG) {
210             
211             if(this.label) {
212                 this.html += '<label for="' + this.id + '">' + this.label + '</label>';
213             }
214 
215             this.html += '<select name="' + (this.name ? this.name : this.id) + '" id="' + this.id + '"' + this.style() + (this.selectionMode === M.MULTIPLE_SELECTION_DIALOG ? ' multiple="multiple"' : '') + '>';
216 
217             this.renderChildViews();
218 
219             this.html += '</select>';
220 
221         } else {
222 
223             this.html += '<fieldset data-role="controlgroup" data-native-menu="false" id="' + this.id + '">';
224 
225             if(this.label) {
226                 this.html += '<legend>' + this.label + '</legend>';
227             }
228 
229             this.renderChildViews();
230 
231             this.html += '</fieldset>';
232 
233         }
234 
235         this.html += '</div>';
236 
237         return this.html;
238     },
239 
240     /**
241      * Triggers render() on all children of type M.ButtonView based on the specified
242      * selection mode (single or multiple selection).
243      *
244      * @private
245      */
246     renderChildViews: function() {
247         if(this.childViews) {
248             var childViews = this.getChildViewsAsArray();
249 
250             for(var i in childViews) {
251                 var view = this[childViews[i]];
252                 if(view.type === 'M.SelectionListItemView') {
253                     view.parentView = this;
254                     view._name = childViews[i];
255                     this.html += view.render();
256 
257                     /* store list item in initialState property */
258                     this.initialState.push({
259                         value: view.value,
260                         label: view.label,
261                         isSelected: view.isSelected
262                     });
263                 } else {
264                     M.Logger.log('Invalid child views specified for SelectionListView. Only SelectionListItemViews accepted.', M.WARN);
265                 }
266             }
267         } else if(!this.contentBinding) {
268             M.Logger.log('No SelectionListItemViews specified.', M.WARN);
269         }
270     },
271 
272     /**
273      * This method is responsible for registering events for view elements and its child views. It
274      * basically passes the view's event-property to M.EventDispatcher to bind the appropriate
275      * events.
276      *
277      * It extend M.View's registerEvents method with some special stuff for text field views and
278      * their internal events.
279      */
280     registerEvents: function() {
281         this.internalEvents = {
282             change: {
283                 target: this,
284                 action: 'itemSelected'
285             }
286         }
287         this.bindToCaller(this, M.View.registerEvents)();
288     },
289 
290     /**
291      * This method adds a new selection list item to the selection list view by simply appending
292      * its html representation to the selection list view inside the DOM. This method is based
293      * on jQuery's append().
294      *
295      * @param {String} item The html representation of a selection list item to be added.
296      */
297     addItem: function(item) {
298         $('#' + this.id).append(item);
299     },
300 
301     /**
302      * This method removes all of the selection list view's items by removing all of its content in
303      * the DOM. This method is based on jQuery's empty().
304      */
305     removeAllItems: function() {
306         $('#' + this.id).empty();
307     },
308 
309     /**
310      * Updates the the selection list view by re-rendering all of its child views, respectively its
311      * item views.
312      *
313      * @private
314      */
315     renderUpdate: function() {
316         if(this.removeItemsOnUpdate || this.selectionMode === M.SINGLE_SELECTION_DIALOG || this.selectionMode === M.MULTIPLE_SELECTION_DIALOG) {
317             this.removeAllItems();
318 
319             if(this.label && !(this.selectionMode === M.SINGLE_SELECTION_DIALOG || this.selectionMode === M.MULTIPLE_SELECTION_DIALOG)) {
320                 this.addItem('<legend>' + this.label + '</legend>');
321             } else if(this.selectionMode === M.SINGLE_SELECTION_DIALOG || this.selectionMode === M.MULTIPLE_SELECTION_DIALOG) {
322             }
323         }
324 
325         /* remove selection before applying new content */
326         this.removeSelection();
327 
328         if(this.contentBinding) {
329             /* assign the value property to 'items' since this was automatically set by contentDidChange of M.View */
330             var items = this.value;
331             for(var i in items) {
332                 var item  = items[i];
333                 var obj = null;
334                 obj = M.SelectionListItemView.design({
335                     value: (item.value !== undefined && item.value !== null) ? item.value : '',
336                     label: item.label ? item.label : ((item.value !== undefined && item.value !== null) ? item.value : ''),
337                     parentView: this,
338                     isSelected: item.isSelected
339                 });
340                 if(this.selectionMode !== M.SINGLE_SELECTION_DIALOG && this.selectionMode !== M.MULTIPLE_SELECTION_DIALOG) {
341                     obj.name = item.name ? item.name : (item.label ? item.label : (item.value ? item.value : ''));
342                 }
343 
344                 this.addItem(obj.render());
345                 obj.theme();
346             }
347             this.themeUpdate();
348         }
349     },
350 
351     /**
352      * Triggers the rendering engine, jQuery mobile, to style the selection list.
353      *
354      * @private
355      */
356     theme: function() {
357         if(this.selectionMode === M.SINGLE_SELECTION_DIALOG || this.selectionMode === M.MULTIPLE_SELECTION_DIALOG) {
358             $('#' + this.id).selectmenu();
359             if(this.selectionMode === M.MULTIPLE_SELECTION_DIALOG && this.initialText && this.selection && this.selection.length === 0) {
360                 $('#' + this.id + '_container').find('.ui-btn-text').html(this.initialText);
361             }
362         } else if(this.selectionMode !== M.SINGLE_SELECTION_DIALOG && this.selectionMode !== M.MULTIPLE_SELECTION_DIALOG) {
363             $('#' + this.id).controlgroup();
364         }
365     },
366 
367     /**
368      * Triggers the rendering engine, jQuery mobile, to style the selection list.
369      *
370      * @private
371      */
372     themeUpdate: function() {
373         if(this.selectionMode === M.SINGLE_SELECTION_DIALOG || this.selectionMode === M.MULTIPLE_SELECTION_DIALOG) {
374             $('#' + this.id).selectmenu('refresh');
375             if(this.selectionMode === M.MULTIPLE_SELECTION_DIALOG && this.initialText && this.selection && this.selection.length === 0) {
376                 $('#' + this.id + '_container').find('.ui-btn-text').html(this.initialText);
377             } else if(this.selectionMode === M.SINGLE_SELECTION_DIALOG && !this.selection) {
378                 var that = this;
379                 var item = M.ViewManager.getViewById($('#' + this.id).find('option:first-child').attr('id'));
380                 that.setSelection(item.value);
381             }
382         } else if(this.selectionMode !== M.SINGLE_SELECTION_DIALOG && this.selectionMode !== M.MULTIPLE_SELECTION_DIALOG) {
383             $('#' + this.id).controlgroup();
384         }
385     },
386 
387     /**
388      * Method to append css styles inline to the rendered selection list.
389      *
390      * @private
391      * @returns {String} The selection list's styling as html representation.
392      */
393     style: function() {
394         var html = '';
395         if(this.cssClass) {
396             html += ' class="' + this.cssClass + '"';
397         }
398         return html;
399     },
400 
401     /**
402      * This method is called everytime a item is selected / clicked. If the selected item
403      * changed, the defined onSelect action is triggered.
404      *
405      * @param {String} id The id of the selected item.
406      * @param {Object} event The event.
407      * @param {Object} nextEvent The application-side event handler.
408      */
409     itemSelected: function(id, event, nextEvent) {
410         var item = null;
411 
412         if(this.selectionMode === M.SINGLE_SELECTION) {
413             item = M.ViewManager.getViewById($('input[name=' + (this.name ? this.name : this.id) + ']:checked').attr('id'));
414             
415             if(item !== this.selection) {
416                 this.selection = item;
417 
418                 if(nextEvent) {
419                     M.EventDispatcher.callHandler(nextEvent, event, NO, [this.selection.value, this.selection]);
420                 }
421             }
422         } else if(this.selectionMode === M.SINGLE_SELECTION_DIALOG) {
423             item = M.ViewManager.getViewById($('#' + this.id + ' :selected').attr('id'));
424 
425             if(item !== this.selection) {
426                 this.selection = item;
427 
428                 $('#' + this.id + '_container').find('.ui-btn-text').html(item.label ? item.label : item.value);
429 
430                 if(nextEvent) {
431                     M.EventDispatcher.callHandler(nextEvent, event, NO, [this.selection.value, this.selection]);
432                 }
433             }
434         } else if(this.selectionMode === M.MULTIPLE_SELECTION) {
435             var that = this;
436             this.selection = [];
437             $('#' + this.id).find('input:checked').each(function() {
438                 that.selection.push(M.ViewManager.getViewById($(this).attr('id')));
439             });
440 
441             var selectionValues = [];
442             for(var i in this.selection) {
443                 selectionValues.push(this.selection[i].value);
444             }
445 
446             if(nextEvent) {
447                 M.EventDispatcher.callHandler(nextEvent, event, NO, [selectionValues, this.selection]);
448             }
449         } else if(this.selectionMode === M.MULTIPLE_SELECTION_DIALOG) {
450             var that = this;
451             this.selection = [];
452             $('#' + this.id).find(':selected').each(function() {
453                 that.selection.push(M.ViewManager.getViewById($(this).attr('id')));
454             });
455 
456             var selectionValues = [];
457             for(var i in this.selection) {
458                 selectionValues.push(this.selection[i].value);
459                 $('#' + this.id + '_container').find('.ui-btn-text').html(this.formatSelectionLabel(this.selection.length));
460             }
461 
462             /* if there is no more item selected, reset the initial text */
463             if(this.selection.length === 0) {
464                 this.themeUpdate();
465             }
466 
467             if(nextEvent) {
468                 M.EventDispatcher.callHandler(nextEvent, event, NO, [selectionValues, this.selection]);
469             }
470         }
471     },
472 
473     /**
474      * This method returns the selected item's value(s) either as a String (single selection)
475      * or as an Array (multiple selection).
476      *
477      * @param {Boolean} returnObject Determines whether to return the selected item(s) as object or not.
478      * @returns {String|Object|Array} The selected item's value(s).
479      */
480     getSelection: function(returnObject) {
481         if(this.selectionMode === M.SINGLE_SELECTION || this.selectionMode === M.SINGLE_SELECTION_DIALOG) {
482             if(this.selection) {
483                 if(returnObject) {
484                     return this.selection;
485                 } else {
486                     return this.selection.value;
487                 }
488             }
489         } else {
490             if(this.selection) {
491                 var selection = [];
492                 _.each(this.selection, function(item) {
493                     if(returnObject) {
494                         selection.push(item);
495                     } else {
496                         selection.push(item.value);
497                     }
498                 });
499                 return selection;
500             }
501             return [];
502         }
503     },
504 
505     /**
506      * This method can be used to select items programmatically. The given parameter can either
507      * be a String (single selection) or an Array (multiple selection).
508      *
509      * @param {String|Array} selection The selection that should be applied to the selection list.
510      */
511     setSelection: function(selection) {
512         var that = this;
513         if(this.selectionMode === M.SINGLE_SELECTION && (typeof(selection) === 'string' || typeof(selection) === 'number' || typeof(selection) === 'boolean')) {
514             $('#' + this.id).find('input').each(function() {
515                 var item = M.ViewManager.getViewById($(this).attr('id'));
516                 if(item.value == selection) {
517                     that.removeSelection();
518                     item.isSelected = YES;
519                     that.selection = item;
520                     $(this).attr('checked', 'checked');
521                     $(this).siblings('label:first').addClass('ui-radio-on');
522                     $(this).siblings('label:first').removeClass('ui-radio-off');
523                     $(this).siblings('label:first').find('span .ui-icon-radio-off').addClass('ui-icon-radio-on');
524                     $(this).siblings('label:first').find('span .ui-icon-radio-off').removeClass('ui-icon-radio-off');
525                 }
526             });
527         } else if(this.selectionMode === M.SINGLE_SELECTION_DIALOG && (typeof(selection) === 'string' || typeof(selection) === 'number' || typeof(selection) === 'boolean')) {
528             var didSetSelection = NO;
529             $('#' + this.id).find('option').each(function() {
530                 var item = M.ViewManager.getViewById($(this).attr('id'));
531                 if(item.value == selection) {
532                     that.removeSelection();
533                     item.isSelected = YES;
534                     that.selection = item;
535                     $('#' + that.id).val(item.value);
536                     didSetSelection = YES;
537                 }
538             });
539             if(didSetSelection) {
540                 $('#' + this.id).selectmenu('refresh');
541             }
542         } else if(typeof(selection) === 'object') {
543             if(this.selectionMode === M.MULTIPLE_SELECTION) {
544                 var removedItems = NO;
545                 $('#' + this.id).find('input').each(function() {
546                     var item = M.ViewManager.getViewById($(this).attr('id'));
547                     for(var i in selection) {
548                         var selectionItem = selection[i];
549                         if(item.value == selectionItem) {
550                             if(!removedItems) {
551                                 that.removeSelection();
552                                 removedItems = YES;
553                             }
554                             item.isSelected = YES;
555                             that.selection.push(item);
556                             $(this).attr('checked', 'checked');
557                             $(this).siblings('label:first').removeClass('ui-checkbox-off');
558                             $(this).siblings('label:first').addClass('ui-checkbox-on');
559                             $(this).siblings('label:first').find('span .ui-icon-checkbox-off').addClass('ui-icon-checkbox-on');
560                             $(this).siblings('label:first').find('span .ui-icon-checkbox-off').removeClass('ui-icon-checkbox-off');
561                         }
562                     }
563                 });
564             } else if(this.selectionMode === M.MULTIPLE_SELECTION_DIALOG) {
565                 var removedItems = NO;
566                 $('#' + this.id).find('option').each(function() {
567                     var item = M.ViewManager.getViewById($(this).attr('id'));
568                     for(var i in selection) {
569                         var selectionItem = selection[i];
570                         if(item.value == selectionItem) {
571                             if(!removedItems) {
572                                 that.removeSelection();
573                                 removedItems = YES;
574                             }
575                             item.isSelected = YES;
576                             that.selection.push(item);
577                             $(this).attr('selected', 'selected');
578                         }
579                     }
580 
581                     /* set the label */
582                     $('#' + that.id + '_container').find('.ui-btn-text').html(that.formatSelectionLabel(that.selection.length));
583                 });
584             }
585         }
586         that.theme();
587     },
588 
589     /**
590      * This method de-selects all of the selection list's items.
591      */
592     removeSelection: function() {
593         var that = this;
594 
595         if(this.selectionMode === M.SINGLE_SELECTION || this.selectionMode === M.SINGLE_SELECTION_DIALOG) {
596             this.selection = null;
597         } else {
598             this.selection = [];
599         }
600         
601         if(this.selectionMode !== M.SINGLE_SELECTION_DIALOG && this.selectionMode !== M.MULTIPLE_SELECTION_DIALOG) {
602             $('#' + this.id).find('input').each(function() {
603                 var item = M.ViewManager.getViewById($(this).attr('id'));
604                 item.isSelected = NO;
605                 $(this).removeAttr('checked');
606                 $(this).siblings('label:first').addClass('ui-' + that.selectionMode + '-off');
607                 $(this).siblings('label:first').removeClass('ui-' + that.selectionMode + '-on');
608                 $(this).siblings('label:first').find('span .ui-icon-' + that.selectionMode + '-on').addClass('ui-icon-' + that.selectionMode + '-off');
609                 $(this).siblings('label:first').find('span .ui-icon-' + that.selectionMode + '-on').removeClass('ui-icon-' + that.selectionMode + '-on');
610             });
611         } else {
612             $('#' + this.id).find('option').each(function() {
613                 var item = M.ViewManager.getViewById($(this).attr('id'));
614                 item.isSelected = NO;
615             });
616             $('#' + this.id).val('').removeAttr('checked').removeAttr('selected');
617         }
618     },
619 
620     /**
621      * This method can be used to reset the selection list. This basically discards
622      * all changes made to the selection by the user or any application-sided calls
623      * and applies the original state.
624      *
625      * The 'original state' can either be the bound content or the state, specified
626      * by the originally assigned child views.
627      */
628     resetSelection: function() {
629         if(this.contentBinding) {
630             this.removeSelection();
631             this.renderUpdate();
632         } else {
633             this.contentBinding = {};
634             this.contentBinding.target = this;
635             this.contentBinding.property = 'initialState';
636             this.removeSelection();
637             this.renderUpdate();
638             this.contentBinding = null;
639         }
640     },
641 
642     /**
643      *  We use this as alias for the form reset function view.clearValues() to reset the selection to its initial state
644      */
645     clearValue: function(){
646         this.resetSelection();
647     },
648 
649     /**
650      * This method returns the selection list view's value.
651      *
652      * @returns {String|Array} The selected item's value(s).
653      */
654     getValue: function() {
655         return this.getSelection();
656     },
657 
658     /**
659      * This method is responsible for rendering the visual text for a selection list
660      * in the M.MULTIPLE_SELECTION_DIALOG mode. It's only parameter is a number, that
661      * specifies the number of selected options of this selection list. To customize
662      * the visual output of such a list, you will need to overwrite this method within
663      * the definition of the selection list in your application.
664      *
665      * @param {Number} v The number of selected options.
666      */
667     formatSelectionLabel: function(v) {
668         return v + ' Object(s)';
669     },
670 
671     /**
672      * This method disables the selection list by setting the disabled property of its
673      * html representation to true.
674      */
675     disable: function() {
676         this.isEnabled = NO;
677         if(this.selectionMode === M.SINGLE_SELECTION || this.selectionMode === M.MULTIPLE_SELECTION) {
678             $('#' + this.id).find('input').each(function() {
679                 $(this).checkboxradio('disable');
680             });
681         } else {
682             $('#' + this.id).select('disable');
683             $('#' + this.id).each(function() {
684                 $(this).attr('disabled', 'disabled');
685             });
686         }
687     },
688 
689     /**
690      * This method enables the selection list by setting the disabled property of its
691      * html representation to false.
692      */
693     enable: function() {
694         this.isEnabled = YES;
695         if(this.selectionMode === M.SINGLE_SELECTION || this.selectionMode === M.MULTIPLE_SELECTION) {
696             $('#' + this.id).find('input').each(function() {
697                 $(this).checkboxradio('enable');
698             });
699         } else {
700             $('#' + this.id).select('enable');
701             $('#' + this.id).each(function() {
702                 $(this).removeAttr('disabled');
703             });
704         }
705     }
706 
707 });