1 // ==========================================================================
  2 // Project:   The M-Project - Mobile HTML5 Application Framework
  3 // Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
  4 // Creator:   Dominik
  5 // Date:      30.11.2010
  6 // License:   Dual licensed under the MIT or GPL Version 2 licenses.
  7 //            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
  8 //            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
  9 // ==========================================================================
 10 
 11 /**
 12  * A constant value for single selection mode.
 13  *
 14  * @type String
 15  */
 16 M.SINGLE_SELECTION = 'radio';
 17 
 18 /**
 19  * A constant value for multiple selection mode.
 20  *
 21  * @type String
 22  */
 23 M.MULTIPLE_SELECTION = 'checkbox';
 24 
 25 m_require('ui/selection_list_item.js');
 26 
 27 /**
 28  * @class
 29  *
 30  * This defines the prototype of any selection list view. A selection list view displays
 31  * a list with several items of which either only one single item (M.SINGLE_SELECTION) or
 32  * many items (M.MULTIPLE_SELECTION) can be selected.
 33  *
 34  * @extends M.View
 35  */
 36 M.SelectionListView = M.View.extend(
 37 /** @scope M.SelectionListView.prototype */ {
 38 
 39     /**
 40      * The type of this object.
 41      *
 42      * @type String
 43      */
 44     type: 'M.SelectionListView',
 45 
 46     /**
 47      * Determines whether to remove all item if the list is updated or not.
 48      *
 49      * @type Boolean
 50      */
 51     removeItemsOnUpdate: YES,
 52 
 53     /**
 54      * The selection mode for this selection list. This can either be single or
 55      * multiple selection. To set this value use one of the two constants:
 56      * 
 57      * - M.SINGLE_SELECTION
 58      * - M.MULTIPLE_SELECTION
 59      *
 60      * @type String
 61      */
 62     selectionMode: M.SINGLE_SELECTION,
 63 
 64     /**
 65      * This property is used to define a method that is executed onSelect of an
 66      * item of this selection list.
 67      *
 68      * @type Object
 69      */
 70     onSelect: null,
 71 
 72     /**
 73      * The selected item(s) of this list.
 74      *
 75      * @type String, Array
 76      */
 77     selection: null,
 78 
 79     /**
 80      * Renders a selection list.
 81      *
 82      * @private
 83      * @returns {String} The selection list view's html representation.
 84      */
 85     render: function() {
 86         this.html += '<fieldset data-role="controlgroup" id="' + this.id + '">';
 87 
 88         if(this.label) {
 89             this.html += '<legend>' + this.label + '</legend>';
 90         }
 91 
 92         this.renderChildViews();
 93 
 94         this.html += '</fieldset>';
 95         
 96         return this.html;
 97     },
 98 
 99     /**
100      * Triggers render() on all children of type M.ButtonView based on the specified
101      * selection mode (single or multiple selection).
102      *
103      * @private
104      */
105     renderChildViews: function() {
106         if(this.childViews) {
107             var childViews = $.trim(this.childViews).split(' ');
108             for(var i in childViews) {
109                 var view = this[childViews[i]];
110                 if(view.type === 'M.SelectionListItemView') {
111                     view.parentView = this;
112                     view.internalTarget = this;
113                     view.internalAction = 'itemSelected';
114                     this.html += view.render();
115                 } else {
116                     M.Logger.log('Invalid child views specified for SelectionListView. Only SelectionListItemViews accepted.', M.WARN);
117                 }
118             }
119         } else if(!this.contentBinding) {
120             M.Logger.log('No SelectionListItemViews specified.', M.WARN);
121         }
122     },
123 
124     /**
125      * This method adds a new selection list item to the selection list view by simply appending
126      * its html representation to the selection list view inside the DOM. This method is based
127      * on jQuery's append().
128      *
129      * @param {String} item The html representation of a selection list item to be added.
130      */
131     addItem: function(item) {
132         $('#' + this.id).append(item);
133     },
134 
135     /**
136      * This method removes all of the selection list view's items by removing all of its content in
137      * the DOM. This method is based on jQuery's empty().
138      */
139     removeAllItems: function() {
140         $('#' + this.id).empty();
141     },
142 
143     /**
144      * Updates the the selection list view by re-rendering all of its child views, respectively its
145      * item views.
146      *
147      * @private
148      */
149     renderUpdate: function() {
150         if(this.removeItemsOnUpdate) {
151             this.removeAllItems();
152 
153             if(this.label) {
154                 this.addItem('<legend>' + this.label + '</legend>');
155             }
156         }
157 
158         if(this.contentBinding) {
159             var items = eval(this.contentBinding);
160             for(var i in items) {
161                 var item  = items[i];
162                 var obj = M.SelectionListItemView.design({
163                     value: item.value,
164                     label: item.label,
165                     name: item.name,
166                     isSelected: item.isSelected,
167                     parentView: this,
168                     internalTarget: this,
169                     internalAction: 'itemSelected'
170                 });
171                 this.addItem(obj.render());
172                 obj.theme()
173             }
174             this.theme();
175         }
176     },
177 
178     /**
179      * Triggers the rendering engine, jQuery mobile, to style the selection list.
180      *
181      * @private
182      */
183     theme: function() {
184         $('#' + this.id).controlgroup();
185     },
186 
187     /**
188      * This method is called everytime a item is selected / clicked. If the selected item
189      * changed, the defined onSelect action is triggered.
190      *
191      * @param {String} id The id of the selected item.
192      */
193     itemSelected: function(id) {
194         var item = M.ViewManager.getViewById(id);
195         
196         if(this.selectionMode === M.SINGLE_SELECTION) {
197             if(!_.isEqual(item, this.selection)) {
198                 this.selection = item;
199                 if(this.onSelect && this.onSelect.target && this.onSelect.action) {
200                     this.onSelect.target[this.onSelect.action]();
201                 }
202             }
203         } else {
204             if(!this.selection) {
205                 this.selection = [];
206             }
207 
208             if($('#' + id + ':checked').length > 0) {
209                 this.selection.push(item);
210             } else {
211                 this.selection = _.select(this.selection,  function(i) {
212                     return i !== item;
213                 });
214             }
215 
216             if(this.onSelect && this.onSelect.target && this.onSelect.action) {
217                 this.onSelect.target[this.onSelect.action]();
218             }
219         }
220     },
221 
222     /**
223      * This method returns the selected item's value(s) either as a String (M.SINGLE_SELECTION)
224      * or as an Array (M.MULTIPLE_SELECTION).
225      *
226      * @returns {String, Array} The selected item's value(s).
227      */
228     getSelection: function() {
229         if(this.selectionMode === M.SINGLE_SELECTION) {
230             if(this.selection) {
231                 return this.selection.value;
232             }
233         } else {
234             if(this.selection) {
235                 var selection = [];
236                 _.each(this.selection, function(item) {
237                     selection.push(item.value);
238                 });
239                 return selection;
240             }
241         }
242     },
243 
244     /**
245      * This method can be used to select items programmatically. The given parameter can either
246      * be a String (M.SINGLE_SELECTION) or an Array (M.MULTIPLE_SELECTION).
247      *
248      * @param {String, Array} selection The selection that should be applied to the selection list.
249      */
250     setSelection: function(selection) {
251         this.removeSelection();
252         var that = this;
253         if(this.selectionMode === M.SINGLE_SELECTION && typeof(selection) === 'string') {
254             $('#' + this.id).find('input').each(function() {
255                 var item = M.ViewManager.getViewById($(this).attr('id'));
256                 if(item.value === selection) {
257                     item.isSelected = YES;
258                     that.selection = item;
259                     $(this).attr('checked', 'checked');
260                     $(this).siblings('label:first').addClass('ui-btn-active');
261                     $(this).siblings('label:first').find('span .ui-icon-radio-off').addClass('ui-icon-radio-on');
262                     $(this).siblings('label:first').find('span .ui-icon-radio-off').removeClass('ui-icon-radio-off');
263                 }
264             });
265         } else if(typeof(selection) === 'object') {
266             $('#' + this.id).find('input').each(function() {
267                 var item = M.ViewManager.getViewById($(this).attr('id'));
268                 for(var i in selection) {
269                     var selectionItem = selection[i];
270                     if(item.value === selectionItem) {
271                         item.isSelected = YES;
272                         that.selection.push(item);
273                         $(this).attr('checked', 'checked');
274                         $(this).siblings('label:first').addClass('ui-btn-active');
275                         $(this).siblings('label:first').find('span .ui-icon-checkbox-off').addClass('ui-icon-checkbox-on');
276                         $(this).siblings('label:first').find('span .ui-icon-checkbox-off').removeClass('ui-icon-checkbox-off');
277                     }
278                 }
279             });
280         }
281         that.theme();
282     },
283 
284     /**
285      * This method de-selects all of the selection list's items.
286      */
287     removeSelection: function() {
288         var that = this;
289         var type = '';
290         if(this.selectionMode === M.SINGLE_SELECTION) {
291             this.selection = null;
292             type = 'radio';
293         } else {
294             this.selection = [];
295             type = 'checkbox';
296         }
297         $('#' + this.id).find('input').each(function() {
298             var item = M.ViewManager.getViewById($(this).attr('id'));
299             item.isSelected = NO;
300             $(this).removeAttr('checked');
301             $(this).siblings('label:first').removeClass('ui-btn-active');
302             $(this).siblings('label:first').find('span .ui-icon-radio-on').addClass('ui-icon-' + type + '-off');
303             $(this).siblings('label:first').find('span .ui-icon-radio-on').removeClass('ui-icon-' + type + '-on');
304         });
305     }
306 
307 });