1 // ==========================================================================
  2 // Project:   The M-Project - Mobile HTML5 Application Framework
  3 // Copyright: (c) 2011 panacoda GmbH. All rights reserved.
  4 // Creator:   dominik
  5 // Date:      05.12.11
  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  * @class
 13  *
 14  * The M.TableView renders a default HTML table, that can be dynamically filled via
 15  * content binding. Depending on the table's configuration, there will be a static
 16  * table header, that is visible even if there is no content. It is also possible
 17  * to always update the header, when applying content binding, too.
 18  *
 19  * @extends M.View
 20  */
 21 M.TableView = M.View.extend(
 22 /** @scope M.TableView.prototype */ {
 23 
 24     /**
 25      * The type of this object.
 26      *
 27      * @type String
 28      */
 29     type: 'M.TableView',
 30 
 31     /**
 32      * Determines whether to remove all content rows if the table is updated or not.
 33      *
 34      * @type Boolean
 35      */
 36     removeContentRowsOnUpdate: YES,
 37 
 38     /**
 39      * Determines whether to remove the header rows if the table is updated or not.
 40      *
 41      * @type Boolean
 42      */
 43     removeHeaderRowOnUpdate: NO,
 44 
 45     /**
 46      * Determines whether the table was initialized. If this flag is set to YES,
 47      * the table's header and colgroup was rendered. Depending on the table's
 48      * configuration (e.g. the removeHeaderRowOnUpdate property), this flag might
 49      * change dynamically at runtime.
 50      *
 51      * @private
 52      * @type Boolean
 53      */
 54     isInitialized: NO,
 55 
 56     /**
 57      * This property can be used to specify the table's header and cols, independent
 58      * from dynamically loaded table content. It can be provided with the table's
 59      * definition within a page component. The table's content, in contrast, can only
 60      * be applied via content binding.
 61      *
 62      * Note: If the removeHeaderRowOnUpdate property is set to YES, the header will
 63      * be removed whenever a content binding is applied. So if the header shall be
 64      * statically specified by the view component, do not set that property to YES!
 65      *
 66      * This property should look something like the following:
 67      *
 68      *   {
 69      *     data: ['col1', 'col2', 'col3'],
 70      *     cols: ['20%', '10%', '70%']
 71      *   }
 72      *
 73      * Note: the cols property of this object is optional. You can also let CSS take
 74      * care of the columns arrangement or simply let the browser do all the work
 75      * automatically.
 76      *
 77      * @type Object
 78      */
 79     header: null,
 80 
 81     /**
 82      * Renders a table view as a table element within a div box.
 83      *
 84      * @private
 85      * @returns {String} The table view's html representation.
 86      */
 87     render: function() {
 88         this.html += '<div id="' + this.id + '_container"><table id="' + this.id +'"' + this.style() + '><thead></thead><tbody></tbody></table></div>';
 89 
 90         return this.html;
 91     },
 92 
 93     /**
 94      * Applies some style-attributes to the table.
 95      *
 96      * @private
 97      * @returns {String} The table's styling as html representation.
 98      */
 99     style: function() {
100         var html = ' class="tmp-table';
101         if(this.cssClass) {
102             html += ' ' + this.cssClass;
103         }
104         html += '"'
105         return html;
106     },
107 
108     /**
109      * This method is called once the initial rendering was applied to the
110      * DOM. So this is where we will add the table's header (if there is
111      * one specified)
112      */
113     theme: function() {
114         if(this.header) {
115             this.renderHeader();
116         }
117     },
118 
119     /**
120      * This method renders the table's header. Based on the table's configuration,
121      * this can either happen right at the first rendering or on every content
122      * binding update.
123      *
124      * @private
125      */
126     renderHeader: function() {
127         /* render the table header (if there is one) and define the cols */
128         if(this.header && this.header.data) {
129 
130             /* render the colgroup element (define the columns) */
131             if(this.header.cols && this.header.cols.length > 0) {
132                 html = '<colgroup>';
133                 _.each(this.header.cols, function(col) {
134                     html += '<col width="' + col + '">';
135                 });
136                 html += '</colgroup>';
137                 $('#' + this.id).prepend(html);
138             }
139 
140             /* render the table header */
141             html = '<tr>';
142             _.each(this.header.data, function(col) {
143                 html += '<th class="tmp-table-th">' + (col && col.toString() ? col.toString() : '') + '</th> ';
144             });
145             html += '</tr>';
146             this.addRow(html, YES);
147         }
148     },
149 
150     /**
151      * Updates the table based on its content binding. This should look like the following:
152      *
153      *   {
154      *     header: {
155      *       data: ['col1', 'col2', 'col3'],
156      *       cols: ['20%', '10%', '70%']
157      *     },
158      *     content: [
159      *       [25, 'Y, 'Lorem Ipsum'],
160      *       [25, 46, 'Dolor Sit'],
161      *       [25, 46, 'Amet']
162      *     ]
163      *   }
164      *
165      * Note: If the content binding specifies a header object, any previously rendered
166      * header (and the col definition) will be overwritten!
167      *
168      * @private
169      */
170     renderUpdate: function() {
171         var html;
172         var content = this.value;
173 
174         /* clear the table before filling it up again */
175         if(this.removeHeaderRowOnUpdate && this.removeContentRowsOnUpdate) {
176             this.removeAllRows();
177         } else if(this.removeContentRowsOnUpdate) {
178             this.removeContentRows();
179         }
180 
181         if(content && content.content && content.content.length > 0) {
182 
183             /* render the table header (if there is one) */
184             if(content.header && content.header.data) {
185                 this.header = content.header;
186                 this.renderHeader();
187             }
188 
189             /* render the table's content (row by row) */
190             if(content.content && content.content.length > 0) {
191                 var that = this;
192                 var zebraFlag = 0;
193                 _.each(content.content, function(row) {
194                     zebraFlag = (zebraFlag === 0 ? 1 : 0);
195                     html = '<tr class="tmp-table-tr-' + (zebraFlag === 1 ? 'a' : 'b') + '">';
196                     _.each(row, function(col) {
197                         html += '<td class="tmp-table-td">' + (col && col.toString() ? col.toString() : '') + '</td> ';
198                     });
199                     html += '</tr>';
200                     that.addRow(html);
201                 });
202             }
203 
204         }
205         else {
206             M.Logger.log('The specified content binding for the table view (' + this.id + ') is invalid!', M.WARN);
207         }
208     },
209 
210     /**
211      * This method adds a new row to the table view by simply appending its html representation
212      * to the table view inside the DOM. This method is based on jQuery's append().
213      *
214      * @param {String} row The html representation of a table row to be added.
215      * @param {Boolean} addToTableHeader Determines whether or not to add the row to the table's header.
216      */
217     addRow: function(row, addToTableHeader) {
218         if(addToTableHeader) {
219             $('#' + this.id + ' thead').append(row);
220         } else {
221             $('#' + this.id + ' tbody').append(row);
222         }
223     },
224 
225     /**
226      * This method removes all of the table view's rows by removing all of its content in the DOM. This
227      * method is based on jQuery's empty().
228      */
229     removeAllRows: function() {
230         $('#' + this.id).empty();
231     },
232 
233     /**
234      * This method removes all content rows of the table view by removing the corresponding
235      * html in the DOM. This method is based on jQuery's remove().
236      */
237     removeContentRows: function() {
238         $('#' + 'm_3' + ' tr td').parent().remove();
239     }
240 
241 });