2f01086faf9666c942e0e6d5dde16e12eefbceb6
[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 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159     /**
160      * This is really a wrapper for addxtypeChild
161      * it handles stuff relating to flexy:foreach / flexy:if
162      * = some of our projects use a flat rendering of the output, and try and overlay it with dynamic data.
163      *  -- this is a bit of a nightmare... and is even more confusing to debug..
164      *
165      *  
166      *
167      */
168     addxtype  : function(tree,cntr)
169     {
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         
172         var cn = Roo.factory(tree); // this is posibly the first of two times that the ctor get's called...
173         cn.parentType = this.xtype; //??
174         cn.parentId = this.id;
175         if (typeof(cn.container_method) == 'string') {
176             cntr = cn.container_method;
177         }
178         
179         
180         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
181         
182         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
183         
184         var build_from_html =  Roo.XComponent.build_from_html;
185           
186         var is_body  = (tree.xtype == 'Body') ;
187           
188         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
189           
190         var self_cntr_el = Roo.get(this[cntr](false));
191         
192         // do not try and build conditional elements 
193         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
194             return false;
195         }
196         
197         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
198             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
199                 //return this.addxtypeChild(tree,cntr, is_body);
200                 return this.addxtypeChild(tree, cntr, is_body);
201             }
202             
203             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
204                 
205             if(echild){
206                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
207             }
208             
209             Roo.log('skipping render');
210             return cn;
211             
212         }
213         
214         var ret = false;
215         if (!build_from_html) {
216             return false;
217         }
218         
219         // this i think handles overlaying multiple children of the same type
220         // with the sam eelement.. - which might be buggy..
221         while (true) {
222             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
223             
224             if (!echild) {
225                 break;
226             }
227             
228             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
229                 break;
230             }
231             
232             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
233         }
234        
235         return ret;
236     },
237     /**
238      * add a child to this element
239      *   - turn the child.cfg into a child_instance
240      *   - call child_instance.render( this { getContainerMethod()} )
241      *   - loop through the children, and call addxtype.. (reall this) on newly created child.
242      *  
243      */
244     
245     addxtypeChild : function (tree, cntr, is_body)
246     {
247         Roo.debug && Roo.log('addxtypeChild:' + cntr);
248         var cn = this;
249         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
250         
251         
252         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
253                     (typeof(tree['flexy:foreach']) != 'undefined');
254           
255     
256         
257         skip_children = false;
258         // render the element if it's not BODY.
259         if (!is_body) {
260             
261             // if parent was disabled, then do not try and create the children..
262             if(!this[cntr](true)){
263                 tree.items = [];
264                 return tree;
265             }
266            
267             cn = Roo.factory(tree);
268            
269             cn.parentType = this.xtype; //??
270             cn.parentId = this.id;
271             
272             var build_from_html =  Roo.XComponent.build_from_html;
273             
274             
275             // does the container contain child eleemnts with 'xtype' attributes.
276             // that match this xtype..
277             // note - when we render we create these as well..
278             // so we should check to see if body has xtype set.
279             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
280                
281                 var self_cntr_el = Roo.get(this[cntr](false));
282                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
283                 if (echild) { 
284                     //Roo.log(Roo.XComponent.build_from_html);
285                     //Roo.log("got echild:");
286                     //Roo.log(echild);
287                 }
288                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
289                 // and are not displayed -this causes this to use up the wrong element when matching.
290                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
291                 
292                 
293                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
294                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
295                   
296                   
297                   
298                     cn.el = echild;
299                   //  Roo.log("GOT");
300                     //echild.dom.removeAttribute('xtype');
301                 } else {
302                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
303                     Roo.debug && Roo.log(self_cntr_el);
304                     Roo.debug && Roo.log(echild);
305                     Roo.debug && Roo.log(cn);
306                 }
307             }
308            
309             
310            
311             // if object has flexy:if - then it may or may not be rendered.
312             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
313                 // skip a flexy if element.
314                 Roo.debug && Roo.log('skipping render');
315                 Roo.debug && Roo.log(tree);
316                 if (!cn.el) {
317                     Roo.debug && Roo.log('skipping all children');
318                     skip_children = true;
319                 }
320                 
321              } else {
322                  
323                 // actually if flexy:foreach is found, we really want to create 
324                 // multiple copies here...
325                 //Roo.log('render');
326                 //Roo.log(this[cntr]());
327                 // some elements do not have render methods.. like the layouts...
328                 /*
329                 if(this[cntr](true) === false){
330                     cn.items = [];
331                     return cn;
332                 }
333                 */
334                 cn.render && cn.render(this[cntr](true));
335                 
336              }
337             // then add the element..
338         }
339         
340         
341         
342         
343         
344         cn.addxtypeChildren(tree.items, skip_children);
345         delete tree.items;
346         return cn;
347     },
348     
349     /**
350      *  add a number of children to this object,
351      *     which in turn calls render...
352      *  
353      */
354     
355     addxtypeChildren: function(child_array, skip_children)
356     {
357         var nitems = [];
358         if (!child_array || !child_array.length ) {
359             this.items = nitems;
360             return;
361         }
362         
363         for(var i =0;i < child_array.length;i++) {
364             if (skip_children) {
365                 break;
366             }
367             //  Roo.log(['add child', items[i]]);
368             nitems.push(this.addxtype(Roo.apply({}, child_array[i])));
369         }
370         this.items = nitems;
371         
372         this.fireEvent('childrenrendered', this);
373          
374     },
375     
376     
377     
378     
379     
380     
381     /**
382      * xAddChildren - the 'sub-compentized' version of the above idea..
383      */
384     xAddChildren: function(child_array, skip_children)
385     {
386         var nitems = [];
387         if (!child_array || !child_array.length ) {
388             this.items = nitems;
389             return;
390         }
391         
392         for(var i =0;i < child_array.length;i++) {
393             if (skip_children) {
394                 break;
395             }
396             //  Roo.log(['add child', items[i]]);
397             nitems.push(this.xAdd(Roo.apply({}, child_array[i])));
398         }
399         this.items = nitems;
400         
401         this.fireEvent('childrenrendered', this);
402          
403     },
404     
405     
406     /**
407      * add a child to this element
408      *   - turn the child.cfg into a child_instance
409      *   - call child_instance.render( this { getContainerMethod()} )
410      *   - loop through the children, and call addxtype.. (reall this) on newly created child.
411      * xAdd differs from addxtype - as it uses the 'items' returned from 
412      *
413      * 
414      */
415     
416     xAdd : function (tree, cntr, is_body)
417     {
418         Roo.debug && Roo.log('xadd:' + cntr);
419         
420         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
421          
422         var parent_ctnr = this[cntr](true);
423         if(parent_ctnr === false){
424             return false; // getChildContainer an return false explicitly to block children being added?
425         }
426         if(!parent_ctnr ){
427             throw new Exception("could not find parent Container for item");
428         }
429            
430         var cn = Roo.factory(tree);
431         // at this point items[] array may be set..
432         // constructors should not really be building their children?
433         cn.parentType = this.xtype; //??
434         cn.parentId = this.id;
435             
436             
437         cn.render && cn.render(parent_ctnr);
438                 
439         cn.xAddChildren(tree.items);
440         delete tree.items; // not really needed?
441         return cn;
442     },
443     
444     
445     /**
446      * Set the element that will be used to show or hide
447      */
448     setVisibilityEl : function(el)
449     {
450         this.visibilityEl = el;
451     },
452     
453      /**
454      * Get the element that will be used to show or hide
455      */
456     getVisibilityEl : function()
457     {
458         if (typeof(this.visibilityEl) == 'object') {
459             return this.visibilityEl;
460         }
461         
462         if (typeof(this.visibilityEl) == 'string') {
463             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
464         }
465         
466         return this.getEl();
467     },
468     
469     /**
470      * Show a component - removes 'hidden' class
471      */
472     show : function()
473     {
474         if(!this.getVisibilityEl()){
475             return;
476         }
477          
478         this.getVisibilityEl().removeClass(['hidden','d-none']);
479         
480         this.fireEvent('show', this);
481         
482         
483     },
484     /**
485      * Hide a component - adds 'hidden' class
486      */
487     hide: function()
488     {
489         if(!this.getVisibilityEl()){
490             return;
491         }
492         
493         this.getVisibilityEl().addClass(['hidden','d-none']);
494         
495         this.fireEvent('hide', this);
496         
497     }
498 });
499
500