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