      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 });