Roo/bootstrap/Component.js
[roojs1] / Roo / bootstrap / Component.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * @abstract
13  * @children Roo.bootstrap.Component
14  * Bootstrap Component base class
15  * @cfg {String} cls css class
16  * @cfg {String} style any extra css
17  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
18  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
19  * @cfg {string} dataId cutomer id
20  * @cfg {string} name Specifies name attribute
21  * @cfg {string} tooltip  Text for the tooltip
22  * @cfg {string} tooltipClass Class for the tooltip element
23  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
24  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
25  
26  * @constructor
27  * Do not use directly - it does not do anything..
28  * @param {Object} config The config object
29  */
30
31
32
33 Roo.bootstrap.Component = function(config){
34     Roo.bootstrap.Component.superclass.constructor.call(this, config);
35        
36     this.addEvents({
37         /**
38          * @event childrenrendered
39          * Fires when the children have been rendered..
40          * @param {Roo.bootstrap.Component} this
41          */
42         "childrenrendered" : true
43         
44         
45         
46     });
47     
48     
49 };
50
51 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
52     
53     
54     allowDomMove : false, // to stop relocations in parent onRender...
55     
56     cls : false,
57     
58     style : false,
59     
60     autoCreate : false,
61     
62     tooltip : null,
63
64     tooltipClass : null,
65     /**
66      * Initialize Events for the element
67      */
68     initEvents : function() { },
69     
70     xattr : false,
71     
72     parentId : false,
73     
74     can_build_overlaid : true,
75     
76     container_method : false,
77     
78     dataId : false,
79     
80     name : false,
81     
82     parent: function() {
83         // returns the parent component..
84         return Roo.ComponentMgr.get(this.parentId)
85         
86         
87     },
88     
89     // private
90     onRender : function(ct, position)
91     {
92        // Roo.log("Call onRender: " + this.xtype);
93         
94         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
95         
96         if(this.el){
97             if (this.el.attr('xtype')) {
98                 this.el.attr('xtypex', this.el.attr('xtype'));
99                 this.el.dom.removeAttribute('xtype');
100                 
101                 this.initEvents();
102             }
103             
104             return;
105         }
106         
107          
108         
109         var cfg = Roo.apply({},  this.getAutoCreate());
110         
111         cfg.id = this.id || Roo.id();
112         
113         // fill in the extra attributes 
114         if (this.xattr && typeof(this.xattr) =='object') {
115             for (var i in this.xattr) {
116                 cfg[i] = this.xattr[i];
117             }
118         }
119         
120         if(this.dataId){
121             cfg.dataId = this.dataId;
122         }
123         
124         if (this.cls) {
125             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
126         }
127         
128         if (this.style) { // fixme needs to support more complex style data.
129             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
130         }
131         
132         if(this.name){
133             cfg.name = this.name;
134         }
135         
136         this.el = ct.createChild(cfg, position);
137         
138         if (this.tooltip) {
139             this.tooltipEl().attr('tooltip', this.tooltip);
140         }
141
142         if (this.tooltipClass) {
143             this.tooltipEl().attr('tooltip-class', this.tooltipClass);
144         }
145         
146         if(this.tabIndex !== undefined){
147             this.el.dom.setAttribute('tabIndex', this.tabIndex);
148         }
149         
150         this.initEvents();
151         
152     },
153     /**
154      * Fetch the element to add children to
155      * @return {Roo.Element} defaults to this.el
156      */
157     getChildContainer : function()
158     {
159         return this.el;
160     },
161     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
162     {
163         return Roo.get(document.body);
164     },
165     
166     /**
167      * Fetch the element to display the tooltip on.
168      * @return {Roo.Element} defaults to this.el
169      */
170     tooltipEl : function()
171     {
172         return this.el;
173     },
174         
175     addxtype  : function(tree,cntr)
176     {
177         var cn = this;
178         
179         cn = Roo.factory(tree);
180         //Roo.log(['addxtype', cn]);
181            
182         cn.parentType = this.xtype; //??
183         cn.parentId = this.id;
184         
185         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
186         if (typeof(cn.container_method) == 'string') {
187             cntr = cn.container_method;
188         }
189         
190         
191         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
192         
193         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
194         
195         var build_from_html =  Roo.XComponent.build_from_html;
196           
197         var is_body  = (tree.xtype == 'Body') ;
198           
199         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
200           
201         var self_cntr_el = Roo.get(this[cntr](false));
202         
203         // do not try and build conditional elements 
204         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
205             return false;
206         }
207         
208         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
209             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
210                 return this.addxtypeChild(tree,cntr, is_body);
211             }
212             
213             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
214                 
215             if(echild){
216                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
217             }
218             
219             Roo.log('skipping render');
220             return cn;
221             
222         }
223         
224         var ret = false;
225         if (!build_from_html) {
226             return false;
227         }
228         
229         // this i think handles overlaying multiple children of the same type
230         // with the sam eelement.. - which might be buggy..
231         while (true) {
232             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
233             
234             if (!echild) {
235                 break;
236             }
237             
238             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
239                 break;
240             }
241             
242             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
243         }
244        
245         return ret;
246     },
247     
248     
249     addxtypeChild : function (tree, cntr, is_body)
250     {
251         Roo.debug && Roo.log('addxtypeChild:' + cntr);
252         var cn = this;
253         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
254         
255         
256         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
257                     (typeof(tree['flexy:foreach']) != 'undefined');
258           
259     
260         
261         skip_children = false;
262         // render the element if it's not BODY.
263         if (!is_body) {
264             
265             // if parent was disabled, then do not try and create the children..
266             if(!this[cntr](true)){
267                 tree.items = [];
268                 return tree;
269             }
270            
271             cn = Roo.factory(tree);
272            
273             cn.parentType = this.xtype; //??
274             cn.parentId = this.id;
275             
276             var build_from_html =  Roo.XComponent.build_from_html;
277             
278             
279             // does the container contain child eleemnts with 'xtype' attributes.
280             // that match this xtype..
281             // note - when we render we create these as well..
282             // so we should check to see if body has xtype set.
283             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
284                
285                 var self_cntr_el = Roo.get(this[cntr](false));
286                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
287                 if (echild) { 
288                     //Roo.log(Roo.XComponent.build_from_html);
289                     //Roo.log("got echild:");
290                     //Roo.log(echild);
291                 }
292                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
293                 // and are not displayed -this causes this to use up the wrong element when matching.
294                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
295                 
296                 
297                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
298                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
299                   
300                   
301                   
302                     cn.el = echild;
303                   //  Roo.log("GOT");
304                     //echild.dom.removeAttribute('xtype');
305                 } else {
306                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
307                     Roo.debug && Roo.log(self_cntr_el);
308                     Roo.debug && Roo.log(echild);
309                     Roo.debug && Roo.log(cn);
310                 }
311             }
312            
313             
314            
315             // if object has flexy:if - then it may or may not be rendered.
316             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
317                 // skip a flexy if element.
318                 Roo.debug && Roo.log('skipping render');
319                 Roo.debug && Roo.log(tree);
320                 if (!cn.el) {
321                     Roo.debug && Roo.log('skipping all children');
322                     skip_children = true;
323                 }
324                 
325              } else {
326                  
327                 // actually if flexy:foreach is found, we really want to create 
328                 // multiple copies here...
329                 //Roo.log('render');
330                 //Roo.log(this[cntr]());
331                 // some elements do not have render methods.. like the layouts...
332                 /*
333                 if(this[cntr](true) === false){
334                     cn.items = [];
335                     return cn;
336                 }
337                 */
338                 cn.render && cn.render(this[cntr](true));
339                 
340              }
341             // then add the element..
342         }
343          
344         // handle the kids..
345         
346         var nitems = [];
347         /*
348         if (typeof (tree.menu) != 'undefined') {
349             tree.menu.parentType = cn.xtype;
350             tree.menu.triggerEl = cn.el;
351             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
352             
353         }
354         */
355         if (!tree.items || !tree.items.length) {
356             cn.items = nitems;
357             //Roo.log(["no children", this]);
358             
359             return cn;
360         }
361          
362         var items = tree.items;
363         delete tree.items;
364         
365         //Roo.log(items.length);
366             // add the items..
367         if (!skip_children) {    
368             for(var i =0;i < items.length;i++) {
369               //  Roo.log(['add child', items[i]]);
370                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
371             }
372         }
373         
374         cn.items = nitems;
375         
376         //Roo.log("fire childrenrendered");
377         
378         cn.fireEvent('childrenrendered', this);
379         
380         return cn;
381     },
382     
383     /**
384      * Set the element that will be used to show or hide
385      */
386     setVisibilityEl : function(el)
387     {
388         this.visibilityEl = el;
389     },
390     
391      /**
392      * Get the element that will be used to show or hide
393      */
394     getVisibilityEl : function()
395     {
396         if (typeof(this.visibilityEl) == 'object') {
397             return this.visibilityEl;
398         }
399         
400         if (typeof(this.visibilityEl) == 'string') {
401             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
402         }
403         
404         return this.getEl();
405     },
406     
407     /**
408      * Show a component - removes 'hidden' class
409      */
410     show : function()
411     {
412         if(!this.getVisibilityEl()){
413             return;
414         }
415          
416         this.getVisibilityEl().removeClass(['hidden','d-none']);
417         
418         this.fireEvent('show', this);
419         
420         
421     },
422     /**
423      * Hide a component - adds 'hidden' class
424      */
425     hide: function()
426     {
427         if(!this.getVisibilityEl()){
428             return;
429         }
430         
431         this.getVisibilityEl().addClass(['hidden','d-none']);
432         
433         this.fireEvent('hide', this);
434         
435     }
436 });
437
438