Roo/bootstrap/Table.js
[roojs1] / Roo / bootstrap / Table.js
1 /*
2  * - LGPL
3  *
4  * table
5  * 
6  */
7
8 /**
9  * @class Roo.bootstrap.Table
10  * @extends Roo.bootstrap.Component
11  * Bootstrap Table class
12  * @cfg {String} cls table class
13  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
14  * @cfg {String} bgcolor Specifies the background color for a table
15  * @cfg {Number} border Specifies whether the table cells should have borders or not
16  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
17  * @cfg {Number} cellspacing Specifies the space between cells
18  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
19  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
20  * @cfg {String} sortable Specifies that the table should be sortable
21  * @cfg {String} summary Specifies a summary of the content of a table
22  * @cfg {Number} width Specifies the width of a table
23  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
24  * 
25  * @cfg {boolean} striped Should the rows be alternative striped
26  * @cfg {boolean} bordered Add borders to the table
27  * @cfg {boolean} hover Add hover highlighting
28  * @cfg {boolean} condensed Format condensed
29  * @cfg {boolean} responsive Format condensed
30  * @cfg {Boolean} loadMask (true|false) default false
31  * @cfg {Boolean} tfoot (true|false) generate tfoot, default true
32  * @cfg {Boolean} thead (true|false) generate thead, default true
33  * @cfg {Boolean} RowSelection (true|false) default false
34  * @cfg {Boolean} CellSelection (true|false) default false
35  *
36  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
37  
38  * 
39  * @constructor
40  * Create a new Table
41  * @param {Object} config The config object
42  */
43
44 Roo.bootstrap.Table = function(config){
45     Roo.bootstrap.Table.superclass.constructor.call(this, config);
46     
47     if (this.sm) {
48         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
49         this.sm = this.selModel;
50         this.sm.xmodule = this.xmodule || false;
51     }
52     if (this.cm && typeof(this.cm.config) == 'undefined') {
53         this.colModel = new Roo.grid.ColumnModel(this.cm);
54         this.cm = this.colModel;
55         this.cm.xmodule = this.xmodule || false;
56     }
57     if (this.store) {
58         this.store= Roo.factory(this.store, Roo.data);
59         this.ds = this.store;
60         this.ds.xmodule = this.xmodule || false;
61          
62     }
63     if (this.footer && this.store) {
64         this.footer.dataSource = this.ds;
65         this.footer = Roo.factory(this.footer);
66     }
67     
68     /** @private */
69     this.addEvents({
70         /**
71          * @event cellclick
72          * Fires when a cell is clicked
73          * @param {Roo.bootstrap.Table} this
74          * @param {Roo.Element} el
75          * @param {Number} rowIndex
76          * @param {Number} columnIndex
77          * @param {Roo.EventObject} e
78          */
79         "cellclick" : true,
80         /**
81          * @event celldblclick
82          * Fires when a cell is double clicked
83          * @param {Roo.bootstrap.Table} this
84          * @param {Roo.Element} el
85          * @param {Number} rowIndex
86          * @param {Number} columnIndex
87          * @param {Roo.EventObject} e
88          */
89         "celldblclick" : true,
90         /**
91          * @event rowclick
92          * Fires when a row is clicked
93          * @param {Roo.bootstrap.Table} this
94          * @param {Roo.Element} el
95          * @param {Number} rowIndex
96          * @param {Roo.EventObject} e
97          */
98         "rowclick" : true,
99         /**
100          * @event rowdblclick
101          * Fires when a row is double clicked
102          * @param {Roo.bootstrap.Table} this
103          * @param {Roo.Element} el
104          * @param {Number} rowIndex
105          * @param {Roo.EventObject} e
106          */
107         "rowdblclick" : true,
108         /**
109          * @event mouseover
110          * Fires when a mouseover occur
111          * @param {Roo.bootstrap.Table} this
112          * @param {Roo.Element} el
113          * @param {Number} rowIndex
114          * @param {Number} columnIndex
115          * @param {Roo.EventObject} e
116          */
117         "mouseover" : true,
118         /**
119          * @event mouseout
120          * Fires when a mouseout occur
121          * @param {Roo.bootstrap.Table} this
122          * @param {Roo.Element} el
123          * @param {Number} rowIndex
124          * @param {Number} columnIndex
125          * @param {Roo.EventObject} e
126          */
127         "mouseout" : true,
128         /**
129          * @event rowclass
130          * Fires when a row is rendered, so you can change add a style to it.
131          * @param {Roo.bootstrap.Table} this
132          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
133          */
134         'rowclass' : true
135         
136     });
137 };
138
139 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
140     
141     cls: false,
142     align: false,
143     bgcolor: false,
144     border: false,
145     cellpadding: false,
146     cellspacing: false,
147     frame: false,
148     rules: false,
149     sortable: false,
150     summary: false,
151     width: false,
152     striped : false,
153     bordered: false,
154     hover:  false,
155     condensed : false,
156     responsive : false,
157     sm : false,
158     cm : false,
159     store : false,
160     loadMask : false,
161     tfoot : true,
162     thead : true,
163     RowSelection : false,
164     CellSelection : false,
165     layout : false,
166     
167     // Roo.Element - the tbody
168     mainBody: false, 
169     
170     getAutoCreate : function(){
171         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
172         
173         cfg = {
174             tag: 'table',
175             cls : 'table',
176             cn : []
177         }
178             
179         if (this.striped) {
180             cfg.cls += ' table-striped';
181         }
182         
183         if (this.hover) {
184             cfg.cls += ' table-hover';
185         }
186         if (this.bordered) {
187             cfg.cls += ' table-bordered';
188         }
189         if (this.condensed) {
190             cfg.cls += ' table-condensed';
191         }
192         if (this.responsive) {
193             cfg.cls += ' table-responsive';
194         }
195         
196         if (this.cls) {
197             cfg.cls+=  ' ' +this.cls;
198         }
199         
200         // this lot should be simplifed...
201         
202         if (this.align) {
203             cfg.align=this.align;
204         }
205         if (this.bgcolor) {
206             cfg.bgcolor=this.bgcolor;
207         }
208         if (this.border) {
209             cfg.border=this.border;
210         }
211         if (this.cellpadding) {
212             cfg.cellpadding=this.cellpadding;
213         }
214         if (this.cellspacing) {
215             cfg.cellspacing=this.cellspacing;
216         }
217         if (this.frame) {
218             cfg.frame=this.frame;
219         }
220         if (this.rules) {
221             cfg.rules=this.rules;
222         }
223         if (this.sortable) {
224             cfg.sortable=this.sortable;
225         }
226         if (this.summary) {
227             cfg.summary=this.summary;
228         }
229         if (this.width) {
230             cfg.width=this.width;
231         }
232         if (this.layout) {
233             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
234         }
235         
236         if(this.store || this.cm){
237             if(this.thead){
238                 cfg.cn.push(this.renderHeader());
239             }
240             
241             cfg.cn.push(this.renderBody());
242             
243             if(this.tfoot){
244                 cfg.cn.push(this.renderFooter());
245             }
246             
247             cfg.cls+=  ' TableGrid';
248         }
249         
250         return { cn : [ cfg ] };
251     },
252     
253     initEvents : function()
254     {   
255         if(!this.store || !this.cm){
256             return;
257         }
258         
259         //Roo.log('initEvents with ds!!!!');
260         
261         this.mainBody = this.el.select('tbody', true).first();
262         
263         
264         var _this = this;
265         
266         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
267             e.on('click', _this.sort, _this);
268         });
269         
270         this.el.on("click", this.onClick, this);
271         this.el.on("dblclick", this.onDblClick, this);
272         
273         this.parent().el.setStyle('position', 'relative');
274         if (this.footer) {
275             this.footer.parentId = this.id;
276             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
277         }
278         
279         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
280         
281         this.store.on('load', this.onLoad, this);
282         this.store.on('beforeload', this.onBeforeLoad, this);
283         this.store.on('update', this.onUpdate, this);
284         
285     },
286     
287     onMouseover : function(e, el)
288     {
289         var cell = Roo.get(el);
290         
291         if(!cell){
292             return;
293         }
294         
295         if(e.getTarget().nodeName.toLowerCase() != 'td'){
296             cell = cell.findParent('td', false, true);
297         }
298         
299         var row = cell.findParent('tr', false, true);
300         var cellIndex = cell.dom.cellIndex;
301         var rowIndex = row.dom.rowIndex - 1; // start from 0
302         
303         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
304         
305     },
306     
307     onMouseout : function(e, el)
308     {
309         var cell = Roo.get(el);
310         
311         if(!cell){
312             return;
313         }
314         
315         if(e.getTarget().nodeName.toLowerCase() != 'td'){
316             cell = cell.findParent('td', false, true);
317         }
318         
319         var row = cell.findParent('tr', false, true);
320         var cellIndex = cell.dom.cellIndex;
321         var rowIndex = row.dom.rowIndex - 1; // start from 0
322         
323         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
324         
325     },
326     
327     onClick : function(e, el)
328     {
329         var cell = Roo.get(el);
330         
331         if(!cell || (!this.CellSelection && !this.RowSelection)){
332             return;
333         }
334         
335         
336         if(e.getTarget().nodeName.toLowerCase() != 'td'){
337             cell = cell.findParent('td', false, true);
338         }
339         
340         var row = cell.findParent('tr', false, true);
341         var cellIndex = cell.dom.cellIndex;
342         var rowIndex = row.dom.rowIndex - 1;
343         
344         if(this.CellSelection){
345             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
346         }
347         
348         if(this.RowSelection){
349             this.fireEvent('rowclick', this, row, rowIndex, e);
350         }
351         
352         
353     },
354     
355     onDblClick : function(e,el)
356     {
357         var cell = Roo.get(el);
358         
359         if(!cell || (!this.CellSelection && !this.RowSelection)){
360             return;
361         }
362         
363         if(e.getTarget().nodeName.toLowerCase() != 'td'){
364             cell = cell.findParent('td', false, true);
365         }
366         
367         var row = cell.findParent('tr', false, true);
368         var cellIndex = cell.dom.cellIndex;
369         var rowIndex = row.dom.rowIndex - 1;
370         
371         if(this.CellSelection){
372             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
373         }
374         
375         if(this.RowSelection){
376             this.fireEvent('rowdblclick', this, row, rowIndex, e);
377         }
378     },
379     
380     sort : function(e,el)
381     {
382         var col = Roo.get(el)
383         
384         if(!col.hasClass('sortable')){
385             return;
386         }
387         
388         var sort = col.attr('sort');
389         var dir = 'ASC';
390         
391         if(col.hasClass('glyphicon-arrow-up')){
392             dir = 'DESC';
393         }
394         
395         this.store.sortInfo = {field : sort, direction : dir};
396         
397         if (this.footer) {
398             Roo.log("calling footer first");
399             this.footer.onClick('first');
400         } else {
401         
402             this.store.load({ params : { start : 0 } });
403         }
404     },
405     
406     renderHeader : function()
407     {
408         var header = {
409             tag: 'thead',
410             cn : []
411         };
412         
413         var cm = this.cm;
414         
415         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
416             
417             var config = cm.config[i];
418                     
419             var c = {
420                 tag: 'th',
421                 style : '',
422                 html: cm.getColumnHeader(i)
423             };
424             
425             if(typeof(config.hidden) != 'undefined' && config.hidden){
426                 c.style += ' display:none;';
427             }
428             
429             if(typeof(config.dataIndex) != 'undefined'){
430                 c.sort = config.dataIndex;
431             }
432             
433             if(typeof(config.sortable) != 'undefined' && config.sortable){
434                 c.cls = 'sortable';
435             }
436             
437 //            if(typeof(config.align) != 'undefined' && config.align.length){
438 //                c.style += ' text-align:' + config.align + ';';
439 //            }
440             
441             if(typeof(config.width) != 'undefined'){
442                 c.style += ' width:' + config.width + 'px;';
443             }
444             
445             header.cn.push(c)
446         }
447         
448         return header;
449     },
450     
451     renderBody : function()
452     {
453         var body = {
454             tag: 'tbody',
455             cn : [
456                 {
457                     tag: 'tr',
458                     cn : [
459                         {
460                             tag : 'td',
461                             colspan :  this.cm.getColumnCount()
462                         }
463                     ]
464                 }
465             ]
466         };
467         
468         return body;
469     },
470     
471     renderFooter : function()
472     {
473         var footer = {
474             tag: 'tfoot',
475             cn : [
476                 {
477                     tag: 'tr',
478                     cn : [
479                         {
480                             tag : 'td',
481                             colspan :  this.cm.getColumnCount()
482                         }
483                     ]
484                 }
485             ]
486         };
487         
488         return footer;
489     },
490     
491     
492     
493     onLoad : function()
494     {
495         Roo.log('ds onload');
496         this.clear();
497         
498         var _this = this;
499         var cm = this.cm;
500         var ds = this.store;
501         
502         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
503             e.removeClass(['glyphicon', 'glyphicon-arrow-up', 'glyphicon-arrow-down']);
504             
505             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
506                 e.addClass(['glyphicon', 'glyphicon-arrow-up']);
507             }
508             
509             if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
510                 e.addClass(['glyphicon', 'glyphicon-arrow-down']);
511             }
512         });
513         
514         var tbody =  this.mainBody;
515         
516         var renders = [];
517                     
518         if(ds.getCount() > 0){
519             ds.data.each(function(d,rowIndex){
520                 var row =  this.renderRow(cm, ds, rowIndex);
521                 
522                 tbody.createChild(row);
523                 
524             }, this);
525         }
526         
527         
528         if(renders.length){
529             var _this = this;
530             Roo.each(renders, function(r){
531                 _this.renderColumn(r);
532             })
533         }
534         
535         Roo.each(this.el.select('tbody td', true).elements, function(e){
536             e.on('mouseover', _this.onMouseover, _this);
537         });
538         
539         Roo.each(this.el.select('tbody td', true).elements, function(e){
540             e.on('mouseout', _this.onMouseout, _this);
541         });
542
543         //if(this.loadMask){
544         //    this.maskEl.hide();
545         //}
546     },
547     
548     
549     onUpdate : function(ds,record)
550     {
551         this.refreshRow(record);
552     },
553     onRemove : function(ds, record, index, isUpdate){
554         if(isUpdate !== true){
555             this.fireEvent("beforerowremoved", this, index, record);
556         }
557         var bt = this.mainBody.dom;
558         if(bt.rows[index]){
559             bt.removeChild(bt.rows[index]);
560         }
561         
562         if(isUpdate !== true){
563             //this.stripeRows(index);
564             //this.syncRowHeights(index, index);
565             //this.layout();
566             this.fireEvent("rowremoved", this, index, record);
567         }
568     },
569     
570     
571     refreshRow : function(record){
572         var ds = this.store, index;
573         if(typeof record == 'number'){
574             index = record;
575             record = ds.getAt(index);
576         }else{
577             index = ds.indexOf(record);
578         }
579         this.insertRow(ds, index, true);
580         this.onRemove(ds, record, index+1, true);
581         //this.syncRowHeights(index, index);
582         //this.layout();
583         this.fireEvent("rowupdated", this, index, record);
584     },
585     
586     insertRow : function(dm, rowIndex, isUpdate){
587         
588         if(!isUpdate){
589             this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
590         }
591             //var s = this.getScrollState();
592         var row = this.renderRow(this.cm, this.store, rowIndex);
593         // insert before rowIndex..
594         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
595         Roo.log(e);
596             
597         if(!isUpdate){
598             this.fireEvent("rowsinserted", this, firstRow, lastRow);
599             //this.syncRowHeights(firstRow, lastRow);
600             //this.stripeRows(firstRow);
601             //this.layout();
602         }
603         
604     },
605     
606     
607     getRowDom : function(rowIndex)
608     {
609         // not sure if I need to check this.. but let's do it anyway..
610         return (this.mainBody.dom.rows && (rowIndex-1) < this.mainBody.dom.rows.length ) ?
611                 this.mainBody.dom.rows[rowIndex] : false
612     },
613     // returns the object tree for a tr..
614   
615     
616     renderRow : function(cm, ds, rowIndex) {
617         
618         var d = ds.getAt(rowIndex);
619         
620         var row = {
621             tag : 'tr',
622             cn : []
623         };
624             
625         
626         
627         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
628             var config = cm.config[i];
629             
630             var renderer = cm.getRenderer(i);
631             var value = '';
632             var id = Roo.id();
633             
634             if(typeof(renderer) !== 'undefined'){
635                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
636             }
637             
638             if(typeof(value) === 'object'){
639                 renders.push({
640                     container : id,
641                     cfg : value 
642                 })
643             }
644             
645             var rowcfg = {
646                 record: d,
647                 rowIndex : rowIndex,
648                 colIndex : i,
649                 rowClass : ''
650             }
651
652             this.fireEvent('rowclass', this, rowcfg);
653             
654             var td = {
655                 tag: 'td',
656                 id: id,
657                 cls : rowcfg.rowClass,
658                 style: '',
659                 html: (typeof(value) === 'object') ? '' : value
660             };
661             
662             if(typeof(config.hidden) != 'undefined' && config.hidden){
663                 td.style += ' display:none;';
664             }
665             
666             if(typeof(config.align) != 'undefined' && config.align.length){
667                 td.style += ' text-align:' + config.align + ';';
668             }
669             
670             if(typeof(config.width) != 'undefined'){
671                 td.style += ' width:' +  config.width + 'px;';
672             }
673              
674             row.cn.push(td);
675            
676         }
677         return row;
678           
679     },
680     
681     
682     
683     onBeforeLoad : function()
684     {
685         //Roo.log('ds onBeforeLoad');
686         
687         //this.clear();
688         
689         //if(this.loadMask){
690         //    this.maskEl.show();
691         //}
692     },
693     
694     clear : function()
695     {
696         this.el.select('tbody', true).first().dom.innerHTML = '';
697     },
698     
699     getSelectionModel : function(){
700         if(!this.selModel){
701             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
702         }
703         return this.selModel;
704     },
705     
706     renderColumn : function(r)
707     {
708         var _this = this;
709         
710         var t = r.cfg.render(r.container);
711         
712         if(r.cfg.cn){
713             Roo.each(r.cfg.cn, function(c){
714                 var child = {
715                     container: t.getChildContainer(),
716                     cfg: c
717                 }
718                 _this.renderColumn(child);
719             })
720         }
721     }
722    
723 });
724
725  
726
727