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