Roo/XComponent.js
[roojs1] / Roo / XComponent.js
1 /*
2  * Original code for Roojs - LGPL
3  * <script type="text/javascript">
4  */
5  
6 /**
7  * @class Roo.XComponent
8  * A delayed Element creator...
9  * Or a way to group chunks of interface together.
10  * 
11  * Mypart.xyx = new Roo.XComponent({
12
13     parent : 'Mypart.xyz', // empty == document.element.!!
14     order : '001',
15     name : 'xxxx'
16     region : 'xxxx'
17     disabled : function() {} 
18      
19     tree : function() { // return an tree of xtype declared components
20         var MODULE = this;
21         return 
22         {
23             xtype : 'NestedLayoutPanel',
24             // technicall
25         }
26      ]
27  *})
28  *
29  *
30  * It can be used to build a big heiracy, with parent etc.
31  * or you can just use this to render a single compoent to a dom element
32  * MYPART.render(Roo.Element | String(id) | dom_element )
33  * 
34  * @extends Roo.util.Observable
35  * @constructor
36  * @param cfg {Object} configuration of component
37  * 
38  */
39 Roo.XComponent = function(cfg) {
40     Roo.apply(this, cfg);
41     this.addEvents({ 
42         /**
43              * @event built
44              * Fires when this the componnt is built
45              * @param {Roo.XComponent} c the component
46              */
47         'built' : true
48         
49     });
50     this.region = this.region || 'center'; // default..
51     Roo.XComponent.register(this);
52     this.modules = false;
53     this.el = false; // where the layout goes..
54     
55     
56 }
57 Roo.extend(Roo.XComponent, Roo.util.Observable, {
58     /**
59      * @property el
60      * The created element (with Roo.factory())
61      * @type {Roo.Layout}
62      */
63     el  : false,
64     
65     /**
66      * @property el
67      * for BC  - use el in new code
68      * @type {Roo.Layout}
69      */
70     panel : false,
71     
72     /**
73      * @property layout
74      * for BC  - use el in new code
75      * @type {Roo.Layout}
76      */
77     layout : false,
78     
79      /**
80      * @cfg {Function|boolean} disabled
81      * If this module is disabled by some rule, return true from the funtion
82      */
83     disabled : false,
84     
85     /**
86      * @cfg {String} parent 
87      * Name of parent element which it get xtype added to..
88      */
89     parent: false,
90     
91     /**
92      * @cfg {String} order
93      * Used to set the order in which elements are created (usefull for multiple tabs)
94      */
95     
96     order : false,
97     /**
98      * @cfg {String} name
99      * String to display while loading.
100      */
101     name : false,
102     /**
103      * @cfg {String} region
104      * Region to render component to (defaults to center)
105      */
106     region : 'center',
107     
108     /**
109      * @cfg {Array} items
110      * A single item array - the first element is the root of the tree..
111      * It's done this way to stay compatible with the Xtype system...
112      */
113     items : false,
114     
115     /**
116      * @property _tree
117      * The method that retuns the tree of parts that make up this compoennt 
118      * @type {function}
119      */
120     _tree  : false,
121     
122      /**
123      * render
124      * render element to dom or tree
125      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
126      */
127     
128     render : function(el)
129     {
130         
131         el = el || false;
132         var hp = this.parent ? 1 : 0;
133         
134         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
135             // if parent is a '#.....' string, then let's use that..
136             var ename = this.parent.substr(1)
137             this.parent = (this.parent == '#bootstrap') ? { el : true}  : false; // flags it as a top module...
138             el = Roo.get(ename);
139             if (!el && !this.parent) {
140                 Roo.log("Warning - element can not be found :#" + ename );
141                 return;
142             }
143         }
144         var tree = this._tree ? this._tree() : this.tree();
145
146         
147         if (!this.parent && ) {
148             
149             
150         
151         if (!this.parent) {
152             
153             Roo.log("no parent - creating one");
154             
155             el = el ? Roo.get(el) : false;      
156             
157             // it's a top level one..
158             this.parent =  {
159                 el : new Roo.BorderLayout(el || document.body, {
160                 
161                      center: {
162                          titlebar: false,
163                          autoScroll:false,
164                          closeOnTab: true,
165                          tabPosition: 'top',
166                           //resizeTabs: true,
167                          alwaysShowTabs: el && hp? false :  true,
168                          hideTabs: el || !hp ? true :  false,
169                          minTabWidth: 140
170                      }
171                  })
172             }
173         }
174         
175                 if (!this.parent.el) {
176                         // probably an old style ctor, which has been disabled.
177                         return;
178                         
179                 }
180                 // The 'tree' method is  '_tree now' 
181             
182         tree.region = tree.region || this.region;
183         
184         if (this.parent.el === true) {
185             // bootstrap... - body..
186             this.parent.el = Roo.factory(tree);
187         }
188         
189         this.el = this.parent.el.addxtype(tree);
190         this.fireEvent('built', this);
191         
192         this.panel = this.el;
193         this.layout = this.panel.layout;
194                 this.parentLayout = this.parent.layout  || false;  
195          
196     }
197     
198 });
199
200 Roo.apply(Roo.XComponent, {
201     /**
202      * @property  hideProgress
203      * true to disable the building progress bar.. usefull on single page renders.
204      * @type Boolean
205      */
206     hideProgress : false,
207     /**
208      * @property  buildCompleted
209      * True when the builder has completed building the interface.
210      * @type Boolean
211      */
212     buildCompleted : false,
213      
214     /**
215      * @property  topModule
216      * the upper most module - uses document.element as it's constructor.
217      * @type Object
218      */
219      
220     topModule  : false,
221       
222     /**
223      * @property  modules
224      * array of modules to be created by registration system.
225      * @type {Array} of Roo.XComponent
226      */
227     
228     modules : [],
229     /**
230      * @property  elmodules
231      * array of modules to be created by which use #ID 
232      * @type {Array} of Roo.XComponent
233      */
234      
235     elmodules : [],
236
237      /**
238      * @property  build_from_html
239      * Build elements from html - used by bootstrap HTML stuff 
240      *    - this is cleared after build is completed
241      * @type {boolean} true  (default false)
242      */
243      
244     build_from_html : false,
245
246     /**
247      * Register components to be built later.
248      *
249      * This solves the following issues
250      * - Building is not done on page load, but after an authentication process has occured.
251      * - Interface elements are registered on page load
252      * - Parent Interface elements may not be loaded before child, so this handles that..
253      * 
254      *
255      * example:
256      * 
257      * MyApp.register({
258           order : '000001',
259           module : 'Pman.Tab.projectMgr',
260           region : 'center',
261           parent : 'Pman.layout',
262           disabled : false,  // or use a function..
263         })
264      
265      * * @param {Object} details about module
266      */
267     register : function(obj) {
268                 
269         Roo.XComponent.event.fireEvent('register', obj);
270         switch(typeof(obj.disabled) ) {
271                 
272             case 'undefined':
273                 break;
274             
275             case 'function':
276                 if ( obj.disabled() ) {
277                         return;
278                 }
279                 break;
280             
281             default:
282                 if (obj.disabled) {
283                         return;
284                 }
285                 break;
286         }
287                 
288         this.modules.push(obj);
289          
290     },
291     /**
292      * convert a string to an object..
293      * eg. 'AAA.BBB' -> finds AAA.BBB
294
295      */
296     
297     toObject : function(str)
298     {
299         if (!str || typeof(str) == 'object') {
300             return str;
301         }
302         if (str.substring(0,1) == '#') {
303             return str;
304         }
305
306         var ar = str.split('.');
307         var rt, o;
308         rt = ar.shift();
309             /** eval:var:o */
310         try {
311             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
312         } catch (e) {
313             throw "Module not found : " + str;
314         }
315         
316         if (o === false) {
317             throw "Module not found : " + str;
318         }
319         Roo.each(ar, function(e) {
320             if (typeof(o[e]) == 'undefined') {
321                 throw "Module not found : " + str;
322             }
323             o = o[e];
324         });
325         
326         return o;
327         
328     },
329     
330     
331     /**
332      * move modules into their correct place in the tree..
333      * 
334      */
335     preBuild : function ()
336     {
337         var _t = this;
338         Roo.each(this.modules , function (obj)
339         {
340             Roo.XComponent.event.fireEvent('beforebuild', obj);
341             
342             var opar = obj.parent;
343             try { 
344                 obj.parent = this.toObject(opar);
345             } catch(e) {
346                 Roo.log("parent:toObject failed: " + e.toString());
347                 return;
348             }
349             
350             if (!obj.parent) {
351                 Roo.debug && Roo.log("GOT top level module");
352                 Roo.debug && Roo.log(obj);
353                 obj.modules = new Roo.util.MixedCollection(false, 
354                     function(o) { return o.order + '' }
355                 );
356                 this.topModule = obj;
357                 return;
358             }
359                         // parent is a string (usually a dom element name..)
360             if (typeof(obj.parent) == 'string') {
361                 this.elmodules.push(obj);
362                 return;
363             }
364             if (obj.parent.constructor != Roo.XComponent) {
365                 Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
366             }
367             if (!obj.parent.modules) {
368                 obj.parent.modules = new Roo.util.MixedCollection(false, 
369                     function(o) { return o.order + '' }
370                 );
371             }
372             if (obj.parent.disabled) {
373                 obj.disabled = true;
374             }
375             obj.parent.modules.add(obj);
376         }, this);
377     },
378     
379      /**
380      * make a list of modules to build.
381      * @return {Array} list of modules. 
382      */ 
383     
384     buildOrder : function()
385     {
386         var _this = this;
387         var cmp = function(a,b) {   
388             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
389         };
390         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
391             throw "No top level modules to build";
392         }
393         
394         // make a flat list in order of modules to build.
395         var mods = this.topModule ? [ this.topModule ] : [];
396                 
397         
398         // elmodules (is a list of DOM based modules )
399         Roo.each(this.elmodules, function(e) {
400             mods.push(e);
401             if (!this.topModule &&
402                 typeof(e.parent) == 'string' &&
403                 e.parent.substring(0,1) == '#' &&
404                 Roo.get(e.parent.substr(1))
405                ) {
406                 
407                 _this.topModule = e;
408             }
409             
410         });
411
412         
413         // add modules to their parents..
414         var addMod = function(m) {
415             Roo.debug && Roo.log("build Order: add: " + m.name);
416                 
417             mods.push(m);
418             if (m.modules && !m.disabled) {
419                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
420                 m.modules.keySort('ASC',  cmp );
421                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
422     
423                 m.modules.each(addMod);
424             } else {
425                 Roo.debug && Roo.log("build Order: no child modules");
426             }
427             // not sure if this is used any more..
428             if (m.finalize) {
429                 m.finalize.name = m.name + " (clean up) ";
430                 mods.push(m.finalize);
431             }
432             
433         }
434         if (this.topModule && this.topModule.modules) { 
435             this.topModule.modules.keySort('ASC',  cmp );
436             this.topModule.modules.each(addMod);
437         } 
438         return mods;
439     },
440     
441      /**
442      * Build the registered modules.
443      * @param {Object} parent element.
444      * @param {Function} optional method to call after module has been added.
445      * 
446      */ 
447    
448     build : function(opts) 
449     {
450         
451         if (typeof(opts) != 'undefined') {
452             Roo.apply(this,opts);
453         }
454         
455         this.preBuild();
456         var mods = this.buildOrder();
457       
458         //this.allmods = mods;
459         //Roo.debug && Roo.log(mods);
460         //return;
461         if (!mods.length) { // should not happen
462             throw "NO modules!!!";
463         }
464         
465         
466         var msg = "Building Interface...";
467         // flash it up as modal - so we store the mask!?
468         if (!this.hideProgress && Roo.MessageBox) {
469             Roo.MessageBox.show({ title: 'loading' });
470             Roo.MessageBox.show({
471                title: "Please wait...",
472                msg: msg,
473                width:450,
474                progress:true,
475                closable:false,
476                modal: false
477               
478             });
479         }
480         var total = mods.length;
481         
482         var _this = this;
483         var progressRun = function() {
484             if (!mods.length) {
485                 Roo.debug && Roo.log('hide?');
486                 if (!this.hideProgress && Roo.MessageBox) {
487                     Roo.MessageBox.hide();
488                 }
489                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
490                 
491                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
492                 
493                 // THE END...
494                 return false;   
495             }
496             
497             var m = mods.shift();
498             
499             
500             Roo.debug && Roo.log(m);
501             // not sure if this is supported any more.. - modules that are are just function
502             if (typeof(m) == 'function') { 
503                 m.call(this);
504                 return progressRun.defer(10, _this);
505             } 
506             
507             
508             msg = "Building Interface " + (total  - mods.length) + 
509                     " of " + total + 
510                     (m.name ? (' - ' + m.name) : '');
511                         Roo.debug && Roo.log(msg);
512             if (!this.hideProgress &&  Roo.MessageBox) { 
513                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
514             }
515             
516          
517             // is the module disabled?
518             var disabled = (typeof(m.disabled) == 'function') ?
519                 m.disabled.call(m.module.disabled) : m.disabled;    
520             
521             
522             if (disabled) {
523                 return progressRun(); // we do not update the display!
524             }
525             
526             // now build 
527             
528                         
529                         
530             m.render();
531             // it's 10 on top level, and 1 on others??? why...
532             return progressRun.defer(10, _this);
533              
534         }
535         progressRun.defer(1, _this);
536      
537         
538         
539     },
540         
541         
542         /**
543          * Event Object.
544          *
545          *
546          */
547         event: false, 
548     /**
549          * wrapper for event.on - aliased later..  
550          * Typically use to register a event handler for register:
551          *
552          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
553          *
554          */
555     on : false
556    
557     
558     
559 });
560
561 Roo.XComponent.event = new Roo.util.Observable({
562                 events : { 
563                         /**
564                          * @event register
565                          * Fires when an Component is registered,
566                          * set the disable property on the Component to stop registration.
567                          * @param {Roo.XComponent} c the component being registerd.
568                          * 
569                          */
570                         'register' : true,
571             /**
572                          * @event beforebuild
573                          * Fires before each Component is built
574                          * can be used to apply permissions.
575                          * @param {Roo.XComponent} c the component being registerd.
576                          * 
577                          */
578                         'beforebuild' : true,
579                         /**
580                          * @event buildcomplete
581                          * Fires on the top level element when all elements have been built
582                          * @param {Roo.XComponent} the top level component.
583                          */
584                         'buildcomplete' : true
585                         
586                 }
587 });
588
589 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
590